Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: paytaca/watchtower-cash
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1.1
Choose a base ref
...
head repository: paytaca/watchtower-cash
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Loading
Showing 921 changed files with 76,616 additions and 1,639 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
watchtowerui
static
.env
anyhedge/js/node_modules
38 changes: 38 additions & 0 deletions .env_template
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
ENV=prod

POSTGRES_HOST=postgres
POSTGRES_DB=watchtower
POSTGRES_USER=postgres
POSTGRES_PASSWORD=badpassword

GOOGLE_APP_CREDENTIALS_PATH=compose/firebase-admin-credentials.json
GOOGLE_CLOUD_MESSAGING_API_KEY=gcm-api-key
APNS_CERTIFICATE_PATH=compose/certificate.pem
APNS_AUTH_KEY_ID=
APNS_TEAM_ID=
APNS_USE_ALTERNATIVE_PORT=
APNS_TOPIC=

TELEGRAM_BOT_TOKEN=990633125:AAGtPGGfYwRHy1hbH2sUGsUrNChYVnE9TaU
TELGRAM_BOT_USER=watchtower_bot

@@ -15,3 +25,31 @@ SLACK_VERIFICATION_TOKEN=522RHGyhPmHZmFfYPvOyNeX8
SLACK_CLIENT_ID=1032025723891.1331971347957
SLACK_CLIENT_SECRET=6ca3325928e9a1baa98c85d07a2d1dc0
SLACK_SIGNING_SECRET=ea95b6aae1f79344bb705bab61e986d6

SBCH_START_BLOCK=3790573
BLOCK_TO_PRELOAD=25
SBCH_BLOCKS_PER_TASK=25
TOTP_SECRET_KEY=xxxx

ANYHEDGE_LP_BASE_URL=https://liquidity.anyhedge.com
ANYHEDGE_DEFAULT_ORACLE_RELAY=oracles.generalprotocols.com
ANYHEDGE_DEFAULT_ORACLE_PORT=7083
ANYHEDGE_DEFAULT_ORACLE_PUBKEY=02d3c1de9d4bc77d6c3608cbe44d10138c7488e592dc2b1e10a6cf0e92c2ecb047
ANYHEDGE_SETTLEMENT_SERVICE_AUTH_TOKEN=9a3133f1f6d9ed2b3ccc0f0b7ab40702a6a4b3e5e6d1493da7da5832b2ccaf72

SIDESHIFT_SECRET_KEY=70f2972189e0dcd6b0c008a360693adf

BCHJS_TOKEN=
SERVICER_ADDR=
SERVICER_PK=
SERVICE_FEE=1000
ARBITRATION_FEE=1000
FERNET_KEY=

COINGECKO_API_KEY=

P2P_EXCHANGE_SLACKBOT_USER_TOKEN=
P2P_EXCHANGE_SLACK_CHANNEL=

STABLEHEDGE_FERNET_KEY=oAiC1fAP0QHbP2LByd67bfRngBcep_Eo6kCZ4at-C4o= # 32byte in base64
STABLEHEDGE_AUTH_KEY_WALLET_WIF=KyKqHDydFEpFK53o6bJrp5LrGhreaFbLruJUU2Fm7D6h6aDAMtVMf
29 changes: 29 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Description
Describe your changes in detail. Why is this change required? What problem does it solve? Include **link** or references to relevant issues or tasks (eg. Bug ID - 1).

Acceptance criteria for the issue or task could also be included here.

Fixes # (issue)


## Screenshots (if applicable):
(Include screenshots for UI-related changes)


## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update


## Test Notes
How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so others can reproduce (e.g. npm install for new dependencies, quasar dev, etc.). Include relevant details for automated or manual testing.

Will the applied changes affect other areas of the code? Will the applied changes affect functionalities of other features?


## @mentions
Mention the person or team responsible for reviewing the proposed changes.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -205,3 +205,11 @@ node_modules
/spiceslp/slp-sdk/
supervisord.pid
.env_prod
.env_mainnet
.env_chipnet
.env_testnet

# exclude pem certificates
**.pem
images
compose/firebase-admin-credentials.json
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python 3.10.9
44 changes: 44 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
FROM nikolaik/python-nodejs:python3.8-nodejs15-slim

RUN apt-key adv --refresh-keys --keyserver keyserver.ubuntu.com
RUN apt-get update -y
RUN apt-get -y install build-essential sudo postgresql libpq-dev postgresql-client curl \
postgresql-client-common libncurses5-dev libjpeg-dev zlib1g-dev git wget redis-server && \
wget -O /usr/local/bin/wait-for-it.sh https://raw.githubusercontent.com/vishnubob/wait-for-it/8ed92e8cab83cfed76ff012ed4a36cef74b28096/wait-for-it.sh && \
chmod +x /usr/local/bin/wait-for-it.sh

RUN pip install --upgrade pip
COPY ./requirements.txt requirements.txt
# RUN pip install --no-cache-dir -r requirements.txt
RUN pip install -r requirements.txt

# For running javascript
RUN sudo apt install -y curl
RUN sudo apt-get update --allow-releaseinfo-change
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
RUN sudo apt install nodejs -y --allow-change-held-packages

COPY ./main/js/package*.json /code/main/js/
RUN npm install --prefix /code/main/js --legacy-peer-deps

COPY ./anyhedge/js/package*.json /code/anyhedge/js/
RUN npm install --prefix /code/anyhedge/js --legacy-peer-deps

COPY ./rampp2p/js/package*.json /code/rampp2p/js/
RUN npm install --prefix /code/rampp2p/js --legacy-peer-deps

COPY ./cts/js/package*.json /code/cts/js/
RUN npm install --prefix /code/cts/js

COPY ./paymentvault/js/package*.json /code/paymentvault/js/
RUN npm install --prefix /code/paymentvault/js

COPY ./stablehedge/js/package*.json /code/stablehedge/js/
RUN npm install --prefix /code/stablehedge/js
COPY . /code
WORKDIR /code

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

ENTRYPOINT [ "sh", "entrypoint.sh" ]
28 changes: 4 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -7,28 +7,8 @@
- Running on an elastic infrastructure that scales according to demand
- The goal is to achieve and guarantee 99.99% uptime and reliability

## Subscription
## Docs and Libraries

Watchtower will only "watch" addresses that are subscribed to by the users.

To subscribe an address, send a POST request to `https://watchtower.cash/api/subscription/` with the BCH or SLP address and the URL where the webhook calls will be sent to (optional). A sample request using `curl` is shown below:
```bash
curl -i -X POST
-H "Content-Type: application/json"
-d '{"address":"simpleledger:qr89dn8th7zj4n74vrqyce4vld522spunv3wkdqd5z", "web_url": "https://0f27bf32c670.ngrok.io"}'
https://watchtower.cash/api/subscription/
```

If the `web_url` is given, a POST request notification is sent to the URL whenever a new transcation is detected. A sample notification is shown below:
```python
{
'source': 'WatchTower',
'address': 'simpleledger:qr89dn8th7zj4n74vrqyce4vld522spunv3wkdqd5z',
'txid': '2cb0b57c9a8cad95d08f9b408b77802961d473abf71f0ec30669f9c2272f3d82',
'token': '7f8889682d57369ed0e32336f8b7e0ffec625a35cca183f4e81fde4e71a538a1',
'index': 1,
'amount': 321.0
}
```

(More docs to follow...)
- [API Docs and Browser](https://watchtower.cash/api/docs/)
- [Javascript/NodeJS Package](https://github.com/paytaca/watchtower-cash-js)
- [Python Package](https://github.com/paytaca/watchtower-cash-py)
161 changes: 161 additions & 0 deletions anyhedge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Anyhedge
Watchtower's API for managing anyhedge contracts

## API
List views are always paginated by limit-offset, defaults to limit=10,offset=0
### HedgePosition - `/anyhedge/hedge-positions/.*`
- List, Create, & Detail view: uses rest-framework's default. Detail view accepts `address` instead of `id`
- POST:`/fund_gp_lp_contract/`
- performs the last step of creating hedge contracts with *General Protocol*'s liquidity provider which is to submit funding utxo to GP's API;
- then saves the contract in the server
- GET:`/gp_lp_contract_fee/`
- returns a fee details that is added before proceeding to propose contract to GP's liquidity provider
- returns an empty response if there is no fee configured
- POST:`/submit_funding_proposal/`
- Submit a utxo for the contract's funding
- This API will attempt to broadcast the funding transaction if funding utxo for both hedge and long are present. See task `complete_contract_funding` in [Other tasks](#other-tasks---anyhedgetasks) below.
- Used for generated contracts through P2P
- Fails silently, or will not return an error, if fails to broadcast funding.
- POST:`/{address}/complete_funding/`
- Attempts to broadcast a funding transaction from the contract's submitted funding proposal, if exists.
- See task `complete_contract_funding` in [Other tasks](#other-tasks---anyhedgetasks) below.
- Used for generated contracts through P2P
- POST:`/{address}/validate_contract_funding/`
- See `validate_contract_funding`in [Other tasks](#other-tasks---anyhedgetasks) task below
- GET:`/summary/`
- Returns aggregated data of hedge contracts, given the filter parameters provided.
- Returns the total nominal unit of hedge side, grouped by oracle_pubkey/asset.
- Returns total satoshis of long side, grouped by oracle_pubkey/asset.
- POST: `{address}/mutual_redemption/`
- Used for creating a mutual redemption offer
- Optionally pass signatures for the payout transaction.
- Will proceed to broadcast the payout transaction if signatures of both parties are present. See task `redeem_contract` in [Other tasks](#other-tasks---anyhedgetasks) below
- Currently for generated contracts through P2P only
- POST: `{address}/cancel_mutual_redemption/`
- For cancelling/declining a mutual redemption proposal, if the `initiator` is making this request, it is considered `cancelling` else it is `declining` the offer.
- This api requires
- `position`: the position that is making the API request
- `signature`: an edcsa signature used for authenticating the request
- Signature verification uses the following information depending on `position`:
- message = `hedge_schnorr_sig`/ `long_schnorr_sig`
- verifying_pubkey = `hedge_position.hedge_pubkey` / `hedge_position.long_pubkey`
- POST: `{address}/complete_mutual_redemption/`
- See task `redeem_contract` in [Other tasks](#other-tasks---anyhedgetasks) below
- Currently for generated contracts through P2P only
- POST: `{address}/cancel/`
- Flags a hedge position as cancelled
- This api requires
- `position`: the position that is making the API request
- `signature`: an edcsa signature used for authenticating the request
- `timestamp`: Used as part of the message for generating/verifying the signature. used to set the cancelled_at timestamp
- Signature verification uses the following:
- message = `{unix_timestamp}:{contract_address}`
- verifying_pubkey = `hedge_position.hedge_pubkey` / `hedge_position.long_pubkey`
- This will fail if the hedge position is already cancelled or funded
### Oracles & Price messages
- `/anyhedge/oracles/`
- provide a list of oracles saved in the db. provides oracles pubkey and asset info(asset name, currency, & decimals).
- `/anyhedge/oracles/{pubkey}/`
- retrieve a single oracle
- `/anyhedge/price-messages/`
- provides a list of oracle price messages, can be filtered by `pubkey`, `timestamp`(range), `price_sequence`(range), and/or `message_sequence`(range)
### HedgePositionOffer - `/anyhedge/hedge-position-offers/.*`
More info on [HedgePositionOffer lifecycle](#hedgepositionoffer-lifecycle)
- List view: rest-framework's default, can be filtered by `wallet_hash`, `exclude_wallet_hash`, `statuses`
- Create view:
- rest-framework's default
- POST:`/find_match/` - finds a matching hedge position offer given a set of parameters
- if there is no suitable match, it will return a list of offers that is similar to the expected match
- POST: `{id}/accept_offer/`
- used for accepting an existing hedge position offer created by other users
- the request payload will contain the other information needed to compile a contract in
- the accepted offer's status will be set to "accepted"
- while the offer is in "accepted" state, the offer will not be matched in the `/find_match/` endpoint
- there will be a `settlement_deadline` where the user that accepted the offer must settle the offer by providing its funding utxo(through `{id}/settle_offer/` API)
- POST:`/{id}/settle_offer/`
- to settle an offer, the offer must be in "accepted" state
- the request payload must contain the funding utxo for the counterparty
- will create a hedge position from the HedgePositionOffer instance and its HedgePositionOfferCounterParty info
- will update the offer's status to "settled"
- "settled" offers no longer be used in the `/find_match/` endpoint

### Other endpoints
- Websocket updates `ws/anyhedge/updates/{wallet_hash}/$`
- Sends messages for a `wallet_hash` to update changes in the contracts
- Messages are generally in the following structure:
```
{ "resource": <string>, "action": <string>, "meta": <any> }
```
## Admin Interface
### Anyhedge \ Oracles
Managing oracles supported by the server
### Constance \ Config
Configuration for setting a fee for P2P & General Protocol's LP contracts
---------------
## Background tasks
### Scheduled tasks
- `update_oracle_prices` - `anyhedge.tasks.check_new_price_messages`
- Retrieves the latest price messages of oracles saved in db.
- `check_new_price_messages()` task retrieves list of oracle pubkeys, then calls new task `check_new_oracle_price_messages(oracle_pubkey)`
- `check_new_oracle_price_messages` will retrieve at most 10 latest price messages of an oracle. It will check the latest price timestamp saved in db and reduce the number of price messages accordingly to `(latest_timestamp - current_timestamp) / 60 seconds` as new price messages are generated per minute.
- `update_anyhedge_contract_settlements` - `anyhedge.tasks.update_matured_contracts`
- The contracts handled if the apply to the following conditions:
- contract is funded
- no settlement transaction saved in db
- maturity_timestamp is less than current timestamp
- Contracts will be passed to either subtasks:
- `update_contract_settlement_from_service`: if the contract has a settlement service (Accessed through `HedgePosition.settlement_service`)
- `settle_contract_maturity`: if the contract has no `settlement_service`, implying the contract should be settled by the server.
- `update_anyhedge_contracts_for_liquidation` - `anyhedge.tasks.update_contracts_for_liquidation`
- Checks for unsettled & funded contracts that are valid for liquidation. More details in `anyhedge.utils.settlement.get_contracts_for_liquidation()` function.
- Retrieved contracts are passed to `liquidate_contract(contract_address, message_sequence)` task
### Other tasks - `anyhedge.tasks.*`
- `update_contract_settlement`
- Main task for updating and saving a contract's settlement data, will either call `update_contract_settlement_from_service` or `update_contract_settlement_from_chain`.
- `update_contract_settlement_from_service(address)`
- Uses `anyhedge.utils.contract.get_contract_status` to get the contract's status directly from its settlement service, if exists, then saves the data to db.
- `update_contract_settlement_from_chain(address)`
- Searches for the contract's settlement tx in the blockchain using `anyhedge.utils.settlement.search_settlement_tx` function, then saves the data to db, if exists.
- `complete_contract_funding`
- The task for submitting/broadcasting the funding transaction of a contract.
- Uses `anyhedge.utils.funding.search_funding_tx` function to search for the funding transaction in the blockchain before proceeding in case it exists but wasnt saved in db.
- `validate_contract_funding`
- Uses `anyhedge.utils.funding.validate_funding_transaction` to search for the funding tx of a contract in the blockchain.
- If the tx exists, it will save the data: `funding_output`, `funding_satoshis`, `fee_output` `fee_satoshis` to `HedgePositionFunding` model. Then flag the contract's funding transaction as valid.
- `redeem_contract`
- The task for creating and broadcasting the mutual redemption transaction of a contract.
- Before proceeding, it will check if the contract is already settled by checking the db or using the `update_contract_settlement` task
------
## Other
### JS scripts
- Anyhedge app takes advantage of functions from `@generalprotocol/anyhedge` library to:
- compiling anyhedge contract, which generates the contract address, parameters, and metadata.
- generating the funding transaction from the given utxos, also has transaction checks to validate that the utxos' satoshis are valid
- parsing settlement data from raw transactions.
- generating the settlement transaction for all cases(maturation, liquidation, mutual redemption), also checks the validity of data (e.g. price messages, & payout satoshis that each party gets)
- fetching, validated, and parsing oracle price messages.
- `anyhedge.js.runner.AnyhedgeFunctions`
- JS functions from the `anyhedge.js.src.funcs` are loaded into `anyhedge.js.runner.AnyhedgeFunctions` class to provide an abstraction on running them.
- Functions are loaded by running `anyhedge.js.src.load.js` which returns the function names.
- Functions in `anyhedge.js.runner.AnyhedgeFunctions` accept will only accept _positional arguments_ that are JSON serializable
### HedgePositionOffer lifecycle
NOTE: `settled` status for `HedgePositionOffer` only implies that the offer has an `HedgePosition` created
1. User1 creates a `HedgePositionOffer`, by default the status is `pending`
- The created position offer will be part of the pool in `/anyhedge/hedge-position-offers/find_match/` API
2. Other users (e.g. User2) can accept the offer created by User1. User2 accepts User1’s offer by providing his `pubkey`, `address`, & `wallet_hash`. The server then gets the latest price of the oracle pubkey (from User1’s offer) and proceeds to construct a contract to save the contract address. After this, the status of the offer is changed to `accepted`
3. After the offer is changed to `accepted`, a settlement deadline is set (a fixed duration after accepting the offer). The counter party(User2) can check the contract details (in the app) to verify.
1. If the counter party chooses to continue, the counter party(User2) must then provide a funding UTXO to settle the offer.
2. If not, the counter party(User2) can cancel accepting the position offer which will revert the position offer into a `pending` state(returning it back in the pool for finding match). This can be skipped & will automatically be reverted after the settlement deadline.
4. After the counter party(User2) submits a funding UTXO, a `HedgePosition` instance is created using the offer’s data then the offer instance’s status is changed to `settled`
Empty file added anyhedge/__init__.py
Empty file.
Loading