Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lending Liquidator V1 #21

Merged
merged 19 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
dde1d78
wip: TDD for lending-liquidator, upgrade to latest interbtc-api
daniel-savu Feb 28, 2023
88b80fa
fix: tests crash only when expected
daniel-savu Mar 1, 2023
292d075
fix: temporarily add sleep to liquidation tests setup
daniel-savu Mar 1, 2023
0c8bb5e
fix(liquidator): wait for market registration in tests
daniel-savu Mar 1, 2023
1eb5da1
feat(liquidator): start adding liquidation logic
daniel-savu Mar 2, 2023
ce5e5c0
feat(liquidator): first liquidation strategy implementation
daniel-savu Mar 7, 2023
fdc8ab9
feat(liquidator): strategy improvements, use instant seal
daniel-savu Mar 8, 2023
79700fb
feat(liquidator): use close factor
daniel-savu Mar 9, 2023
b2a092f
feat(liquidator): attempt at graceful termination on api disconnect
daniel-savu Mar 10, 2023
5b85b1d
fix: update usage of docker compose
daniel-savu Mar 14, 2023
919a09f
chore(loans): upgrade `interbtc-api` dependency
daniel-savu Mar 14, 2023
aa8f5a9
feat(liquidator): run from CLI
daniel-savu Mar 14, 2023
9f1f504
feat(liquidator): improve bot termination logic, fix some review commens
daniel-savu Mar 15, 2023
9241517
chore(liquidator): add profitability todo
daniel-savu Mar 15, 2023
161e524
fix(liquidator): add profitability check
daniel-savu Mar 16, 2023
768c1a5
feat(liquidator): auto-redeem qToken reward, improve docs
daniel-savu Mar 16, 2023
c108792
fix(liquidator): rates map key, catch oracle errors, default ref curr…
daniel-savu Mar 17, 2023
03d865d
fix(liquidator): use string keys in the markets map
daniel-savu Mar 17, 2023
9a48e49
fix(liquidator): readme
daniel-savu Mar 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ jobs:
username: ${{ secrets.GITLAB_USERNAME }}
password: ${{ secrets.GITLAB_TOKEN }}
- name: Run and set up the parachain, oracle, staked relayer and vault
run: yarn install && yarn docker-parachain-start
run: docker-compose up -d
- run: yarn install && yarn test
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Repo with agents that perform actions in response to on-chain states. Includes a
## Testing
```bash
yarn install
yarn docker-parachain-start
docker-compose up
# open a new terminal...
yarn test
```
2 changes: 0 additions & 2 deletions bots/bridge-tester/.env.local
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ export BITCOIN_RPC_PORT="18443"
export ISSUE_TOP_UP_AMOUNT="0.1"
export REDEEM_ADDRESS="bcrt1qujs29q4gkyn2uj6y570xl460p4y43ruayxu8ry"
export PARACHAIN_URL="ws://0.0.0.0:9944"
export STATS_URL="http://0.0.0.0:3007"
export FAUCET_URL="http://0.0.0.0:3036"
export BITCOIN_RPC_WALLET="Alice"
6 changes: 3 additions & 3 deletions bots/bridge-tester/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Bot for testing the Interlay and Kintsugi bridges.",
"main": "build/index.js",
"typings": "build/index.d.ts",
"repository": "https://github.com/interlay/bots/bridge-tester",
"repository": "https://github.com/interlay/bots/bots/bridge-tester",
"author": "Interlay",
"license": "Apache-2.0",
"engines": {
Expand All @@ -20,8 +20,8 @@
"test:integration": "mocha test/**/*.test.ts --timeout 10000000"
},
"dependencies": {
"@interlay/interbtc-api": "1.8.3",
"@interlay/monetary-js": "0.5.3",
"@interlay/interbtc-api": "1.21.0",
"@interlay/monetary-js": "0.7.0",
daniel-savu marked this conversation as resolved.
Show resolved Hide resolved
"@types/big.js": "6.1.2",
"@types/node": "^14.14.31",
"@types/underscore": "^1.11.2",
Expand Down
7 changes: 4 additions & 3 deletions bots/bridge-tester/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BitcoinNetwork,
createInterBtcApi,
InterBtcApi,
newMonetaryAmount,
sleep,
} from "@interlay/interbtc-api";
import { KeyringPair } from "@polkadot/keyring/types";
Expand All @@ -11,8 +12,8 @@ import { cryptoWaitReady } from "@polkadot/util-crypto";
import { MS_IN_AN_HOUR } from "./consts";
import { Issue } from "./issue";
import { Redeem } from "./redeem";
import { BitcoinAmount } from "@interlay/monetary-js";
import logger from "./logger";
import { Bitcoin } from "@interlay/monetary-js";

const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
Expand Down Expand Up @@ -100,7 +101,7 @@ async function heartbeats(
);
const redeem = new Redeem(
interBtcApi,
BitcoinAmount.from.BTC(process.env.ISSUE_TOP_UP_AMOUNT)
newMonetaryAmount(process.env.ISSUE_TOP_UP_AMOUNT, Bitcoin, true)
);
await redeem.performHeartbeatRedeems(
account,
Expand Down Expand Up @@ -151,7 +152,7 @@ async function main(inputFlag: InputFlag, requestWaitingTime: number) {
}
case InputFlag.heartbeats: {
heartbeats(account, process.env.REDEEM_ADDRESS as string);
setInterval(heartbeats, requestWaitingTime, account);
setInterval(heartbeats, requestWaitingTime, account, process.env.REDEEM_ADDRESS as string);
break;
}
}
Expand Down
29 changes: 12 additions & 17 deletions bots/bridge-tester/src/issue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,22 @@ import {
BitcoinNetwork,
InterbtcPrimitivesVaultId,
WrappedCurrency,
CurrencyUnit,
newMonetaryAmount,
getCorrespondingCollateralCurrency,
CollateralUnit,
encodeVaultId,
} from "@interlay/interbtc-api";
import { BitcoinUnit, Currency, MonetaryAmount } from "@interlay/monetary-js";
import { Currency, MonetaryAmount } from "@interlay/monetary-js";
import { KeyringPair } from "@polkadot/keyring/types";
import Big from "big.js";
import _ from "underscore";

import { LOAD_TEST_ISSUE_AMOUNT } from "./consts";
import logger from "./logger";
import { sleep, waitForEmptyMempool } from "./utils";
import { sleep } from "./utils";

export class Issue {
interBtcApi: InterBtcApi;
private redeemDustValue:
| MonetaryAmount<WrappedCurrency, BitcoinUnit>
| MonetaryAmount<WrappedCurrency>
| undefined;

constructor(interBtc: InterBtcApi) {
Expand All @@ -37,9 +34,7 @@ export class Issue {
"AccountId",
requester.address
);
const collateralCurrency = getCorrespondingCollateralCurrency(
this.interBtcApi.getGovernanceCurrency()
) as Currency<CollateralUnit>;
const collateralCurrency = this.interBtcApi.api.consts.currency.getRelayChainCurrencyId;
const balance = await this.interBtcApi.tokens.balance(
collateralCurrency,
requesterAccountId
Expand Down Expand Up @@ -69,7 +64,7 @@ export class Issue {

async requestAndExecuteIssue(
requester: KeyringPair,
amount: MonetaryAmount<WrappedCurrency, BitcoinUnit>,
amount: MonetaryAmount<WrappedCurrency>,
bitcoinCoreClient: BitcoinCoreClient,
vaultId?: InterbtcPrimitivesVaultId
): Promise<boolean> {
Expand All @@ -91,22 +86,22 @@ export class Issue {
}

async getCachedRedeemDustValue(): Promise<
MonetaryAmount<WrappedCurrency, BitcoinUnit>
MonetaryAmount<WrappedCurrency>
> {
if (!this.redeemDustValue) {
this.redeemDustValue = await this.interBtcApi.redeem.getDustValue();
}
return this.redeemDustValue;
}

increaseByFiftyPercent<U extends CurrencyUnit>(
x: MonetaryAmount<Currency<U>, U>
): MonetaryAmount<Currency<U>, U> {
increaseByFiftyPercent(
x: MonetaryAmount<Currency>
): MonetaryAmount<Currency> {
return x.mul(new Big(15)).div(new Big(10));
}

async getAmountToIssue(): Promise<
MonetaryAmount<WrappedCurrency, BitcoinUnit>
MonetaryAmount<WrappedCurrency>
> {
const redeemDustValue = await this.getCachedRedeemDustValue();
// We need to account for redeem fees to redeem later
Expand Down Expand Up @@ -161,9 +156,9 @@ export class Issue {
for (const vault of vaults) {
try {
logger.info(
`Issuing ${amountToIssue.toString(amountToIssue.currency.base)} ${
`Issuing ${amountToIssue.toString(false)} ${
amountToIssue.currency.ticker
} with vault ID ${encodeVaultId(vault.id)}`
} with vault ID ${encodeVaultId(this.interBtcApi.assetRegistry, this.interBtcApi.loans, vault.id)}`
);
this.requestAndExecuteIssue(
account,
Expand Down
24 changes: 15 additions & 9 deletions bots/bridge-tester/src/redeem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
newMonetaryAmount,
encodeVaultId,
} from "@interlay/interbtc-api";
import { BitcoinUnit, MonetaryAmount } from "@interlay/monetary-js";
import { MonetaryAmount } from "@interlay/monetary-js";
import { KeyringPair } from "@polkadot/keyring/types";
import { H256 } from "@polkadot/types/interfaces";
import Big from "big.js";
Expand All @@ -22,20 +22,20 @@ export class Redeem {
vaultHeartbeats = new Map<string, number>();
issue: Issue;
private redeemDustValue:
| MonetaryAmount<WrappedCurrency, BitcoinUnit>
| MonetaryAmount<WrappedCurrency>
| undefined;
interBtc: InterBtcApi;
expiredRedeemRequests: H256[] = [];
constructor(
interBtc: InterBtcApi,
private issueTopUpAmount: MonetaryAmount<WrappedCurrency, BitcoinUnit>
private issueTopUpAmount: MonetaryAmount<WrappedCurrency>
) {
this.issue = new Issue(interBtc);
this.interBtc = interBtc;
}

async getCachedRedeemDustValue(): Promise<
MonetaryAmount<WrappedCurrency, BitcoinUnit>
MonetaryAmount<WrappedCurrency>
> {
if (!this.redeemDustValue) {
this.redeemDustValue = await this.interBtc.redeem.getDustValue();
Expand All @@ -44,14 +44,14 @@ export class Redeem {
}

increaseByThirtyPercent(
x: MonetaryAmount<WrappedCurrency, BitcoinUnit>
): MonetaryAmount<WrappedCurrency, BitcoinUnit> {
x: MonetaryAmount<WrappedCurrency>
): MonetaryAmount<WrappedCurrency> {
return x.mul(new Big(13)).div(new Big(10));
}

async getMinimumBalanceForHeartbeat(
vaultCount?: number
): Promise<MonetaryAmount<WrappedCurrency, BitcoinUnit>> {
): Promise<MonetaryAmount<WrappedCurrency>> {
if (!this.interBtc.vaults) {
logger.error("Parachain not connected");
return newMonetaryAmount(0, this.interBtc.getWrappedCurrency());
Expand All @@ -69,7 +69,7 @@ export class Redeem {
}

async getMinRedeemableAmount(): Promise<
MonetaryAmount<WrappedCurrency, BitcoinUnit>
MonetaryAmount<WrappedCurrency>
> {
const redeemDustValue = await this.getCachedRedeemDustValue();
const bitcoinNetworkFees =
Expand Down Expand Up @@ -228,6 +228,8 @@ export class Redeem {
if (issuedTokens.gte(amountToRedeem)) {
logger.info(
`Redeeming ${amountToRedeem.toHuman()} out of ${issuedTokens.toHuman()} from vault ID ${encodeVaultId(
this.interBtc.assetRegistry,
this.interBtc.loans,
vault.id
)}`
);
Expand All @@ -239,7 +241,11 @@ export class Redeem {
logger.info(
`Requested redeem: ${
requestResult.id
} from vault ID ${encodeVaultId(vault.id)}`
} from vault ID ${encodeVaultId(
this.interBtc.assetRegistry,
this.interBtc.loans,
vault.id
)}`
);
// TODO: Uncomment once redeems are executed quickly by vaults.
// const redeemRequestId = requestResult.id.toString();
Expand Down
9 changes: 0 additions & 9 deletions bots/bridge-tester/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
import { BitcoinCoreClient } from "@interlay/interbtc-api";

export async function waitForEmptyMempool(
bitcoinCoreClient: BitcoinCoreClient
): Promise<void> {
while ((await bitcoinCoreClient.getMempoolInfo()).size === 0) {
await sleep(1000);
}
}

export async function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
Expand Down
Loading