Merge branch 'main' into desktopApp
11
.github/workflows/android-build.yml
vendored
@ -6,6 +6,15 @@ on:
|
||||
semver:
|
||||
required: true
|
||||
type: string
|
||||
secrets:
|
||||
KEYSTORE:
|
||||
required: true
|
||||
KEY_ALIAS:
|
||||
required: true
|
||||
KEY_PASS:
|
||||
required: true
|
||||
KEY_STORE_PASS:
|
||||
required: true
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
paths: [ "mobile", "frontend" ]
|
||||
@ -23,7 +32,7 @@ jobs:
|
||||
|
||||
- name: 'Download Android Web.bundle Artifact (built frontend)'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
|
4
.github/workflows/coordinator-image.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
- name: 'Download Basic main.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
- name: 'Download pro.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
|
5
.github/workflows/integration-tests.yml
vendored
@ -22,8 +22,8 @@ jobs:
|
||||
matrix:
|
||||
python-tag: ['3.12.3-slim-bookworm', '3.13-rc-slim-bookworm']
|
||||
lnd-version: ['v0.17.4-beta']
|
||||
cln-version: ['v23.11.2'] #,'v24.02']
|
||||
ln-vendor: ['LND'] #, 'CLN']
|
||||
cln-version: ['v24.05']
|
||||
ln-vendor: ['LND', 'CLN']
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
@ -56,7 +56,6 @@ jobs:
|
||||
env:
|
||||
LND_VERSION: ${{ matrix.lnd-version }}
|
||||
CLN_VERSION: ${{ matrix.cln-version }}
|
||||
BITCOIND_VERSION: ${{ matrix.bitcoind-version }}
|
||||
ROBOSATS_ENVS_FILE: ".env-sample"
|
||||
|
||||
- name: Wait for coordinator (django server)
|
||||
|
1
.github/workflows/release.yml
vendored
@ -71,6 +71,7 @@ jobs:
|
||||
android-build:
|
||||
uses: RoboSats/robosats/.github/workflows/android-build.yml@main
|
||||
needs: [frontend-build, check-versions]
|
||||
secrets: inherit
|
||||
with:
|
||||
semver: ${{ needs.check-versions.outputs.semver }}
|
||||
|
||||
|
@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: 'Download basic.selfhosted.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: 'Download pro.selfhosted.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
|
4
.github/workflows/web-client-image.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: 'Download main.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: 'Download pro.js Artifact'
|
||||
if: inputs.semver == '' # Only if workflow fired from frontend-build.yml
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
with:
|
||||
workflow: frontend-build.yml
|
||||
workflow_conclusion: success
|
||||
|
17
api/admin.py
@ -21,17 +21,13 @@ admin.site.unregister(TokenProxy)
|
||||
class RobotInline(admin.StackedInline):
|
||||
model = Robot
|
||||
can_delete = False
|
||||
fields = ("avatar_tag",)
|
||||
readonly_fields = ["avatar_tag"]
|
||||
show_change_link = True
|
||||
|
||||
|
||||
# extended users with avatars
|
||||
@admin.register(User)
|
||||
class EUserAdmin(AdminChangeLinksMixin, UserAdmin):
|
||||
inlines = [RobotInline]
|
||||
list_display = (
|
||||
"avatar_tag",
|
||||
"id",
|
||||
"robot_link",
|
||||
"username",
|
||||
@ -43,25 +39,18 @@ class EUserAdmin(AdminChangeLinksMixin, UserAdmin):
|
||||
change_links = ("robot",)
|
||||
ordering = ("-id",)
|
||||
|
||||
def avatar_tag(self, obj):
|
||||
return obj.robot.avatar_tag()
|
||||
|
||||
|
||||
# extended tokens with raw id fields and avatars
|
||||
# extended tokens with raw id fields
|
||||
@admin.register(TokenProxy)
|
||||
class ETokenAdmin(AdminChangeLinksMixin, TokenAdmin):
|
||||
raw_id_fields = ["user"]
|
||||
list_display = (
|
||||
"avatar_tag",
|
||||
"key",
|
||||
"user_link",
|
||||
)
|
||||
list_display_links = ("key",)
|
||||
change_links = ("user",)
|
||||
|
||||
def avatar_tag(self, obj):
|
||||
return obj.user.robot.avatar_tag()
|
||||
|
||||
|
||||
class LNPaymentInline(admin.StackedInline):
|
||||
model = LNPayment
|
||||
@ -510,7 +499,6 @@ class OnchainPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
@admin.register(Robot)
|
||||
class UserRobotAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
list_display = (
|
||||
"avatar_tag",
|
||||
"id",
|
||||
"user_link",
|
||||
"telegram_enabled",
|
||||
@ -523,9 +511,8 @@ class UserRobotAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||
)
|
||||
raw_id_fields = ("user",)
|
||||
list_editable = ["earned_rewards"]
|
||||
list_display_links = ("avatar_tag", "id")
|
||||
list_display_links = ["id"]
|
||||
change_links = ["user"]
|
||||
readonly_fields = ["avatar_tag"]
|
||||
search_fields = ["user__username", "id"]
|
||||
readonly_fields = ("hash_id", "public_key", "encrypted_private_key")
|
||||
|
||||
|
@ -90,9 +90,9 @@ class CLNNode:
|
||||
@classmethod
|
||||
def decode_payreq(cls, invoice):
|
||||
"""Decodes a lightning payment request (invoice)"""
|
||||
request = hold_pb2.DecodeBolt11Request(bolt11=invoice)
|
||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
||||
response = holdstub.DecodeBolt11(request)
|
||||
nodestub = node_pb2_grpc.NodeStub(cls.node_channel)
|
||||
request = node_pb2.DecodeRequest(string=invoice)
|
||||
response = nodestub.Decode(request)
|
||||
return response
|
||||
|
||||
@classmethod
|
||||
@ -236,7 +236,7 @@ class CLNNode:
|
||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
||||
response = holdstub.HoldInvoiceCancel(request)
|
||||
|
||||
return response.state == hold_pb2.HoldInvoiceCancelResponse.Holdstate.CANCELED
|
||||
return response.state == hold_pb2.Holdstate.CANCELED
|
||||
|
||||
@classmethod
|
||||
def settle_hold_invoice(cls, preimage):
|
||||
@ -247,7 +247,7 @@ class CLNNode:
|
||||
holdstub = hold_pb2_grpc.HoldStub(cls.hold_channel)
|
||||
response = holdstub.HoldInvoiceSettle(request)
|
||||
|
||||
return response.state == hold_pb2.HoldInvoiceSettleResponse.Holdstate.SETTLED
|
||||
return response.state == hold_pb2.Holdstate.SETTLED
|
||||
|
||||
@classmethod
|
||||
def gen_hold_invoice(
|
||||
@ -272,7 +272,7 @@ class CLNNode:
|
||||
|
||||
request = hold_pb2.HoldInvoiceRequest(
|
||||
description=description,
|
||||
amount_msat=primitives__pb2.Amount(msat=num_satoshis * 1_000),
|
||||
amount_msat=hold_pb2.Amount(msat=num_satoshis * 1_000),
|
||||
label=f"Order:{order_id}-{lnpayment_concept}-{time}",
|
||||
expiry=invoice_expiry,
|
||||
cltv=cltv_expiry_blocks,
|
||||
@ -286,7 +286,7 @@ class CLNNode:
|
||||
hold_payment["preimage"] = preimage.hex()
|
||||
hold_payment["payment_hash"] = response.payment_hash.hex()
|
||||
hold_payment["created_at"] = timezone.make_aware(
|
||||
datetime.fromtimestamp(payreq_decoded.timestamp)
|
||||
datetime.fromtimestamp(payreq_decoded.created_at)
|
||||
)
|
||||
hold_payment["expires_at"] = timezone.make_aware(
|
||||
datetime.fromtimestamp(response.expires_at)
|
||||
@ -309,13 +309,13 @@ class CLNNode:
|
||||
# Will fail if 'unable to locate invoice'. Happens if invoice expiry
|
||||
# time has passed (but these are 15% padded at the moment). Should catch it
|
||||
# and report back that the invoice has expired (better robustness)
|
||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.OPEN:
|
||||
if response.state == hold_pb2.Holdstate.OPEN:
|
||||
pass
|
||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.SETTLED:
|
||||
if response.state == hold_pb2.Holdstate.SETTLED:
|
||||
pass
|
||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.CANCELED:
|
||||
if response.state == hold_pb2.Holdstate.CANCELED:
|
||||
pass
|
||||
if response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.ACCEPTED:
|
||||
if response.state == hold_pb2.Holdstate.ACCEPTED:
|
||||
lnpayment.expiry_height = response.htlc_expiry
|
||||
lnpayment.status = LNPayment.Status.LOCKED
|
||||
lnpayment.save(update_fields=["expiry_height", "status"])
|
||||
@ -359,7 +359,7 @@ class CLNNode:
|
||||
except Exception as e:
|
||||
# If it fails at finding the invoice: it has been expired for more than an hour (and could be paid or just expired).
|
||||
# In RoboSats DB we make a distinction between cancelled and returned
|
||||
# (cln-grpc-hodl has separate state for hodl-invoices, which it forgets after an invoice expired more than an hour ago)
|
||||
# (holdinvoice plugin has separate state for hodl-invoices, which it forgets after an invoice expired more than an hour ago)
|
||||
if "empty result for listdatastore_state" in str(e):
|
||||
print(str(e))
|
||||
request2 = node_pb2.ListinvoicesRequest(
|
||||
@ -418,7 +418,7 @@ class CLNNode:
|
||||
|
||||
# Some wallet providers (e.g. Muun) force routing through a private channel with high fees >1500ppm
|
||||
# These payments will fail. So it is best to let the user know in advance this invoice is not valid.
|
||||
route_hints = payreq_decoded.route_hints.hints
|
||||
route_hints = payreq_decoded.routes.hints
|
||||
|
||||
# Max amount RoboSats will pay for routing
|
||||
if routing_budget_ppm == 0:
|
||||
@ -438,8 +438,10 @@ class CLNNode:
|
||||
route_cost = 0
|
||||
# ...add up the cost of every hinted hop...
|
||||
for hop_hint in hinted_route.hops:
|
||||
route_cost += hop_hint.feebase.msat / 1_000
|
||||
route_cost += hop_hint.feeprop * num_satoshis / 1_000_000
|
||||
route_cost += hop_hint.fee_base_msat.msat / 1_000
|
||||
route_cost += (
|
||||
hop_hint.fee_proportional_millionths * num_satoshis / 1_000_000
|
||||
)
|
||||
|
||||
# ...and store the cost of the route to the array
|
||||
routes_cost.append(route_cost)
|
||||
@ -466,7 +468,7 @@ class CLNNode:
|
||||
return payout
|
||||
|
||||
payout["created_at"] = timezone.make_aware(
|
||||
datetime.fromtimestamp(payreq_decoded.timestamp)
|
||||
datetime.fromtimestamp(payreq_decoded.created_at)
|
||||
)
|
||||
payout["expires_at"] = payout["created_at"] + timedelta(
|
||||
seconds=payreq_decoded.expiry
|
||||
@ -869,4 +871,4 @@ class CLNNode:
|
||||
else:
|
||||
raise e
|
||||
|
||||
return response.state == hold_pb2.HoldInvoiceLookupResponse.Holdstate.SETTLED
|
||||
return response.state == hold_pb2.Holdstate.SETTLED
|
||||
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
|
||||
from decimal import Decimal
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
@ -18,7 +19,7 @@ class Currency(models.Model):
|
||||
decimal_places=4,
|
||||
default=None,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
validators=[MinValueValidator(Decimal(0))],
|
||||
)
|
||||
timestamp = models.DateTimeField(default=timezone.now)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import uuid
|
||||
|
||||
from decimal import Decimal
|
||||
from decouple import config
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
@ -27,21 +28,24 @@ class MarketTick(models.Model):
|
||||
decimal_places=2,
|
||||
default=None,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
validators=[MinValueValidator(Decimal(0))],
|
||||
)
|
||||
volume = models.DecimalField(
|
||||
max_digits=8,
|
||||
decimal_places=8,
|
||||
default=None,
|
||||
null=True,
|
||||
validators=[MinValueValidator(0)],
|
||||
validators=[MinValueValidator(Decimal(0))],
|
||||
)
|
||||
premium = models.DecimalField(
|
||||
max_digits=5,
|
||||
decimal_places=2,
|
||||
default=None,
|
||||
null=True,
|
||||
validators=[MinValueValidator(-100), MaxValueValidator(999)],
|
||||
validators=[
|
||||
MinValueValidator(Decimal(-100)),
|
||||
MaxValueValidator(Decimal(999))
|
||||
],
|
||||
blank=True,
|
||||
)
|
||||
currency = models.ForeignKey("api.Currency", null=True, on_delete=models.SET_NULL)
|
||||
@ -52,7 +56,10 @@ class MarketTick(models.Model):
|
||||
max_digits=4,
|
||||
decimal_places=4,
|
||||
default=0,
|
||||
validators=[MinValueValidator(0), MaxValueValidator(1)],
|
||||
validators=[
|
||||
MinValueValidator(Decimal(0)),
|
||||
MaxValueValidator(Decimal(1))
|
||||
],
|
||||
)
|
||||
|
||||
def log_a_tick(order):
|
||||
|
@ -1,3 +1,4 @@
|
||||
from decimal import Decimal
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
@ -58,7 +59,10 @@ class OnchainPayment(models.Model):
|
||||
default=2.05,
|
||||
null=False,
|
||||
blank=False,
|
||||
validators=[MinValueValidator(1), MaxValueValidator(999)],
|
||||
validators=[
|
||||
MinValueValidator(Decimal(1)),
|
||||
MaxValueValidator(Decimal(999))
|
||||
],
|
||||
)
|
||||
mining_fee_rate = models.DecimalField(
|
||||
max_digits=6,
|
||||
@ -66,7 +70,10 @@ class OnchainPayment(models.Model):
|
||||
default=2.05,
|
||||
null=False,
|
||||
blank=False,
|
||||
validators=[MinValueValidator(1), MaxValueValidator(999)],
|
||||
validators=[
|
||||
MinValueValidator(Decimal(1)),
|
||||
MaxValueValidator(Decimal(999))
|
||||
],
|
||||
)
|
||||
mining_fee_sats = models.PositiveBigIntegerField(default=0, null=False, blank=False)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
# We use custom seeded UUID generation during testing
|
||||
import uuid
|
||||
|
||||
from decimal import Decimal
|
||||
from decouple import config
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
@ -90,7 +91,10 @@ class Order(models.Model):
|
||||
decimal_places=2,
|
||||
default=0,
|
||||
null=True,
|
||||
validators=[MinValueValidator(-100), MaxValueValidator(999)],
|
||||
validators=[
|
||||
MinValueValidator(Decimal(-100)),
|
||||
MaxValueValidator(Decimal(999))
|
||||
],
|
||||
blank=True,
|
||||
)
|
||||
# explicit
|
||||
@ -135,8 +139,8 @@ class Order(models.Model):
|
||||
default=settings.DEFAULT_BOND_SIZE,
|
||||
null=False,
|
||||
validators=[
|
||||
MinValueValidator(settings.MIN_BOND_SIZE), # 2 %
|
||||
MaxValueValidator(settings.MAX_BOND_SIZE), # 15 %
|
||||
MinValueValidator(Decimal(settings.MIN_BOND_SIZE)), # 2 %
|
||||
MaxValueValidator(Decimal(settings.MAX_BOND_SIZE)), # 15 %
|
||||
],
|
||||
blank=False,
|
||||
)
|
||||
@ -147,8 +151,8 @@ class Order(models.Model):
|
||||
decimal_places=6,
|
||||
null=True,
|
||||
validators=[
|
||||
MinValueValidator(-90),
|
||||
MaxValueValidator(90),
|
||||
MinValueValidator(Decimal(-90)),
|
||||
MaxValueValidator(Decimal(90)),
|
||||
],
|
||||
blank=True,
|
||||
)
|
||||
@ -157,8 +161,8 @@ class Order(models.Model):
|
||||
decimal_places=6,
|
||||
null=True,
|
||||
validators=[
|
||||
MinValueValidator(-180),
|
||||
MaxValueValidator(180),
|
||||
MinValueValidator(Decimal(-180)),
|
||||
MaxValueValidator(Decimal(180)),
|
||||
],
|
||||
blank=True,
|
||||
)
|
||||
|
@ -494,7 +494,8 @@ class OrderPublicSerializer(serializers.ModelSerializer):
|
||||
maker_nick = serializers.CharField(required=False)
|
||||
maker_hash_id = serializers.CharField(required=False)
|
||||
maker_status = serializers.CharField(
|
||||
help_text='Status of the nick - "Active" or "Inactive"', required=False
|
||||
help_text='Status of the nick - "Active", "Seen Recently" or "Inactive"',
|
||||
required=False,
|
||||
)
|
||||
price = serializers.FloatField(
|
||||
help_text="Price in order's fiat currency", required=False
|
||||
|
@ -1,5 +1,4 @@
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
from decouple import config
|
||||
from django.conf import settings
|
||||
@ -55,9 +54,6 @@ from control.models import AccountingDay, BalanceLog
|
||||
EXP_MAKER_BOND_INVOICE = int(config("EXP_MAKER_BOND_INVOICE"))
|
||||
RETRY_TIME = int(config("RETRY_TIME"))
|
||||
|
||||
avatar_path = Path(settings.AVATAR_ROOT)
|
||||
avatar_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
class MakerView(CreateAPIView):
|
||||
serializer_class = MakeOrderSerializer
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
# Some useful handy commands that hopefully are never needed
|
||||
|
||||
# docker-compose -f docker-tests.yml --env-file tests/compose.env down --volumes
|
||||
|
||||
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test createwallet default
|
||||
# docker exec -it btc bitcoin-cli -chain=regtest -rpcpassword=test -rpcuser=test -generate 101
|
||||
|
||||
@ -27,6 +29,8 @@ services:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- bitcoin:/bitcoin/.bitcoin/
|
||||
- ./tests/bitcoind/entrypoint.sh:/entrypoint.sh
|
||||
entrypoint: ["/entrypoint.sh"]
|
||||
command:
|
||||
--txindex=1
|
||||
--printtoconsole
|
||||
@ -77,16 +81,16 @@ services:
|
||||
network_mode: service:bitcoind
|
||||
|
||||
coordinator-CLN:
|
||||
image: elementsproject/lightningd:${CLN_VERSION:-v23.08.1}
|
||||
image: elementsproject/lightningd:${CLN_VERSION:-v24.05}
|
||||
restart: always
|
||||
container_name: coordinator-CLN
|
||||
environment:
|
||||
LIGHTNINGD_NETWORK: 'regtest'
|
||||
volumes:
|
||||
- cln:/root/.lightning
|
||||
- ./docker/cln/plugins/cln-grpc-hold:/root/.lightning/plugins/cln-grpc-hold
|
||||
- ./docker/cln/plugins/holdinvoice:/root/.lightning/plugins/holdinvoice
|
||||
- bitcoin:/root/.bitcoin
|
||||
command: --regtest --wumbo --bitcoin-rpcuser=test --bitcoin-rpcpassword=test --log-level=debug --rest-host=0.0.0.0 --rest-port=3010 --bind-addr=127.0.0.1:9737 --max-concurrent-htlcs=483 --grpc-port=9999 --grpc-hold-port=9998 --important-plugin=/root/.lightning/plugins/cln-grpc-hold --database-upgrade=true
|
||||
command: --regtest --bitcoin-rpcuser=test --bitcoin-rpcpassword=test --developer --dev-bitcoind-poll=1 --dev-fast-gossip --log-level=debug --bind-addr=127.0.0.1:9737 --max-concurrent-htlcs=483 --grpc-port=9999 --grpc-hold-port=9998 --important-plugin=/root/.lightning/plugins/holdinvoice --database-upgrade=true
|
||||
depends_on:
|
||||
- bitcoind
|
||||
network_mode: service:bitcoind
|
||||
|
@ -1,7 +1,7 @@
|
||||
FROM debian:bullseye-slim as builder
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG LIGHTNINGD_VERSION=v23.08
|
||||
ARG LIGHTNINGD_VERSION=v24.05
|
||||
RUN apt-get update -qq && \
|
||||
apt-get install -qq -y --no-install-recommends \
|
||||
autoconf \
|
||||
@ -18,13 +18,13 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
RUN rustup toolchain install stable --component rustfmt --allow-downgrade
|
||||
|
||||
WORKDIR /opt/lightningd
|
||||
RUN git clone --recursive --branch cln-grpc-hold https://github.com/daywalker90/lightning.git /tmp/cln-grpc-hold
|
||||
RUN cd /tmp/cln-grpc-hold \
|
||||
RUN git clone https://github.com/daywalker90/holdinvoice.git /tmp/holdinvoice
|
||||
RUN cd /tmp/holdinvoice \
|
||||
&& cargo build --release
|
||||
|
||||
FROM elementsproject/lightningd:v23.08 as final
|
||||
FROM elementsproject/lightningd:v24.05 as final
|
||||
|
||||
COPY --from=builder /tmp/cln-grpc-hold/target/release/cln-grpc-hold /tmp/cln-grpc-hold
|
||||
COPY --from=builder /tmp/holdinvoice/target/release/holdinvoice /tmp/holdinvoice
|
||||
COPY config /tmp/config
|
||||
COPY entrypoint.sh entrypoint.sh
|
||||
RUN chmod +x entrypoint.sh
|
||||
|
@ -5,6 +5,6 @@ addr=statictor:127.0.0.1:9051
|
||||
grpc-port=9999
|
||||
grpc-hold-port=9998
|
||||
always-use-proxy=true
|
||||
important-plugin=/root/.lightning/plugins/cln-grpc-hold
|
||||
important-plugin=/root/.lightning/plugins/holdinvoice
|
||||
# wallet=postgres://user:pass@localhost:5433/cln
|
||||
# bookkeeper-db=postgres://user:pass@localhost:5433/cln
|
@ -17,9 +17,9 @@ if [ "$EXPOSE_TCP" == "true" ]; then
|
||||
socat "TCP4-listen:$LIGHTNINGD_RPC_PORT,fork,reuseaddr" "UNIX-CONNECT:${networkdatadir}/lightning-rpc" &
|
||||
fg %-
|
||||
else
|
||||
# Always copy the cln-grpc-hodl plugin into the plugins directory on start up
|
||||
# Always copy the holdinvoice plugin into the plugins directory on start up
|
||||
mkdir -p /root/.lightning/plugins
|
||||
cp /tmp/cln-grpc-hold /root/.lightning/plugins/cln-grpc-hold
|
||||
cp /tmp/holdinvoice /root/.lightning/plugins/holdinvoice
|
||||
if [ ! -f /root/.lightning/config ]; then
|
||||
cp /tmp/config /root/.lightning/config
|
||||
fi
|
||||
|
BIN
docker/cln/plugins/holdinvoice
Executable file
@ -28,15 +28,17 @@ This is a non-exhaustive compilation based on past experience of users. We have
|
||||
| Wallet | Version | Device | UX<sup>1</sup> | Bonds<sup>2</sup> | Payout<sup>3</sup> | Comp<sup>4</sup> | Total<sup>5</sup> |
|
||||
|:---|:---|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||
|[Alby](#alby-browser-extension)|[v1.14.2](https://github.com/getAlby/lightning-browser-extension)|{{page.laptop}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}} |{{page.thumbsup}}|
|
||||
|[Aqua](#aqua-mobile)|[v0.1.55](https://aquawallet.io/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.unclear}}|{{page.good}} |{{page.thumbsup}}|
|
||||
|[Blink](#blink-mobile-former-bitcoin-beach-wallet)|[2.2.73](https://www.blink.sv/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}} |{{page.thumbsup}}|
|
||||
|[Blixt](#blixt-androidios-lnd-light-backend-on-device)|[v0.4.1](https://github.com/hsjoberg/blixt-wallet)|{{page.phone}}|{{page.soso}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}|
|
||||
|[Blue](#bluewallet-mobile)|[1.4.4](https://bluewallet.io/)|{{page.phone}}|{{page.good}}|{{page.unclear}}|{{page.unclear}}|{{page.good}}|{{page.unclear}}|
|
||||
|[Breez](#breez-mobile)|[0.16](https://breez.technology/mobile/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}|
|
||||
|[Cash App](#cash-app-mobile)|[4.7](https://cash.app/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}} |{{page.thumbsup}}|
|
||||
|[Core Lightning](#core-lightning--cln-cli-interface)|[v0.11.1](https://github.com/ElementsProject/lightning)|{{page.cli}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}|
|
||||
|[Electrum](#electrum-desktop)|[4.1.4](https://github.com/spesmilo/electrum)|{{page.laptop}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}||
|
||||
|[Electrum](#electrum-mobile--desktop)|[4.1.4](https://github.com/spesmilo/electrum)|{{page.laptop}}{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}||
|
||||
|[LND](#lnd-cli-interface)|[v0.14.2](https://github.com/LightningNetwork/lnd)|{{page.cli}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}|
|
||||
|[Mash](https://app.mash.com/wallet)|[Beta](https://mash.com/consumer-experience/)|{{page.laptop}}{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}} | {{page.thumbsup}}|
|
||||
|[Mutiny](#mutiny-mobile--web-browser-wallet)|[1.7.1](https://www.mutinywallet.com/)|{{page.laptop}}{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsdown}}||
|
||||
|[Muun](#muun-mobile)|[47.3](https://muun.com/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.bad}}|{{page.bad}}|{{page.thumbsdown}}|
|
||||
|[Phoenix](#phoenix-mobile)|[35-1.4.20](https://phoenix.acinq.co/)|{{page.phone}}|{{page.good}}|{{page.soso}}|{{page.soso}}|{{page.soso}}|{{page.unclear}}|
|
||||
|[SBW](https://github.com/RoboSats/robosats/issues/44#issue-1135544303)|[2.4.27](https://github.com/btcontract/wallet/)|{{page.phone}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.good}}|{{page.thumbsup}}|
|
||||
@ -57,6 +59,12 @@ Instructions to install Alby in Tor Browser:
|
||||
1. Install the Alby extension from the [Firefox add-ons store](https://addons.mozilla.org/en-US/firefox/addon/alby/)
|
||||
2. Click on the Alby extension and follow the prompts to setup your wallet.
|
||||
|
||||
### Aqua (Mobile)
|
||||
Overall the wallet works as expected. Hold invoices work reliably.
|
||||
What is inconvenient:
|
||||
- Lightning payments are encapsulated into Liquid Bitcoin so there is a small additional fee for conversion in/out
|
||||
- Bond refund is locked for 3 days
|
||||
|
||||
### Blink (Mobile, former Bitcoin Beach Wallet)
|
||||
Works well with RoboSats. Hodl invoices (Bonds) show as "Pending" in the transaction history. Payouts to the Blink wallet function as intended. Custodial wallet by Galoy which originated from the Bitcoin Beach project in El Salvador (formerly known as "Bitcoin Beach Wallet").
|
||||
|
||||
@ -75,8 +83,11 @@ Works well with RoboSats. Hodl invoices (Bonds) show as "Pending" in the transac
|
||||
### Core Lightning / CLN (CLI Interface)
|
||||
Works as expected. The `lightning-cli pay <invoice>` command does not conclude while the payment is pending, but can use `lightning-cli paystatus <invoice>` to monitor the state.
|
||||
|
||||
### Electrum (Desktop)
|
||||
Works as expected. Some payments and locks may fail depending on the Lightning node the channel is created to. Channels to ASINQ work fine.
|
||||
### Electrum (Mobile & Desktop)
|
||||
Overall the wallet works as expected. The interface is precise and clear.
|
||||
What is inconvenient:
|
||||
- all your Lightning channels have to be created to the node: ACINQ
|
||||
|
||||
|
||||
### LND (CLI Interface)
|
||||
Raw; it shows exactly what is happening and what it knows "IN_FLIGHT". It is not user friendly and therefore not recommended to interact with RoboSats by beginners. However, everything works just fine. If you are using LNCLI regularly, then you will find no issue using it with RoboSats.
|
||||
@ -85,6 +96,13 @@ Raw; it shows exactly what is happening and what it knows "IN_FLIGHT". It is not
|
||||
### Mash Wallet App (Mobile PWA & Desktop Web-Wallet)
|
||||
Overall the [Mash](https://mash.com/consumer-experience/) wallet works end2end with Robosats on both selling & buying over lightning. Majority of relevant invoice details in the mash wallet are shown and clear to users throughout the process. When the transactions are complete, they open in the mobile app on both sender/receiver sides to highlight that the transactions are completed.The one UX hick-up is that the pending invoices list doesn't explicitly show HOLD invoices and there is a "spinning" screen on first HOLD invoice payment. The team has a bug open to fix this issue shortly (this note is from Aug 21st 2023).
|
||||
|
||||
### Mutiny (Mobile & Web Browser Wallet)
|
||||
The wallet should work as expected, but the interface, transaction states, and the structure of the funds can sometimes be very confusing in the current release version.
|
||||
Use the default free Fedimint(Chaumian eCash) account, with the possibility to use zero fee Lightning transfers.
|
||||
What is inconvenient:
|
||||
- occasionally wallet restart is needed
|
||||
- more than two pending hold invoices at the same time may cause a rejection of the new transaction
|
||||
|
||||
### Muun (Mobile)
|
||||
Similar to Blixt or LND, Muun plays nicely with hold invoices. You can be a seller in RoboSats using Muun and the user experience will be great. However, in order to be a buyer when using Muun, you need to submit an on-chain address for the payout as a Lightning invoice won't work. Muun is _fee siphoning attacking_ any sender to Muun wallet. There is a mandatory hop through a private channel with a fee of +1500ppm. RoboSats will strictly not route a buyer payout for a net loss. Given that RoboSats trading fees are {{site.robosats.total_fee}}% and it needs to cover the routing fees, **RoboSats will never find a suitable route to a Muun wallet user**. At the moment, RoboSats will scan your invoice for routing hints that can potentially encode a _fee siphoning attack_. If this trick is found, then the invoice will be rejected: submit an on-chain address instead for an on-the-fly swap. Refer to [Understand > On-Chain Payouts](/docs/on-chain-payouts/) for more information about on-the-fly swaps. Important to note that Muun has issues during times of high on chain fee spikes. Regardless, the workaround to receive to Muun is: either submit an on chain address or choose a higher routing budget after enabling the "Advanced Options" switch.
|
||||
|
||||
@ -93,6 +111,7 @@ One of the simplest and one of the best. The hodl invoice shows as "on fly", it
|
||||
*Update 26-10-23: At this moment it has no development or support
|
||||
|
||||
### Phoenix (Mobile)
|
||||
DEV team stated that they do not want to support hold invoices.
|
||||
*Update 21-10-23. Phoenix used to work as described here, but many things changed to worse with the last update of the wallet.
|
||||
Phoenix works very well as an order taker. Phoenix will also work well as an order maker as long as the order settings `public duration` + `deposit duration` are lower than 10 hours. Otherwise, you might have problems locking the maker bond. If the total duraton of bonds/escrow invoices exceeds 450 blocks, then Phoenix will not allow users to lock the bond (`Cannot add htlc (...) reason=expiry too big`).
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: RoboSats REST API
|
||||
version: 0.6.0
|
||||
version: 0.6.2
|
||||
x-logo:
|
||||
url: https://raw.githubusercontent.com/Reckless-Satoshi/robosats/main/frontend/static/assets/images/robosats-0.1.1-banner.png
|
||||
backgroundColor: '#FFFFFF'
|
||||
@ -1638,7 +1638,7 @@ components:
|
||||
type: string
|
||||
maker_status:
|
||||
type: string
|
||||
description: Status of the nick - "Active" or "Inactive"
|
||||
description: Status of the nick - "Active", "Seen Recently" or "Inactive"
|
||||
price:
|
||||
type: number
|
||||
format: double
|
||||
|
@ -1539,7 +1539,7 @@ components:
|
||||
type: string
|
||||
maker_status:
|
||||
type: string
|
||||
description: Status of the nick - "Active" or "Inactive"
|
||||
description: Status of the nick - "Active", "Seen Recently" or "Inactive"
|
||||
price:
|
||||
type: integer
|
||||
description: Price in order's fiat currency
|
||||
|
@ -1712,7 +1712,7 @@ components:
|
||||
type: string
|
||||
maker_status:
|
||||
type: string
|
||||
description: Status of the nick - "Active" or "Inactive"
|
||||
description: Status of the nick - "Active", "Seen Recently" or "Inactive"
|
||||
price:
|
||||
type: number
|
||||
format: double
|
||||
|
12
fastlane/metadata/en-US/full_description.txt
Normal file
@ -0,0 +1,12 @@
|
||||
<p>RoboSats is a simple and private app to exchange bitcoin for national currencies. Robosats simplifies the P2P user experience and uses lightning hold invoices to minimize custody and trust requirements. The deterministically generated robot avatars help users stick to best privacy practices.</p>
|
||||
<p><br><b>Features:</b></p><ul>
|
||||
<li>Privacy focused: your robot avatar is deterministically generated, no need for registration.</li>
|
||||
<li>More than 10 languages available and over 60 fiat currencies</li>
|
||||
<li>Safe: simply lock a lightning hodl invoice and show you are real and committed.</li>
|
||||
<li>No data collection. Your communication with your peer is PGP encrypted, only you can read it.</li>
|
||||
<li>Lightning fast: the average sovereign trade finishes in ~ 8 minutes. Faster than a single block confirmation!</li>
|
||||
<li>Fully collateralized escrow: your peer is always committed and cannot run away with the funds.</li>
|
||||
<li>Strong incentives system: attempts of cheating are penalized with the slashing of the Sats in the fidelity bond.</li>
|
||||
<li>Guides and video tutorials available at https://learn.robosats.com/watch/en</li>
|
||||
</ul>
|
||||
<p>You can join other cool Robots and get community support at <a href="https://t.me/robosats">our Telegram group</a>.</p>
|
BIN
fastlane/metadata/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/01.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/02.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/03.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/04.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/05.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
fastlane/metadata/en-US/images/phoneScreenshots/06.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
1
fastlane/metadata/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
Simple and private app to exchange bitcoin for national currencies.
|
2545
frontend/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@ -41,7 +41,7 @@
|
||||
"eslint-plugin-react": "^7.34.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"jest": "^29.6.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier": "^3.3.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.2",
|
||||
"webpack": "^5.89.0",
|
||||
@ -57,10 +57,10 @@
|
||||
"@mui/lab": "^5.0.0-alpha.136",
|
||||
"@mui/material": "^5.15.14",
|
||||
"@mui/system": "^5.15.11",
|
||||
"@mui/x-data-grid": "^7.3.0",
|
||||
"@mui/x-data-grid": "^7.6.0",
|
||||
"@mui/x-date-pickers": "^7.2.0",
|
||||
"@nivo/core": "^0.85.1",
|
||||
"@nivo/line": "^0.85.1",
|
||||
"@nivo/core": "^0.86.0",
|
||||
"@nivo/line": "^0.86.0",
|
||||
"base-ex": "^0.8.1",
|
||||
"country-flag-icons": "^1.5.11",
|
||||
"date-fns": "^2.30.0",
|
||||
|
@ -67,9 +67,11 @@ const RobotProfile = ({
|
||||
};
|
||||
|
||||
const handleChangeSlot = (e: SelectChangeEvent<number | 'loading'>): void => {
|
||||
garage.currentSlot = e.target.value;
|
||||
setInputToken(garage.getSlot()?.token ?? '');
|
||||
setLoading(true);
|
||||
if (e?.target?.value) {
|
||||
garage.setCurrentSlot(e.target.value as string);
|
||||
setInputToken(garage.getSlot()?.token ?? '');
|
||||
setLoading(true);
|
||||
}
|
||||
};
|
||||
|
||||
const slot = garage.getSlot();
|
||||
|
@ -44,7 +44,7 @@ const RobotPage = (): JSX.Element => {
|
||||
const token = urlToken ?? garage.currentSlot;
|
||||
if (token !== undefined && token !== null && page === 'robot') {
|
||||
setInputToken(token);
|
||||
if (window.NativeRobosats === undefined || torStatus === 'ON') {
|
||||
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) {
|
||||
getGenerateRobot(token);
|
||||
setView('profile');
|
||||
}
|
||||
@ -71,7 +71,7 @@ const RobotPage = (): JSX.Element => {
|
||||
encPrivKey: key.encryptedPrivateKeyArmored,
|
||||
});
|
||||
void federation.fetchRobot(garage, token);
|
||||
garage.currentSlot = token;
|
||||
garage.setCurrentSlot(token);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
@ -83,7 +83,7 @@ const RobotPage = (): JSX.Element => {
|
||||
garage.deleteSlot();
|
||||
};
|
||||
|
||||
if (!(window.NativeRobosats === undefined) && !(torStatus === 'ON')) {
|
||||
if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) {
|
||||
return (
|
||||
<Paper
|
||||
elevation={12}
|
||||
|
@ -90,12 +90,6 @@ const MakerForm = ({
|
||||
const minRangeAmountMultiple = 1.6;
|
||||
const amountSafeThresholds = [1.03, 0.98];
|
||||
|
||||
useEffect(() => {
|
||||
// Why?
|
||||
// const slot = garage.getSlot();
|
||||
// if (slot?.token) void federation.fetchRobot(garage, slot?.token);
|
||||
}, [garage.currentSlot]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrencyCode(currencyDict[fav.currency === 0 ? 1 : fav.currency]);
|
||||
}, [coordinatorUpdatedAt]);
|
||||
|
@ -178,7 +178,7 @@ const OrderDetails = ({
|
||||
: coordinator.info?.taker_fee ?? 0;
|
||||
const defaultRoutingBudget = 0.001;
|
||||
const btc_now = order.satoshis_now / 100000000;
|
||||
const rate = order.amount > 0 ? order.amount / btc_now : Number(order.max_amount) / btc_now;
|
||||
const rate = Number(order.max_amount ?? order.amount) / btc_now;
|
||||
|
||||
if (isBuyer) {
|
||||
if (order.amount > 0) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { type UseAppStoreType, AppContext } from '../../contexts/AppContext';
|
||||
import {
|
||||
@ -28,17 +28,19 @@ import {
|
||||
QrCode,
|
||||
} from '@mui/icons-material';
|
||||
import { systemClient } from '../../services/System';
|
||||
import { TorIcon } from '../Icons';
|
||||
import SwapCalls from '@mui/icons-material/SwapCalls';
|
||||
import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext';
|
||||
import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext';
|
||||
|
||||
interface SettingsFormProps {
|
||||
dense?: boolean;
|
||||
}
|
||||
|
||||
const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
const { fav, setFav, origin, hostUrl, settings, setSettings } =
|
||||
useContext<UseAppStoreType>(AppContext);
|
||||
const { fav, setFav, settings, setSettings } = useContext<UseAppStoreType>(AppContext);
|
||||
const { federation } = useContext<UseFederationStoreType>(FederationContext);
|
||||
const { garage } = useContext<UseGarageStoreType>(GarageContext);
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const fontSizes = [
|
||||
@ -237,6 +239,29 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => {
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItem>
|
||||
|
||||
{window.NativeRobosats !== undefined && (
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<TorIcon />
|
||||
</ListItemIcon>
|
||||
<ToggleButtonGroup
|
||||
exclusive={true}
|
||||
value={settings.useProxy}
|
||||
onChange={(_e, useProxy) => {
|
||||
setSettings({ ...settings, useProxy });
|
||||
systemClient.setItem('settings_use_proxy', String(useProxy));
|
||||
}}
|
||||
>
|
||||
<ToggleButton value={true} color='primary'>
|
||||
{t('Build-in')}
|
||||
</ToggleButton>
|
||||
<ToggleButton value={false} color='secondary'>
|
||||
{t('Disabled')}
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -55,10 +55,10 @@ const TorIndicator = ({
|
||||
};
|
||||
|
||||
const TorConnectionBadge = (): JSX.Element => {
|
||||
const { torStatus } = useContext<UseAppStoreType>(AppContext);
|
||||
const { torStatus, settings } = useContext<UseAppStoreType>(AppContext);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (window?.NativeRobosats == null) {
|
||||
if (window?.NativeRobosats == null || !settings.useProxy) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,7 @@ export const SuccessfulPrompt = ({
|
||||
takerSummary={order.taker_summary}
|
||||
platformSummary={order.platform_summary}
|
||||
orderId={order.id}
|
||||
coordinatorLongAlias={federation.getCoordinator(order.shortAlias)?.longAlias}
|
||||
/>
|
||||
</Grid>
|
||||
) : (
|
||||
|
@ -43,6 +43,7 @@ interface Props {
|
||||
makerHashId: string;
|
||||
takerHashId: string;
|
||||
currencyCode: string;
|
||||
coordinatorLongAlias: string;
|
||||
makerSummary: TradeRobotSummary;
|
||||
takerSummary: TradeRobotSummary;
|
||||
platformSummary: TradeCoordinatorSummary;
|
||||
@ -54,6 +55,7 @@ const TradeSummary = ({
|
||||
makerHashId,
|
||||
takerHashId,
|
||||
currencyCode,
|
||||
coordinatorLongAlias,
|
||||
makerSummary,
|
||||
takerSummary,
|
||||
platformSummary,
|
||||
@ -72,6 +74,7 @@ const TradeSummary = ({
|
||||
|
||||
const onClickExport = function (): void {
|
||||
const summary = {
|
||||
coordinator: coordinatorLongAlias,
|
||||
order_id: orderId,
|
||||
currency: currencyCode,
|
||||
maker: makerSummary,
|
||||
|
@ -111,12 +111,14 @@ export const FederationContextProvider = ({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// On bitcoin network change we reset book, limits and federation info and fetch everything again
|
||||
if (window.NativeRobosats === undefined || torStatus === 'ON') {
|
||||
if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) {
|
||||
void federation.updateUrl(origin, settings, hostUrl);
|
||||
void federation.update();
|
||||
|
||||
const token = garage.getSlot()?.getRobot()?.token;
|
||||
if (token) void federation.fetchRobot(garage, token);
|
||||
}
|
||||
}, [settings.network, torStatus]);
|
||||
}, [settings.network, settings.useProxy, torStatus]);
|
||||
|
||||
const onOrderReceived = (order: Order): void => {
|
||||
let newDelay = defaultDelay;
|
||||
@ -178,15 +180,6 @@ export const FederationContextProvider = ({
|
||||
if (page === 'offers') void federation.updateBook();
|
||||
}, [page]);
|
||||
|
||||
// use effects to fetchRobots on app start and network change
|
||||
useEffect(() => {
|
||||
const slot = garage.getSlot();
|
||||
const robot = slot?.getRobot();
|
||||
|
||||
if (robot && garage.currentSlot && slot?.token && robot.encPrivKey && robot.pubKey) {
|
||||
void federation.fetchRobot(garage, slot.token);
|
||||
}
|
||||
}, [settings.network]);
|
||||
// use effects to fetchRobots on Profile open
|
||||
useEffect(() => {
|
||||
const slot = garage.getSlot();
|
||||
|
@ -145,7 +145,7 @@ export class Coordinator {
|
||||
public loadingInfo: boolean = false;
|
||||
public limits: LimitList = {};
|
||||
public loadingLimits: boolean = false;
|
||||
public loadingRobot: boolean = true;
|
||||
public loadingRobot: string | null;
|
||||
|
||||
updateUrl = (origin: Origin, settings: Settings, hostUrl: string): void => {
|
||||
if (settings.selfhostedClient && this.shortAlias !== 'local') {
|
||||
@ -185,6 +185,7 @@ export class Coordinator {
|
||||
if (this.loadingBook) return;
|
||||
|
||||
this.loadingBook = true;
|
||||
this.book = [];
|
||||
|
||||
apiClient
|
||||
.get(this.url, `${this.basePath}/api/book/`)
|
||||
@ -297,7 +298,7 @@ export class Coordinator {
|
||||
};
|
||||
|
||||
fetchRobot = async (garage: Garage, token: string): Promise<Robot | null> => {
|
||||
if (!this.enabled || !token) return null;
|
||||
if (!this.enabled || !token || this.loadingRobot === token) return null;
|
||||
|
||||
const robot = garage?.getSlot(token)?.getRobot() ?? null;
|
||||
const authHeaders = robot?.getAuthHeaders();
|
||||
@ -308,6 +309,8 @@ export class Coordinator {
|
||||
|
||||
if (!hasEnoughEntropy) return null;
|
||||
|
||||
this.loadingRobot = token;
|
||||
|
||||
garage.updateRobot(token, this.shortAlias, { loading: true });
|
||||
|
||||
const newAttributes = await apiClient
|
||||
@ -330,7 +333,8 @@ export class Coordinator {
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
})
|
||||
.finally(() => (this.loadingRobot = null));
|
||||
|
||||
garage.updateRobot(token, this.shortAlias, {
|
||||
...newAttributes,
|
||||
|
@ -101,7 +101,7 @@ export class Federation {
|
||||
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
||||
this.updateEnabledCoordinators();
|
||||
for (const coor of Object.values(this.coordinators)) {
|
||||
await coor.update(() => {
|
||||
coor.update(() => {
|
||||
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
|
||||
this.onCoordinatorSaved();
|
||||
});
|
||||
@ -110,10 +110,11 @@ export class Federation {
|
||||
|
||||
updateBook = async (): Promise<void> => {
|
||||
this.loading = true;
|
||||
this.book = [];
|
||||
this.triggerHook('onCoordinatorUpdate');
|
||||
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
||||
for (const coor of Object.values(this.coordinators)) {
|
||||
await coor.updateBook(() => {
|
||||
coor.updateBook(() => {
|
||||
this.onCoordinatorSaved();
|
||||
});
|
||||
}
|
||||
|
@ -106,6 +106,11 @@ class Garage {
|
||||
return slot;
|
||||
};
|
||||
|
||||
setCurrentSlot: (currentSlot: string) => void = (currentSlot) => {
|
||||
this.currentSlot = currentSlot;
|
||||
this.triggerHook('onRobotUpdate');
|
||||
};
|
||||
|
||||
// Robots
|
||||
createRobot: (token: string, shortAliases: string[], attributes: Record<any, any>) => void = (
|
||||
token,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import i18n from '../i18n/Web';
|
||||
import { systemClient } from '../services/System';
|
||||
import { apiClient } from '../services/api';
|
||||
import { getHost } from '../utils';
|
||||
|
||||
export type Language =
|
||||
@ -42,8 +43,13 @@ class BaseSettings {
|
||||
: i18n.resolvedLanguage.substring(0, 2);
|
||||
|
||||
const networkCookie = systemClient.getItem('settings_network');
|
||||
this.network = networkCookie !== '' ? networkCookie : 'mainnet';
|
||||
this.network = networkCookie && networkCookie !== '' ? networkCookie : 'mainnet';
|
||||
this.host = getHost();
|
||||
|
||||
const useProxy = systemClient.getItem('settings_use_proxy');
|
||||
this.useProxy = window.NativeRobosats !== undefined && useProxy !== 'false';
|
||||
|
||||
apiClient.useProxy = this.useProxy;
|
||||
}
|
||||
|
||||
public frontend: 'basic' | 'pro' = 'basic';
|
||||
@ -56,6 +62,7 @@ class BaseSettings {
|
||||
public host?: string;
|
||||
public unsafeClient: boolean = false;
|
||||
public selfhostedClient: boolean = false;
|
||||
public useProxy: boolean;
|
||||
}
|
||||
|
||||
export default BaseSettings;
|
||||
|
@ -28,7 +28,7 @@ class SystemNativeClient implements SystemClient {
|
||||
};
|
||||
|
||||
public setCookie: (key: string, value: string) => void = (key, value) => {
|
||||
delete window.NativeRobosats?.cookies[key];
|
||||
window.NativeRobosats?.loadCookie({ key, value });
|
||||
void window.NativeRobosats?.postMessage({
|
||||
category: 'system',
|
||||
type: 'setCookie',
|
||||
|
@ -1,8 +1,12 @@
|
||||
import { type ApiClient, type Auth } from '..';
|
||||
import { systemClient } from '../../System';
|
||||
import ApiWebClient from '../ApiWebClient';
|
||||
|
||||
class ApiNativeClient implements ApiClient {
|
||||
private readonly assetsCache: Record<string, string> = {};
|
||||
public useProxy = true;
|
||||
|
||||
private webClient: ApiClient = new ApiWebClient();
|
||||
|
||||
private readonly assetsPromises = new Map<string, Promise<string | undefined>>();
|
||||
|
||||
private readonly getHeaders: (auth?: Auth) => HeadersInit = (auth) => {
|
||||
@ -51,6 +55,7 @@ class ApiNativeClient implements ApiClient {
|
||||
|
||||
public delete: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined> =
|
||||
async (baseUrl, path, auth) => {
|
||||
if (!this.proxy) this.webClient.delete(baseUrl, path, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'delete',
|
||||
@ -66,6 +71,7 @@ class ApiNativeClient implements ApiClient {
|
||||
body: object,
|
||||
auth?: Auth,
|
||||
) => Promise<object | undefined> = async (baseUrl, path, body, auth) => {
|
||||
if (!this.proxy) this.webClient.post(baseUrl, path, body, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'post',
|
||||
@ -81,6 +87,7 @@ class ApiNativeClient implements ApiClient {
|
||||
path,
|
||||
auth,
|
||||
) => {
|
||||
if (!this.proxy) this.webClient.get(baseUrl, path, auth);
|
||||
return await window.NativeRobosats?.postMessage({
|
||||
category: 'http',
|
||||
type: 'get',
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { type ApiClient, type Auth } from '..';
|
||||
|
||||
class ApiWebClient implements ApiClient {
|
||||
public useProxy = false;
|
||||
|
||||
private readonly getHeaders: (auth?: Auth) => HeadersInit = (auth) => {
|
||||
let headers = {
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -7,6 +7,7 @@ export interface Auth {
|
||||
}
|
||||
|
||||
export interface ApiClient {
|
||||
useProxy: boolean;
|
||||
post: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>;
|
||||
put: (baseUrl: string, path: string, body: object, auth?: Auth) => Promise<object | undefined>;
|
||||
get: (baseUrl: string, path: string, auth?: Auth) => Promise<object | undefined>;
|
||||
|
@ -45,7 +45,6 @@ export default function federationLottery(federation: Federation): string[] {
|
||||
// federation[shortAlias] = { badges:{ donatesToDevFund }};
|
||||
// }
|
||||
|
||||
// console.log(federation)
|
||||
// return federation;
|
||||
// }
|
||||
|
||||
@ -58,5 +57,4 @@ export default function federationLottery(federation: Federation): string[] {
|
||||
// results.push(rankedCoordinators);
|
||||
// }
|
||||
|
||||
// console.log(results)
|
||||
// }
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "L'ordre ha expirat",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Encara no pots prendre cap ordre! Espera {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "Tu reps via Lightning {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "Reps via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "Tu reps via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "Tu envies via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "Envies via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Prima: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "La teva última ordre #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Fosc",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Clar",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Nabídka vypršela",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Nabídku nemůžeš zatím příjmout! Počkej {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Přirážka: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Tvá poslední nabídka #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Die Order ist abgelaufen",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Du kannst noch keine Order annehmen! Warte {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Aufschlag: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Deine letzte Order #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "The order has expired",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Your last order #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "La orden ha expirado",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "¡No puedes tomar una orden aún! Espera {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Prima: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Tu última orden #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Oscuro",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Claro",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Eskaera iraungi da",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Oraindik ezin duzu eskaerarik hartu! Itxaron{{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Prima: %{{premium}}",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Zure azken eskaera #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "L'ordre a expiré",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Vous ne pouvez pas encore prendre un ordre! Attendez {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "Vous recevez via Lightning {{amount}} Sats (environ)",
|
||||
"You receive via {{method}} {{amount}}": "Vous recevez via {{méthode}} {{montant}}",
|
||||
"You receive {{amount}} Sats (Approx)": "Vous recevez via Lightning {{amount}} Sats (environ)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "Vous envoyez via Lightning {{amount}} Sats (environ)",
|
||||
"You send via {{method}} {{amount}}": "Vous envoyez via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Prime: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Votre dernière commande #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Sombre",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "L'ordine è scaduto",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "La posizione appuntata è approssimativa. La posizione esatta del luogo dell'incontro deve essere indicata nella chat crittografata.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Non puoi ancora accettare un ordine! Aspetta {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "Ricevi {{amount}} Sats via Lightning (approssimativo)",
|
||||
"You receive via {{method}} {{amount}}": "Ricevi {{amount}} via {{method}}",
|
||||
"You receive {{amount}} Sats (Approx)": "Ricevi {{amount}} Sats via Lightning (approssimativo)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "Invii {{amount}} Sats via Lightning (approssimativo)",
|
||||
"You send via {{method}} {{amount}}": "Invii {{amount}} via {{method}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Premio: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Il tuo ultimo ordine #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Scuro",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Chiaro",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "注文は期限切れになりました",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "まだ注文を受け取ることはできません!{{timeMin}}分{{timeSec}}秒待ってください",
|
||||
"You receive {{amount}} Sats (Approx)": "ライトニングで{{amount}} Sats(約)を受け取ります",
|
||||
"You receive via {{method}} {{amount}}": "{{method}}で{{amount}}を受け取ります",
|
||||
"You receive {{amount}} Sats (Approx)": "ライトニングで{{amount}} Sats(約)を受け取ります",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "ライトニングで{{amount}} Sats(約)を送信します",
|
||||
"You send via {{method}} {{amount}}": "{{method}}で{{amount}}を送信します",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - プレミアム: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "前回のオーダー #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "ダーク",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "フィアット",
|
||||
"Light": "ライト",
|
||||
"Mainnet": "メインネット",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Zamówienie wygasło",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Nie możesz jeszcze przyjąć zamówienia! Czekać {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Premia: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Your last order #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "A ordem expirou",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Você ainda não pode fazer um pedido! Espere {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Prêmio: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Sua última ordem #{{orderID}}",
|
||||
"finished order": "ordem finalizada",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Срок действия ордера истёк",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "Закрепленное местоположение является приблизительным. Точное местоположение места встречи необходимо сообщить в зашифрованном чате.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Вы ещё не можете взять ордер! Подождите {{timeMin}}м {{timeSec}}с",
|
||||
"You receive {{amount}} Sats (Approx)": "Вы получаете через Lightning {{amount}} Сатоши (приблизительно)",
|
||||
"You receive via {{method}} {{amount}}": "Вы получаете через {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "Вы получаете через Lightning {{amount}} Сатоши (приблизительно)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "Вы отправляете через Lightning {{amount}} Сатоши (приблизительно)",
|
||||
"You send via {{method}} {{amount}}": "Вы отправляете через {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Наценка: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Ваш последний ордер #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Темный",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Фиат",
|
||||
"Light": "Светлый",
|
||||
"Mainnet": "Основная сеть",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Ordern har förfallit",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Du kan inte ta en order ännu! Vänta {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Din senaste order #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "Agizo limekwisha muda",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "Hauwezi kuchukua agizo bado! Subiri {{timeMin}}m {{timeSec}}s",
|
||||
"You receive {{amount}} Sats (Approx)": "Utapokea kupitia Lightning {{amount}} Sats (Takriban)",
|
||||
"You receive via {{method}} {{amount}}": "Utapokea kupitia {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "Utapokea kupitia Lightning {{amount}} Sats (Takriban)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "Utatuma kupitia Lightning {{amount}} Sats (Takriban)",
|
||||
"You send via {{method}} {{amount}}": "Utatuma kupitia {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "Amri yako ya mwisho #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Giza",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Nuru",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "รายการหมดอายุแล้ว",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "คุณยังไม่สามารถดำเนินรายการได้! รออีก {{timeMin}} นาที {{timeSec}} วินาที",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "You receive via {{method}} {{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "You send via {{method}} {{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - ค่าพรีเมี่ยม: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "รายการล่าสุดของคุณ #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "Dark",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "Fiat",
|
||||
"Light": "Light",
|
||||
"Mainnet": "Mainnet",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "订单已到期",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "你暂时还不能吃单!请等{{timeMin}}分 {{timeSec}}秒",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "你通过{{method}}接收{{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "你通过{{method}}发送{{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - 溢价: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "你的上一笔交易 #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "深色",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "法币",
|
||||
"Light": "浅色",
|
||||
"Mainnet": "主网",
|
||||
|
@ -464,8 +464,8 @@
|
||||
"The order has expired": "訂單已到期",
|
||||
"The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.": "The pinned location is approximate. The exact location for the meeting place must be exchanged in the encrypted chat.",
|
||||
"You cannot take an order yet! Wait {{timeMin}}m {{timeSec}}s": "你暫時還不能吃單!請等{{timeMin}}分 {{timeSec}}秒",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You receive via {{method}} {{amount}}": "你通過{{method}}接收{{amount}}",
|
||||
"You receive {{amount}} Sats (Approx)": "You receive {{amount}} Sats (Approx)",
|
||||
"You send via Lightning {{amount}} Sats (Approx)": "You send via Lightning {{amount}} Sats (Approx)",
|
||||
"You send via {{method}} {{amount}}": "你通過{{method}}發送{{amount}}",
|
||||
"{{price}} {{currencyCode}}/BTC - Premium: {{premium}}%": "{{price}} {{currencyCode}}/BTC - 溢價: {{premium}}%",
|
||||
@ -489,7 +489,9 @@
|
||||
"Your last order #{{orderID}}": "你的上一筆交易 #{{orderID}}",
|
||||
"finished order": "finished order",
|
||||
"#43": "Phrases in components/SettingsForm/index.tsx",
|
||||
"Build-in": "Build-in",
|
||||
"Dark": "深色",
|
||||
"Disabled": "Disabled",
|
||||
"Fiat": "法幣",
|
||||
"Light": "淺色",
|
||||
"Mainnet": "主網",
|
||||
|
@ -71,6 +71,7 @@ const App = () => {
|
||||
loadCookie('settings_mode');
|
||||
loadCookie('settings_light_qr');
|
||||
loadCookie('settings_network');
|
||||
loadCookie('settings_use_proxy');
|
||||
loadCookie('garage_slots').then(() => injectMessageResolve(responseId));
|
||||
};
|
||||
|
||||
|
@ -151,8 +151,8 @@ android {
|
||||
applicationId "com.robosats"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "0.6.0-alpha"
|
||||
versionCode 2
|
||||
versionName "0.6.2-alpha"
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
|
12
mobile/fastlane/metadata/en-US/full_description.txt
Normal file
@ -0,0 +1,12 @@
|
||||
<p>RoboSats is a simple and private app to exchange bitcoin for national currencies. Robosats simplifies the P2P user experience and uses lightning hold invoices to minimize custody and trust requirements. The deterministically generated robot avatars help users stick to best privacy practices.</p>
|
||||
<p><br><b>Features:</b></p><ul>
|
||||
<li>Privacy focused: your robot avatar is deterministically generated, no need for registration.</li>
|
||||
<li>More than 10 languages available and over 60 fiat currencies</li>
|
||||
<li>Safe: simply lock a lightning hodl invoice and show you are real and committed.</li>
|
||||
<li>No data collection. Your communication with your peer is PGP encrypted, only you can read it.</li>
|
||||
<li>Lightning fast: the average sovereign trade finishes in ~ 8 minutes. Faster than a single block confirmation!</li>
|
||||
<li>Fully collateralized escrow: your peer is always committed and cannot run away with the funds.</li>
|
||||
<li>Strong incentives system: attempts of cheating are penalized with the slashing of the Sats in the fidelity bond.</li>
|
||||
<li>Guides and video tutorials available at https://learn.robosats.com/watch/en</li>
|
||||
</ul>
|
||||
<p>You can join other cool Robots and get community support at <a href="https://t.me/robosats">our Telegram group</a>.</p>
|
BIN
mobile/fastlane/metadata/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/01.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/02.jpg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/03.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/04.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/05.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
mobile/fastlane/metadata/en-US/images/phoneScreenshots/06.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
1
mobile/fastlane/metadata/en-US/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
Simple and private app to exchange bitcoin for national currencies.
|
12
mobile/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "robosats",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "robosats",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"dependencies": {
|
||||
"@react-native-clipboard/clipboard": "^1.13.2",
|
||||
"@react-native-community/netinfo": "^11.3.0",
|
||||
@ -37,7 +37,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"jest": "^29.7.0",
|
||||
"metro-react-native-babel-preset": "^0.75.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier": "^3.3.2",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
@ -12714,9 +12714,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
|
||||
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
|
||||
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "robosats",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
@ -41,7 +41,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"jest": "^29.7.0",
|
||||
"metro-react-native-babel-preset": "^0.75.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier": "^3.3.2",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
|
@ -1,41 +1,38 @@
|
||||
RoboSats v0.6.0 is now out! :rocket:
|
||||
RoboSats v0.6.2 is now out! :rocket:
|
||||
|
||||
# Changes
|
||||
## New Features
|
||||
### Decentralization
|
||||
RoboSats v0.6.0 introduces a major upgrade, introducing the RoboSats Federation, a decentralized system of independent coordinators to host orders, enhancing the platform's robustness and user experience. This version is a significant step towards decentralization, allowing users to interact with any coordinator seamlessly.
|
||||
|
||||
It's crucial to choose trustworthy coordinators due to the potential risks of malicious activity. The federated client is available for testing at specific URLs, with a stable release planned. Key features include multiple coordinators competing for users, decentralized instances for increased robustness, and a focus on coordinators profiles and trust.
|
||||
|
||||
Learn more in https://learn.robosats.com/robosats/update/pre-release-robosats-decentralized/
|
||||
|
||||
### New avatar generator
|
||||
Your Robot identity is now generated in your client app, when previously, the robot identity was created by the coordinator. This allows now for super-fast Robot avatar and nickname generation that works even if your connection to the coordinators is down. The new robot avatars, are in fact, more diverse and better looking, however, the same token will now yield a different avatar when compared to v0.5.4 (but the robot identity remains the same, also keeping the same nickname).
|
||||
|
||||
## Bug Fixes and Performance Improvements
|
||||
The whole app architecture is new. There might be new bugs, solved bugs, worse performance and better performance: who knows!! :D
|
||||
|
||||
## Special thanks
|
||||
Special thanks to @KoalaSat who has driven some of the largest development pushes needed to get The Federation Layer fully working.
|
||||
## What's new
|
||||
In v0.6.2 we only fix a regression introduced in v0.6.2 that makes the depth chart crash.
|
||||
|
||||
# Android
|
||||
|
||||
The Android app is currently not supported on this early phase of the Federated app.
|
||||
**[Click to download universal RoboSats APK for Android](https://github.com/RoboSats/robosats/releases/download/v0.6.2-alpha/robosats-v0.6.2.alpha-universal.apk)**
|
||||
Smaller bundles for each CPU architecture available in the attachments.
|
||||
|
||||
### Verify the app using GPG:
|
||||
|
||||
1. [Download the ascii armored signature](https://github.com/Reckless-Satoshi/robosats/releases/download/v0.6.2-alpha/robosats-v0.6.2.alpha-universal.apk.asc)
|
||||
|
||||
2. Run this command on a directory that contains the apk file and and the ascii armored signature.
|
||||
`gpg --verify robosats-v0.6.2.alpha-universal.apk.asc`
|
||||
|
||||
3. Verify the signer is actually Reckless-Satoshi (fingerprints match): [B4AB5F19113D4125DDF217739C4585B561315571](https://keys.openpgp.org/vks/v1/by-fingerprint/B4AB5F19113D4125DDF217739C4585B561315571)
|
||||
|
||||
Alternatively you can also verify with the release with the SHA256 checksum.
|
||||
|
||||
# Docker Images
|
||||
|
||||
[Coordinator Backend Image v0.6.0-alpha (Docker Hub)](https://hub.docker.com/r/recksato/robosats/tags?page=1&name=v0.6.0-alpha)
|
||||
[Coordinator Backend Image v0.6.2-alpha (Docker Hub)](https://hub.docker.com/r/recksato/robosats/tags?page=1&name=v0.6.2-alpha)
|
||||
|
||||
|
||||
```bash
|
||||
docker pull recksato/robosats:v0.6.0-alpha
|
||||
docker pull recksato/robosats:v0.6.2-alpha
|
||||
```
|
||||
|
||||
[Client App Image v0.6.0-alpha (Docker Hub)](https://hub.docker.com/r/recksato/robosats-client/tags?page=1&name=v0.6.0-alpha)
|
||||
[Client App Image v0.6.2-alpha (Docker Hub)](https://hub.docker.com/r/recksato/robosats-client/tags?page=1&name=v0.6.2-alpha)
|
||||
|
||||
```bash
|
||||
docker pull recksato/robosats-client:v0.6.0-alpha
|
||||
docker pull recksato/robosats-client:v0.6.2-alpha
|
||||
```
|
||||
|
||||
See [nodeapp/docker-compose.yml](https://github.com/Reckless-Satoshi/robosats/blob/2cd9d748706a8dcc0f03006b483acc6000e0572a/nodeapp/docker-compose.yml) for an example docker-compose usage of the `robosats-client` image.
|
||||
|
@ -1,4 +1,4 @@
|
||||
django==5.0.4
|
||||
django==5.0.6
|
||||
django-admin-relation-links==0.2.5
|
||||
django-celery-beat==2.6.0
|
||||
django-celery-results==2.5.1
|
||||
@ -16,11 +16,10 @@ Pillow==10.1.0
|
||||
python-decouple==3.8
|
||||
requests==2.31.0
|
||||
ring==0.10.1
|
||||
git+https://github.com/RoboSats/Robohash.git@master
|
||||
gunicorn==22.0.0
|
||||
psycopg2==2.9.9
|
||||
SQLAlchemy==2.0.16
|
||||
django-import-export==3.3.8
|
||||
django-import-export==4.0.7
|
||||
requests[socks]
|
||||
shapely==2.0.4
|
||||
python-gnupg==0.5.2
|
||||
|
@ -1,16 +1,13 @@
|
||||
import hashlib
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
from channels.db import database_sync_to_async
|
||||
from channels.middleware import BaseMiddleware
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User, update_last_login
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.http import JsonResponse
|
||||
from rest_framework.authtoken.models import Token
|
||||
from robohash import Robohash
|
||||
|
||||
from api.nick_generator.nick_generator import NickGenerator
|
||||
from api.utils import base91_to_hex, hex_to_base91, is_valid_token, validate_pgp_keys
|
||||
@ -19,9 +16,6 @@ NickGen = NickGenerator(
|
||||
lang="English", use_adv=False, use_adj=True, use_noun=True, max_num=999
|
||||
)
|
||||
|
||||
avatar_path = Path(settings.AVATAR_ROOT)
|
||||
avatar_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
class DisableCSRFMiddleware(object):
|
||||
def __init__(self, get_response):
|
||||
@ -164,21 +158,6 @@ class RobotTokenSHA256AuthenticationMiddleWare:
|
||||
if not user.robot.encrypted_private_key:
|
||||
user.robot.encrypted_private_key = encrypted_private_key
|
||||
|
||||
# Generate avatar. Does not replace if existing.
|
||||
image_path = avatar_path.joinpath(nickname + ".webp")
|
||||
if not image_path.exists():
|
||||
rh = Robohash(hash)
|
||||
rh.assemble(roboset="set1", bgset="any") # for backgrounds ON
|
||||
with open(image_path, "wb") as f:
|
||||
rh.img.save(f, format="WEBP", quality=80)
|
||||
|
||||
image_small_path = avatar_path.joinpath(nickname + ".small.webp")
|
||||
with open(image_small_path, "wb") as f:
|
||||
resized_img = rh.img.resize((80, 80))
|
||||
resized_img.save(f, format="WEBP", quality=80)
|
||||
|
||||
user.robot.avatar = "static/assets/avatars/" + nickname + ".webp"
|
||||
|
||||
update_last_login(None, user)
|
||||
user.save()
|
||||
|
||||
|
@ -23,8 +23,6 @@ from .celery.conf import * # noqa
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
|
||||
|
||||
@ -32,8 +30,10 @@ STATIC_URL = "/static/"
|
||||
SECRET_KEY = config("SECRET_KEY")
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
STATIC_URL = "static/"
|
||||
STATIC_ROOT = "/usr/src/static/"
|
||||
|
||||
# RoboSats version
|
||||
with open("version.json") as f:
|
||||
@ -42,9 +42,6 @@ with open("version.json") as f:
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
if config("DEVELOPMENT", default=False):
|
||||
DEBUG = True
|
||||
STATIC_ROOT = "frontend/static/"
|
||||
|
||||
AVATAR_ROOT = STATIC_ROOT + "assets/avatars/"
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
config("HOST_NAME"),
|
||||
@ -228,10 +225,6 @@ USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
ASGI_APPLICATION = "robosats.routing.application"
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
|
@ -10,9 +10,9 @@ curl --parallel -o lightning.proto https://raw.githubusercontent.com/lightningne
|
||||
-o router.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/routerrpc/router.proto \
|
||||
-o signer.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/signrpc/signer.proto \
|
||||
-o verrpc.proto https://raw.githubusercontent.com/lightningnetwork/lnd/master/lnrpc/verrpc/verrpc.proto \
|
||||
-o hold.proto https://raw.githubusercontent.com/daywalker90/lightning/cln-grpc-hold/proto/hold.proto \
|
||||
-o primitives.proto https://raw.githubusercontent.com/ElementsProject/lightning/v23.08/cln-grpc/proto/primitives.proto \
|
||||
-o node.proto https://raw.githubusercontent.com/ElementsProject/lightning/v23.08/cln-grpc/proto/node.proto
|
||||
-o hold.proto https://raw.githubusercontent.com/daywalker90/holdinvoice/master/proto/hold.proto \
|
||||
-o primitives.proto https://raw.githubusercontent.com/ElementsProject/lightning/v24.05/cln-grpc/proto/primitives.proto \
|
||||
-o node.proto https://raw.githubusercontent.com/ElementsProject/lightning/v24.05/cln-grpc/proto/node.proto
|
||||
|
||||
echo -n "Done\nBuilding api from GRPC specs..."
|
||||
python3 -m grpc_tools.protoc --proto_path=googleapis:. --python_out=. --grpc_python_out=. lightning.proto invoices.proto router.proto signer.proto verrpc.proto
|
||||
|
7
tests/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Run e2e tests
|
||||
|
||||
```
|
||||
docker compose -f docker-tests.yml --env-file tests/compose.env up -d
|
||||
docker exec coordinator coverage run manage.py test
|
||||
docker exec coordinator coverage report
|
||||
```
|
2013
tests/api_specs.yaml
17
tests/bitcoind/entrypoint.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Start bitcoind in the background
|
||||
bitcoind "$@" &
|
||||
|
||||
# Wait for bitcoind to be ready
|
||||
while ! bitcoin-cli --regtest --rpcuser=test --rpcpassword=test getblockchaininfo 2>/dev/null | grep '"verificationprogress":'; do
|
||||
echo "Waiting for bitcoind to be ready..."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Run initialization commands
|
||||
bitcoin-cli --regtest --rpcuser=test --rpcpassword=test createwallet default
|
||||
bitcoin-cli --regtest --rpcuser=test --rpcpassword=test generatetoaddress 1 $(bitcoin-cli --regtest --rpcuser=test --rpcpassword=test getnewaddress)
|
||||
|
||||
# Bring bitcoind to the foreground
|
||||
wait
|
@ -2,6 +2,6 @@ ROBOSATS_ENVS_FILE=".env-sample"
|
||||
|
||||
BITCOIND_VERSION='24.0.1'
|
||||
LND_VERSION='v0.17.0-beta'
|
||||
CLN_VERSION='v23.08.1'
|
||||
CLN_VERSION='v24.05'
|
||||
REDIS_VERSION='7.2.1'
|
||||
POSTGRES_VERSION='14.2'
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"major": 0,
|
||||
"minor": 6,
|
||||
"patch": 0
|
||||
"patch": 2
|
||||
}
|
||||
|