Skip to content

Commit febc2b8

Browse files
Merge branch 'master' into main
2 parents 77c3567 + 35330f7 commit febc2b8

File tree

3 files changed

+88
-68
lines changed

3 files changed

+88
-68
lines changed

README.public.md

Lines changed: 34 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ To stop the service:
6262
docker kill liquity
6363
```
6464

65-
### Running with `docker-compose`
66-
67-
See [packages/dev-frontend/docker-compose.yml](packages/dev-frontend/docker-compose.yml) for a simple example of docker-compose configuration.
68-
6965
### Configuring a public Dev UI
7066

7167
If you're planning to publicly host Dev UI, you might need to pass the Docker container some extra configuration in the form of environment variables.
@@ -93,9 +89,42 @@ docker run --name liquity -d --rm -p 3000:80 \
9389

9490
Remember to replace the environment variables in the above example. After executing this command, open http://localhost:3000/ in a browser with MetaMask installed, then switch MetaMask to the account whose address you specified as FRONTEND_TAG to begin setting the kickback rate.
9591

92+
### Next steps for hosting Dev UI
93+
94+
Now that you've set a kickback rate, you'll need to decide how you want to host your frontend. There are way too many options to list here, so these are going to be just a few examples.
95+
96+
#### Example 1: using static website hosting
97+
98+
Dev UI doesn't require any database or server-side computation, so the easiest way to host it is to use a service that lets you upload a folder of static files (HTML, CSS, JS, etc).
99+
100+
To obtain the files you need to upload, you need to extract them from a Dev UI Docker container. If you were following the guide for setting a kickback rate and haven't stopped the container yet, then you already have one! Otherwise, you can create it with a command like this (remember to use your own `FRONTEND_TAG` and `INFURA_API_KEY`):
101+
102+
```
103+
docker run --name liquity -d --rm \
104+
-e FRONTEND_TAG=0x2781fD154358b009abf6280db4Ec066FCC6cb435 \
105+
-e INFURA_API_KEY=158b6511a5c74d1ac028a8a2afe8f626 \
106+
liquity/dev-frontend
107+
```
108+
109+
While the container is running, use `docker cp` to extract Dev UI's files to a folder of your choosing. For example to extract them to a new folder named "devui" inside the current folder, run:
110+
111+
```
112+
docker cp liquity:/usr/share/nginx/html ./devui
113+
```
114+
115+
Upload the contents of this folder to your chosen hosting service (or serve them using your own infrastructure), and you're set!
116+
117+
#### Example 2: wrapping the Dev UI container in HTTPS
118+
119+
If you have command line access to a server with Docker installed, hosting Dev UI from a Docker container is a viable option.
120+
121+
The Dev UI Docker container simply serves files using plain HTTP, which is susceptible to man-in-the-middle attacks. Therefore it is highly recommended to wrap it in HTTPS using a reverse proxy. You can find an example docker-compose config [here](packages/dev-frontend/docker-compose-example/docker-compose.yml) that secures Dev UI using [SWAG (Secure Web Application Gateway)](https://github.com/linuxserver/docker-swag) and uses [watchtower](https://github.com/containrrr/watchtower) for automatically updating the Dev UI image to the latest version on Docker Hub.
122+
123+
Remember to customize both [docker-compose.yml](packages/dev-frontend/docker-compose-example/docker-compose.yml) and the [site config](packages/dev-frontend/docker-compose-example/config/nginx/site-confs/liquity.example.com).
124+
96125
## Building Dev UI from source
97126

98-
If you want to customize the functionality or look of Dev UI, or you don't want to use the Docker image, you'll need to build it yourself from source.
127+
If you want to customize the functionality or look of Dev UI, you'll need to build it yourself from source.
99128

100129
### Prerequisites
101130

@@ -138,66 +167,3 @@ Your custom built Dev UI can be configured by putting a file named `config.json`
138167
"infuraApiKey": "158b6511a5c74d1ac028a8a2afe8f626"
139168
}
140169
```
141-
142-
## Supplying Hints to Trove operations
143-
144-
Troves in Liquity are recorded in a sorted doubly linked list, sorted by their NICR, from high to low. NICR stands for the nominal collateral ratio that is simply the amount of collateral (in ETH) divided by the amount of debt (in LUSD), without taking the ETH:USD price into account. Given that all Troves are equally affected by Ether price changes, they do not need to be sorted by their real ICR.
145-
146-
All Trove operations that change the collateralization ratio need to either insert or reinsert the Trove to the `SortedTroves` list. To reduce the computational complexity (and gas cost) of the insertion to the linked list, two ‘hints’ may be provided.
147-
148-
A hint is the address of a Trove with a position in the sorted list close to the correct insert position.
149-
150-
All Trove operations take two ‘hint’ arguments: a `_lowerHint` referring to the `nextId` and an `_upperHint` referring to the `prevId` of the two adjacent nodes in the linked list that are (or would become) the neighbors of the given Trove. Taking both direct neighbors as hints has the advantage of being much more resilient to situations where a neighbor gets moved or removed before the caller's transaction is processed: the transaction would only fail if both neighboring Troves are affected during the pendency of the transaction.
151-
152-
The better the ‘hint’ is, the shorter the list traversal, and the cheaper the gas cost of the function call. `SortedList::findInsertPosition(uint256 _NICR, address _prevId, address _nextId)` that is called by the Trove operation firsts check if `prevId` is still existant and valid (larger NICR than the provided `_NICR`) and then descends the list starting from `prevId`. If the check fails, the function further checks if `nextId` is still existant and valid (smaller NICR than the provided `_NICR`) and then ascends list starting from `nextId`.
153-
154-
The `HintHelpers::getApproxHint(...)` function can be used to generate a useful hint pointing to a Trove relatively close to the target position, which can then be passed as an argument to the desired Trove operation or to `SortedTroves::findInsertPosition(...)` to get its two direct neighbors as ‘exact‘ hints (based on the current state of the system).
155-
156-
`getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed)` randomly selects `numTrials` amount of Troves, and returns the one with the closest position in the list to where a Trove with a nominal collateralization ratio of `_CR` should be inserted. It can be shown mathematically that for `numTrials = k * sqrt(n)`, the function's gas cost is with very high probability worst case `O(sqrt(n)) if k >= 10`. For scalability reasons (Infura is able to serve up to ~4900 trials), the function also takes a random seed `_inputRandomSeed` to make sure that calls with different seeds may lead to a different results, allowing for better approximations through multiple consecutive runs.
157-
158-
**Trove operation without a hint**
159-
160-
1. User performs Trove operation in their browser
161-
2. Call the Trove operation with `_lowerHint = _upperHint = userAddress`
162-
163-
Gas cost will be worst case `O(n)`, where n is the size of the `SortedTroves` list.
164-
165-
**Trove operation with hints**
166-
167-
1. User performs Trove operation in their browser
168-
2. The front end computes a new collateralization ratio locally, based on the change in collateral and/or debt.
169-
3. Call `HintHelpers::getApproxHint(...)`, passing it the computed nominal collateralization ratio. Returns an address close to the correct insert position
170-
4. Call `SortedTroves::findInsertPosition(uint256 _NICR, address _prevId, address _nextId)`, passing it the same approximate hint via both `_prevId` and `_nextId` and the new nominal collateralization ratio via `_NICR`.
171-
5. Pass the ‘exact‘ hint in the form of the two direct neighbors, i.e. `_nextId` as `_lowerHint` and `_prevId` as `_upperHint`, to the Trove operation function call. (Note that the hint may become slightly inexact due to pending transactions that are processed first, though this is gracefully handled by the system that can ascend or descend the list as needed to find the right position.)
172-
173-
Gas cost of steps 2-4 will be free, and step 5 will be `O(1)`.
174-
175-
Hints allow cheaper Trove operations for the user, at the expense of a slightly longer time to completion, due to the need to await the result of the two read calls in steps 1 and 2 - which may be sent as JSON-RPC requests to Infura, unless the Frontend Operator is running a full Ethereum node.
176-
177-
### Hints for `redeemCollateral`
178-
179-
`TroveManager::redeemCollateral` as a special case requires additional hints:
180-
- `_firstRedemptionHint` hints at the position of the first Trove that will be redeemed from,
181-
- `_lowerPartialRedemptionHint` hints at the `nextId` neighbor of the last redeemed Trove upon reinsertion, if it's partially redeemed,
182-
- `_upperPartialRedemptionHint` hints at the `prevId` neighbor of the last redeemed Trove upon reinsertion, if it's partially redeemed,
183-
- `_partialRedemptionHintNICR` ensures that the transaction won't run out of gas if neither `_lowerPartialRedemptionHint` nor `_upperPartialRedemptionHint` are valid anymore.
184-
185-
`redeemCollateral` will only redeem from Troves that have an ICR >= MCR. In other words, if there are Troves at the bottom of the SortedTroves list that are below the minimum collateralization ratio (which can happen after an ETH:USD price drop), they will be skipped. To make this more gas-efficient, the position of the first redeemable Trove should be passed as `_firstRedemptionHint`.
186-
187-
#### First redemption hint
188-
189-
The first redemption hint is the address of the trove from which to start the redemption sequence - i.e the address of the first trove in the system with ICR >= 110%.
190-
191-
If when the transaction is confirmed the address is in fact not valid - the system will start from the lowest ICR trove in the system, and step upwards until it finds the first trove with ICR >= 110% to redeem from. In this case, since the number of troves below 110% will be limited due to ongoing liquidations, there's a good chance that the redemption transaction still succeed.
192-
193-
#### Partial redemption hints
194-
195-
All Troves that are fully redeemed from in a redemption sequence are left with zero debt, and are closed. The remaining collateral (the difference between the orginal collateral and the amount used for the redemption) will be claimable by the owner.
196-
197-
It’s likely that the last Trove in the redemption sequence would be partially redeemed from - i.e. only some of its debt cancelled with LUSD. In this case, it should be reinserted somewhere between top and bottom of the list. The `_lowerPartialRedemptionHint` and `_upperPartialRedemptionHint` hints passed to `redeemCollateral` describe the future neighbors the expected reinsert position.
198-
199-
However, if between the off-chain hint computation and on-chain execution a different transaction changes the state of a Trove that would otherwise be hit by the redemption sequence, then the off-chain hint computation could end up totally inaccurate. This could lead to the whole redemption sequence reverting due to out-of-gas error.
200-
201-
To mitigate this, another hint needs to be provided: `_partialRedemptionHintNICR`, the expected nominal ICR of the final partially-redeemed-from Trove. The on-chain redemption function checks whether, after redemption, the nominal ICR of this Trove would equal the nominal ICR hint.
202-
203-
If not, the redemption sequence doesn’t perform the final partial redemption, and terminates early. This ensures that the transaction doesn’t revert, and most of the requested LUSD redemption can be fulfilled.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
server {
2+
listen 443 ssl;
3+
4+
server_name liquity.example.com;
5+
6+
include /config/nginx/ssl.conf;
7+
8+
location / {
9+
include /config/nginx/proxy.conf;
10+
resolver 127.0.0.11 valid=5s;
11+
set $container dev-frontend;
12+
proxy_pass http://$container;
13+
}
14+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
version: "2.1"
2+
3+
services:
4+
swag:
5+
image: linuxserver/swag
6+
container_name: swag
7+
cap_add:
8+
- NET_ADMIN
9+
environment:
10+
- PUID=1000 # see https://github.com/linuxserver/docker-swag#user--group-identifiers
11+
- PGID=1011 # see https://github.com/linuxserver/docker-swag#user--group-identifiers
12+
- TZ=Europe/Berlin # customize this
13+
- URL=example.com # customize this
14+
- SUBDOMAINS=liquity # customize this
15+
- VALIDATION=http
16+
- ONLY_SUBDOMAINS=true
17+
volumes:
18+
- ./config:/config
19+
ports:
20+
- 443:443
21+
- 80:80
22+
restart: unless-stopped
23+
24+
watchtower:
25+
image: containrrr/watchtower
26+
container_name: watchtower
27+
volumes:
28+
- /var/run/docker.sock:/var/run/docker.sock
29+
command: --interval 1000 --cleanup --label-enable
30+
restart: unless-stopped
31+
32+
dev-frontend:
33+
image: liquity/dev-frontend:latest
34+
container_name: liquity-dev-frontend
35+
environment:
36+
- FRONTEND_TAG= # customize this
37+
- INFURA_API_KEY= # (optional) customize this
38+
restart: unless-stopped
39+
labels:
40+
com.centurylinklabs.watchtower.enable: "true"

0 commit comments

Comments
 (0)