Skip to content

Commit 12ea5bb

Browse files
committed
loop: set swap deadline based on the network (regtest is immediate)
1 parent 50917a0 commit 12ea5bb

File tree

8 files changed

+94
-23
lines changed

8 files changed

+94
-23
lines changed

app/src/__tests__/store/buildSwapStore.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { values } from 'mobx';
22
import { SwapDirection } from 'types/state';
33
import { grpc } from '@improbable-eng/grpc-web';
44
import { waitFor } from '@testing-library/react';
5-
import * as config from 'config';
65
import { injectIntoGrpcUnary } from 'util/tests';
76
import { BuildSwapStore, createStore, Store } from 'store';
87
import { SWAP_ABORT_DELAY } from 'store/stores/buildSwapStore';
@@ -149,8 +148,8 @@ describe('BuildSwapStore', () => {
149148
deadline = (props.request.toObject() as any).swapPublicationDeadline;
150149
});
151150

152-
// run a loop in production and verify the deadline
153-
Object.defineProperty(config, 'IS_PROD', { get: () => true });
151+
// run a loop on mainnet and verify the deadline
152+
rootStore.nodeStore.network = 'mainnet';
154153
store.requestSwap();
155154
await waitFor(() => expect(deadline).toBeGreaterThan(0));
156155

@@ -159,8 +158,8 @@ describe('BuildSwapStore', () => {
159158
deadline = (props.request.toObject() as any).swapPublicationDeadline;
160159
});
161160

162-
// run a loop NOT in production and verify the deadline
163-
Object.defineProperty(config, 'IS_PROD', { get: () => false });
161+
// run a loop on regtest and verify the deadline
162+
rootStore.nodeStore.network = 'regtest';
164163
store.requestSwap();
165164
await waitFor(() => expect(deadline).toEqual(0));
166165
});

app/src/__tests__/store/nodeStore.spec.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { values } from 'mobx';
22
import { grpc } from '@improbable-eng/grpc-web';
33
import { waitFor } from '@testing-library/react';
4-
import { lndChannelBalance, lndWalletBalance } from 'util/tests/sampleData';
4+
import { lndChannelBalance, lndGetInfo, lndWalletBalance } from 'util/tests/sampleData';
55
import { createStore, NodeStore, Store } from 'store';
66

77
const grpcMock = grpc as jest.Mocked<typeof grpc>;
@@ -15,6 +15,31 @@ describe('NodeStore', () => {
1515
store = rootStore.nodeStore;
1616
});
1717

18+
it('should fetch node info', async () => {
19+
expect(store.pubkey).toBe('');
20+
expect(store.alias).toBe('');
21+
expect(store.chain).toBe('bitcoin');
22+
expect(store.network).toBe('mainnet');
23+
await store.fetchInfo();
24+
expect(store.pubkey).toEqual(lndGetInfo.identityPubkey);
25+
expect(store.alias).toEqual(lndGetInfo.alias);
26+
expect(store.chain).toEqual(lndGetInfo.chainsList[0].chain);
27+
expect(store.network).toEqual(lndGetInfo.chainsList[0].network);
28+
});
29+
30+
it('should handle errors fetching balances', async () => {
31+
grpcMock.unary.mockImplementationOnce(desc => {
32+
if (desc.methodName === 'GetInfo') throw new Error('test-err');
33+
return undefined as any;
34+
});
35+
expect(rootStore.uiStore.alerts.size).toBe(0);
36+
await store.fetchInfo();
37+
await waitFor(() => {
38+
expect(rootStore.uiStore.alerts.size).toBe(1);
39+
expect(values(rootStore.uiStore.alerts)[0].message).toBe('test-err');
40+
});
41+
});
42+
1843
it('should fetch node balances', async () => {
1944
expect(store.wallet.channelBalance).toBe(0);
2045
expect(store.wallet.walletBalance).toBe(0);
@@ -23,7 +48,7 @@ describe('NodeStore', () => {
2348
expect(store.wallet.walletBalance).toEqual(lndWalletBalance.totalBalance);
2449
});
2550

26-
it('should handle errors fetching channels', async () => {
51+
it('should handle errors fetching balances', async () => {
2752
grpcMock.unary.mockImplementationOnce(desc => {
2853
if (desc.methodName === 'ChannelBalance') throw new Error('test-err');
2954
return undefined as any;

app/src/api/lnd.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class LndApi {
1818
this._grpc = grpc;
1919
}
2020

21+
/**
22+
* call the LND `GetInfo` RPC and return the response
23+
*/
24+
async getInfo(): Promise<LND.GetInfoResponse.AsObject> {
25+
const req = new LND.GetInfoRequest();
26+
const res = await this._grpc.request(Lightning.GetInfo, req, this._meta);
27+
return res.toObject();
28+
}
29+
2130
/**
2231
* call the LND `ChannelBalance` RPC and return the response
2332
*/

app/src/api/loop.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as LOOP from 'types/generated/loop_pb';
22
import { SwapClient } from 'types/generated/loop_pb_service';
33
import { Quote } from 'types/state';
4-
import { IS_PROD } from 'config';
4+
55
import GrpcClient from './grpc';
66

77
/**
@@ -89,6 +89,7 @@ class LoopApi {
8989
amount: number,
9090
quote: Quote,
9191
chanIds: number[],
92+
deadline: number,
9293
): Promise<LOOP.SwapResponse.AsObject> {
9394
const req = new LOOP.LoopOutRequest();
9495
req.setAmt(amount);
@@ -98,15 +99,7 @@ class LoopApi {
9899
req.setMaxSwapRoutingFee(this._calcRoutingFee(quote.swapFee));
99100
req.setMaxPrepayRoutingFee(this._calcRoutingFee(quote.prepayAmount));
100101
req.setOutgoingChanSetList(chanIds);
101-
102-
if (IS_PROD) {
103-
// in prod env, push the deadline out for 30 mins for lower fees
104-
const thirtyMins = 30 * 60 * 1000;
105-
req.setSwapPublicationDeadline(Date.now() + thirtyMins);
106-
} else {
107-
// use the --fast option in dev env
108-
req.setSwapPublicationDeadline(0);
109-
}
102+
req.setSwapPublicationDeadline(deadline);
110103

111104
const res = await this._grpc.request(SwapClient.LoopOut, req, this._meta);
112105
return res.toObject();

app/src/store/store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export class Store {
6565
*/
6666
async init() {
6767
this.settingsStore.init();
68+
await this.nodeStore.fetchInfo();
6869
await this.channelStore.fetchChannels();
6970
await this.swapStore.fetchSwaps();
7071
await this.nodeStore.fetchBalances();

app/src/store/stores/buildSwapStore.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { action, computed, observable, runInAction, toJS, values } from 'mobx';
2+
import { SwapResponse } from 'types/generated/loop_pb';
23
import { BuildSwapSteps, Quote, SwapDirection, SwapTerms } from 'types/state';
34
import { formatSats } from 'util/formatters';
45
import { Store } from 'store';
@@ -326,11 +327,19 @@ class BuildSwapStore {
326327
);
327328
this.processingTimeout = setTimeout(async () => {
328329
try {
329-
const chanIds = this.selectedChanIds.map(v => parseInt(v));
330-
const res =
331-
direction === SwapDirection.IN
332-
? await this._store.api.loop.loopIn(amount, quote, this.loopInLastHop)
333-
: await this._store.api.loop.loopOut(amount, quote, chanIds);
330+
let res: SwapResponse.AsObject;
331+
if (direction === SwapDirection.IN) {
332+
res = await this._store.api.loop.loopIn(amount, quote, this.loopInLastHop);
333+
} else {
334+
// on regtest, set the publication deadline to 0 for faster swaps, otherwise
335+
// set it to 30 minutes in the future to reduce swap fees
336+
const thirtyMins = 30 * 60 * 1000;
337+
const deadline =
338+
this._store.nodeStore.network === 'regtest' ? 0 : Date.now() + thirtyMins;
339+
// convert the selected channel ids to numbers
340+
const chanIds = this.selectedChanIds.map(v => parseInt(v));
341+
res = await this._store.api.loop.loopOut(amount, quote, chanIds, deadline);
342+
}
334343
this._store.log.info('completed loop', toJS(res));
335344
runInAction('requestSwapContinuation', () => {
336345
// hide the swap UI after it is complete

app/src/store/stores/nodeStore.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,51 @@ import { action, observable, runInAction, toJS } from 'mobx';
22
import { Store } from 'store';
33
import { Wallet } from '../models';
44

5+
type NodeChain = 'bitcoin' | 'litecoin';
6+
type NodeNetwork = 'mainnet' | 'testnet' | 'regtest';
7+
58
export default class NodeStore {
69
private _store: Store;
710

11+
/** the pubkey of the LND node */
12+
@observable pubkey = '';
13+
/** the alias of the LND node */
14+
@observable alias = '';
15+
/** the chain that the LND node is connected to */
16+
@observable chain: NodeChain = 'bitcoin';
17+
/** the network that the LND node is connected to */
18+
@observable network: NodeNetwork = 'mainnet';
819
/** the channel and wallet balances */
920
@observable wallet: Wallet = new Wallet();
1021

1122
constructor(store: Store) {
1223
this._store = store;
1324
}
1425

26+
/**
27+
* fetch wallet balances from the LND RPC
28+
*/
29+
@action.bound
30+
async fetchInfo() {
31+
this._store.log.info('fetching node info');
32+
try {
33+
const info = await this._store.api.lnd.getInfo();
34+
runInAction('getInfoContinuation', () => {
35+
this.pubkey = info.identityPubkey;
36+
this.alias = info.alias;
37+
if (info.chainsList && info.chainsList[0]) {
38+
this.chain = info.chainsList[0].chain as NodeChain;
39+
this.network = info.chainsList[0].network as NodeNetwork;
40+
}
41+
this._store.log.info('updated nodeStore info', toJS(this));
42+
});
43+
} catch (error) {
44+
runInAction('getInfoError', () => {
45+
this._store.uiStore.notify(error.message, 'Unable to fetch node info');
46+
});
47+
}
48+
}
49+
1550
/**
1651
* fetch wallet balances from the LND RPC
1752
*/

app/src/util/tests/sampleData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as LOOP from 'types/generated/loop_pb';
77
//
88

99
export const lndGetInfo: LND.GetInfoResponse.AsObject = {
10-
version: '0.9.0-beta commit=v0.9.0-beta',
10+
version: '0.10.0-beta commit=v0.10.0-beta',
1111
identityPubkey: '038b3fc29cfc195c9b190d86ad2d40ce7550a5c6f13941f53c7d7ac5b25c912a6c',
1212
alias: 'alice',
1313
color: '#cccccc',

0 commit comments

Comments
 (0)