add swap cost command

This commit is contained in:
Alex Bosworth 2019-09-09 17:46:15 -07:00
parent 9d32a81151
commit e02ae462e9
No known key found for this signature in database
GPG key ID: E80D2F3F311FD87E
19 changed files with 506 additions and 130 deletions

View file

@ -1,5 +1,10 @@
# Versions
## Version 3.7.0
- `liquidity-cost`: command added to check the cost of swap liquidity
- `peers`: added `--offline` restriction flag
## Version 3.6.0
- Add update notifier to notify of new versions

View file

@ -71,6 +71,9 @@ bos inbound-liquidity
# Increase inbound liquidity to the node
bos increase-inbound-liquidity
# Show liquidity adjustment costs
bos liquidity-cost "inbound" "amount"
# See market price history
bos market
@ -163,7 +166,7 @@ expr $(bos balance --node=savedNode1) + $(bos balance --node=savedNode1)
### Send Alerts
```shell
bos inbound-liquidity --below=1000000 | sendnotification SNS "AWS_SNS_ID" "WARNING inbound-liquidity deficit: %s sats" --nonzero --subject="Low inbound liquidity warning: node1"
bos inbound-liquidity --below=1000000 2>&1 | sendnotification SNS "AWS_SNS_ID" "WARNING inbound-liquidity deficit: %s sats" --nonzero --subject="Low inbound liquidity warning: node1"
# sends email if the inbound liquidity drops below a 1,000,000 sats
```

View file

@ -1,10 +1,12 @@
const accountingCategories = require('./accounting_categories');
const balanceFromTokens = require('./balance_from_tokens');
const getAccountingReport = require('./get_accounting_report');
const getBalance = require('./get_balance');
const getLiquidity = require('./get_liquidity');
module.exports = {
accountingCategories,
balanceFromTokens,
getAccountingReport,
getBalance,
getLiquidity,

24
bos
View file

@ -17,6 +17,8 @@ const {getLiquidity} = require('./balances');
const {getPeers} = require('./network');
const {getPriceChart} = require('./fiat');
const {getReport} = require('./wallets');
const {getSwapCost} = require('./swaps');
const {getSwapService} = require('./swaps');
const {getUtxos} = require('./chain');
const marketPairs = require('./fiat').pairs;
const {probeDestination} = require('./network');
@ -30,6 +32,7 @@ const {setAutopilot} = require('./network');
const {splitUtxos} = require('./chain');
const {swapIn} = require('./swaps');
const {swapOut} = require('./swaps');
const {swapTypes} = require('./swaps');
const {unlockWallet} = require('./wallets');
const {version} = require('./package');
@ -291,6 +294,25 @@ prog
});
})
// Get the price for liquidity
.command('liquidity-cost', 'Get the price of liquidity')
.argument('<type>', 'Liquidity direction', swapTypes)
.argument('<amount>', 'Amount of liquidity to get quote for', INT)
.option('--above <tokens>', 'Return amount above watermark', INT)
.option('--no-color', 'Mute all colors')
.option('--node <node_name>', 'Node to get liquidity cost')
.action((args, options, logger) => {
return new Promise(async (resolve, reject) => {
return getSwapCost({
above: options.above,
service: (await getSwapService({node: options.node})).service,
tokens: args.amount,
type: args.type,
},
returnNumber({logger, reject, resolve, number: 'cost'}));
});
})
// Get outbound liquidity information: available outbound off-chain tokens
.command('outbound-liquidity', 'Get outbound liquidity size')
.option('--above <tokens>', 'Return amount above watermark', INT)
@ -360,6 +382,7 @@ prog
.option('--active', 'Only active peer channels')
.option('--inbound-below <amount>', 'Inbound liquidity below amount')
.option('--node <node_name>', 'Node to get peers for')
.option('--offline', 'Only offline peer channels')
.option('--outbound-below <amount>', 'Outbound liquidity below amount', INT)
.option('--public', 'Only peers with public channels')
.option('--sort', 'Sort results by peer attribute')
@ -368,6 +391,7 @@ prog
return getPeers({
inbound_liquidity_below: options.inboundBelow,
is_active: !!options.active,
is_offline: !!options.offline,
is_public: !!options.public,
node: options.node,
outbound_liquidity_below: options.outboundBelow,

View file

@ -16,6 +16,7 @@ const uniq = arr => Array.from(new Set(arr));
{
[inbound_liquidity_below]: <Inbound Liquidity Below Tokens Number>
[is_active]: <Active Channels Only Bool>
[is_offline]: <Offline Channels Only Bool>
[is_public]: <Public Channels Only Bool>
[node]: <Node Name String>
[outbound_liquidity_below]: <Outbound Liquidity Below Tokens Number>
@ -42,6 +43,7 @@ module.exports = (args, cbk) => {
getChannels: ['getLnd', async ({getLnd}) => {
return await getChannels({
is_active: args.is_active,
is_offline: args.is_offline,
is_public: args.is_public,
lnd: getLnd.lnd,
});

343
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "balanceofsatoshis",
"version": "3.6.0",
"version": "3.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -14,12 +14,12 @@
}
},
"@babel/generator": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz",
"integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.0.tgz",
"integrity": "sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA==",
"dev": true,
"requires": {
"@babel/types": "^7.5.5",
"@babel/types": "^7.6.0",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0",
@ -112,42 +112,56 @@
"dev": true
},
"@babel/runtime": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
"integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz",
"integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"@babel/template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz",
"integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz",
"integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/parser": "^7.4.4",
"@babel/types": "^7.4.4"
"@babel/parser": "^7.6.0",
"@babel/types": "^7.6.0"
},
"dependencies": {
"@babel/parser": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz",
"integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==",
"dev": true
}
}
},
"@babel/traverse": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz",
"integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==",
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.0.tgz",
"integrity": "sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.5.5",
"@babel/generator": "^7.5.5",
"@babel/generator": "^7.6.0",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.4.4",
"@babel/parser": "^7.5.5",
"@babel/types": "^7.5.5",
"@babel/parser": "^7.6.0",
"@babel/types": "^7.6.0",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
},
"dependencies": {
"@babel/parser": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.0.tgz",
"integrity": "sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ==",
"dev": true
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@ -166,9 +180,9 @@
}
},
"@babel/types": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz",
"integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==",
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz",
"integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@ -907,16 +921,27 @@
"write-file-atomic": "^2.4.2"
},
"dependencies": {
"write-file-atomic": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.2"
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
@ -1219,26 +1244,6 @@
"unique-string": "^1.0.0",
"write-file-atomic": "^2.0.0",
"xdg-basedir": "^3.0.0"
},
"dependencies": {
"make-dir": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
"integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"requires": {
"pify": "^3.0.0"
}
},
"write-file-atomic": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.2"
}
}
}
},
"content-disposition": {
@ -1329,11 +1334,27 @@
"safe-buffer": "^5.0.1"
},
"dependencies": {
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
@ -1363,12 +1384,12 @@
}
},
"cross-spawn": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
"integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
"dev": true,
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"requires": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
@ -1615,18 +1636,6 @@
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"requires": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
}
}
},
"exit-hook": {
@ -1764,6 +1773,30 @@
"commondir": "^1.0.1",
"make-dir": "^2.0.0",
"pkg-dir": "^3.0.0"
},
"dependencies": {
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"find-up": {
@ -1806,6 +1839,18 @@
"requires": {
"cross-spawn": "^4",
"signal-exit": "^3.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
"integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=",
"dev": true,
"requires": {
"lru-cache": "^4.0.1",
"which": "^1.2.9"
}
}
}
},
"forever-agent": {
@ -2872,14 +2917,6 @@
"@babel/types": "^7.4.0",
"istanbul-lib-coverage": "^2.0.5",
"semver": "^6.0.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"istanbul-lib-processinfo": {
@ -2907,6 +2944,12 @@
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
@ -2921,6 +2964,28 @@
"supports-color": "^6.1.0"
},
"dependencies": {
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
@ -2954,11 +3019,33 @@
"ms": "^2.1.1"
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
@ -3191,9 +3278,9 @@
}
},
"ln-service": {
"version": "44.0.3",
"resolved": "https://registry.npmjs.org/ln-service/-/ln-service-44.0.3.tgz",
"integrity": "sha512-nlhitgiqxnlnzZRXXjnEsrElQ4Sf54C5NKv2b/rw2Ge5nybaWWN8plL/pKiiXu+57F4LgZOH0SNtYy3gzRJbJw==",
"version": "45.0.0",
"resolved": "https://registry.npmjs.org/ln-service/-/ln-service-45.0.0.tgz",
"integrity": "sha512-nAKNS/Fm5pvfkAGf5+0FLKcxd1dhIGdRNwunk6XcnnMYHX8fxtOoTsrE+oA778Fd+3trDQq/X09Bnkdvcxr89g==",
"requires": {
"@datastructures-js/priority-queue": "1.0.2",
"@grpc/proto-loader": "0.5.2",
@ -3336,21 +3423,11 @@
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
"integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"dependencies": {
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
}
"pify": "^3.0.0"
}
},
"make-error": {
@ -3454,9 +3531,9 @@
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.5.0.tgz",
"integrity": "sha512-9FwMVYhn6ERvMR8XFdOavRz4QK/VJV8elU1x50vYexf9lslDcWe/f4HBRxCPd185ekRSjU6CfYyJCECa/CQy7Q==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.5.1.tgz",
"integrity": "sha512-dmpSnLJtNQioZFI5HfQ55Ad0DzzsMAb+HfokwRTNXwEQjepbTkl5mtIlSVxGIkOkxlpX7wIn5ET/oAd9fZ/Y/Q==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.2",
@ -3562,6 +3639,14 @@
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"normalize-path": {
@ -3657,6 +3742,28 @@
"wrap-ansi": "^5.1.0"
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"requires": {
"pify": "^4.0.1",
"semver": "^5.6.0"
}
},
"pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
@ -3873,13 +3980,6 @@
"registry-auth-token": "^4.0.0",
"registry-url": "^5.0.0",
"semver": "^6.2.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"parse-json": {
@ -4049,9 +4149,9 @@
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"psl": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.3.1.tgz",
"integrity": "sha512-2KLd5fKOdAfShtY2d/8XDWVRnmp3zp40Qt6ge2zBPFARLXOGUf2fHD5eg+TV/5oxBtQKVhjUaKFsAaE4HnwfSA=="
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz",
"integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw=="
},
"pump": {
"version": "3.0.0",
@ -4370,9 +4470,9 @@
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"semver-diff": {
"version": "2.1.0",
@ -4380,6 +4480,13 @@
"integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
"requires": {
"semver": "^5.0.3"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
}
}
},
"send": {
@ -5777,6 +5884,18 @@
"string-width": "^2.1.1"
}
},
"write-file-atomic": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.0.tgz",
"integrity": "sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==",
"dev": true,
"requires": {
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
"signal-exit": "^3.0.2",
"typedarray-to-buffer": "^3.1.5"
}
},
"yaml": {
"version": "1.6.0",
"bundled": true,
@ -6303,15 +6422,13 @@
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"write-file-atomic": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.0.tgz",
"integrity": "sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==",
"dev": true,
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
"signal-exit": "^3.0.2",
"typedarray-to-buffer": "^3.1.5"
"signal-exit": "^3.0.2"
}
},
"ws": {

View file

@ -19,7 +19,7 @@
"goldengate": "2.0.1",
"html2unicode": "1.1.3",
"ln-accounting": "3.0.3",
"ln-service": "44.0.3",
"ln-service": "45.0.0",
"moment": "2.24.0",
"request": "2.88.0",
"stats-lite": "2.2.0",
@ -49,5 +49,5 @@
"scripts": {
"test": "tap test/arrays/*.js test/balances/*.js test/encryption/*.js test/fiat/*.js test/network/*.js test/responses/*.js test/routing/*.js"
},
"version": "3.6.0"
"version": "3.7.0"
}

View file

@ -15,7 +15,9 @@
module.exports = ({logger, number, reject, resolve}) => {
return (err, res) => {
if (!!err) {
return reject(err);
logger.error(err);
return reject();
}
logger.info(`${res[number]}`);

View file

@ -14,7 +14,9 @@
module.exports = ({logger, reject, resolve}) => {
return (err, res) => {
if (!!err) {
return reject(err);
logger.error(err);
return reject();
}
logger.info(res);

View file

@ -14,5 +14,6 @@
"minSweepConfs": 1,
"minutesPerBlock": 10,
"requiredBufferBlocks": 36,
"swapTypes": ["inbound", "outbound"],
"sweepProgressLogDelayMs": 2000
}

77
swaps/get_swap_cost.js Normal file
View file

@ -0,0 +1,77 @@
const asyncAuto = require('async/auto');
const {getSwapInQuote} = require('goldengate');
const {getSwapOutQuote} = require('goldengate');
const {lightningLabsSwapService} = require('goldengate');
const {returnResult} = require('asyncjs-util');
const {balanceFromTokens} = require('./../balances');
const {feeRateDenominator} = require('./constants');
const {getNetwork} = require('./../network');
const {swapTypes} = require('./constants');
/** Get the cost of liquidity via swap
{
[above]: <Cost Above Tokens Number>
service: <Swap Service API Object>
tokens: <Liquidity Tokens Number>
type: <Liquidity Type String>
}
@returns via cbk or Promise
{
cost: <Cost of Swap in Tokens Number>
}
*/
module.exports = ({above, service, tokens, type}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!service) {
return cbk([400, 'ExpectedSwapServiceToGetSwapCost']);
}
if (!tokens) {
return cbk([400, 'ExpectedTokensCountToGetSwapCost']);
}
return cbk();
},
// Get a swap quote
getQuote: ['validate', ({}, cbk) => {
switch (type) {
case 'inbound':
return getSwapInQuote({service}, cbk);
case 'outbound':
return getSwapOutQuote({service}, cbk);
default:
return cbk([400, 'GotUnexpectedSwapTypeWhenGettingSwapCost']);
}
}],
// Final cost
cost: ['getQuote', ({getQuote}, cbk) => {
const quote = getQuote;
if (tokens > quote.max_tokens) {
return cbk([400, 'AmountExceedsMaximum', {max: quote.max_tokens}]);
}
if (tokens < quote.min_tokens) {
return cbk([400, 'AmountBelowMinimumSwap', {min: quote.min_tokens}]);
}
const rateFee = quote.fee_rate / feeRateDenominator * tokens;
const fee = rateFee + quote.base_fee;
return cbk(null, {cost: balanceFromTokens({above, tokens: [fee]})});
}],
},
returnResult({reject, resolve, of: 'cost'}, cbk));
});
};

44
swaps/get_swap_service.js Normal file
View file

@ -0,0 +1,44 @@
const asyncAuto = require('async/auto');
const {lightningLabsSwapService} = require('goldengate');
const {returnResult} = require('asyncjs-util');
const {authenticatedLnd} = require('./../lnd');
const {getNetwork} = require('./../network');
/** Get swap service
{
[node]: <Node Name String>
}
@returns via cbk or Promise
{
service: <Swap Service Object>
}
*/
module.exports = ({node}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Get LND
getLnd: cbk => authenticatedLnd({node}, cbk),
// LND object
lnd: ['getLnd', ({getLnd}, cbk) => cbk(null, getLnd.lnd)],
// Get network
getNetwork: ['lnd', ({lnd}, cbk) => getNetwork({lnd}, cbk)],
// Swap service
service: ['getNetwork', ({getNetwork}, cbk) => {
const {network} = getNetwork;
try {
return cbk(null, lightningLabsSwapService({network}));
} catch (err) {
return cbk([400, 'FailedToFindSupportedSwapService', {err}]);
}
}],
},
returnResult({reject, resolve, of: 'service'}));
});
};

View file

@ -1,5 +1,15 @@
const getSwapCost = require('./get_swap_cost');
const getSwapService = require('./get_swap_service');
const rebalance = require('./rebalance');
const swapIn = require('./swap_in');
const swapOut = require('./swap_out');
const {swapTypes} = require('./constants');
module.exports = {rebalance, swapIn, swapOut};
module.exports = {
getSwapCost,
getSwapService,
rebalance,
swapIn,
swapOut,
swapTypes,
};

View file

@ -426,7 +426,7 @@ module.exports = (args, cbk) => {
send_exact_amount: bigFormat(args.tokens),
},
service_fee: args.tokens - createInvoice.tokens,
refund_recovery: recovery,
refund_recovery_secret: recovery,
timing: {
earliest_completion: moment(now() + msPerBlock).fromNow(),
refund_available: moment(now() + expiryBlocks*msPerBlock).fromNow(),

View file

@ -397,7 +397,7 @@ module.exports = (args, cbk) => {
tokens: args.tokens,
});
args.logger.info({restart_with_recovery: recovery.toString('hex')});
args.logger.info({restart_recovery_secret: recovery.toString('hex')});
} catch (err) {
return cbk([500, 'UnexpectedErrorGeneratingRecoveryState', {err}]);
}

View file

@ -1,6 +1,6 @@
const {test} = require('tap');
const balanceFromTokens = require('./../../balances/balance_from_tokens');
const {balanceFromTokens} = require('./../../balances');
const tests = [
// No tokens

View file

@ -17,11 +17,16 @@ const tests = [
tests.forEach(({args, description, error, expected}) => {
return test(description, ({end, equal, throws}) => {
if (!!error) {
return returnNumber({reject: err => {
equal(err, error, 'Error as expected');
let err;
return end();
}})(error);
return returnNumber({
logger: {error: n => err = n},
reject: () => {
equal(err, error, 'Error as expected');
return end();
},
})(error);
}
let number;

View file

@ -17,11 +17,16 @@ const tests = [
tests.forEach(({args, description, error, expected}) => {
return test(description, ({end, equal, throws}) => {
if (!!error) {
return returnOutput({reject: err => {
equal(err, error, 'Error as expected');
let err;
return end();
}})(error);
return returnOutput({
logger: {error: n => err = n},
reject: () => {
equal(err, error, 'Error as expected');
return end();
},
})(error);
}
let output;

View file

@ -0,0 +1,77 @@
const {test} = require('tap');
const {getSwapCost} = require('./../../swaps');
const makeQuote = ({max, min}) => ({
cltv_delta: 1,
max_swap_amount: max || 1,
min_swap_amount: min || 1,
prepay_amt: 1,
swap_fee_base: 1,
swap_fee_rate: 1,
swap_payment_dest: Buffer.alloc(33).toString('hex'),
});
const tests = [
{
args: {},
description: 'Swap service is required',
error: [400, 'ExpectedSwapServiceToGetSwapCost'],
},
{
args: {service: {}},
description: 'Tokens are required',
error: [400, 'ExpectedTokensCountToGetSwapCost'],
},
{
args: {service: {}, tokens: 1},
description: 'Swap type is required',
error: [400, 'GotUnexpectedSwapTypeWhenGettingSwapCost'],
},
{
args: {service: {}, tokens: 1, type: 'type'},
description: 'Known swap type is required',
error: [400, 'GotUnexpectedSwapTypeWhenGettingSwapCost'],
},
{
args: {
service: {loopInQuote: ({}, cbk) => cbk(null, makeQuote({}))},
tokens: 1e6,
type: 'inbound',
},
description: 'Amount must be under maximum',
error: [400, 'AmountExceedsMaximum', {max: 1}],
},
{
args: {
service: {
loopOutQuote: ({}, cbk) => cbk(null, makeQuote({max: 1e7, min: 1e6})),
},
tokens: 1e5,
type: 'outbound',
},
description: 'Amount must be over minimum',
error: [400, 'AmountBelowMinimumSwap', {min: 1e6}],
},
{
args: {
service: {loopInQuote: ({}, cbk) => cbk(null, makeQuote({max: 1e7}))},
tokens: 1e6,
type: 'inbound',
},
description: 'Amount must be under maximum',
expected: {cost: 2},
},
];
tests.forEach(({args, description, error, expected}) => {
return test(description, async ({end, equal, rejects}) => {
if (!!error) {
rejects(getSwapCost(args), error, 'Got expected error');
} else {
equal((await getSwapCost(args)).cost, expected.cost, 'Got cost');
}
return end();
});
});