Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Commit

Permalink
Merge pull request #116 from niteshbalusu11:niteshbalusu11/issue115
Browse files Browse the repository at this point in the history
Add support for bos fees command
  • Loading branch information
niteshbalusu11 authored Sep 23, 2022
2 parents 5fde741 + c81266f commit 3afabde
Show file tree
Hide file tree
Showing 16 changed files with 502 additions and 8 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ bos chart-payments-received
# See details on how closed channels resolved on-chain
bos closed

# View outbound fee rates and update outbound fee rates to peers
bos fees

# Query the node to find something like a payment, channel or node
bos find "query"

Expand Down Expand Up @@ -1108,6 +1111,49 @@ try {

<br></br>

### Fees

```javascript
/**
@PostRequest
@Url
http://localhost:8055/api/fees
@Body
{
[cltv_delta]: <Set CLTV Delta Number>
[fee_rate]: <Fee Rate String>
to: [<Adjust Routing Fee To Peer Alias or Public Key or Tag String>]
}
@Response
{
rows: [[<Table Cell String>]]
}
*/

try {
const url = 'http://localhost:8055/api/open';

const postBody = {
fee_rate: ['100'],
to: ['bob', 'carol']
};


const config = {
headers: { Authorization: `Bearer ${accessToken}` },
};

const response = await axios.post(url, postBody, config);
} catch (error) {
console.error(error);
}
```
<br></br>


### Find

```javascript
Expand Down
25 changes: 25 additions & 0 deletions src/client/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,31 @@ const commands: Commands = [
limit: 'Limit',
},
},
{
name: 'Fees',
value: 'Fees',
description: 'Show and adjust outbound fee rates',
longDescription: `List out fee rates, fix problems with routing policies, set out fees.
When setting fee, if channels pending, will wait for confirm to set.
Set-fee-rate can use formulas: https://formulajs.info/functions/.
Specify PERCENT(0.00) to set the fee as a fraction of routed amount.
Specify BIPS() to set the fee as parts per thousand.
You can use INBOUND and OUTBOUND in formulas for IF formulas.
You can use INBOUND_FEE_RATE to mirror an inbound fee.
You can use FEE_RATE_OF_<PUBKEY> to reference other node rates.`,
flags: {
set_cltv_delta: 'SetCltvDelta',
set_fee_rate: 'SetFeeRate',
to: 'To',
},
},
{
name: 'Find',
value: 'Find',
Expand Down
20 changes: 20 additions & 0 deletions src/client/output/FeesOutput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { StandardTableOutput } from '~client/standard_components/app-components';

// Renders the output of the bos fees command

type Props = {
data: {
rows: string[][];
};
};

const FeesOutput = ({ data }: Props) => {
return (
<div style={{ marginTop: '30px' }}>
{!!data ? <StandardTableOutput data={data} tableId="feesOutput" /> : <h2>No Output to display</h2>}
</div>
);
};

export default FeesOutput;
2 changes: 2 additions & 0 deletions src/client/output/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ChartFeesEarnedOutput from './ChartFeesEarnedOutput';
import ChartFeesPaidOutput from './ChartFeesPaidOutput';
import ChartPaymentsReceivedOutput from './ChartPaymentsReceivedOutput';
import ClosedOutput from './ClosedOutput';
import FeesOutput from './FeesOutput';
import FindOutput from './FindOutput';
import ForwardsOutput from './ForwardsOutput';
import OpenOutput from './OpenOutput';
Expand All @@ -24,6 +25,7 @@ export {
ChartFeesPaidOutput,
ChartPaymentsReceivedOutput,
ClosedOutput,
FeesOutput,
FindOutput,
ForwardsOutput,
OpenOutput,
Expand Down
172 changes: 172 additions & 0 deletions src/client/pages/commands/Fees.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import * as types from '~shared/types';

import { Button, CssBaseline, IconButton, Stack, TextField } from '@mui/material';
import React, { useState } from 'react';
import { StandardHomeButtonLink, StartFlexBox, SubmitButton } from '~client/standard_components/app-components';
import commands, { globalCommands } from '../../commands';

import DeleteIcon from '@mui/icons-material/Delete';
import { FeesOutput } from '~client/output';
import Head from 'next/head';
import { axiosPostWithAlert } from '~client/utils/axios';

const FeesCommand = commands.find(n => n.value === 'Fees');

/*
Renders the bos fees command
POST call to the NestJs process to get fees information
*/

const styles = {
form: {
marginLeft: '50px',
marginTop: '100px',
width: '800px',
},
textField: {
width: '500px',
marginTop: '10px',
},
h4: {
marginTop: '0px',
},
button: {
color: 'white',
fontWeight: 'bold',
borderRadius: '10px',
border: '1px solid black',
marginTop: '20px',
width: '50px',
},
iconButton: {
width: '50px',
marginTop: '0px',
},
};

const Fees = () => {
const [cltvDelta, setCltvDelta] = useState(undefined);
const [data, setData] = useState(undefined);
const [feeRate, setFeeRate] = useState(undefined);
const [formValues, setFormValues] = useState([{ node: '' }]);
const [node, setNode] = useState(undefined);

const handeNodeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setNode(event.target.value);
};

const handleFeeRateChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setFeeRate(event.target.value);
};

const handleCltvDeltaChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setCltvDelta(event.target.value);
};

// ============================================================================
const removeFormFields = (i: number) => {
const newFormValues = [...formValues];
newFormValues.splice(i, 1);
setFormValues(newFormValues);
};

const addFormFields = () => {
setFormValues([...formValues, { node: '' }]);
};

const handleChange = (i: number, e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const newFormValues = [...formValues];
newFormValues[i][e.target.name] = e.target.value;
setFormValues(newFormValues);
};

// ============================================================================

const fetchData = async () => {
const query: types.commandFees = {
node,
cltv_delta: Number(cltvDelta),
fee_rate: feeRate,
to: formValues.map(n => n.node),
};

const result = await axiosPostWithAlert({ path: 'fees', postBody: query });

if (!!result) {
setData(result);
}
};

return (
<CssBaseline>
<Head>
<title>Fees</title>
</Head>
<StartFlexBox>
<StandardHomeButtonLink />

<Stack style={styles.form}>
<h2>{FeesCommand.name}</h2>
<h4 style={styles.h4}>{FeesCommand.longDescription}</h4>

<TextField
type="text"
placeholder="Fee Rate (Optional)"
label={FeesCommand.flags.set_fee_rate}
id={FeesCommand.flags.set_fee_rate}
onChange={handleFeeRateChange}
style={styles.textField}
/>

<TextField
type="text"
placeholder="Cltv Delta (Optional)"
label={FeesCommand.flags.set_cltv_delta}
id={FeesCommand.flags.set_cltv_delta}
onChange={handleCltvDeltaChange}
style={styles.textField}
/>

<>
<Button href="#text-buttons" onClick={() => addFormFields()} style={styles.button}>
Add +
</Button>
{formValues.map((element, index) => (
<div key={index}>
<TextField
type="text"
label="To"
name="node"
placeholder="Peer key/alias/tag to set fees"
value={element.node || ''}
onChange={e => handleChange(index, e)}
style={styles.textField}
id={`key-${index}`}
/>
{!!index ? (
<IconButton aria-label="delete" onClick={() => removeFormFields(index)} style={styles.iconButton}>
<DeleteIcon />
</IconButton>
) : null}
</div>
))}
</>

<TextField
type="text"
placeholder={globalCommands.node.name}
label={globalCommands.node.name}
id={globalCommands.node.value}
onChange={handeNodeChange}
style={styles.textField}
/>

<SubmitButton onClick={fetchData}>Run Command</SubmitButton>
{!!data ? <FeesOutput data={data.result}></FeesOutput> : null}
</Stack>
</StartFlexBox>
</CssBaseline>
);
};

export default Fees;
2 changes: 2 additions & 0 deletions src/server/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigModule } from '@nestjs/config';
import { CredentialsModule } from './modules/credentials/credentials.module';
import { CronModule } from './modules/cron/cron.module';
import { ExternalServicesModule } from './modules/external-services/external-services.module';
import { FeesModule } from './modules/fees/fees.module';
import { GrpcModule } from './modules/grpc/grpc.module';
import { JwtAuthGuard } from './modules/auth/jwt-auth.guard';
import { LndModule } from './modules/lnd/lnd.module';
Expand All @@ -29,6 +30,7 @@ import { join } from 'path';
CommandsModule,
CronModule,
ExternalServicesModule,
FeesModule,
GrpcModule,
ConfigModule.forRoot({
isGlobal: true,
Expand Down
43 changes: 43 additions & 0 deletions src/server/commands/fees/fees_command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as types from '~shared/types';

import { AuthenticatedLnd } from 'lightning';
import { Logger } from 'winston';
import { adjustFees } from 'balanceofsatoshis/routing';
import { readFile } from 'fs';

/** View and adjust routing fees
{
[cltv_delta]: <Set CLTV Delta Number>
[fee_rate]: <Fee Rate String>
lnd: <Authenticated LND API Object>
logger: <Winstone Logger Object>
to: [<Adjust Routing Fee To Peer Alias or Public Key or Tag String>]
}
@returns via cbk or Promise
{
rows: [[<Table Cell String>]]
}
*/
type Args = {
args: types.commandFees;
lnd: AuthenticatedLnd;
logger: Logger;
};
const feesCommand = async ({ args, lnd, logger }: Args) => {
const toArray = !!args.to ? args.to.filter((n: string) => !!n) : [];

const result = await adjustFees({
lnd,
logger,
cltv_delta: args.cltv_delta || undefined,
fee_rate: args.fee_rate,
fs: { getFile: readFile },
to: toArray,
});

return { result };
};

export default feesCommand;
2 changes: 2 additions & 0 deletions src/server/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import chartFeesEarnedCommand from './chartFeesEarned/chart_fees_earned_command'
import chartFeesPaidCommand from './chartFeesPaid/chart_fees_paid_command';
import chartPaymentsReceivedCommand from './chartPaymentsReceived/chart_payments_received_command';
import closedCommand from './closed/closed_command';
import feesCommand from './fees/fees_command';
import findCommand from './find/find_command';
import forwardsCommand from './forwards/forwards_command';
import graphCommand from './graph/graph_command';
Expand All @@ -35,6 +36,7 @@ export {
chartFeesPaidCommand,
chartPaymentsReceivedCommand,
closedCommand,
feesCommand,
findCommand,
forwardsCommand,
graphCommand,
Expand Down
15 changes: 15 additions & 0 deletions src/server/modules/fees/fees.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Body, Controller, Post } from '@nestjs/common';
import { feesDto } from '~shared/commands.dto';
import { FeesService } from './fees.service';

// Fees controller: Defines routes for fees command

@Controller()
export class FeesController {
constructor(private feeService: FeesService) {}

@Post('api/fees')
async fees(@Body() args: feesDto) {
return this.feeService.feesCommand(args);
}
}
Loading

0 comments on commit 3afabde

Please sign in to comment.