mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Merge pull request #5 from Permissionless-Software-Foundation/ct-unstable
Got /order/take to generate a partial TX
This commit is contained in:
+3
-3
@@ -10,16 +10,16 @@ There are three major pieces of software behind the bch-dex concept. They work t
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
- _Client_ could be a web browser, or a command-line client like [psf-bch-wallet](https://github.com/Permissionless-Software-Foundation/psf-bch-wallet) or [psf-avax-wallet](https://github.com/Permissionless-Software-Foundation/psf-avax-wallet).
|
- _Client_ could be a web browser like [wallet.fullstack.cash](https://bchn-wallet.fullstack.cash), or a command-line client like [psf-bch-wallet](https://github.com/Permissionless-Software-Foundation/psf-bch-wallet) or [psf-avax-wallet](https://github.com/Permissionless-Software-Foundation/psf-avax-wallet).
|
||||||
- [bch-dex](https://github.com/Permissionless-Software-Foundation/bch-dex) is the back end REST API that maintains a local database of information that the client reads from.
|
- [bch-dex](https://github.com/Permissionless-Software-Foundation/bch-dex) is the back end REST API that maintains a local database of information that the client reads from.
|
||||||
- [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) is the pay-to-write global database with a REST API for interfacing with the other two pieces of software.
|
- [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) is the pay-to-write global database with a REST API for interfacing with the other two pieces of software.
|
||||||
|
|
||||||
The arrows in the image represent the information flow between the three pieces of software:
|
The arrows in the image represent the information flow between the three pieces of software:
|
||||||
|
|
||||||
- The _Client_ is essentially a 'dummy terminal' with a bidirectional interface to bch-dex. bch-dex does the heavy lifting, and the _Client_ is a 'thin' UI wrapper.
|
- The _Client_ is essentially a 'dummy terminal' with a bidirectional interface to bch-dex. bch-dex does the heavy lifting, and the _Client_ is a 'thin' UI wrapper.
|
||||||
- `bch-dex` imports data from the global P2WDB database into its local database, using a [webhook](https://en.wikipedia.org/wiki/Webhook) (dashed line). It can also custody funds, pay transaction fees, and create an _Offer_ by submitting the data to the P2WDB to generate an _Order_ (solid line).
|
- `bch-dex` imports data from the global P2WDB database into its local database, using a [webhook](https://en.wikipedia.org/wiki/Webhook) (dashed line). It can also custody funds, pay transaction fees, and create an _Order_ by submitting the data to the P2WDB to generate an _Offer_ (solid line).
|
||||||
|
|
||||||
This architecture keeps the global database highly censorship resistant, while allowing local installations to maintain tight control over the user experience. The goal is to have many redundant copies of `bch-dex` on the network, and to empower individual traders to run their own, private copy.
|
This architecture keeps the global database highly censorship resistant, while allowing local installations to maintain tight control over the user experience. The goal is to have many redundant copies of `bch-dex` on the network, and to empower individual traders to run their own, private copy, while maintaining a single source of truth via the P2WDB.
|
||||||
|
|
||||||
# Back End
|
# Back End
|
||||||
|
|
||||||
|
|||||||
Generated
+191
-35
@@ -9,13 +9,13 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "5.3.2",
|
"@psf/bch-js": "5.4.0",
|
||||||
"axios": "0.21.1",
|
"axios": "0.21.1",
|
||||||
"bch-message-lib": "2.1.4",
|
"bch-message-lib": "2.1.4",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"glob": "7.1.6",
|
"glob": "7.1.6",
|
||||||
"ipfs": "0.60.0",
|
"ipfs": "0.60.0",
|
||||||
"ipfs-coord": "7.1.4",
|
"ipfs-coord": "7.1.6",
|
||||||
"ipfs-http-client": "55.0.0",
|
"ipfs-http-client": "55.0.0",
|
||||||
"jsonrpc-lite": "2.2.0",
|
"jsonrpc-lite": "2.2.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"koa2-ratelimit": "0.9.1",
|
"koa2-ratelimit": "0.9.1",
|
||||||
"libp2p": "^0.36.2",
|
"libp2p": "^0.36.2",
|
||||||
"line-reader": "0.4.0",
|
"line-reader": "0.4.0",
|
||||||
"minimal-slp-wallet": "4.4.2",
|
"minimal-slp-wallet": "4.4.3",
|
||||||
"mongoose": "5.13.13",
|
"mongoose": "5.13.13",
|
||||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||||
"nodemailer": "6.4.17",
|
"nodemailer": "6.4.17",
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"apidoc": "0.26.0",
|
"apidoc": "0.26.0",
|
||||||
|
"bch-token-sweep": "1.5.16",
|
||||||
"chai": "4.3.0",
|
"chai": "4.3.0",
|
||||||
"coveralls": "2.11.4",
|
"coveralls": "2.11.4",
|
||||||
"eslint": "7.19.0",
|
"eslint": "7.19.0",
|
||||||
@@ -74,8 +75,6 @@
|
|||||||
"@psf/bitcoincash-ops": "2.0.0",
|
"@psf/bitcoincash-ops": "2.0.0",
|
||||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||||
"@psf/coininfo": "4.0.0",
|
"@psf/coininfo": "4.0.0",
|
||||||
"@uppy/core": "1.10.4",
|
|
||||||
"@uppy/tus": "1.5.12",
|
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"bc-bip68": "1.0.5",
|
"bc-bip68": "1.0.5",
|
||||||
"bchaddrjs-slp": "0.2.5",
|
"bchaddrjs-slp": "0.2.5",
|
||||||
@@ -97,7 +96,7 @@
|
|||||||
"wif": "2.0.6"
|
"wif": "2.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"apidoc": "0.17.7",
|
"apidoc": "^0.50.5",
|
||||||
"assert": "2.0.0",
|
"assert": "2.0.0",
|
||||||
"chai": "4.1.2",
|
"chai": "4.1.2",
|
||||||
"coveralls": "3.0.2",
|
"coveralls": "3.0.2",
|
||||||
@@ -109,10 +108,10 @@
|
|||||||
"eslint-plugin-standard": "4.0.0",
|
"eslint-plugin-standard": "4.0.0",
|
||||||
"husky": "^4.3.8",
|
"husky": "^4.3.8",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"mocha": "9.1.3",
|
"mocha": "^9.2.1",
|
||||||
"node-mocks-http": "1.7.0",
|
"node-mocks-http": "1.7.0",
|
||||||
"nyc": "15.1.0",
|
"nyc": "15.1.0",
|
||||||
"semantic-release": "18.0.0",
|
"semantic-release": "^19.0.2",
|
||||||
"sinon": "9.2.2",
|
"sinon": "9.2.2",
|
||||||
"standard": "^16.0.4"
|
"standard": "^16.0.4"
|
||||||
}
|
}
|
||||||
@@ -122,7 +121,7 @@
|
|||||||
"extraneous": true,
|
"extraneous": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "5.3.2",
|
"@psf/bch-js": "../bch-js",
|
||||||
"apidoc": "0.25.0",
|
"apidoc": "0.25.0",
|
||||||
"bch-consumer": "1.2.0",
|
"bch-consumer": "1.2.0",
|
||||||
"bch-donation": "1.1.1",
|
"bch-donation": "1.1.1",
|
||||||
@@ -140,9 +139,9 @@
|
|||||||
"eslint-plugin-standard": "4.0.1",
|
"eslint-plugin-standard": "4.0.1",
|
||||||
"husky": "4.3.8",
|
"husky": "4.3.8",
|
||||||
"lodash.clonedeep": "4.5.0",
|
"lodash.clonedeep": "4.5.0",
|
||||||
"mocha": "8.2.0",
|
"mocha": "^9.2.1",
|
||||||
"nyc": "15.1.0",
|
"nyc": "15.1.0",
|
||||||
"semantic-release": "17.4.4",
|
"semantic-release": "^19.0.2",
|
||||||
"sinon": "9.2.0",
|
"sinon": "9.2.0",
|
||||||
"standard": "16.0.4",
|
"standard": "16.0.4",
|
||||||
"tinyify": "3.0.0"
|
"tinyify": "3.0.0"
|
||||||
@@ -1604,9 +1603,9 @@
|
|||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/@psf/bch-js": {
|
"node_modules/@psf/bch-js": {
|
||||||
"version": "5.3.2",
|
"version": "5.4.0",
|
||||||
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.3.2.tgz",
|
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.4.0.tgz",
|
||||||
"integrity": "sha512-LzYekwuee+T8AEZ1MKBe5KnZKXzpRZ57SpXRL2S8qsHefokTIN+XnDPX7dsOFkN4VGGgyjYyZ33iN6xhOSHWJw==",
|
"integrity": "sha512-HyBCpobC9moxSbQl4k/tb/GW9p8L12Fu+8yR1x8eflxBe7c2bwBlG218LmuoNcvjJMAdDxk0VEHvtO64vpltsw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chris.troutner/bip32-utils": "1.0.5",
|
"@chris.troutner/bip32-utils": "1.0.5",
|
||||||
@@ -1614,8 +1613,6 @@
|
|||||||
"@psf/bitcoincash-ops": "2.0.0",
|
"@psf/bitcoincash-ops": "2.0.0",
|
||||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||||
"@psf/coininfo": "4.0.0",
|
"@psf/coininfo": "4.0.0",
|
||||||
"@uppy/core": "1.10.4",
|
|
||||||
"@uppy/tus": "1.5.12",
|
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"bc-bip68": "1.0.5",
|
"bc-bip68": "1.0.5",
|
||||||
"bchaddrjs-slp": "0.2.5",
|
"bchaddrjs-slp": "0.2.5",
|
||||||
@@ -2972,6 +2969,17 @@
|
|||||||
"minimal-slp-wallet": "4.4.1"
|
"minimal-slp-wallet": "4.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bch-token-sweep": {
|
||||||
|
"version": "1.5.16",
|
||||||
|
"resolved": "http://94.130.170.209:4873/bch-token-sweep/-/bch-token-sweep-1.5.16.tgz",
|
||||||
|
"integrity": "sha512-9Fsb0qXUZ62OUDdH/tIF8QZDs12vWduoGDtU9txpSEF6GEyK0NOHfu8YYx5henoHz7SGS/fMUWDCibmedSpsmA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"bch-donation": "1.1.1",
|
||||||
|
"bignumber.js": "9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bchaddrjs-slp": {
|
"node_modules/bchaddrjs-slp": {
|
||||||
"version": "0.2.5",
|
"version": "0.2.5",
|
||||||
"resolved": "http://94.130.170.209:4873/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
|
"resolved": "http://94.130.170.209:4873/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
|
||||||
@@ -8411,9 +8419,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ipfs-coord": {
|
"node_modules/ipfs-coord": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/ipfs-coord/-/ipfs-coord-7.1.4.tgz",
|
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-7.1.6.tgz",
|
||||||
"integrity": "sha512-YsCdrQ30JW03uOLNrpZKqNmWr5zmxF9AyKeXSQA/OTPBoOOqM2nomEYhTPVJjGxee9Qa2dtxCQzw3JwsLDCe4Q==",
|
"integrity": "sha512-wtdJ/PgRMv3r2pOZ1WIc7blTkk3YftMbXTNX9hN9Lu+IblUhNoWUyjq7E4i7J3zBZQVQs7J3TyckRdLCekmc9A==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "0.21.4",
|
"axios": "0.21.4",
|
||||||
"bch-encrypt-lib": "2.0.0",
|
"bch-encrypt-lib": "2.0.0",
|
||||||
@@ -13012,12 +13021,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimal-slp-wallet": {
|
"node_modules/minimal-slp-wallet": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.3",
|
||||||
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.2.tgz",
|
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.3.tgz",
|
||||||
"integrity": "sha512-6JNRnH8mQQb/gmfwdd0KgJeHFJycK60EDmeEdjghvm1I2J0z2HVI3gZzrVqjBC/tBDYKtuDuvRtix7q2LqKqhw==",
|
"integrity": "sha512-wpELTh4WYems96SxyRW/KdglJ6T3ieR7VVDCLcqq7bwZ+tJXFznOxDRggS5sREI9tTxiAHIyVSg+nbXZZk8buQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "5.3.2",
|
"@psf/bch-js": "5.4.0",
|
||||||
"apidoc": "0.25.0",
|
"apidoc": "0.25.0",
|
||||||
"bch-consumer": "1.2.0",
|
"bch-consumer": "1.2.0",
|
||||||
"bch-donation": "1.1.1",
|
"bch-donation": "1.1.1",
|
||||||
@@ -17923,6 +17932,49 @@
|
|||||||
"minimal-slp-wallet": "4.4.2"
|
"minimal-slp-wallet": "4.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p2wdb/node_modules/@psf/bch-js": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-LzYekwuee+T8AEZ1MKBe5KnZKXzpRZ57SpXRL2S8qsHefokTIN+XnDPX7dsOFkN4VGGgyjYyZ33iN6xhOSHWJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@chris.troutner/bip32-utils": "1.0.5",
|
||||||
|
"@psf/bip21": "2.0.1",
|
||||||
|
"@psf/bitcoincash-ops": "2.0.0",
|
||||||
|
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||||
|
"@psf/coininfo": "4.0.0",
|
||||||
|
"@uppy/core": "1.10.4",
|
||||||
|
"@uppy/tus": "1.5.12",
|
||||||
|
"axios": "^0.21.4",
|
||||||
|
"bc-bip68": "1.0.5",
|
||||||
|
"bchaddrjs-slp": "0.2.5",
|
||||||
|
"bigi": "1.4.2",
|
||||||
|
"bignumber.js": "9.0.0",
|
||||||
|
"bip-schnorr": "0.3.0",
|
||||||
|
"bip38": "2.0.2",
|
||||||
|
"bip39": "3.0.2",
|
||||||
|
"bip66": "1.1.5",
|
||||||
|
"bitcoinjs-message": "2.0.0",
|
||||||
|
"bs58": "4.0.1",
|
||||||
|
"ecashaddrjs": "1.0.7",
|
||||||
|
"ini": "1.3.8",
|
||||||
|
"randombytes": "2.0.6",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"satoshi-bitcoin": "1.0.4",
|
||||||
|
"slp-mdm": "0.0.6",
|
||||||
|
"slp-parser": "0.0.4",
|
||||||
|
"wif": "2.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p2wdb/node_modules/@psf/bch-js/node_modules/axios": {
|
||||||
|
"version": "0.21.4",
|
||||||
|
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.21.4.tgz",
|
||||||
|
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p2wdb/node_modules/apidoc": {
|
"node_modules/p2wdb/node_modules/apidoc": {
|
||||||
"version": "0.25.0",
|
"version": "0.25.0",
|
||||||
"resolved": "http://94.130.170.209:4873/apidoc/-/apidoc-0.25.0.tgz",
|
"resolved": "http://94.130.170.209:4873/apidoc/-/apidoc-0.25.0.tgz",
|
||||||
@@ -17954,6 +18006,34 @@
|
|||||||
"follow-redirects": "^1.14.4"
|
"follow-redirects": "^1.14.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p2wdb/node_modules/minimal-slp-wallet": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-6JNRnH8mQQb/gmfwdd0KgJeHFJycK60EDmeEdjghvm1I2J0z2HVI3gZzrVqjBC/tBDYKtuDuvRtix7q2LqKqhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@psf/bch-js": "5.3.2",
|
||||||
|
"apidoc": "0.25.0",
|
||||||
|
"bch-consumer": "1.2.0",
|
||||||
|
"bch-donation": "1.1.1",
|
||||||
|
"crypto-js": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p2wdb/node_modules/randombytes": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "http://94.130.170.209:4873/randombytes/-/randombytes-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p2wdb/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/package-hash": {
|
"node_modules/package-hash": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -23321,17 +23401,15 @@
|
|||||||
"version": "1.1.0"
|
"version": "1.1.0"
|
||||||
},
|
},
|
||||||
"@psf/bch-js": {
|
"@psf/bch-js": {
|
||||||
"version": "5.3.2",
|
"version": "5.4.0",
|
||||||
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.3.2.tgz",
|
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.4.0.tgz",
|
||||||
"integrity": "sha512-LzYekwuee+T8AEZ1MKBe5KnZKXzpRZ57SpXRL2S8qsHefokTIN+XnDPX7dsOFkN4VGGgyjYyZ33iN6xhOSHWJw==",
|
"integrity": "sha512-HyBCpobC9moxSbQl4k/tb/GW9p8L12Fu+8yR1x8eflxBe7c2bwBlG218LmuoNcvjJMAdDxk0VEHvtO64vpltsw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@chris.troutner/bip32-utils": "1.0.5",
|
"@chris.troutner/bip32-utils": "1.0.5",
|
||||||
"@psf/bip21": "2.0.1",
|
"@psf/bip21": "2.0.1",
|
||||||
"@psf/bitcoincash-ops": "2.0.0",
|
"@psf/bitcoincash-ops": "2.0.0",
|
||||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||||
"@psf/coininfo": "4.0.0",
|
"@psf/coininfo": "4.0.0",
|
||||||
"@uppy/core": "1.10.4",
|
|
||||||
"@uppy/tus": "1.5.12",
|
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"bc-bip68": "1.0.5",
|
"bc-bip68": "1.0.5",
|
||||||
"bchaddrjs-slp": "0.2.5",
|
"bchaddrjs-slp": "0.2.5",
|
||||||
@@ -24320,6 +24398,16 @@
|
|||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"bch-token-sweep": {
|
||||||
|
"version": "1.5.16",
|
||||||
|
"resolved": "http://94.130.170.209:4873/bch-token-sweep/-/bch-token-sweep-1.5.16.tgz",
|
||||||
|
"integrity": "sha512-9Fsb0qXUZ62OUDdH/tIF8QZDs12vWduoGDtU9txpSEF6GEyK0NOHfu8YYx5henoHz7SGS/fMUWDCibmedSpsmA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"bch-donation": "1.1.1",
|
||||||
|
"bignumber.js": "9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bchaddrjs-slp": {
|
"bchaddrjs-slp": {
|
||||||
"version": "0.2.5",
|
"version": "0.2.5",
|
||||||
"resolved": "http://94.130.170.209:4873/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
|
"resolved": "http://94.130.170.209:4873/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
|
||||||
@@ -28077,9 +28165,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ipfs-coord": {
|
"ipfs-coord": {
|
||||||
"version": "7.1.4",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/ipfs-coord/-/ipfs-coord-7.1.4.tgz",
|
"resolved": "http://94.130.170.209:4873/ipfs-coord/-/ipfs-coord-7.1.6.tgz",
|
||||||
"integrity": "sha512-YsCdrQ30JW03uOLNrpZKqNmWr5zmxF9AyKeXSQA/OTPBoOOqM2nomEYhTPVJjGxee9Qa2dtxCQzw3JwsLDCe4Q==",
|
"integrity": "sha512-wtdJ/PgRMv3r2pOZ1WIc7blTkk3YftMbXTNX9hN9Lu+IblUhNoWUyjq7E4i7J3zBZQVQs7J3TyckRdLCekmc9A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "0.21.4",
|
"axios": "0.21.4",
|
||||||
"bch-encrypt-lib": "2.0.0",
|
"bch-encrypt-lib": "2.0.0",
|
||||||
@@ -31595,11 +31683,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimal-slp-wallet": {
|
"minimal-slp-wallet": {
|
||||||
"version": "4.4.2",
|
"version": "4.4.3",
|
||||||
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.2.tgz",
|
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.3.tgz",
|
||||||
"integrity": "sha512-6JNRnH8mQQb/gmfwdd0KgJeHFJycK60EDmeEdjghvm1I2J0z2HVI3gZzrVqjBC/tBDYKtuDuvRtix7q2LqKqhw==",
|
"integrity": "sha512-wpELTh4WYems96SxyRW/KdglJ6T3ieR7VVDCLcqq7bwZ+tJXFznOxDRggS5sREI9tTxiAHIyVSg+nbXZZk8buQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@psf/bch-js": "5.3.2",
|
"@psf/bch-js": "5.4.0",
|
||||||
"apidoc": "0.25.0",
|
"apidoc": "0.25.0",
|
||||||
"bch-consumer": "1.2.0",
|
"bch-consumer": "1.2.0",
|
||||||
"bch-donation": "1.1.1",
|
"bch-donation": "1.1.1",
|
||||||
@@ -34958,6 +35046,49 @@
|
|||||||
"minimal-slp-wallet": "4.4.2"
|
"minimal-slp-wallet": "4.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@psf/bch-js": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/@psf%2fbch-js/-/bch-js-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-LzYekwuee+T8AEZ1MKBe5KnZKXzpRZ57SpXRL2S8qsHefokTIN+XnDPX7dsOFkN4VGGgyjYyZ33iN6xhOSHWJw==",
|
||||||
|
"requires": {
|
||||||
|
"@chris.troutner/bip32-utils": "1.0.5",
|
||||||
|
"@psf/bip21": "2.0.1",
|
||||||
|
"@psf/bitcoincash-ops": "2.0.0",
|
||||||
|
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||||
|
"@psf/coininfo": "4.0.0",
|
||||||
|
"@uppy/core": "1.10.4",
|
||||||
|
"@uppy/tus": "1.5.12",
|
||||||
|
"axios": "^0.21.4",
|
||||||
|
"bc-bip68": "1.0.5",
|
||||||
|
"bchaddrjs-slp": "0.2.5",
|
||||||
|
"bigi": "1.4.2",
|
||||||
|
"bignumber.js": "9.0.0",
|
||||||
|
"bip-schnorr": "0.3.0",
|
||||||
|
"bip38": "2.0.2",
|
||||||
|
"bip39": "3.0.2",
|
||||||
|
"bip66": "1.1.5",
|
||||||
|
"bitcoinjs-message": "2.0.0",
|
||||||
|
"bs58": "4.0.1",
|
||||||
|
"ecashaddrjs": "1.0.7",
|
||||||
|
"ini": "1.3.8",
|
||||||
|
"randombytes": "2.0.6",
|
||||||
|
"safe-buffer": "5.1.2",
|
||||||
|
"satoshi-bitcoin": "1.0.4",
|
||||||
|
"slp-mdm": "0.0.6",
|
||||||
|
"slp-parser": "0.0.4",
|
||||||
|
"wif": "2.0.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.4",
|
||||||
|
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.21.4.tgz",
|
||||||
|
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.14.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"apidoc": {
|
"apidoc": {
|
||||||
"version": "0.25.0",
|
"version": "0.25.0",
|
||||||
"resolved": "http://94.130.170.209:4873/apidoc/-/apidoc-0.25.0.tgz",
|
"resolved": "http://94.130.170.209:4873/apidoc/-/apidoc-0.25.0.tgz",
|
||||||
@@ -34980,6 +35111,31 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "^1.14.4"
|
"follow-redirects": "^1.14.4"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"minimal-slp-wallet": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-6JNRnH8mQQb/gmfwdd0KgJeHFJycK60EDmeEdjghvm1I2J0z2HVI3gZzrVqjBC/tBDYKtuDuvRtix7q2LqKqhw==",
|
||||||
|
"requires": {
|
||||||
|
"@psf/bch-js": "5.3.2",
|
||||||
|
"apidoc": "0.25.0",
|
||||||
|
"bch-consumer": "1.2.0",
|
||||||
|
"bch-donation": "1.1.1",
|
||||||
|
"crypto-js": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"randombytes": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "http://94.130.170.209:4873/randombytes/-/randombytes-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "http://94.130.170.209:4873/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-3
@@ -27,13 +27,13 @@
|
|||||||
},
|
},
|
||||||
"repository": "Permissionless-Software-Foundation/bch-dex",
|
"repository": "Permissionless-Software-Foundation/bch-dex",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "5.3.2",
|
"@psf/bch-js": "5.4.0",
|
||||||
"axios": "0.21.1",
|
"axios": "0.21.1",
|
||||||
"bch-message-lib": "2.1.4",
|
"bch-message-lib": "2.1.4",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"glob": "7.1.6",
|
"glob": "7.1.6",
|
||||||
"ipfs": "0.60.0",
|
"ipfs": "0.60.0",
|
||||||
"ipfs-coord": "7.1.4",
|
"ipfs-coord": "7.1.6",
|
||||||
"ipfs-http-client": "55.0.0",
|
"ipfs-http-client": "55.0.0",
|
||||||
"jsonrpc-lite": "2.2.0",
|
"jsonrpc-lite": "2.2.0",
|
||||||
"jsonwebtoken": "8.5.1",
|
"jsonwebtoken": "8.5.1",
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"koa2-ratelimit": "0.9.1",
|
"koa2-ratelimit": "0.9.1",
|
||||||
"libp2p": "^0.36.2",
|
"libp2p": "^0.36.2",
|
||||||
"line-reader": "0.4.0",
|
"line-reader": "0.4.0",
|
||||||
"minimal-slp-wallet": "4.4.2",
|
"minimal-slp-wallet": "4.4.3",
|
||||||
"mongoose": "5.13.13",
|
"mongoose": "5.13.13",
|
||||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||||
"nodemailer": "6.4.17",
|
"nodemailer": "6.4.17",
|
||||||
@@ -63,6 +63,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"apidoc": "0.26.0",
|
"apidoc": "0.26.0",
|
||||||
|
"bch-token-sweep": "1.5.16",
|
||||||
"chai": "4.3.0",
|
"chai": "4.3.0",
|
||||||
"coveralls": "2.11.4",
|
"coveralls": "2.11.4",
|
||||||
"eslint": "7.19.0",
|
"eslint": "7.19.0",
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const Order = new mongoose.Schema({
|
|||||||
timestamp: { type: String },
|
timestamp: { type: String },
|
||||||
localTimestamp: { type: String },
|
localTimestamp: { type: String },
|
||||||
p2wdbTxid: { type: String },
|
p2wdbTxid: { type: String },
|
||||||
p2wdbHash: { type: String }
|
p2wdbHash: { type: String },
|
||||||
|
orderStatus: { type: String }
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = mongoose.model('order', Order)
|
module.exports = mongoose.model('order', Order)
|
||||||
|
|||||||
+125
-57
@@ -178,63 +178,131 @@ class WalletAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Burn enough PSF to generate a valide proof-of-burn for writing to the P2WDB.
|
// Generate a partial transcation to *take* a 'sell' order.
|
||||||
// async burnPsf () {
|
async generatePartialTx (orderInfo) {
|
||||||
// try {
|
try {
|
||||||
// // TODO: Throw error if this.bchWallet has not been instantiated.
|
console.log(`orderInfo: ${JSON.stringify(orderInfo, null, 2)}`)
|
||||||
//
|
|
||||||
// // console.log('walletData: ', walletData)
|
const bchjs = this.bchWallet.bchjs
|
||||||
// // console.log(
|
|
||||||
// // `walletData.utxos.utxoStore.slpUtxos: ${JSON.stringify(
|
// instance of transaction builder
|
||||||
// // walletData.utxos.utxoStore.slpUtxos,
|
const transactionBuilder = new bchjs.TransactionBuilder()
|
||||||
// // null,
|
|
||||||
// // 2,
|
// Get a payment UTXO.
|
||||||
// // )}`,
|
// TODO: Create a segregated UTXO.
|
||||||
// // )
|
const bchUtxos = this.bchWallet.utxos.utxoStore.bchUtxos
|
||||||
//
|
const paymentUtxo = await bchjs.Utxo.findBiggestUtxo(bchUtxos)
|
||||||
// // Get token UTXOs held by the wallet.
|
console.log('paymentUtxo: ', paymentUtxo)
|
||||||
// const tokenUtxos = this.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens
|
|
||||||
// // console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
|
// Get token info on the offered UTXO
|
||||||
//
|
const txData = await this.bchWallet.getTxData([orderInfo.utxoTxid])
|
||||||
// // Find a token UTXO that contains PSF with a quantity higher than needed
|
console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
|
||||||
// // to generate a proof-of-burn.
|
|
||||||
// let tokenUtxo = {}
|
// Construct the UTXO being offered for sale.
|
||||||
// for (let i = 0; i < tokenUtxos.length; i++) {
|
const offeredUtxo = {
|
||||||
// const thisUtxo = tokenUtxos[i]
|
txid: orderInfo.utxoTxid,
|
||||||
//
|
vout: orderInfo.utxoVout,
|
||||||
// // If token ID matches.
|
tokenId: orderInfo.tokenId,
|
||||||
// if (thisUtxo.tokenId === P2WDB_TOKEN_ID) {
|
decimals: txData[0].tokenDecimals,
|
||||||
// if (parseFloat(thisUtxo.qtyStr) >= PROOF_OF_BURN_QTY) {
|
tokenQty: orderInfo.numTokens.toString()
|
||||||
// tokenUtxo = thisUtxo
|
}
|
||||||
// break
|
console.log(`offeredUtxo: ${JSON.stringify(offeredUtxo, null, 2)}`)
|
||||||
// }
|
|
||||||
// }
|
// Build First part of the collaborative Tx a.k.a. Alice
|
||||||
// }
|
// Generate the OP_RETURN code.
|
||||||
//
|
const slpSendObj = bchjs.SLP.TokenType1.generateSendOpReturn(
|
||||||
// if (tokenUtxo.tokenId !== P2WDB_TOKEN_ID) {
|
[offeredUtxo],
|
||||||
// throw new Error(
|
orderInfo.numTokens.toString()
|
||||||
// `Token UTXO of with ID of ${P2WDB_TOKEN_ID} and quantity greater than ${PROOF_OF_BURN_QTY} could not be found in wallet.`
|
)
|
||||||
// )
|
const slpData = slpSendObj.script
|
||||||
// }
|
console.log(`slpOutputs: ${slpSendObj.outputs}`)
|
||||||
// // console.log(`tokenUtxo: ${JSON.stringify(tokenUtxo, null, 2)}`)
|
|
||||||
//
|
// Currently this app only supports a single SLP token UTXO for exact
|
||||||
// const result = await this.bchWallet.burnTokens(
|
// token quantities (no token change). e.g. 1 UTXO representing the
|
||||||
// PROOF_OF_BURN_QTY,
|
// exact number of 'numTokens'
|
||||||
// P2WDB_TOKEN_ID
|
if (slpSendObj.outputs > 1) {
|
||||||
// )
|
console.log('WARNING: choose one UTXO with all tokens to exchange')
|
||||||
// // console.log('walletData.burnTokens() result: ', result)
|
return
|
||||||
//
|
}
|
||||||
// return result
|
|
||||||
//
|
// Calculate sats needed to pay the offer.
|
||||||
// // return {
|
const satsNeeded = orderInfo.numTokens * parseInt(orderInfo.rateInSats)
|
||||||
// // success: true,
|
|
||||||
// // txid: 'fakeTxid',
|
// Calculate miner fees.
|
||||||
// // }
|
// Get byte count (minimum 2 inputs, 3 outputs)
|
||||||
// } catch (err) {
|
const opReturnBufLength = slpData.byteLength + 32 // add padding
|
||||||
// console.error('Error in burnPsf(): ', err)
|
const byteCount =
|
||||||
// throw err
|
bchjs.BitcoinCash.getByteCount({ P2PKH: 2 }, { P2PKH: 4 }) +
|
||||||
// }
|
opReturnBufLength
|
||||||
// }
|
const totalSatsNeeded = byteCount + satsNeeded
|
||||||
|
// console.log(`satoshis needed: ${satsNeeded}`)
|
||||||
|
|
||||||
|
// One last check to ensure the app wallet has enough BCH to complete
|
||||||
|
// the trade.
|
||||||
|
if (totalSatsNeeded > paymentUtxo.value) {
|
||||||
|
console.log(`Selected payment UTXO is not big enough. Sats needed: ${totalSatsNeeded}, UTXO value: ${paymentUtxo.value}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add UTXO for sell(STILL CANNOT SPEND - not signed yet)
|
||||||
|
transactionBuilder.addInput(orderInfo.utxoTxid, orderInfo.utxoVout)
|
||||||
|
|
||||||
|
// add payment UTXO
|
||||||
|
transactionBuilder.addInput(paymentUtxo.tx_hash, paymentUtxo.tx_pos)
|
||||||
|
|
||||||
|
const originalAmount = paymentUtxo.value
|
||||||
|
const dust = 546
|
||||||
|
const remainder = originalAmount - satsNeeded - dust // exchange fee + token UTXO dust
|
||||||
|
|
||||||
|
// Add the SLP OP_RETURN data as the first output.
|
||||||
|
transactionBuilder.addOutput(slpData, 0)
|
||||||
|
|
||||||
|
const buyerAddr = this.bchWallet.walletInfo.legacyAddress
|
||||||
|
// console.log(`buyAddr: ${JSON.stringify(buyAddr, null, 2)}`)
|
||||||
|
|
||||||
|
// Send dust transaction representing tokens being sent.
|
||||||
|
transactionBuilder.addOutput(
|
||||||
|
buyerAddr,
|
||||||
|
dust
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get seller address
|
||||||
|
// TODO: Seller should explicitly define the address to send to in the
|
||||||
|
// orderInfo object. Right now it retrieves the address from the UTXO
|
||||||
|
// for sale, which is not ideal.
|
||||||
|
const sellerAddr = txData[0].vout[1].scriptPubKey.addresses[0]
|
||||||
|
|
||||||
|
// Send payment to the offer side
|
||||||
|
transactionBuilder.addOutput(sellerAddr, satsNeeded)
|
||||||
|
|
||||||
|
// Send the BCH change back to the buyer
|
||||||
|
if (remainder > 550) {
|
||||||
|
transactionBuilder.addOutput(buyerAddr, remainder)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buyerECPair = bchjs.ECPair.fromWIF(this.bchWallet.walletInfo.privateKey)
|
||||||
|
|
||||||
|
// Sign the buyers input UTXO for spending.
|
||||||
|
transactionBuilder.sign(
|
||||||
|
1,
|
||||||
|
buyerECPair,
|
||||||
|
null,
|
||||||
|
transactionBuilder.hashTypes.SIGHASH_ALL,
|
||||||
|
originalAmount
|
||||||
|
)
|
||||||
|
|
||||||
|
const tx = transactionBuilder.transaction.buildIncomplete()
|
||||||
|
|
||||||
|
const hex = tx.toHex()
|
||||||
|
console.log('hex: ', hex)
|
||||||
|
|
||||||
|
return hex
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in wallet.js/generatePartialTx(): ', err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
|
||||||
|
// return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WalletAdapter
|
module.exports = WalletAdapter
|
||||||
|
|||||||
@@ -61,6 +61,27 @@ class OrderRESTControllerLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Currently only supports 'sell' orders, and will only buy the 'numTokens'
|
||||||
|
// listed in the order.
|
||||||
|
async takeOrder (ctx) {
|
||||||
|
try {
|
||||||
|
console.log('body: ', ctx.request.body)
|
||||||
|
|
||||||
|
const orderCid = ctx.request.body.orderCid
|
||||||
|
|
||||||
|
// Find the Order.
|
||||||
|
// const orderEntity = await _this.useCases.order.findOrder(orderId)
|
||||||
|
|
||||||
|
// 'Take' the Order.
|
||||||
|
const hash = await _this.useCases.order.takeOrder(orderCid)
|
||||||
|
|
||||||
|
ctx.body = { hash }
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error in takeOrder REST API handler.')
|
||||||
|
_this.handleError(ctx, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DRY error handler
|
// DRY error handler
|
||||||
handleError (ctx, err) {
|
handleError (ctx, err) {
|
||||||
console.log('err', err.message)
|
console.log('err', err.message)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class OrderRouter {
|
|||||||
// Define the routes and attach the controller.
|
// Define the routes and attach the controller.
|
||||||
this.router.post('/', _this.orderRESTController.createOrder)
|
this.router.post('/', _this.orderRESTController.createOrder)
|
||||||
this.router.get('/list', _this.orderRESTController.listOrders)
|
this.router.get('/list', _this.orderRESTController.listOrders)
|
||||||
|
this.router.post('/take', _this.orderRESTController.takeOrder)
|
||||||
|
|
||||||
// Attach the Controller routes to the Koa app.
|
// Attach the Controller routes to the Koa app.
|
||||||
app.use(_this.router.routes())
|
app.use(_this.router.routes())
|
||||||
|
|||||||
+10
-12
@@ -4,6 +4,10 @@
|
|||||||
It's destroyed when the UTXO described in the Signal has been detected as spent.
|
It's destroyed when the UTXO described in the Signal has been detected as spent.
|
||||||
*/
|
*/
|
||||||
class OrderEntity {
|
class OrderEntity {
|
||||||
|
constructor () {
|
||||||
|
this.orderStatus = ['posted', 'taken', 'completed']
|
||||||
|
}
|
||||||
|
|
||||||
validate (orderData = {}) {
|
validate (orderData = {}) {
|
||||||
// Throw an error if input object does not have a data property
|
// Throw an error if input object does not have a data property
|
||||||
if (!orderData.data) {
|
if (!orderData.data) {
|
||||||
@@ -12,17 +16,7 @@ class OrderEntity {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { messageType, messageClass, tokenId, buyOrSell, rateInSats, minSatsToExchange, numTokens, utxoTxid, utxoVout, orderStatus } = orderData.data
|
||||||
messageType,
|
|
||||||
messageClass,
|
|
||||||
tokenId,
|
|
||||||
buyOrSell,
|
|
||||||
rateInSats,
|
|
||||||
minSatsToExchange,
|
|
||||||
numTokens,
|
|
||||||
utxoTxid,
|
|
||||||
utxoVout
|
|
||||||
} = orderData.data
|
|
||||||
|
|
||||||
// Input Validation
|
// Input Validation
|
||||||
if (!messageType || typeof messageType !== 'number') {
|
if (!messageType || typeof messageType !== 'number') {
|
||||||
@@ -52,6 +46,9 @@ class OrderEntity {
|
|||||||
if (typeof utxoVout !== 'number') {
|
if (typeof utxoVout !== 'number') {
|
||||||
throw new Error("Property 'utxoVout' must be an integer number.")
|
throw new Error("Property 'utxoVout' must be an integer number.")
|
||||||
}
|
}
|
||||||
|
if (orderStatus && !this.orderStatus.includes(orderStatus)) {
|
||||||
|
throw new Error("Property 'orderStatus' must be a valid string")
|
||||||
|
}
|
||||||
|
|
||||||
const validatedOrderData = {
|
const validatedOrderData = {
|
||||||
messageType,
|
messageType,
|
||||||
@@ -66,7 +63,8 @@ class OrderEntity {
|
|||||||
timestamp: orderData.timestamp,
|
timestamp: orderData.timestamp,
|
||||||
localTimestamp: orderData.localTimeStamp,
|
localTimestamp: orderData.localTimeStamp,
|
||||||
txid: orderData.txid,
|
txid: orderData.txid,
|
||||||
p2wdbHash: orderData.hash
|
p2wdbHash: orderData.hash,
|
||||||
|
orderStatus: orderStatus || this.orderStatus[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return validatedOrderData
|
return validatedOrderData
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class OfferLib {
|
|||||||
|
|
||||||
const utxoInfo = {
|
const utxoInfo = {
|
||||||
txid,
|
txid,
|
||||||
vout: 0,
|
vout: 1,
|
||||||
hdIndex: keyPair.hdIndex
|
hdIndex: keyPair.hdIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
local Offers are no different than Orders generated by other peers.
|
local Offers are no different than Orders generated by other peers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Local libraries
|
||||||
const OrderEntity = require('../../entities/order')
|
const OrderEntity = require('../../entities/order')
|
||||||
|
const config = require('../../../config')
|
||||||
|
|
||||||
class OrderUseCases {
|
class OrderUseCases {
|
||||||
constructor (localConfig = {}) {
|
constructor (localConfig = {}) {
|
||||||
@@ -20,6 +22,9 @@ class OrderUseCases {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encapsulate dependencies
|
||||||
|
this.config = config
|
||||||
|
|
||||||
this.orderEntity = new OrderEntity()
|
this.orderEntity = new OrderEntity()
|
||||||
this.OrderModel = this.adapters.localdb.Order
|
this.OrderModel = this.adapters.localdb.Order
|
||||||
}
|
}
|
||||||
@@ -43,6 +48,9 @@ class OrderUseCases {
|
|||||||
console.log('utxoStatus: ', utxoStatus)
|
console.log('utxoStatus: ', utxoStatus)
|
||||||
if (utxoStatus === null) return false
|
if (utxoStatus === null) return false
|
||||||
|
|
||||||
|
// A new order gets a status of 'posted'
|
||||||
|
orderObj.data.orderStatus = 'posted'
|
||||||
|
|
||||||
const orderEntity = this.orderEntity.validate(orderObj)
|
const orderEntity = this.orderEntity.validate(orderObj)
|
||||||
console.log('orderEntity: ', orderEntity)
|
console.log('orderEntity: ', orderEntity)
|
||||||
|
|
||||||
@@ -65,6 +73,133 @@ class OrderUseCases {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate phase 2 of 3 - take the other side of an Order.
|
||||||
|
// Based on this example:
|
||||||
|
// https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js
|
||||||
|
async takeOrder (orderCid) {
|
||||||
|
try {
|
||||||
|
console.log('orderCid: ', orderCid)
|
||||||
|
|
||||||
|
// Get the Order information
|
||||||
|
const orderInfo = await this.findOrderByHash(orderCid)
|
||||||
|
console.log(`orderInfo: ${JSON.stringify(orderInfo, null, 2)}`)
|
||||||
|
|
||||||
|
// Ensure the order is in a 'posted' state and not already 'taken'
|
||||||
|
if (orderInfo.orderStatus && orderInfo.orderStatus !== 'posted') {
|
||||||
|
throw new Error('order already taken')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that UTXO for sale is unspent. Abort if it's been spent.
|
||||||
|
const txid = orderInfo.utxoTxid
|
||||||
|
const vout = orderInfo.utxoVout
|
||||||
|
const utxoStatus = await this.adapters.bchjs.Blockchain.getTxOut(
|
||||||
|
txid,
|
||||||
|
vout
|
||||||
|
)
|
||||||
|
console.log('utxoStatus: ', utxoStatus)
|
||||||
|
if (utxoStatus === null) {
|
||||||
|
console.log(`utxo txid: ${txid}, vout: ${vout}`)
|
||||||
|
throw new Error('UTXO does not exist. Aborting.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the app has enough funds to complete the trade.
|
||||||
|
await this.ensureFunds(orderInfo)
|
||||||
|
|
||||||
|
// Get UTXOs.
|
||||||
|
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||||
|
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||||
|
|
||||||
|
// TODO: Move funds to create a segrated UTXO for taking the order
|
||||||
|
|
||||||
|
// Create a partially signed transaction.
|
||||||
|
// https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/bch/applications/collaborate/sell-slp/e2e-exchange/step2-purchase-tx.js#L59
|
||||||
|
const partialTxHex = await this.adapters.wallet.generatePartialTx(orderInfo)
|
||||||
|
// return partialTxHex
|
||||||
|
|
||||||
|
// Create valid Order object
|
||||||
|
const takenOrderInfo = Object.assign({}, orderInfo)
|
||||||
|
takenOrderInfo.patialTxHex = partialTxHex
|
||||||
|
delete takenOrderInfo.p2wdbHash
|
||||||
|
delete takenOrderInfo._id
|
||||||
|
takenOrderInfo.offerHash = orderInfo.p2wdbHash
|
||||||
|
|
||||||
|
// Write order info to the P2WDB
|
||||||
|
// TODO: This will trigger the webhook. Find some way of triggering the
|
||||||
|
// webhook on new orders, but not on counteroffers
|
||||||
|
const p2wdbObj = {
|
||||||
|
wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
|
||||||
|
data: takenOrderInfo,
|
||||||
|
appId: this.config.p2wdbAppId
|
||||||
|
}
|
||||||
|
const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||||
|
|
||||||
|
// Return the P2WDB CID
|
||||||
|
return hash
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in use-cases/order/takeOrder(): ', err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the wallet has enough BCH and tokens to complete the requested
|
||||||
|
// trade. Will return true if it does. Will throw an error if it doesn't.
|
||||||
|
async ensureFunds (orderEntity) {
|
||||||
|
try {
|
||||||
|
// console.log('this.adapters.wallet: ', this.adapters.wallet.bchWallet)
|
||||||
|
// console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`)
|
||||||
|
|
||||||
|
await this.adapters.wallet.bchWallet.walletInfoPromise
|
||||||
|
|
||||||
|
// Ensure the app wallet has enough funds to write to the P2WDB.
|
||||||
|
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
|
||||||
|
const canWriteToP2WDB = await this.adapters.p2wdb.checkForSufficientFunds(wif)
|
||||||
|
if (!canWriteToP2WDB) throw new Error('App wallet does not have funds for writing to the P2WDB.')
|
||||||
|
|
||||||
|
if (orderEntity.buyOrSell.includes('sell')) {
|
||||||
|
// Sell Offer
|
||||||
|
|
||||||
|
// Ensure the app wallet controlls enough BCH to pay for the tokens.
|
||||||
|
const satsNeeded = orderEntity.numTokens * parseInt(orderEntity.rateInSats)
|
||||||
|
const balance = await this.adapters.wallet.bchWallet.getBalance()
|
||||||
|
console.log(`wallet balance: ${balance}, sats needed: ${satsNeeded}`)
|
||||||
|
const SATS_MARGIN = 5000
|
||||||
|
if (satsNeeded + SATS_MARGIN > balance) { throw new Error('App wallet does not control enough BCH to purchase the tokens.') }
|
||||||
|
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
// Buy Offer
|
||||||
|
throw new Error('Buy orders are not supported yet.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in ensureFunds()')
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOrderByHash (p2wdbHash) {
|
||||||
|
try {
|
||||||
|
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
|
||||||
|
throw new Error('p2wdbHash must be a string')
|
||||||
|
}
|
||||||
|
|
||||||
|
const order = await this.OrderModel.findOne({ p2wdbHash })
|
||||||
|
|
||||||
|
if (!order) {
|
||||||
|
throw new Error('order not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderObject = order.toObject()
|
||||||
|
// return this.orderEntity.validateFromModel(orderObject)
|
||||||
|
|
||||||
|
return orderObject
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in findOrder(): ', err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = OrderUseCases
|
module.exports = OrderUseCases
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Part 2 of 3: Take an order
|
||||||
|
*/
|
||||||
|
|
||||||
|
const axios = require('axios')
|
||||||
|
|
||||||
|
const LOCALHOST = 'http://localhost:5700'
|
||||||
|
|
||||||
|
async function start () {
|
||||||
|
try {
|
||||||
|
const options = {
|
||||||
|
method: 'post',
|
||||||
|
url: `${LOCALHOST}/order/take`,
|
||||||
|
data: {
|
||||||
|
orderCid: 'zdpuAkp98gTuivaNzGP31jTQi3ADXrFA6uANrceQcrQkTXy2j'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await axios(options)
|
||||||
|
console.log('result.data: ', result.data)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start()
|
||||||
@@ -90,7 +90,7 @@ describe('#offer-use-case', () => {
|
|||||||
assert.property(result, 'vout')
|
assert.property(result, 'vout')
|
||||||
|
|
||||||
assert.equal(result.txid, 'fakeTxid')
|
assert.equal(result.txid, 'fakeTxid')
|
||||||
assert.equal(result.vout, 0)
|
assert.equal(result.vout, 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
This script will travers the HD wallet and sweep funds and tokens back
|
||||||
|
into the root address (index 0). That root address needs to have funds
|
||||||
|
to pay for the transactions.
|
||||||
|
|
||||||
|
The root address will make a final transaction to consolidate all tokens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Public npm libraries
|
||||||
|
const BCHJS = require('@psf/bch-js')
|
||||||
|
const BchTokenSweep = require('bch-token-sweep/index')
|
||||||
|
|
||||||
|
// Local libraries
|
||||||
|
const WalletAdapter = require('../../src/adapters/wallet')
|
||||||
|
|
||||||
|
async function sweepFunds () {
|
||||||
|
try {
|
||||||
|
// Open the wallet files.
|
||||||
|
const wallet = new WalletAdapter()
|
||||||
|
const walletInfo = await wallet.openWallet()
|
||||||
|
console.log('walletInfo: ', walletInfo)
|
||||||
|
|
||||||
|
const rootAddr = walletInfo.cashAddress
|
||||||
|
const rootWif = walletInfo.privateKey
|
||||||
|
console.log(`Sweeping all funds into root address ${rootAddr}...`)
|
||||||
|
|
||||||
|
// Generate an HD tree
|
||||||
|
const bchjs = new BCHJS()
|
||||||
|
const rootSeed = await bchjs.Mnemonic.toSeed(walletInfo.mnemonic)
|
||||||
|
const masterHDNode = bchjs.HDNode.fromSeed(rootSeed)
|
||||||
|
|
||||||
|
let emptyAddrCnt = 0
|
||||||
|
let hdIndex = 1
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Generate a keypair from the HD wallet.
|
||||||
|
const childNode = masterHDNode.derivePath(`m/44'/245'/0'/0/${hdIndex}`)
|
||||||
|
// const cashAddress = bchjs.HDNode.toCashAddress(childNode)
|
||||||
|
const wifToSweep = bchjs.HDNode.toWIF(childNode)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Sweep tokens from address
|
||||||
|
const sweeper = new BchTokenSweep(
|
||||||
|
wifToSweep,
|
||||||
|
rootWif,
|
||||||
|
bchjs,
|
||||||
|
550,
|
||||||
|
rootAddr
|
||||||
|
)
|
||||||
|
await sweeper.populateObjectFromNetwork()
|
||||||
|
|
||||||
|
const hex = await sweeper.sweepTo(rootAddr)
|
||||||
|
// console.log(`hex: ${hex}`)
|
||||||
|
|
||||||
|
const txid = await sweeper.blockchain.broadcast(hex)
|
||||||
|
|
||||||
|
// console.log('Transaction ID', txid)
|
||||||
|
console.log(`Swept HD index ${hdIndex}. TXID: ${txid}`)
|
||||||
|
|
||||||
|
emptyAddrCnt = 0
|
||||||
|
|
||||||
|
// Wait between loop iterations.
|
||||||
|
await bchjs.Util.sleep(3000)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`error message: ${err.message}`)
|
||||||
|
emptyAddrCnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
hdIndex++
|
||||||
|
} while (emptyAddrCnt < 5)
|
||||||
|
|
||||||
|
console.log('5 empty addresses detected. Exiting.')
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error in sweepFunds(): ', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sweepFunds()
|
||||||
Reference in New Issue
Block a user