Skip to content

Commit

Permalink
Loan due date using current block height
Browse files Browse the repository at this point in the history
  • Loading branch information
da-kami committed Aug 2, 2021
1 parent 90130e4 commit f366dd9
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 12 deletions.
5 changes: 4 additions & 1 deletion bobtimus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ where
/// We return the range of possible loan terms to the borrower.
/// The borrower can then request a loan using parameters that are within our terms.
pub async fn handle_loan_offer_request(&mut self) -> Result<LoanOffer> {
let current_height = self.elementsd.get_blockcount().await?;

Ok(LoanOffer {
rate: self.rate_service.latest_rate(),
// TODO: Dynamic fee estimation
Expand All @@ -277,8 +279,9 @@ where
max_ltv: dec!(0.8),
// TODO: Dynamic interest based on current market values
interest: vec![Interest {
// Absolute timelock calculated from current block height
// Assuming 1 min block interval, 43200 mins = 30 days
timelock: 43200,
timelock: current_height + 43200,
interest_rate: dec!(0.15),
}],
})
Expand Down
2 changes: 1 addition & 1 deletion bobtimus/src/loan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub struct LoanOffer {
#[derive(Debug, Clone, serde::Serialize)]
pub struct Interest {
/// Timelock in blocks
pub timelock: u64,
pub timelock: u32,
/// Interest rate in percent
pub interest_rate: Decimal,
}
5 changes: 5 additions & 0 deletions extension/src/background-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@ export async function getPastTransactions(): Promise<Txid[]> {
// @ts-ignore
return proxy.getPastTransactions();
}

export async function getBlockHeight(): Promise<number> {
// @ts-ignore
return proxy.getBlockHeight();
}
6 changes: 6 additions & 0 deletions extension/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
extractTrade,
getAddress,
getBalances,
getBlockHeight,
getOpenLoans,
getPastTransactions,
makeBuyCreateSwapPayload,
Expand Down Expand Up @@ -214,6 +215,11 @@ window.createWalletFromBip39 = async (seed_words: string, password: string) => {
return createNewBip39Wallet(walletName, seed_words, password);
};

// @ts-ignore
window.getBlockHeight = async () => {
return getBlockHeight();
};

function updateBadge() {
let count = 0;
if (loanToSign) count++;
Expand Down
26 changes: 23 additions & 3 deletions extension/src/components/ConfirmLoan.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Box, Button, Flex, Heading, Image, Spacer, Text } from "@chakra-ui/react";
import { Box, Button, Flex, Heading, Image, Spacer, Text, useInterval } from "@chakra-ui/react";
import Debug from "debug";
import moment from "moment";
import React from "react";
import { useAsync } from "react-async";
import { signLoan } from "../background-proxy";
import { getBlockHeight, signLoan } from "../background-proxy";
import { LoanToSign, USDT_TICKER } from "../models";
import YouSwapItem from "./SwapItem";
import Usdt from "./tether.svg";

const debug = Debug("confirmloan:error");

interface ConfirmLoanProps {
onCancel: (tabId: number) => void;
onSuccess: () => void;
Expand All @@ -24,6 +28,22 @@ export default function ConfirmLoan(

let { details: { collateral, principal, principalRepayment, term } } = loanToSign;

const blockHeightHook = useAsync({
promiseFn: getBlockHeight,
onReject: (e) => debug("Failed to fetch block height %s", e),
});
let { data: blockHeight, reload: reloadBlockHeight } = blockHeightHook;

useInterval(() => {
reloadBlockHeight();
}, 6000); // 1 min

// format the time nicely into something like : `in 13 hours` or `in 1 month`.
// block-height and loan-term are in "blocktime" ^= minutes
const deadline = blockHeight && term
? moment().add(Math.abs(blockHeight - term), "minutes").fromNow()
: null;

return (<Box>
<form
onSubmit={async e => {
Expand Down Expand Up @@ -64,7 +84,7 @@ export default function ConfirmLoan(
<Box w="100%">
<Flex>
<Box h="40px" p="1">
<Text>Loan term: {term}</Text>
<Text>Loan term: {term} block-height {deadline ? "(due " + deadline + ")" : ""}</Text>
</Box>
</Flex>
</Box>
Expand Down
38 changes: 31 additions & 7 deletions extension/src/components/OpenLoans.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { Box, Button, FormControl, FormErrorMessage, HStack, Image, VStack } from "@chakra-ui/react";
import {
Box,
Button,
FormControl,
FormErrorMessage,
HStack,
Image,
SkeletonText,
useInterval,
VStack,
} from "@chakra-ui/react";
import { Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel } from "@chakra-ui/react";
import Debug from "debug";
import moment from "moment";
import * as React from "react";
import { useAsync } from "react-async";
import { repayLoan } from "../background-proxy";
import { getBlockHeight } from "../background-proxy";
import { LoanDetails } from "../models";
import Btc from "./bitcoin.svg";
import Usdt from "./tether.svg";
Expand Down Expand Up @@ -45,10 +56,21 @@ function OpenLoan({ loanDetails, onRepayed, index }: OpenLoanProps) {
onReject: (e) => error("Failed to repay loan %s: %s", loanDetails.txid, e),
});

// TODO: get current block height from explorer
const currentHeight = 10;
// this will format the time nicely into something like : `in 13 hours` or `in 1 month`.
const deadline = moment().add((currentHeight - loanDetails.term) * 60, "hours").fromNow();
const blockHeightHook = useAsync({
promiseFn: getBlockHeight,
onReject: (e) => error("Failed to fetch block height %s", e),
});
let { data: blockHeight, reload: reloadBlockHeight } = blockHeightHook;

useInterval(() => {
reloadBlockHeight();
}, 6000); // 1 min

// format the time nicely into something like : `in 13 hours` or `in 1 month`.
// block-height and loan-term are in "blocktime" ^= minutes
const deadline = blockHeight
? moment().add(Math.abs(blockHeight - loanDetails.term), "minutes").fromNow()
: null;

return <AccordionItem>
<h2>
Expand All @@ -66,7 +88,9 @@ function OpenLoan({ loanDetails, onRepayed, index }: OpenLoanProps) {
<Box w="32px" h="32px">
<Image src={Usdt} h="32px" />
</Box>
<Box>are due {deadline}</Box>
{deadline
? <Box>are due {deadline}</Box>
: <Box><SkeletonText noOfLines={2} spacing="4" /></Box>}
</HStack>
<AccordionIcon />
</AccordionButton>
Expand Down Expand Up @@ -104,7 +128,7 @@ function OpenLoan({ loanDetails, onRepayed, index }: OpenLoanProps) {
</Box>
</HStack>
<Box>
Loan term: {loanDetails.term}
Loan term: {loanDetails.term} (due block-height)
</Box>
<FormControl id="repayment" isInvalid={repayFailed}>
<Box>
Expand Down
7 changes: 7 additions & 0 deletions extension/src/wasmProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,10 @@ export async function createNewBip39Wallet(name: string, seedWords: string, pass
debug("create_new_bip39_wallet");
return create_new_bip39_wallet(name, seedWords, password);
}

export async function getBlockHeight(): Promise<string> {
const { get_block_height } = await import("./wallet");

debug("getBlockHeight");
return get_block_height();
}
17 changes: 17 additions & 0 deletions extension/wallet/src/esplora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,23 @@ pub async fn get_fee_estimates() -> Result<FeeEstimatesResponse> {
Ok(fee_estimates)
}

pub async fn get_block_height() -> Result<u32> {
let esplora_url = {
let guard = ESPLORA_API_URL.lock().expect_throw("can get lock");
guard.clone()
};
let esplora_url = esplora_url.join("/blocks/tip/height")?;

let latest_block_height = reqwest::get(esplora_url.clone())
.await
.with_context(|| format!("failed to GET {}", esplora_url))?
.json()
.await
.context("failed to deserialize latest block height")?;

Ok(latest_block_height)
}

/// The response object for the `/fee-estimates` endpoint.
///
/// The key is the confirmation target (in number of blocks) and the value is the estimated feerate (in sat/vB).
Expand Down
9 changes: 9 additions & 0 deletions extension/wallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ pub async fn wallet_status(name: String) -> Result<JsValue, JsValue> {
Ok(status)
}

/// Retrieve the latest block height from Esplora
#[wasm_bindgen]
pub async fn get_block_height() -> Result<JsValue, JsValue> {
let latest_block_height = map_err_from_anyhow!(esplora::get_block_height().await)?;
let latest_block_height = map_err_from_anyhow!(JsValue::from_serde(&latest_block_height))?;

Ok(latest_block_height)
}

/// Get an address for the wallet with the given name.
///
/// Fails if the wallet is currently not loaded.
Expand Down
2 changes: 2 additions & 0 deletions waves/src/Borrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ function Borrow({ dispatch, state, rate, wavesProvider, walletStatusAsyncState }
// Liquid has 1 minute blocktime
const loanTermInBlocks = state.loanTermInDays * 24 * 60;

debug("loan term in blocks: " + loanTermInBlocks);

let loanRequest = await wavesProvider.makeLoanRequestPayload(
collateralAmount.toString(),
feeRate.toString(),
Expand Down

0 comments on commit f366dd9

Please sign in to comment.