get10101 / itchysats Goto Github PK
View Code? Open in Web Editor NEWCFDs on Bitcoin.
Home Page: https://itchysats.network
License: MIT License
CFDs on Bitcoin.
Home Page: https://itchysats.network
License: MIT License
Currently the maker auto-accepts it (a shortcut to allow testing other features)
The taker should be able to say that he wants to roll over the contract for another iterations.
The maker should only accept this if the taker should not have gotten liquidated already.
Proposal:
Initially we can make this a manual step, e.g. the user has to click a button to say: roll over
. Eventually I'd see this as an automated action. Whenever the user comes online, he rolls over to the next iteration.
Note: do later because we can test logic without being able to close
Likely, we want some kind of UI indicator, which transactions of a CFD have been broadcasted and what their state is.
This should be the default user interaction.
Instead of a static offer the maker has UI fields to create an offer.
Currently the taker panics because we pass a ZERO
Bitcoin amount when building params.
Make construction of party params work.
Currently the refund timeout is depicted in blocks
This is risky as the contract term expires at a fixed point of time, i.e. when the oracle publishes the attestation. Refund should be possible shortly afterwards.
(Note: We cannot use an absolute timelock here because it would allow a malicious user to publish an old state and immediately publish the refund transaction.)
On average Bitcoin has a block every 10 minutes. There are times when blocks are showing up faster or slower. I can happen that a relative timelock in blocks could be faster than a relative timelock in seconds.
Hence, it could happen that the refund timelock expires faster than expected allowing an user to refund before the oracle would publish its signature.
Our API would support witching from blocks to time easily but care should be taken when doing so:
see https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki
If bit (1 << 31) of the sequence number is set, then no consensus meaning is applied to the sequence number and can be included in any block under all currently possible circumstances.
If bit (1 << 31) of the sequence number is not set, then the sequence number is interpreted as an encoded relative lock-time.
The sequence number encoding is interpreted as follows:
Bit (1 << 22) determines if the relative lock-time is time-based or block based: If the bit is set, the relative lock-time specifies a timespan in units of 512 seconds granularity. The timespan starts from the median-time-past of the output’s previous block, and ends at the MTP of the previous block. If the bit is not set, the relative lock-time specifies a number of blocks.
The flag (1<<22) is the highest order bit in a 3-byte signed integer for use in bitcoin scripts as a 3-byte PUSHDATA with OP_CHECKSEQUENCEVERIFY (BIP 112).
Automate embedding of frontend artifacts in the respective binaries and serve them on the same port as the HTTP API.
Outcome should be a self-contained binary that serves its own frontend once started.
At the moment we load and save types by using serde_json::to_string
and serde_json::from_str
directly in code.
sqlx offers en- and de-coding traits that would make this code more readable and easier to use:
https://docs.rs/sqlx/0.5.7/sqlx/trait.Decode.html
https://docs.rs/sqlx/0.5.7/sqlx/derive.Decode.html
What we currently have:
The maker currently has to enter a price manually when pricing the CFD.
This is quite cumbersome and can be optimized.
In an initial iteration I propose that we change the fields to:
OrderPrice
have an editable field Spread
(in percent) that is defaulted to 3%
OrderPrice
field non-editableOrderPrice
from current price and spread.(3.) can easily be achieved with a calculation endpoint (similar to the taker's /calculate/margin
) that is triggered on either a price change or a spread change.
Note: This ticket is depending on having a price feed as described in #96
Note: This could eventually be extended with automated offer re-publication, but that is out of scope for this ticket.
We need an actor that interacts with the olivia instance at https://h00.ooo/ and informs other actors about attestations to price events.
The above two can easily be added later because they are just about sending a message to the correct address.
A minimal useful step is to log each price attestation.
Upon restarting a deamon the application has to be able to handle already existing Cfds.
This means, when starting the Cfd actor and the monitoring actor we have to reason about the Cfd state and might want to trigger certain interaction so the Cfd is properly loaded and displayed, as well as monitoring is picked up to trigger state changes.
When implementing the monitor actor we had planned to just load all Cfds from the db and pass them to the monitor actor so it could trigger monitoring as needed based on the Cfd state.
i.e.: match statement in the constructor of monitor actor that triggers the remaining part of the transaction monitoring based on the state.
For the Cfd actor we did not invest too much time in what a restart would require. The assumption is that it does no have to do anything, because it always gets messages from another actor to transition the Cfd into a new state.
One thing we will have to think about is how to deal with Cfds that upon restart are in a state that is prior to PendingOpen
. I think such Cfds should be transitioned to a state SavelyAborted
by the Cfd actor upon a message from whom?
i.e. we don't go on with any Cfd that was not completely set up.
Note: I would still not delete SafelyAborted
Cfds for now, including all states (since we always append the state it will keep it unless that logic is changed). We can filter them out upon initial load (...). Once we have more confidence in the implementation we could add removing them.
For some reason, the serving the frontend via rocket is broken.
Happens for release and debug builds (i.e. it has nothing to do with the actual embedding)
Configure tracing
for both maker and taking so we have proper logs and not just prints.
We will initially host the broker with a publicly accessible IP. We need to guard the API and frontend with some authentication to make sure no one can muck around with our instance.
recommentation: sqlx + sqlite similar to oracle impl
recommentation: Append stuff as you learn things. i.e. keep appending state so you see all the states you went through. (we should not lose data that is important when updating storage)
Publish offer
button that publishes a static offerConfirm
buttonWe should sort out everything that is between us and sending a download link to someone for trying out the software. This does not include features but only infrastructure, shipping etc. We can "play" with the software in an unfinished state. The point of prioritizing this is to avoid surprises after we are "done" that prevent us from trying out the software.
The CFD profit is dependent on the current price (calculated). We should take the current price into account for the CFD feed.
This requires some kind of price feed (from the Oracle or a component that publishes a reliable price related to the Oracle's price).
With the current model, offers will "disappear" if a taker took it.
With this model we have an in-between state on the maker side: the state in between of a taker requesting to take an offer and the maker accepting/rejecting it.
Differently said: multiple takers might want to take the same offer from the same maker.
This has some further implications:
Atm an incoming TakeOrder
request will overwrite an existing one. This can lead to a problem where the user (the maker) wants to accept an order for one user but it gets overwritten by an incoming request from a different taker.
The question is how do we want to deal with this?
When the time comes, one click / action should be all we need to do create a "release" that provides us with download-able production binaries.
Note: We might not need a full blown wallet view, might be good enough to integrate balance display and only have the CFDs to represent the "transaction view".
Having the oracle attest to each individual price point results in an explosion of signatures. We need to define and implement a more clever solution to this, likely along the lines of what is specified by dlc-specs.
The oracle will need to expose an API that both the broker and trader daemon can consume. We need to define this API.
After rejecting first offer and creating a new one which was meant to be accepted (to test the happy path), I discovered that I could not accept or reject the offer.
Maker log:
2021-09-24 10:27:38 INFO POST /api/order/sell application/json:
2021-09-24 10:28:02 DEBUG Taker wants to take an order taker_id=e53977ec-fb86-4e81-bd23-d1a45320153f quantity=101 order_id=15762b72-9644-4da7-80d4-ad62d56cc74b
2021-09-24 10:28:08 INFO POST /api/order/reject text/plain:
2021-09-24 10:28:08 DEBUG Maker rejects an order msg.order_id=15762b72-9644-4da7-80d4-ad62d56cc74b
2021-09-24 10:28:15 INFO BitMex quote updated bid=44638 ask=44638.5
2021-09-24 10:28:23 INFO POST /api/order/sell application/json:
2021-09-24 10:28:30 DEBUG Taker wants to take an order taker_id=e53977ec-fb86-4e81-bd23-d1a45320153f quantity=101 order_id=2438638b-8897-4f1c-ab25-50ae1443852d
2021-09-24 10:28:35 INFO POST /api/order/accept text/plain:
2021-09-24 10:28:35 DEBUG Maker accepts an order msg.order_id=2438638b-8897-4f1c-ab25-50ae1443852d
thread 'rocket-worker-thread' panicked at 'called `Result::unwrap()` on an `Err` value: no rows returned by a query that expected to return at least one row', daemon/src/maker_cfd_actor.rs:276:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'thread 'rocket-worker-threadrocket-worker-thread' panicked at '' panicked at 'all branches are disabled and there is no else branchall branches are disabled and there is no else branch', ', daemon/src/routes_maker.rsdaemon/src/routes_maker.rs::3232::55
After restarting the maker the faulty CFD is still stuck in the database and still crashes the maker when one of the buttons is clicked.
I realized that a taker can only take once and not again:
How to reproduce:
I'm not sure if this was introduced after #119 or was already present before.
It looks like the connection between maker and taker is lost:
Maker log
2021-09-24 07:45:04 DEBUG Taker wants to take an order taker_id=c7cbc045-96d2-436c-92a8-d56028545249 quantity=200 order_id=ddc8b504-2485-46cf-884d-a081b3017b34
2021-09-24 07:45:07 INFO POST /api/order/reject text/plain:
2021-09-24 07:45:07 DEBUG Maker rejects an order msg.order_id=ddc8b504-2485-46cf-884d-a081b3017b34
2021-09-24 07:45:07 ERROR e=channel closed
Taker log:
2021-09-24 07:45:04 INFO Taking current order: Order { id: OrderId(ddc8b504-2485-46cf-884d-a081b3017b34), trading_pair: BtcUsd, position: Sell, price: Usd(10000), min_quantity: Usd(100), max_quantity: Usd(1000), leverage: Leverage(5), liquidation_price: Usd(8368.200836820083682008368201), creation_timestamp: SystemTime { tv_sec: 1632433368, tv_nsec: 569400000 }, term: 28800s, origin: Theirs }
2021-09-24 07:45:07 DEBUG Order rejected order_id=ddc8b504-2485-46cf-884d-a081b3017b34
In order to be able to price the CFD as the maker we will need the current price feed. Given, that the maker might want to open counterpositions on Bitmex we should potentially use the Bitmex price feed.
This will enable: #33
I recommend using Bitmex API feed. Our "usual" source (kraken) does not offer CFDs.
I played around with this library which works nicely: https://github.com/dovahcrow/bitmex-rs/
unfortunately it hasn't been released for a while but the latest master works great.
It comes with ws subscription. This gives you the latest rates
let mut client = BitMEXWebsocket::new().await?;
client
.send(Command::Subscribe(vec![
Topic::QuoteBin1m(Some("XBTUSD".to_string())),
]))
.await?;
while let Some(msg) = client.next().await {
println!("{:?}", msg);
}
Originally posted by @thomaseizinger in #16 (comment)
The oracle will need to be hosted somewhere. Make a decision on where.
Originally posted by @thomaseizinger in #16 (comment)
Building the transactions involves the need for funding (InsufficientFunds
).
The idea is to fund maker and taker and have two known seed that result in funded BTC wallets that will be shared.
The collateral is still missing in the UI. Without the collateral to be locked up in BTC there is no DLC, so we should get that in.
We will have to find a way to let the taker daemon handle the calc upon the taker changing the quantity.
Since this value is independent of the offer I think we will have to handle it separately.
Something like:
POST /collateral
{ quantity, price, leverage }
collateral
event to the feed and update collateral UI element upon feed updatesInitial plan is to use a curve that corresponds to a clipped version of the function
As a first target, we want to be able to do a full run-through of the happy path:
The taker daemon should be more resilient upon startup. If the maker is not available it should print a message like maker currently not available, retrying connection setup in x seconds...
and then retry setting up the connection.
We should do something similar as with the BitMex's rate:
watch
channel.This is important to fix because it currently forces us to hardcode localhost
and port in the frontend soure-code!
In production, the frontend will be served from the same port as the API, hence the code needs to use relative paths to hit the correct endpoints.
At the moment, we are using the attestation scheme defined in the DLC specs. This is not compatible with Olivia and might actually have security problems if used in the wrong way.
The implementation in Olivia is here: https://github.com/LLFourn/olivia/blob/master/olivia_secp256k1/src/lib.rs
We need to adapt our protocol to be compatible with that.
Following our decision on no-automation-empower-the-user, both parties should be able to trigger the non-collaborative settlement path by broadcasting the necessary transactions.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.