mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c92ffac645 | ||
|
|
216f0b6910 | ||
|
|
c31bf96986 | ||
|
|
7f8ef994a7 | ||
|
|
72ba1c48c2 | ||
|
|
2a78145170 | ||
|
|
d15fee7c7a | ||
|
|
9437b8fb80 | ||
|
|
b611663379 | ||
|
|
03b7ed980a | ||
|
|
bf74d50bb0 | ||
|
|
003480bcb5 | ||
|
|
9dbd18d0d6 | ||
|
|
b3ba7f03e4 | ||
|
|
0a8bb52241 | ||
|
|
5eeafd7ead | ||
|
|
df146382cb | ||
|
|
af8a7c3ff3 |
Vendored
+1
-1
@@ -166,7 +166,7 @@ export default {
|
||||
chatPubSubChan: 'psf-ipfs-chat-001',
|
||||
|
||||
// IPFS gateway
|
||||
ipfsGateway: process.env.IPFS_GATEWAY ? process.env.IPFS_GATEWAY : 'https://p2wdb-gateway-678.fullstack.cash/ipfs/',
|
||||
ipfsGateway: process.env.IPFS_GATEWAY ? process.env.IPFS_GATEWAY : 'https://pin.fullstack.cash/ipfs/download/',
|
||||
|
||||
// This can add specific Circuit Relay v2 servers to connect to.
|
||||
bootstrapRelays: [
|
||||
|
||||
Generated
+105
-35
@@ -26,7 +26,7 @@
|
||||
"@psf/bch-js": "6.7.3",
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bch-nostr": "1.3.1",
|
||||
"bch-nostr": "1.3.3",
|
||||
"bch-token-sweep": "2.2.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bitcoincashjs-lib": "3.3.3",
|
||||
@@ -53,10 +53,11 @@
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "1.9.1",
|
||||
"line-reader": "0.4.0",
|
||||
"minimal-slp-wallet": "5.12.0",
|
||||
"minimal-slp-wallet": "5.12.1",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"nostr-tools": "2.10.4",
|
||||
"p2wdb-esm": "2.2.9",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "6.0.1",
|
||||
@@ -5503,7 +5504,7 @@
|
||||
},
|
||||
"node_modules/@scure/base": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@scure%2fbase/-/base-1.1.1.tgz",
|
||||
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
|
||||
"funding": [
|
||||
{
|
||||
@@ -5515,7 +5516,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@scure%2fbip32/-/bip32-1.3.1.tgz",
|
||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5529,7 +5530,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fcurves/-/curves-1.1.0.tgz",
|
||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5541,7 +5542,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5553,7 +5554,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5565,7 +5566,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@scure%2fbip39/-/bip39-1.2.1.tgz",
|
||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5578,7 +5579,7 @@
|
||||
},
|
||||
"node_modules/@scure/bip39/node_modules/@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -7383,7 +7384,7 @@
|
||||
},
|
||||
"node_modules/base58-js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base58-js/-/base58-js-2.0.0.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/base58-js/-/base58-js-2.0.0.tgz",
|
||||
"integrity": "sha512-nAV5d32QXuGcGptSApkKpC1gGakWBnfJMNjKrYTBh4tb0szfZF+ooueFLy8T4VrY+o4SrE/TyrtUnRZcwZchaA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -7420,9 +7421,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bch-consumer": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "http://94.130.170.209:4873/bch-consumer/-/bch-consumer-1.6.1.tgz",
|
||||
"integrity": "sha512-ZhyZ54kgF6f3sB7J4ylMBJHSoBXozUdM2bvsm2nTz31515LFfHra9d9w4Ommlmy5BvuDkdX4lUeGSLRvggXnFg==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "http://94.130.170.209:4873/bch-consumer/-/bch-consumer-1.5.1.tgz",
|
||||
"integrity": "sha512-tEmds6Mk7g074+fGflm0d9At2ZV8DFhWErEN0nUzzW4vG5Ega9+yjREY16P/QuwwykzodnleQqOsNwcmvvgnAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@psf/bch-js": "6.7.3",
|
||||
@@ -7597,9 +7598,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bch-nostr/-/bch-nostr-1.3.1.tgz",
|
||||
"integrity": "sha512-bSq70zQkJ0ELXnZHqY2NjaZ9ovwL6eMOdvxbajPzuq3ZcNce5r8mTgyFgJASLdloDxFUjfP6yeJBJj0HZzE8Pg==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "http://94.130.170.209:4873/bch-nostr/-/bch-nostr-1.3.3.tgz",
|
||||
"integrity": "sha512-2Mod/PEDr/GJzQq20oSJKwJDtiXYm/Qj5KxIvFqsvBY1SYsHGyDu1FMBQ1UvOhY3ZP5fgiVLbIGvMRuWXqS4oQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"apidoc": "0.53.0",
|
||||
@@ -7610,9 +7611,54 @@
|
||||
"ws": "8.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/@noble/ciphers": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fcurves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/@noble/hashes": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/apidoc": {
|
||||
"version": "0.53.0",
|
||||
"resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.53.0.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/apidoc/-/apidoc-0.53.0.tgz",
|
||||
"integrity": "sha512-H4hYToCrI6TgSksdVHxS7h5/Xoqr/rR9m9/IOQaYqXbX07cPVbg0O3RM4+bR4tJKG1aqBWKKm+xojQ8+4i7O5A==",
|
||||
"license": "MIT",
|
||||
"os": [
|
||||
@@ -7653,7 +7699,7 @@
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/bch-message-lib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bch-message-lib/-/bch-message-lib-2.3.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/bch-message-lib/-/bch-message-lib-2.3.1.tgz",
|
||||
"integrity": "sha512-xAWMRvmcP2mYTdp9KgjSQfxlJF4DTloCJrjGx0TumD3HPF9mmYZ/CiCGUAFdQk3LBCavnK0AZi9v3+JTNSnrcA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
@@ -7662,9 +7708,8 @@
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
@@ -7681,6 +7726,31 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/bch-nostr/node_modules/nostr-tools": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "http://94.130.170.209:4873/nostr-tools/-/nostr-tools-2.10.1.tgz",
|
||||
"integrity": "sha512-fnAxLi92UgyMAEw4fMEhDKzH53uBJ0WkkVPTNfX0b3bspeWWn0n5tDZtKRJbvW0wAGOPmU6LYWi/IKPOsq3p2Q==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "^0.5.1",
|
||||
"@noble/curves": "1.2.0",
|
||||
"@noble/hashes": "1.3.1",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.1",
|
||||
"@scure/bip39": "1.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"nostr-wasm": "v0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/bch-token-sweep": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "http://94.130.170.209:4873/bch-token-sweep/-/bch-token-sweep-2.2.1.tgz",
|
||||
@@ -17186,15 +17256,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimal-slp-wallet": {
|
||||
"version": "5.12.0",
|
||||
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-5.12.0.tgz",
|
||||
"integrity": "sha512-zSJcBLF/tt7NH0xkuukTJ4BxnC2GIR8kjUJRWW91/LwhI6c2K9qNVV/G5E7SjYx125aHkGk5lq3pjGePsJ18rA==",
|
||||
"version": "5.12.1",
|
||||
"resolved": "http://94.130.170.209:4873/minimal-slp-wallet/-/minimal-slp-wallet-5.12.1.tgz",
|
||||
"integrity": "sha512-DnggKv1KC/Y6qxT6GiplrxfKPgBOdYbu7uK4bWVO+6RgTtcgMxrd4pxCFh4kHV3nV7vKBpE1wsmVZgtZ3x9qcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@chris.troutner/retry-queue-commonjs": "1.0.8",
|
||||
"@psf/bch-js": "6.8.0",
|
||||
"apidoc": "0.51.0",
|
||||
"bch-consumer": "1.6.1",
|
||||
"bch-consumer": "1.5.1",
|
||||
"bch-donation": "1.1.2",
|
||||
"crypto-js": "4.0.0"
|
||||
}
|
||||
@@ -17971,7 +18041,7 @@
|
||||
},
|
||||
"node_modules/noble-secp256k1": {
|
||||
"version": "1.2.14",
|
||||
"resolved": "https://registry.npmjs.org/noble-secp256k1/-/noble-secp256k1-1.2.14.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/noble-secp256k1/-/noble-secp256k1-1.2.14.tgz",
|
||||
"integrity": "sha512-GSCXyoZBUaaPwVWdYncMEmzlSUjF9J/YeEHpklYJCyg8wPuJP3NzDx0BkiwArzINkdX2HJHvUJhL6vVWPOQQcg==",
|
||||
"deprecated": "Switch to namespaced @noble/secp256k1 for security and feature updates",
|
||||
"license": "MIT"
|
||||
@@ -18226,7 +18296,7 @@
|
||||
},
|
||||
"node_modules/nostr": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/nostr/-/nostr-0.2.8.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/nostr/-/nostr-0.2.8.tgz",
|
||||
"integrity": "sha512-p25OQiEB5x8rWWoWLRRbxtxQiaQgypaQQakSKw5+cp/SW0DXea/soMzqmTXei1I6HrcARuyfvedGTdoEU471Ow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -18235,9 +18305,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.10.1.tgz",
|
||||
"integrity": "sha512-fnAxLi92UgyMAEw4fMEhDKzH53uBJ0WkkVPTNfX0b3bspeWWn0n5tDZtKRJbvW0wAGOPmU6LYWi/IKPOsq3p2Q==",
|
||||
"version": "2.10.4",
|
||||
"resolved": "http://94.130.170.209:4873/nostr-tools/-/nostr-tools-2.10.4.tgz",
|
||||
"integrity": "sha512-biU7sk+jxHgVASfobg2T5ttxOGGSt69wEVBC51sHHOEaKAAdzHBLV/I2l9Rf61UzClhliZwNouYhqIso4a3HYg==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "^0.5.1",
|
||||
@@ -18248,7 +18318,7 @@
|
||||
"@scure/bip39": "1.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"nostr-wasm": "v0.1.0"
|
||||
"nostr-wasm": "0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
@@ -18261,7 +18331,7 @@
|
||||
},
|
||||
"node_modules/nostr-tools/node_modules/@noble/ciphers": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -18270,7 +18340,7 @@
|
||||
},
|
||||
"node_modules/nostr-tools/node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fcurves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -18282,7 +18352,7 @@
|
||||
},
|
||||
"node_modules/nostr-tools/node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -18294,7 +18364,7 @@
|
||||
},
|
||||
"node_modules/nostr-tools/node_modules/@noble/hashes": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/@noble%2fhashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -18306,7 +18376,7 @@
|
||||
},
|
||||
"node_modules/nostr-wasm": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
||||
"resolved": "http://94.130.170.209:4873/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
|
||||
+3
-2
@@ -45,7 +45,7 @@
|
||||
"@psf/bch-js": "6.7.3",
|
||||
"axios": "0.27.2",
|
||||
"bch-message-lib": "2.2.1",
|
||||
"bch-nostr": "1.3.1",
|
||||
"bch-nostr": "1.3.3",
|
||||
"bch-token-sweep": "2.2.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bitcoincashjs-lib": "3.3.3",
|
||||
@@ -72,10 +72,11 @@
|
||||
"koa2-ratelimit": "0.9.1",
|
||||
"libp2p": "1.9.1",
|
||||
"line-reader": "0.4.0",
|
||||
"minimal-slp-wallet": "5.12.0",
|
||||
"minimal-slp-wallet": "5.12.1",
|
||||
"mongoose": "5.13.14",
|
||||
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
|
||||
"nodemailer": "6.7.5",
|
||||
"nostr-tools": "2.10.4",
|
||||
"p2wdb-esm": "2.2.9",
|
||||
"passport-local": "1.0.0",
|
||||
"public-ip": "6.0.1",
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN apt-get update
|
||||
RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
|
||||
#Install Node and NPM
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
|
||||
RUN curl -sL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
|
||||
@@ -34,7 +34,7 @@ WORKDIR /home/safeuser
|
||||
|
||||
# Update to the latest version of npm.
|
||||
#RUN npm install -g npm@7.23.0
|
||||
RUN npm install -g npm
|
||||
#RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
#RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN apt-get update
|
||||
RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
|
||||
#Install Node and NPM
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
|
||||
RUN curl -sL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
|
||||
@@ -34,7 +34,7 @@ WORKDIR /home/safeuser
|
||||
|
||||
# Update to the latest version of npm.
|
||||
#RUN npm install -g npm@7.23.0
|
||||
RUN npm install -g npm
|
||||
#RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
mongo-p2wdb-service:
|
||||
mongo-dex:
|
||||
image: mongo:4.2.0
|
||||
container_name: mongo-p2wdb-service
|
||||
container_name: mongo-dex
|
||||
ports:
|
||||
- '5666:27017' # <host port>:<container port>
|
||||
volumes:
|
||||
@@ -13,69 +13,11 @@ services:
|
||||
command: mongod --logpath=/dev/null # -- quiet
|
||||
restart: always
|
||||
|
||||
ipfs:
|
||||
# https://github.com/christroutner/trickle-ipfs
|
||||
image: christroutner/trickle-ipfs:v1.0.1
|
||||
#build:
|
||||
# context: ./
|
||||
# dockerfile: Dockerfile
|
||||
container_name: ipfs
|
||||
environment:
|
||||
IPFS_DAEMON_ARGUMENTS: '--enable-pubsub-experiment --migrate=true --agent-version-suffix=docker --routing=dhtclient'
|
||||
UPLOAD_KBPS: '1000'
|
||||
DOWNLOAD_KBPS: '1000'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 2000mb
|
||||
ports:
|
||||
- 4001:4001
|
||||
- 4003:4003
|
||||
- 172.17.0.1:5001:5001
|
||||
- 172.17.0.1:8080:8080
|
||||
command: [
|
||||
'./start-ipfs.sh'
|
||||
]
|
||||
volumes:
|
||||
- ../data/go-ipfs/data:/root/.ipfs
|
||||
restart: always
|
||||
|
||||
p2wdb:
|
||||
#build:
|
||||
# context: ./p2wdb/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/p2wdb:v4.1.3
|
||||
container_name: p2wdb
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
#ENABLE_BCH_PAYMENT: 1
|
||||
DEBUG_LEVEL: 1
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '10'
|
||||
mem_limit: 1gb
|
||||
links:
|
||||
- mongo-p2wdb-service
|
||||
ports:
|
||||
# <host port>:<container port>
|
||||
- 5667:5667 # REST API
|
||||
#- 5668:5668 # IPFS TCP port
|
||||
#- 5669:5669 # IPFS WS Port
|
||||
volumes:
|
||||
- ../data/ipfsdata/p2wdb:/home/safeuser/ipfs-p2wdb-service/.ipfsdata/p2wdb
|
||||
- ./p2wdb/start-production.sh:/home/safeuser/ipfs-p2wdb-service/start-production.sh
|
||||
#- ../scripts/wallet.json:/home/safeuser/ipfs-p2wdb-service/wallet.json
|
||||
restart: always
|
||||
|
||||
bch-dex:
|
||||
#build:
|
||||
# context: ./bch-dex/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex:v2.2.4
|
||||
build:
|
||||
context: ./bch-dex/
|
||||
dockerfile: Dockerfile
|
||||
#image: christroutner/bch-dex:v2.2.4
|
||||
container_name: bch-dex
|
||||
environment:
|
||||
CONSUMER_URL: 'https://free-bch.fullstack.cash'
|
||||
@@ -86,8 +28,8 @@ services:
|
||||
max-file: '10'
|
||||
mem_limit: 1000mb
|
||||
links:
|
||||
- mongo-p2wdb-service
|
||||
- p2wdb
|
||||
- mongo-dex
|
||||
#- p2wdb
|
||||
ports:
|
||||
- '5700:5700' # <host port>:<container port>
|
||||
volumes:
|
||||
@@ -96,13 +38,13 @@ services:
|
||||
restart: always
|
||||
|
||||
dex-ui:
|
||||
#build:
|
||||
# context: ./bch-dex-ui/
|
||||
# dockerfile: Dockerfile
|
||||
image: christroutner/bch-dex-ui:v1.2.4
|
||||
build:
|
||||
context: ./bch-dex-ui/
|
||||
dockerfile: Dockerfile
|
||||
#image: christroutner/bch-dex-ui:v1.2.4
|
||||
container_name: dex-ui
|
||||
environment:
|
||||
SERVER: 'http://192.168.2.3'
|
||||
SERVER: 'http://172.17.0.1'
|
||||
logging:
|
||||
driver: 'json-file'
|
||||
options:
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
# Create a Docker container for the P2WDB
|
||||
#
|
||||
|
||||
#IMAGE BUILD COMMANDS
|
||||
FROM ubuntu:20.04
|
||||
MAINTAINER Chris Troutner <chris.troutner@gmail.com>
|
||||
|
||||
#Update the OS and install any OS packages needed.
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y sudo git curl nano gnupg wget
|
||||
|
||||
#Install Node and NPM
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
|
||||
RUN bash nodesource_setup.sh
|
||||
RUN apt-get install -y nodejs build-essential
|
||||
|
||||
#Create the user 'safeuser' and add them to the sudo group.
|
||||
RUN useradd -ms /bin/bash safeuser
|
||||
RUN adduser safeuser sudo
|
||||
|
||||
#Set password to 'abcd8765' change value below if you want a different password
|
||||
RUN echo safeuser:abcd8765 | chpasswd
|
||||
|
||||
#Set the working directory to be the users home directory
|
||||
WORKDIR /home/safeuser
|
||||
|
||||
#Setup NPM for non-root global install (like on a mac)
|
||||
RUN mkdir /home/safeuser/.npm-global
|
||||
RUN chown -R safeuser .npm-global
|
||||
RUN echo "export PATH=~/.npm-global/bin:$PATH" >> /home/safeuser/.profile
|
||||
RUN runuser -l safeuser -c "npm config set prefix '~/.npm-global'"
|
||||
|
||||
# Update to the latest version of npm.
|
||||
#RUN npm install -g npm@7.23.0
|
||||
RUN npm install -g npm
|
||||
|
||||
# npm mirror to prevent direct dependency on npm.
|
||||
RUN npm set registry http://94.130.170.209:4873/
|
||||
|
||||
# Switch to user account.
|
||||
#USER safeuser
|
||||
# Prep 'sudo' commands.
|
||||
#RUN echo 'abcd8765' | sudo -S pwd
|
||||
|
||||
# Clone the rest.bitcoin.com repository
|
||||
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service
|
||||
WORKDIR /home/safeuser/ipfs-p2wdb-service
|
||||
RUN mkdir .ipfsdata
|
||||
RUN git checkout ct-unstable
|
||||
|
||||
#RUN npm install fsevents
|
||||
|
||||
# Install dependencies
|
||||
#RUN mkdir .ipfsdata
|
||||
RUN npm install
|
||||
|
||||
# Generate the API docs
|
||||
RUN npm run docs
|
||||
|
||||
# Expose the port the API will be served on.
|
||||
#EXPOSE 5010
|
||||
|
||||
# Start the application.
|
||||
#COPY start-production.sh start-production.sh
|
||||
CMD ["./start-production.sh"]
|
||||
|
||||
#CMD ["npm", "start"]
|
||||
|
||||
# Used for debugging
|
||||
#COPY dummyapp.js dummyapp.js
|
||||
#CMD ["node", "dummyapp.js"]
|
||||
@@ -1,4 +0,0 @@
|
||||
setInterval(function () {
|
||||
const now = new Date()
|
||||
console.log(`ping ${now.toLocaleString()}`)
|
||||
}, 10000)
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BEGIN: Optional configuration settings
|
||||
|
||||
# This mnemonic is used to set up persistent public key for e2ee
|
||||
# Replace this with your own 12-word mnemonic.
|
||||
export MNEMONIC="mass response fiscal world message exact series swallow forward confirm canoe festival"
|
||||
|
||||
# The human readable name this IPFS node identifies as.
|
||||
export COORD_NAME=generic-p2wdb-production
|
||||
|
||||
# Allow this node to function as a circuit relay. It must not be behind a firewall.
|
||||
#export ENABLE_CIRCUIT_RELAY=true
|
||||
# For browsers to use your circuit realy, you must set up a domain, SSL certificate,
|
||||
# and you must forward that subdomain to the IPFS_WS_PORT.
|
||||
#export CR_DOMAIN=subdomain.yourdomain.com
|
||||
|
||||
# Debug level. 0 = minimal info. 2 = max info.
|
||||
# This value set by docker-compose.yml
|
||||
#export DEBUG_LEVEL=1
|
||||
|
||||
# Log-in information for retrieving a JWT token from FullStack.cash.
|
||||
export FULLSTACKLOGIN=demo@demo.com
|
||||
export FULLSTACKPASS=demo
|
||||
|
||||
# END: Optional configuration settings
|
||||
|
||||
|
||||
# Production database connection string.
|
||||
export DBURL=mongodb://172.17.0.1:5666/p2wdb-service-dev
|
||||
|
||||
# Configure IPFS ports
|
||||
#export IPFS_TCP_PORT=5668
|
||||
#export IPFS_WS_PORT=5669
|
||||
|
||||
# Configure REST API port
|
||||
export PORT=5667
|
||||
|
||||
export P2W_ENV=production
|
||||
export IPFS_HOST=172.17.0.1
|
||||
export IPFS_API_PORT=5001
|
||||
export IPFS_TCP_PORT=4001
|
||||
|
||||
# P2WDB specific env vars
|
||||
#export ORBITDB_NAME=/orbitdb/zdpuAm4fPafpjs5mgbBUJV8Az7kd8Xj1ttD6M1JaAxF8RMRrw/psf-bch-p2wdb-keyvalue-v1.0.0-0004
|
||||
|
||||
npm start
|
||||
@@ -34,7 +34,8 @@ const Offer = new mongoose.Schema({
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
messageType: { type: Number },
|
||||
messageClass: { type: Number }
|
||||
messageClass: { type: Number },
|
||||
nostrEventId: { type: String } // Nostr Event Id.
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ const Order = new mongoose.Schema({
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
messageType: { type: Number },
|
||||
messageClass: { type: Number }
|
||||
messageClass: { type: Number },
|
||||
nostrEventId: { type: String } // Nostr Event Id.
|
||||
})
|
||||
|
||||
export default mongoose.model('order', Order)
|
||||
|
||||
+18
-2
@@ -2,8 +2,11 @@
|
||||
Adapter library for working with Nostr.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
import BchNostr from 'bch-nostr'
|
||||
import { RelayPool } from 'nostr'
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
import * as nip19 from 'nostr-tools/nip19'
|
||||
|
||||
class NostrAdapter {
|
||||
constructor (localConfig = { nostrRelay: '', nostrTopic: '' }) {
|
||||
@@ -25,11 +28,17 @@ class NostrAdapter {
|
||||
// Encapsulate dependencies
|
||||
this.bchNostr = new BchNostr()
|
||||
this.RelayPool = RelayPool
|
||||
this.retryQueue = new RetryQueue({
|
||||
concurrency: 1,
|
||||
attempts: 5,
|
||||
retryPeriod: 1000
|
||||
})
|
||||
|
||||
// Bind the 'this' object
|
||||
this.start = this.start.bind(this)
|
||||
this.post = this.post.bind(this)
|
||||
this.read = this.read.bind(this)
|
||||
this.eventId2note = this.eventId2note.bind(this)
|
||||
}
|
||||
|
||||
// Create nostr keys.
|
||||
@@ -71,7 +80,7 @@ class NostrAdapter {
|
||||
msg,
|
||||
tags: [['t', this.topic]]
|
||||
}
|
||||
const eventId = await this.bchNostr.post.uploadToNostr(inObj)
|
||||
const eventId = await this.retryQueue.addToQueue(this.bchNostr.post.uploadToNostr, inObj)
|
||||
return eventId
|
||||
} catch (error) {
|
||||
console.log(`Error in nostr.js/post() ${error.message} `)
|
||||
@@ -103,7 +112,8 @@ class NostrAdapter {
|
||||
})
|
||||
|
||||
pool.on('event', (relay, subId, ev) => {
|
||||
messages.push(ev.content)
|
||||
// console.log('ev: ', ev)
|
||||
messages.push({ content: ev.content, eventId: ev.id })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -115,6 +125,12 @@ class NostrAdapter {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an Event ID into a `noteabc..` syntax that Astral expects.
|
||||
// This can be used to generate a link to Astral to display the post.
|
||||
eventId2note (eventId) {
|
||||
return nip19.noteEncode(eventId)
|
||||
}
|
||||
}
|
||||
|
||||
export default NostrAdapter
|
||||
|
||||
+10
-4
@@ -5,12 +5,11 @@
|
||||
// Public npm libraries
|
||||
import BchWallet from 'minimal-slp-wallet'
|
||||
import BchTokenSweep from 'bch-token-sweep/index.js'
|
||||
|
||||
import bitcoinJs from 'bitcoincashjs-lib'
|
||||
import RetryQueue from '@chris.troutner/retry-queue'
|
||||
|
||||
// Local libraries
|
||||
import JsonFiles from './json-files.js'
|
||||
|
||||
import config from '../../config/index.js'
|
||||
|
||||
// Hack to get __dirname back.
|
||||
@@ -35,6 +34,11 @@ class WalletAdapter {
|
||||
this.BchWallet = BchWallet
|
||||
this.bchWallet = {} // Will be replaced when initialized.
|
||||
// this.advancedConfig = localConfig.advancedConfig
|
||||
this.retryQueue = new RetryQueue({
|
||||
concurrency: 1,
|
||||
attempts: 5,
|
||||
retryPeriod: 5000
|
||||
})
|
||||
|
||||
// Bind the 'this' object
|
||||
this.moveTokens = this.moveTokens.bind(this)
|
||||
@@ -441,7 +445,7 @@ class WalletAdapter {
|
||||
|
||||
return txObj2
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/deserializePartialTx()')
|
||||
console.error('Error in wallet.js/deserializeTx()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -508,7 +512,9 @@ class WalletAdapter {
|
||||
// return csTxHex
|
||||
|
||||
// Broadcast transaction to the network
|
||||
const txid = await this.bchWallet.ar.sendTx(csTxHex)
|
||||
// const txid = await this.bchWallet.ar.sendTx(csTxHex)
|
||||
const txid = await this.retryQueue.addToQueue(this.bchWallet.broadcast, { hex: csTxHex })
|
||||
console.log('completeTx() txid: ', txid)
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
import wlogger from '../../../adapters/wlogger.js'
|
||||
|
||||
let _this
|
||||
|
||||
class OfferRESTControllerLib {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
@@ -26,7 +24,13 @@ class OfferRESTControllerLib {
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
_this = this
|
||||
// Bind 'this' object to all subfunctions.
|
||||
this.createOffer = this.createOffer.bind(this)
|
||||
this.listOffers = this.listOffers.bind(this)
|
||||
this.listNftOffers = this.listNftOffers.bind(this)
|
||||
this.listFungibleOffers = this.listFungibleOffers.bind(this)
|
||||
this.takeOffer = this.takeOffer.bind(this)
|
||||
this.handleError = this.handleError.bind(this)
|
||||
}
|
||||
|
||||
// No api-doc documentation because this wont be a public endpoint
|
||||
@@ -36,7 +40,7 @@ class OfferRESTControllerLib {
|
||||
|
||||
const offerObj = ctx.request.body
|
||||
|
||||
await _this.useCases.offer.createOffer(offerObj)
|
||||
await this.useCases.offer.createOffer(offerObj)
|
||||
|
||||
ctx.body = {
|
||||
success: true
|
||||
@@ -45,7 +49,7 @@ class OfferRESTControllerLib {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log('err: ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
_this.handleError(ctx, err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,12 +59,12 @@ class OfferRESTControllerLib {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listOffers(page)
|
||||
const offers = await this.useCases.offer.listOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +74,12 @@ class OfferRESTControllerLib {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listNftOffers(page)
|
||||
const offers = await this.useCases.offer.listNftOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in listNftOffers REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,12 +89,12 @@ class OfferRESTControllerLib {
|
||||
let page = ctx.params.page
|
||||
if (!page) page = 0
|
||||
|
||||
const offers = await _this.useCases.offer.listFungibleOffers(page)
|
||||
const offers = await this.useCases.offer.listFungibleOffers(page)
|
||||
|
||||
ctx.body = offers
|
||||
} catch (err) {
|
||||
console.log('Error in Fungible REST API handler: ', err)
|
||||
_this.handleError(ctx, err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,14 +104,14 @@ class OfferRESTControllerLib {
|
||||
try {
|
||||
console.log('REST API controller, body: ', ctx.request.body)
|
||||
|
||||
const offerCid = ctx.request.body.offerCid
|
||||
const nostrEventId = ctx.request.body.nostrEventId
|
||||
|
||||
const hash = await _this.useCases.offer.takeOffer(offerCid)
|
||||
const { eventId, noteId } = await this.useCases.offer.takeOffer(nostrEventId)
|
||||
|
||||
ctx.body = { hash }
|
||||
ctx.body = { eventId, noteId }
|
||||
} catch (err) {
|
||||
wlogger.error('Error in takeOffer() REST API handler.')
|
||||
_this.handleError(ctx, err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,13 @@ class OfferRouter {
|
||||
)
|
||||
}
|
||||
|
||||
// 12/3/24 CT:
|
||||
// Note: The createOffer() path was used by a P2WDB webhook to generate an
|
||||
// Offer from and Order. This has been deprecated and Offers are now created
|
||||
// by a Timer Controller monitoring a Nostr topic.
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.offerRESTController.createOffer)
|
||||
// this.router.post('/', _this.offerRESTController.createOffer) // Deprecated.
|
||||
this.router.post('/take', _this.offerRESTController.takeOffer)
|
||||
this.router.get('/list/all/:page', _this.offerRESTController.listOffers)
|
||||
this.router.get('/list/nft/:page', _this.offerRESTController.listNftOffers)
|
||||
|
||||
@@ -37,9 +37,9 @@ class OrderRESTControllerLib {
|
||||
const orderObj = ctx.request.body.order
|
||||
console.log('orderObj: ', orderObj)
|
||||
|
||||
const hash = await _this.useCases.order.createOrder(orderObj)
|
||||
const { eventId, noteId } = await _this.useCases.order.createOrder(orderObj)
|
||||
|
||||
ctx.body = { hash }
|
||||
ctx.body = { eventId, noteId }
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log('err: ', err)
|
||||
@@ -69,10 +69,10 @@ class OrderRESTControllerLib {
|
||||
try {
|
||||
// console.log('body: ', ctx.request.body)
|
||||
|
||||
const p2wdbHash = ctx.request.body.p2wdbHash
|
||||
const nostrEventId = ctx.request.body.nostrEventId
|
||||
// console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
const txid = await _this.useCases.order.deleteOrder(p2wdbHash)
|
||||
const txid = await _this.useCases.order.deleteOrder(nostrEventId)
|
||||
|
||||
ctx.body = { txid }
|
||||
} catch (err) {
|
||||
|
||||
@@ -29,35 +29,39 @@ class TimerControllers {
|
||||
this.gcOrders = this.gcOrders.bind(this)
|
||||
this.gcOffers = this.gcOffers.bind(this)
|
||||
this.checkDupOffers = this.checkDupOffers.bind(this)
|
||||
this.loadOffers = this.loadOffers.bind(this)
|
||||
|
||||
// State
|
||||
this.gcOrdersInt = null
|
||||
this.gcOffersInt = null
|
||||
this.checkDupOffersInt = null
|
||||
|
||||
this.startTimers()
|
||||
this.loadOffersInt = null
|
||||
}
|
||||
|
||||
// Start all the time-based controllers.
|
||||
startTimers () {
|
||||
this.gcOrdersInt = setInterval(this.gcOrders, 60000 * 5)
|
||||
this.gcOffersInt = setInterval(this.gcOffers, 60000 * 5)
|
||||
this.checkDupOffersInt = setInterval(this.checkDupOffers, 60000 * 4.5)
|
||||
// this.checkDupOffersInt = setInterval(this.checkDupOffers, 60000 * 4.5)
|
||||
this.loadOffersInt = setInterval(this.loadOffers, 60000 * 2)
|
||||
return true
|
||||
}
|
||||
|
||||
stopTimers () {
|
||||
clearInterval(this.gcOrdersInt)
|
||||
clearInterval(this.gcOffersInt)
|
||||
clearInterval(this.checkDupOffers)
|
||||
// clearInterval(this.checkDupOffers)
|
||||
}
|
||||
|
||||
// Garbage Collect the Orders.
|
||||
gcOrders () {
|
||||
try {
|
||||
this.useCases.order.removeStaleOrders()
|
||||
return true
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/gcOrders(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,9 +69,11 @@ class TimerControllers {
|
||||
gcOffers () {
|
||||
try {
|
||||
this.useCases.offer.removeStaleOffers()
|
||||
return true
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/gcOffers(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +81,23 @@ class TimerControllers {
|
||||
checkDupOffers () {
|
||||
try {
|
||||
this.useCases.offer.removeDuplicateOffers()
|
||||
return true
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/checkDupOffers(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Load offers From nostr .
|
||||
async loadOffers () {
|
||||
try {
|
||||
await this.useCases.offer.loadOffers()
|
||||
return true
|
||||
} catch (err) {
|
||||
// Do not throw an error. This is a top-level function.
|
||||
console.log('Error in timer-controllers.js/loadOffers(): ', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -17,6 +17,8 @@ class OfferEntity {
|
||||
)
|
||||
}
|
||||
|
||||
// console.log('offer entity validate() offerData: ', offerData)
|
||||
|
||||
const {
|
||||
messageType,
|
||||
messageClass,
|
||||
@@ -30,7 +32,8 @@ class OfferEntity {
|
||||
offerStatus,
|
||||
makerAddr,
|
||||
ticker,
|
||||
tokenType
|
||||
tokenType,
|
||||
nostrEventId
|
||||
} = offerData.data
|
||||
|
||||
// Input Validation
|
||||
@@ -73,6 +76,9 @@ class OfferEntity {
|
||||
if (!tokenType || typeof tokenType !== 'number') {
|
||||
throw new Error("Property 'tokenType' must be a number.")
|
||||
}
|
||||
if (!nostrEventId || typeof nostrEventId !== 'string') {
|
||||
throw new Error("Property 'nostrEventId' must be a string.")
|
||||
}
|
||||
|
||||
// Convert the timestamp to a number.
|
||||
let timestamp = new Date(offerData.timestamp)
|
||||
@@ -96,8 +102,10 @@ class OfferEntity {
|
||||
offerStatus: offerStatus || this.offerStatus[0],
|
||||
makerAddr,
|
||||
ticker,
|
||||
tokenType
|
||||
tokenType,
|
||||
nostrEventId
|
||||
}
|
||||
// console.log('offer entity validatedOfferData: ', validatedOfferData)
|
||||
|
||||
return validatedOfferData
|
||||
}
|
||||
|
||||
+256
-85
@@ -37,7 +37,7 @@ class OfferUseCases {
|
||||
this.orderUseCase = localConfig.order
|
||||
if (!this.orderUseCase) {
|
||||
throw new Error(
|
||||
'Instance of Offer Use Cases must be passed in when instantiating Offer Use Cases library.'
|
||||
'Instance of Order Use Cases must be passed in when instantiating Offer Use Cases library.'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,20 +49,37 @@ class OfferUseCases {
|
||||
this.retryQueue = new RetryQueue({ retryPeriod: 1000, attempts: 3 })
|
||||
|
||||
// Bind 'this' object to functions
|
||||
this.createOffer = this.createOffer.bind(this)
|
||||
this.detectNsfw = this.detectNsfw.bind(this)
|
||||
this.categorizeToken = this.categorizeToken.bind(this)
|
||||
this.listOffers = this.listOffers.bind(this)
|
||||
this.listNftOffers = this.listNftOffers.bind(this)
|
||||
this.listFungibleOffers = this.listFungibleOffers.bind(this)
|
||||
this.takeOffer = this.takeOffer.bind(this)
|
||||
this.ensureFunds = this.ensureFunds.bind(this)
|
||||
this.findOfferByEvent = this.findOfferByEvent.bind(this)
|
||||
this.findOfferByTxid = this.findOfferByTxid.bind(this)
|
||||
this.acceptCounterOffer = this.acceptCounterOffer.bind(this)
|
||||
this.removeDuplicateOffers = this.removeDuplicateOffers.bind(this)
|
||||
this.removeStaleOffers = this.removeStaleOffers.bind(this)
|
||||
this.flagOffer = this.flagOffer.bind(this)
|
||||
this.loadOffers = this.loadOffers.bind(this)
|
||||
|
||||
// State
|
||||
this.seenOffers = []
|
||||
this.seenCounterOffers = []
|
||||
}
|
||||
|
||||
// This method is called by the POST /offer REST API controller, which is
|
||||
// triggered by a P2WDB webhook.
|
||||
// This method is called by timer controller to load offers from a Nostr topic.
|
||||
async createOffer (offerObj) {
|
||||
try {
|
||||
console.log('Use Case createOffer(offerObj): ', offerObj)
|
||||
// console.log('Use Case createOffer(offerObj): ', offerObj)
|
||||
|
||||
// Return if Offer already exists in database with the same P2WDB CID.
|
||||
// Return if Offer already exists in database with the same utxo transaction id.
|
||||
try {
|
||||
await this.findOfferByHash(offerObj.hash)
|
||||
await this.findOfferByTxid(offerObj.data.utxoTxid)
|
||||
|
||||
console.log('Offer already found in local database.')
|
||||
// console.log('Offer already found in local database.')
|
||||
return false
|
||||
} catch (err) { /* exit quietly */ }
|
||||
|
||||
@@ -71,9 +88,17 @@ class OfferUseCases {
|
||||
// Input Validation
|
||||
// TODO: This is a hack. Find a better way to protect against the corner-
|
||||
// case of counter-offers getting routed here.
|
||||
if (offerObj.data.dataType === 'counter-offer') {
|
||||
console.log('WARN: Counter Offer innappropriately routed to createOffer()')
|
||||
// if (offerObj.data.dataType === 'counter-offer') {
|
||||
// console.log('WARN: Counter Offer innappropriately routed to createOffer()')
|
||||
// }
|
||||
|
||||
// Quickly skip over offers that have already been processed.
|
||||
const eventId = offerObj.data.nostrEventId
|
||||
if (this.seenOffers.includes(eventId)) {
|
||||
// console.log(`Offer with event ID ${eventId} already processed. Skipping.`)
|
||||
return false
|
||||
}
|
||||
this.seenOffers.push(eventId)
|
||||
|
||||
// Verify that UTXO in offer is unspent. If it is spent, then ignore the
|
||||
// offer.
|
||||
@@ -83,21 +108,26 @@ class OfferUseCases {
|
||||
}
|
||||
// const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
const utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
// if (utxoStatus === null) return false
|
||||
if (!utxoStatus) return false
|
||||
|
||||
// A new offer gets a status of 'posted'
|
||||
offerObj.data.offerStatus = 'posted'
|
||||
|
||||
// Set timestamp
|
||||
offerObj.timestamp = new Date().getTime()
|
||||
|
||||
const offerEntity = this.offerEntity.validate(offerObj)
|
||||
console.log('offerEntity: ', offerEntity)
|
||||
// console.log('offerEntity: ', offerEntity)
|
||||
|
||||
console.log(`New Offer for token ID ${offerEntity.tokenId} detected from Nostr post ${eventId}`)
|
||||
|
||||
// Get data about the token.
|
||||
const tokenId = offerEntity.tokenId
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
// Generate a 'display category' for the token. This will allow the
|
||||
// front end UI to figure out how to display the token.
|
||||
@@ -117,7 +147,7 @@ class OfferUseCases {
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in createOffer()')
|
||||
console.error('Error in createOffer()', err.message)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -139,16 +169,29 @@ class OfferUseCases {
|
||||
|
||||
// Retrieve the mutable data from Filecoin/IPFS.
|
||||
// const url = `https://${cid}.ipfs.w3s.link/data.json`
|
||||
const url = `${this.config.ipfsGateway}${cid}/data.json`
|
||||
const result = await axios.get(url)
|
||||
const mutableData = result.data
|
||||
console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
let mutableData = {}
|
||||
|
||||
try {
|
||||
// Try the conventional data URL.
|
||||
const url = `${this.config.ipfsGateway}${cid}/data.json`
|
||||
const result = await this.axios.get(url)
|
||||
mutableData = result.data
|
||||
// console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
} catch (err) {
|
||||
// Try the newer data URL.
|
||||
const url = `${this.config.ipfsGateway}${cid}`
|
||||
const result = await this.axios.get(url)
|
||||
mutableData = result.data
|
||||
// console.log(`mutableData: ${JSON.stringify(mutableData, null, 2)}`)
|
||||
}
|
||||
console.log('mutableData: ', mutableData)
|
||||
|
||||
// Logical tests
|
||||
const hasNsfw = !!mutableData.nsfw
|
||||
const nsfwSetTrue = mutableData.nsfw === true
|
||||
const nsfwStringTrue = mutableData.nsfw === 'true'
|
||||
const nsfwDetected = hasNsfw && (nsfwSetTrue || nsfwStringTrue)
|
||||
// console.log(`hasNsfw: ${hasNsfw}, nsfwSetTrue: ${nsfwSetTrue}, nsfwStringTrue: ${nsfwStringTrue}, nsfwDetected: ${nsfwDetected}`)
|
||||
|
||||
if (nsfwDetected) {
|
||||
console.log('NSFW flag set as true')
|
||||
@@ -207,11 +250,11 @@ class OfferUseCases {
|
||||
async listOffers (page = 0) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({})
|
||||
// Sort entries so newest entries show first.
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * DEFAULT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
// Only return 20 results.
|
||||
.limit(DEFAULT_ENTRIES_PER_PAGE)
|
||||
|
||||
return data
|
||||
@@ -227,11 +270,11 @@ class OfferUseCases {
|
||||
displayCategory: { $ne: 'fungible' },
|
||||
nsfw
|
||||
})
|
||||
// Sort entries so newest entries show first.
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * NFT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
// Only return 20 results.
|
||||
.limit(NFT_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listNftOffers() returning this data: ', data)
|
||||
@@ -246,11 +289,11 @@ class OfferUseCases {
|
||||
async listFungibleOffers (page = 0) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({ displayCategory: 'fungible' })
|
||||
// Sort entries so newest entries show first.
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
// Only return 20 results.
|
||||
.limit(FUNGIBLE_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listFungibleOffers() returning this data: ', data)
|
||||
@@ -270,12 +313,12 @@ class OfferUseCases {
|
||||
// provide functionality to take less than the total amount of tokens offered
|
||||
// (offerInfo.numTokens). Taking less than the offered amount will be added
|
||||
// in the future.
|
||||
async takeOffer (offerCid) {
|
||||
async takeOffer (eventId) {
|
||||
try {
|
||||
console.log('offerCid: ', offerCid)
|
||||
if (!eventId || typeof eventId !== 'string') throw new Error('eventId must be a string')
|
||||
|
||||
// Get the Offer information
|
||||
const offerInfo = await this.findOfferByHash(offerCid)
|
||||
const offerInfo = await this.findOfferByEvent(eventId)
|
||||
console.log(`offerInfo: ${JSON.stringify(offerInfo, null, 2)}`)
|
||||
|
||||
// Ensure the offer is in a 'posted' state and not already 'taken'
|
||||
@@ -288,8 +331,10 @@ class OfferUseCases {
|
||||
tx_hash: offerInfo.utxoTxid,
|
||||
tx_pos: offerInfo.utxoVout
|
||||
}
|
||||
|
||||
// Note : should be added to retry-queue?
|
||||
const utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
if (!utxoStatus) {
|
||||
console.log(`utxo txid: ${offerInfo.utxoTxid}, vout: ${offerInfo.utxoVout}`)
|
||||
|
||||
@@ -302,11 +347,12 @@ class OfferUseCases {
|
||||
await this.ensureFunds(offerInfo)
|
||||
|
||||
// Get UTXOs.
|
||||
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||
console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
// const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
// Calculate amount of sats to generate a counter offer.
|
||||
let satsToMove = Math.ceil(offerInfo.numTokens * parseInt(offerInfo.rateInBaseUnit))
|
||||
console.log('satsToMove', satsToMove, offerInfo)
|
||||
if (isNaN(satsToMove)) {
|
||||
throw new Error('Could not calculate the amount of BCH to generate counter offer')
|
||||
}
|
||||
@@ -332,9 +378,9 @@ class OfferUseCases {
|
||||
// Create valid Offer object
|
||||
const takenOfferInfo = Object.assign({}, offerInfo)
|
||||
takenOfferInfo.partialTxHex = partialTxHex
|
||||
delete takenOfferInfo.p2wdbHash
|
||||
delete takenOfferInfo.nostrEventId
|
||||
delete takenOfferInfo._id
|
||||
takenOfferInfo.offerHash = offerInfo.p2wdbHash
|
||||
takenOfferInfo.offerHash = offerInfo.nostrEventId
|
||||
|
||||
// Add P2WDB specific flag for signaling that this is a new offer.
|
||||
takenOfferInfo.dataType = 'counter-offer'
|
||||
@@ -342,19 +388,21 @@ class OfferUseCases {
|
||||
// Write offer info to the P2WDB
|
||||
// TODO: This will trigger the webhook. Find some way of triggering the
|
||||
// webhook on new offers, but not on counteroffers
|
||||
const p2wdbObj = {
|
||||
wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
|
||||
data: takenOfferInfo,
|
||||
appId: this.config.p2wdbAppId
|
||||
const nostrData = {
|
||||
// wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
|
||||
data: takenOfferInfo
|
||||
// appId: this.config.p2wdbAppId
|
||||
}
|
||||
const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
const resultEventId = await this.adapters.nostr.post(JSON.stringify(nostrData))
|
||||
|
||||
const noteId = this.adapters.nostr.eventId2note(resultEventId)
|
||||
|
||||
// Delete the Offer from the database, so that the user doesn't attempt
|
||||
// to take the offer more than once.
|
||||
offerInfo.remove()
|
||||
// offerInfo.remove()
|
||||
|
||||
// Return the P2WDB CID
|
||||
return hash
|
||||
return { eventId: resultEventId, noteId }
|
||||
|
||||
// return 'fake-hash'
|
||||
} catch (err) {
|
||||
@@ -377,9 +425,10 @@ class OfferUseCases {
|
||||
await this.adapters.wallet.bchWallet.initialize()
|
||||
|
||||
// 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.')
|
||||
// Note : this validation should be deprecated for nostr functionality?
|
||||
// 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 (offerEntity.buyOrSell.includes('sell')) {
|
||||
// Sell Offer
|
||||
@@ -398,7 +447,7 @@ class OfferUseCases {
|
||||
throw new Error('App wallet does not control enough BCH to purchase the tokens.')
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
} else {
|
||||
// Buy Offer
|
||||
throw new Error('Buy offers are not supported yet.')
|
||||
@@ -417,27 +466,50 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
async findOfferByHash (p2wdbHash) {
|
||||
// Retrieve an Order model from the database. Find it by its event Id.
|
||||
async findOfferByEvent (nostrEventId) {
|
||||
try {
|
||||
if (typeof nostrEventId !== 'string' || !nostrEventId) {
|
||||
throw new Error('nostrEventId must be a string')
|
||||
}
|
||||
|
||||
const order = await this.OfferModel.findOne({ nostrEventId })
|
||||
|
||||
if (!order) {
|
||||
throw new Error('offer not found')
|
||||
}
|
||||
|
||||
const orderObject = order.toObject()
|
||||
// return this.offerEntity.validateFromModel(offerObject)
|
||||
|
||||
return orderObject
|
||||
} catch (err) {
|
||||
console.error('Error in findOfferByEvent()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async findOfferByTxid (utxoTxid) {
|
||||
// try {
|
||||
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
|
||||
throw new Error('p2wdbHash must be a string')
|
||||
// try {
|
||||
if (typeof utxoTxid !== 'string' || !utxoTxid) {
|
||||
throw new Error('utxoTxid must be a string')
|
||||
}
|
||||
|
||||
const offer = await this.OfferModel.findOne({ p2wdbHash })
|
||||
const offer = await this.OfferModel.findOne({ utxoTxid })
|
||||
|
||||
// TODO: Offer should be found by TXID, then if there is more than one
|
||||
// result, they should be filtered by the vout property. That will leave
|
||||
// one remaining UTXO.
|
||||
|
||||
if (!offer) {
|
||||
throw new Error('offer not found')
|
||||
}
|
||||
|
||||
return offer
|
||||
|
||||
// const offerObject = offer.toObject()
|
||||
// return this.offerEntity.validateFromModel(offerObject)
|
||||
|
||||
// return offerObject
|
||||
// } catch (err) {
|
||||
// // console.error('Error in findOffer(): ', err)
|
||||
// throw err
|
||||
// } catch (error) {
|
||||
// // console.error('Error in use-cases/offer/findOfferByTxid(): ', error.message)
|
||||
// throw error
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -445,29 +517,79 @@ class OfferUseCases {
|
||||
// Counter Offer is passed to bch-dex by the P2WDB, the data is then passed
|
||||
// to this function. It does due dilligence on the Counter Offer, then signs
|
||||
// and broadcasts the transaction to accept the Counter Offer.
|
||||
async acceptCounterOffer (p2wdbData) {
|
||||
async acceptCounterOffer (offerData) {
|
||||
try {
|
||||
console.log(`acceptCounterOffer() p2wdbData: ${JSON.stringify(p2wdbData, null, 2)}`)
|
||||
// console.log(`acceptCounterOffer() offerData: ${JSON.stringify(offerData, null, 2)}`)
|
||||
|
||||
// Quickly skip over offers that have already been processed.
|
||||
const eventId = offerData.data.nostrEventId
|
||||
if (this.seenOffers.includes(eventId)) {
|
||||
// console.log(`Offer with event ID ${eventId} already processed. Skipping.`)
|
||||
return false
|
||||
}
|
||||
|
||||
// See if this instance of bch-dex is managing the Order associated with
|
||||
// the incoming Counter Offer.
|
||||
const orderHash = p2wdbData.data.offerHash
|
||||
|
||||
// Note : this should be handled by nostrEvent id or UtxoId?
|
||||
// const orderHash = offerData.data.nostrEventId
|
||||
let orderData = {}
|
||||
try {
|
||||
orderData = await this.orderUseCase.findOrderByHash(orderHash)
|
||||
console.log(`orderData: ${JSON.stringify(orderData, null, 2)}`)
|
||||
orderData = await this.orderUseCase.findOrderByUtxo(offerData)
|
||||
// console.log(`orderData: ${JSON.stringify(orderData, null, 2)}`)
|
||||
} catch (err) {
|
||||
console.log('Order matching this Counter Offer is not managed by this instance of bch-dex. Exiting.')
|
||||
console.log('Order matching this Counter Offer is not managed by this instance of bch-dex. Skipping.')
|
||||
|
||||
// Add order to list of seen orders, so that we don't spent time trying to validate it again.
|
||||
this.seenOffers.push(eventId)
|
||||
|
||||
return 'N/A'
|
||||
}
|
||||
|
||||
// Verify that the UTXO for sale is valid.
|
||||
let utxoStatus = null
|
||||
try {
|
||||
// Get the status of the UTXO associate with this Offer.
|
||||
const utxo = {
|
||||
tx_hash: offerData.data.utxoTxid,
|
||||
tx_pos: offerData.data.utxoVout
|
||||
}
|
||||
// console.log(`Checking this UTXO: ${JSON.stringify(utxo, null, 2)}`)
|
||||
|
||||
// utxoStatus = await this.adapters.wallet.bchWallet.utxoIsValid(utxo)
|
||||
utxoStatus = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.utxoIsValid, utxo)
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
} catch (err) {
|
||||
console.log('acceptCounterOffer() err validating UTXO: ', err)
|
||||
|
||||
// Handle corner case of bad-data in the Offer model.
|
||||
if (err.isAxiosError) {
|
||||
console.log('Error trying to contact wallet service: ', err)
|
||||
return 'N/A'
|
||||
} else {
|
||||
return 'N/A'
|
||||
}
|
||||
}
|
||||
|
||||
// If the Offer UTXO is spent, exit.
|
||||
if (utxoStatus === false) {
|
||||
// Add order to list of seen orders, so that we don't spent time trying to validate it again.
|
||||
this.seenOffers.push(eventId)
|
||||
|
||||
console.log(`Aborting Counter Offer from Event ID ${eventId}`)
|
||||
console.log(`https://astral.psfoundation.info/${this.adapters.nostr.eventId2note(eventId)}`)
|
||||
|
||||
return 'N/A'
|
||||
}
|
||||
|
||||
// Deserialize the partially signed transaction.
|
||||
const txHex = p2wdbData.data.partialTxHex
|
||||
const txHex = offerData.data.partialTxHex
|
||||
const txObj = await this.adapters.wallet.deseralizeTx(txHex)
|
||||
console.log(`txObj: ${JSON.stringify(txObj, null, 2)}`)
|
||||
|
||||
// Ensure the 3rd output (vout=2) contains the required amount of BCH.
|
||||
const satsToReceive = Math.ceil(orderData.numTokens * parseInt(orderData.rateInBaseUnit))
|
||||
console.log('Ceil', satsToReceive)
|
||||
if (isNaN(satsToReceive)) {
|
||||
throw new Error('Could not calculate the amount of BCH offered in the Counter Offer')
|
||||
}
|
||||
@@ -492,7 +614,7 @@ class OfferUseCases {
|
||||
|
||||
return txid
|
||||
} catch (err) {
|
||||
console.error('Error in acceptCounterOffer()')
|
||||
console.error('Error in acceptCounterOffer(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -580,7 +702,7 @@ class OfferUseCases {
|
||||
|
||||
// If the Offer UTXO is spent, delete the Offer model.
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Offer: ${JSON.stringify(thisOffer, null, 2)}`)
|
||||
await thisOffer.remove()
|
||||
}
|
||||
@@ -600,32 +722,81 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
async flagOffer (flagData) {
|
||||
console.log(`flagData: ${JSON.stringify(flagData, null, 2)}`)
|
||||
// Flag offers as NSFW.
|
||||
async flagOffer (flagData = {}) {
|
||||
try {
|
||||
if (!flagData.data) throw new Error('"data" property is required')
|
||||
|
||||
const p2wdbHash = flagData.data.p2wdbHash
|
||||
console.log(`flagData: ${JSON.stringify(flagData, null, 2)}`)
|
||||
|
||||
// Get the offer from the database.
|
||||
const offer = await this.findOfferByHash(p2wdbHash)
|
||||
console.log(`Flagging this offer: ${JSON.stringify(offer, null, 2)}`)
|
||||
const eventId = flagData.data.nostrEventId
|
||||
|
||||
if (!offer) {
|
||||
throw new Error(`Offer ${p2wdbHash} not found in the database.`)
|
||||
// Get the offer from the database.
|
||||
const offer = await this.findOfferByEvent(eventId)
|
||||
console.log(`Flagging this offer: ${JSON.stringify(offer, null, 2)}`)
|
||||
|
||||
if (!offer) {
|
||||
throw new Error(`Offer ${eventId} not found in the database.`)
|
||||
}
|
||||
|
||||
// Add the raw flag data to the database model.
|
||||
offer.flags.push(flagData)
|
||||
|
||||
// If flag count is 3 or more, mark the Offer as NSFW
|
||||
const flagCnt = offer.flags.length
|
||||
if (flagCnt >= 3) {
|
||||
offer.nsfw = true
|
||||
}
|
||||
|
||||
// Save the updated offer data to the database.
|
||||
await offer.save()
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('Error in flagOffer(): ', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Add the raw flag data to the database model.
|
||||
offer.flags.push(flagData)
|
||||
// Get offers data from nostr.
|
||||
async loadOffers () {
|
||||
try {
|
||||
// Retrieve offers array.
|
||||
const offers = await this.adapters.nostr.read()
|
||||
// console.log('offers: ', offers)
|
||||
|
||||
// If flag count is 3 or more, mark the Offer as NSFW
|
||||
const flagCnt = offer.flags.length
|
||||
if (flagCnt >= 3) {
|
||||
offer.nsfw = true
|
||||
for (let i = 0; i < offers.length; i++) {
|
||||
try {
|
||||
const offer = offers[i]
|
||||
|
||||
// offer data
|
||||
const offerObj = JSON.parse(offer.content)
|
||||
|
||||
// Append the Nostr Event ID to the offer object
|
||||
offerObj.data.nostrEventId = offer.eventId
|
||||
// console.log('loadOffers() offerObj: ', offerObj)
|
||||
|
||||
if (offerObj.data.dataType === 'offer') {
|
||||
// Try to create new offer
|
||||
await this.createOffer(offerObj)
|
||||
}
|
||||
|
||||
if (offerObj.data.dataType === 'counter-offer') {
|
||||
// console.log('Counter offer detected: ', offerObj)
|
||||
// console.log('Counter offer detected: ', offerObj.data.nostrEventId)
|
||||
console.log(`Counter offer detected: https://astral.psfoundation.info/${this.adapters.nostr.eventId2note(offerObj.data.nostrEventId)}`)
|
||||
await this.acceptCounterOffer(offerObj)
|
||||
}
|
||||
|
||||
// Ignore the post if it doesn't fit the above filters.
|
||||
} catch (error) {
|
||||
/* exit quietly */
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in loadOffers(): ', error)
|
||||
throw error
|
||||
}
|
||||
|
||||
// Save the updated offer data to the database.
|
||||
await offer.save()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+56
-28
@@ -31,23 +31,25 @@ class OrderLib {
|
||||
|
||||
// Bind subfunctions to the 'this' object.
|
||||
this.ensureFunds = this.ensureFunds.bind(this)
|
||||
this.createOrder = this.createOrder.bind(this)
|
||||
this.findOrderByUtxo = this.findOrderByUtxo.bind(this)
|
||||
}
|
||||
|
||||
// Create a new order model and add it to the Mongo database.
|
||||
async createOrder (entryObj) {
|
||||
try {
|
||||
console.log('createOrder(entryObj): ', entryObj)
|
||||
// console.log('createOrder(entryObj): ', entryObj)
|
||||
if (!entryObj) return false
|
||||
|
||||
if (!entryObj.tokenId) throw new Error('entry does not contain required properties')
|
||||
|
||||
// Specify the address to send payment.
|
||||
entryObj.makerAddr = this.adapters.wallet.bchWallet.walletInfo.cashAddress
|
||||
console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
// console.log('entryObj.makerAddr: ', entryObj.makerAddr)
|
||||
|
||||
// Input Validation
|
||||
const orderEntity = this.orderEntity.inputValidate(entryObj)
|
||||
console.log('orderEntity: ', orderEntity)
|
||||
// console.log('orderEntity: ', orderEntity)
|
||||
|
||||
// Optimize the wallet to speed up working with it.
|
||||
console.log('Optimizing wallet before creating new order.')
|
||||
@@ -55,7 +57,7 @@ class OrderLib {
|
||||
|
||||
// Ensure sufficient tokens exist to create the order.
|
||||
// await this.ensureFunds(orderEntity)
|
||||
await this.retryQueue.addToQueue(this.ensureFunds, orderEntity)
|
||||
// await this.retryQueue.addToQueue(this.ensureFunds, orderEntity)
|
||||
|
||||
// Get Ticker for token ID.
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTxData([entryObj.tokenId])
|
||||
@@ -70,7 +72,7 @@ class OrderLib {
|
||||
}
|
||||
// const utxoInfo = await this.adapters.wallet.moveTokens(moveObj)
|
||||
const utxoInfo = await this.retryQueue.addToQueue(this.adapters.wallet.moveTokens, moveObj)
|
||||
console.log('utxoInfo: ', utxoInfo)
|
||||
// console.log('utxoInfo: ', utxoInfo)
|
||||
|
||||
// Update the UTXO store for the wallet.
|
||||
await this.adapters.wallet.bchWallet.bchjs.Util.sleep(3000)
|
||||
@@ -85,28 +87,29 @@ class OrderLib {
|
||||
// Add P2WDB specific flag for signaling that this is a new offer.
|
||||
orderEntity.dataType = 'offer'
|
||||
|
||||
// Add order to P2WDB.
|
||||
const p2wdbObj = {
|
||||
wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
|
||||
data: orderEntity,
|
||||
appId: this.config.p2wdbAppId
|
||||
// Post the new Order information to Nostr under the topic set in the
|
||||
// config file.
|
||||
const postObj = {
|
||||
data: orderEntity
|
||||
}
|
||||
// const hash = await this.adapters.p2wdb.write(p2wdbObj)
|
||||
const hash = await this.retryQueue.addToQueue(this.adapters.p2wdb.write, p2wdbObj)
|
||||
const postMsg = JSON.stringify(postObj)
|
||||
const eventId = await this.adapters.nostr.post(postMsg)
|
||||
// console.log('hash: ', hash)
|
||||
|
||||
// Create a MongoDB model to hold the Order
|
||||
orderEntity.hdIndex = utxoInfo.hdIndex
|
||||
orderEntity.p2wdbHash = hash
|
||||
orderEntity.nostrEventId = eventId
|
||||
|
||||
console.log(`creating new order model: ${JSON.stringify(orderEntity, null, 2)}`)
|
||||
const order = new this.OrderModel(orderEntity)
|
||||
await order.save()
|
||||
|
||||
return hash
|
||||
const noteId = this.adapters.nostr.eventId2note(eventId)
|
||||
|
||||
return { eventId, noteId }
|
||||
} catch (err) {
|
||||
// console.log("Error in use-cases/entry.js/createEntry()", err.message)
|
||||
wlogger.error('Error in use-cases/order.js/createOrder())')
|
||||
wlogger.error('Error in use-cases/order.js/createOrder(): ', err)
|
||||
console.log('error entryObj: ', entryObj)
|
||||
throw err
|
||||
}
|
||||
@@ -137,12 +140,10 @@ class OrderLib {
|
||||
|
||||
// Combine Fungible and NFT token UTXOs.
|
||||
let tokenUtxos = utxos.slpUtxos.type1.tokens.concat(utxos.slpUtxos.nft.tokens)
|
||||
|
||||
// Get token UTXOs that match the token in the order.
|
||||
tokenUtxos = tokenUtxos.filter(
|
||||
x => x.tokenId === orderEntity.tokenId
|
||||
)
|
||||
console.log('tokenUtxos: ', tokenUtxos)
|
||||
|
||||
// Get the total amount of tokens in the wallet that match the token
|
||||
// in the order.
|
||||
@@ -171,14 +172,14 @@ class OrderLib {
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve an Order model from the database. Find it by its P2WDB CID.
|
||||
async findOrderByHash (p2wdbHash) {
|
||||
// Retrieve an Order model from the database. Find it by its event Id.
|
||||
async findOrderByEvent (nostrEventId) {
|
||||
try {
|
||||
if (typeof p2wdbHash !== 'string' || !p2wdbHash) {
|
||||
throw new Error('p2wdbHash must be a string')
|
||||
if (typeof nostrEventId !== 'string' || !nostrEventId) {
|
||||
throw new Error('nostrEventId must be a string')
|
||||
}
|
||||
|
||||
const order = await this.OrderModel.findOne({ p2wdbHash })
|
||||
const order = await this.OrderModel.findOne({ nostrEventId })
|
||||
|
||||
if (!order) {
|
||||
throw new Error('order not found')
|
||||
@@ -189,7 +190,35 @@ class OrderLib {
|
||||
|
||||
return orderObject
|
||||
} catch (err) {
|
||||
console.error('Error in findOrder()')
|
||||
console.error('Error in findOrderByEvent()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve an Order model from the database. Find it by its UTXO (TXID & Vout)
|
||||
async findOrderByUtxo (offerData = {}) {
|
||||
try {
|
||||
// console.log('findOrderByUtxo() offerData: ', offerData)
|
||||
|
||||
// const order = await this.OrderModel.findOne({ nostrEventId })
|
||||
const order = await this.OrderModel.findOne({ utxoTxid: offerData.data.utxoTxid, utxoVout: offerData.data.utxoVout })
|
||||
// const order = await this.OrderModel.findOne({ utxoTxid: offerData.data.utxoTxid })
|
||||
// const order = await this.OrderModel.findOne({ tokenId: offerData.data.tokenId })
|
||||
console.log('findOrderByUtxo() order: ', order)
|
||||
|
||||
if (!order) {
|
||||
throw new Error('order not found')
|
||||
}
|
||||
if (!order.nostrEventId) {
|
||||
throw new Error('order not found')
|
||||
}
|
||||
|
||||
const orderObject = order.toObject()
|
||||
// return this.offerEntity.validateFromModel(offerObject)
|
||||
|
||||
return orderObject
|
||||
} catch (err) {
|
||||
console.error('Error in findOrderByUtxo(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -242,7 +271,7 @@ class OrderLib {
|
||||
|
||||
// If the Order UTXO is spent, delete the Order model.
|
||||
if (utxoStatus === false) {
|
||||
console.log('utxoStatus: ', utxoStatus)
|
||||
// console.log('utxoStatus: ', utxoStatus)
|
||||
console.log(`Spent UTXO detected. Deleting this Order: ${JSON.stringify(thisOrder, null, 2)}`)
|
||||
await thisOrder.remove()
|
||||
}
|
||||
@@ -256,6 +285,7 @@ class OrderLib {
|
||||
async listOrders (page = 0) {
|
||||
try {
|
||||
const data = await this.OrderModel.find({})
|
||||
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
@@ -272,12 +302,10 @@ class OrderLib {
|
||||
|
||||
// Delete an Order model by sending the token back to the root address. This
|
||||
// will allow the garbage collectors to delete the Order and the Offer.
|
||||
async deleteOrder (p2wdbHash) {
|
||||
async deleteOrder (nostrEventId) {
|
||||
try {
|
||||
console.log('p2wdbHash: ', p2wdbHash)
|
||||
|
||||
// Find the order by the given hash.
|
||||
const order = await this.findOrderByHash(p2wdbHash)
|
||||
const order = await this.findOrderByEvent(nostrEventId)
|
||||
console.log('order: ', order)
|
||||
|
||||
// Reclaim the tokens
|
||||
|
||||
@@ -67,11 +67,12 @@ describe('#Order-REST-Router', () => {
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.useCases.order, 'createOrder').resolves('testHash')
|
||||
sandbox.stub(uut.useCases.order, 'createOrder').resolves({ eventId: 'testEventId', noteId: 'testNoteId' })
|
||||
|
||||
await uut.createOrder(ctx)
|
||||
|
||||
assert.equal(ctx.body.hash, 'testHash')
|
||||
assert.equal(ctx.body.eventId, 'testEventId')
|
||||
assert.equal(ctx.body.noteId, 'testNoteId')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
|
||||
@@ -56,27 +56,70 @@ describe('#Timer-Controllers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// describe('#startTimers', () => {
|
||||
// it('should start the timers', () => {
|
||||
// const result = uut.startTimers()
|
||||
//
|
||||
// // uut.stopTimers()
|
||||
//
|
||||
// assert.equal(result, true)
|
||||
// })
|
||||
// })
|
||||
describe('#startTimers', () => {
|
||||
it('should start the timers', () => {
|
||||
const result = uut.startTimers()
|
||||
|
||||
// describe('#exampleTimerFunc', () => {
|
||||
// it('should kick off the Use Case', async () => {
|
||||
// const result = await uut.exampleTimerFunc()
|
||||
//
|
||||
// assert.equal(result, true)
|
||||
// })
|
||||
//
|
||||
// it('should return false on error', async () => {
|
||||
// const result = await uut.exampleTimerFunc(true)
|
||||
//
|
||||
// assert.equal(result, false)
|
||||
// })
|
||||
// })
|
||||
// uut.stopTimers()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#gcOrders', () => {
|
||||
it('should kick off the Use Case', async () => {
|
||||
const result = await uut.gcOrders()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false on error', async () => {
|
||||
sandbox.stub(uut.useCases.order, 'removeStaleOrders').throws(new Error('test error'))
|
||||
const result = await uut.gcOrders()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
describe('#gcOffers', () => {
|
||||
it('should kick off the Use Case', async () => {
|
||||
const result = await uut.gcOffers()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false on error', async () => {
|
||||
sandbox.stub(uut.useCases.offer, 'removeStaleOffers').throws(new Error('test error'))
|
||||
const result = await uut.gcOffers()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
describe('#checkDupOffers', () => {
|
||||
it('should kick off the Use Case', async () => {
|
||||
const result = await uut.checkDupOffers()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false on error', async () => {
|
||||
sandbox.stub(uut.useCases.offer, 'removeDuplicateOffers').throws(new Error('test error'))
|
||||
const result = await uut.checkDupOffers()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
describe('#loadOffers', () => {
|
||||
it('should kick off the Use Case', async () => {
|
||||
const result = await uut.loadOffers()
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false on error', async () => {
|
||||
sandbox.stub(uut.useCases.offer, 'loadOffers').throws(new Error('test error'))
|
||||
const result = await uut.loadOffers()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -237,6 +237,36 @@ describe('#Offer-Entity', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if nostrEventId is not included', () => {
|
||||
try {
|
||||
const offerData = {
|
||||
data: {
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: 'fakeId',
|
||||
buyOrSell: 'buy',
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 350,
|
||||
numTokens: 1,
|
||||
utxoTxid: 'fakeTxid',
|
||||
utxoVout: 0,
|
||||
offerStatus: 'posted',
|
||||
makerAddr: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
|
||||
tokenType: 1
|
||||
}
|
||||
}
|
||||
uut.validate(offerData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(
|
||||
err.message,
|
||||
"Property 'nostrEventId' must be a string."
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should validate a new offer', () => {
|
||||
const offerObj = {
|
||||
appId: 'swapTest555',
|
||||
@@ -255,7 +285,8 @@ describe('#Offer-Entity', () => {
|
||||
offerStatus: 'posted',
|
||||
makerAddr: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
|
||||
ticker: 'TROUT',
|
||||
tokenType: 1
|
||||
tokenType: 1,
|
||||
nostrEventId: 'test'
|
||||
},
|
||||
timestamp: '2021-09-20T17:54:26.395Z',
|
||||
localTimeStamp: '9/20/2021, 10:54:26 AM',
|
||||
|
||||
@@ -150,7 +150,12 @@ const wallet = {
|
||||
return { cashAddress: 'fakeAddr', wif: 'fakeWif', hdIndex: 1 }
|
||||
},
|
||||
bchWallet: new MockBchWallet(),
|
||||
moveTokens: async () => {}
|
||||
moveTokens: async () => {},
|
||||
moveBch: async () => {},
|
||||
reclaimTokens: async ()=>{},
|
||||
generatePartialTx: async ()=>{},
|
||||
deseralizeTx:async ()=>{},
|
||||
completeTx:async ()=>{ return ''},
|
||||
}
|
||||
|
||||
const p2wdb = {
|
||||
@@ -159,4 +164,10 @@ const p2wdb = {
|
||||
checkForSufficientFunds: async () => true
|
||||
}
|
||||
|
||||
export default { ipfs, localdb, bch, wallet, p2wdb, bchjs}
|
||||
const nostr = {
|
||||
post: async () => {return true },
|
||||
read: async () => { return true },
|
||||
eventId2note: () => { return 'testNoteId' }
|
||||
}
|
||||
|
||||
export default { ipfs, localdb, bch, wallet, p2wdb, bchjs, nostr}
|
||||
|
||||
@@ -24,6 +24,7 @@ class MockBchWallet {
|
||||
this.sendTokens = async () => {
|
||||
return 'fakeTxid';
|
||||
};
|
||||
this.utxoIsValid =async ()=>{}
|
||||
this.getUtxos = async () => { };
|
||||
this.getBalance = async () => { };
|
||||
this.listTokens = async () => { };
|
||||
|
||||
@@ -41,12 +41,24 @@ class Offer {
|
||||
async createOffer() {
|
||||
return {}
|
||||
}
|
||||
async removeStaleOffers(){
|
||||
|
||||
}
|
||||
async removeDuplicateOffers(){
|
||||
|
||||
}
|
||||
async loadOffers(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Order {
|
||||
async createOrder() {
|
||||
return {}
|
||||
}
|
||||
async removeStaleOrders(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class UseCasesMock {
|
||||
|
||||
@@ -3,45 +3,45 @@
|
||||
*/
|
||||
|
||||
const nftOffer01 = {
|
||||
"messageType": 1,
|
||||
"messageClass": 1,
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"buyOrSell": "sell",
|
||||
"rateInBaseUnit": 7672536,
|
||||
"minUnitsToExchange": 7672536,
|
||||
"numTokens": 1,
|
||||
"utxoTxid": "c736d73e274df20e9b069b4990d1a264fb80aa98d67cc6c7a39e42bff48e7c04",
|
||||
"utxoVout": 1,
|
||||
"timestamp": 1662930309998,
|
||||
"globaltimestamp": "2022-09-11T21:05:09.998Z",
|
||||
"localTimestamp": "9/11/2022, 9:05:09 PM",
|
||||
"txid": "4266862b8358664996038d8c29d49dc0f3d3058fd1ef3d567b084e6b16ceb5b2",
|
||||
"p2wdbHash": "zdpuArq7rCuVCGPyTnWpLjy9AYsh8yrbwjGdKwg2GDDALkM8t",
|
||||
"offerStatus": "posted",
|
||||
"makerAddr": "bitcoincash:qrqlz63cwmu0hcmsrfnd8jemn3atkpaqds6tf4ksrr",
|
||||
"ticker": "TV001",
|
||||
"tokenType": 65
|
||||
"messageType": 1,
|
||||
"messageClass": 1,
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"buyOrSell": "sell",
|
||||
"rateInBaseUnit": 7672536,
|
||||
"minUnitsToExchange": 7672536,
|
||||
"numTokens": 1,
|
||||
"utxoTxid": "c736d73e274df20e9b069b4990d1a264fb80aa98d67cc6c7a39e42bff48e7c04",
|
||||
"utxoVout": 1,
|
||||
"timestamp": 1662930309998,
|
||||
"globaltimestamp": "2022-09-11T21:05:09.998Z",
|
||||
"localTimestamp": "9/11/2022, 9:05:09 PM",
|
||||
"txid": "4266862b8358664996038d8c29d49dc0f3d3058fd1ef3d567b084e6b16ceb5b2",
|
||||
"p2wdbHash": "zdpuArq7rCuVCGPyTnWpLjy9AYsh8yrbwjGdKwg2GDDALkM8t",
|
||||
"offerStatus": "posted",
|
||||
"makerAddr": "bitcoincash:qrqlz63cwmu0hcmsrfnd8jemn3atkpaqds6tf4ksrr",
|
||||
"ticker": "TV001",
|
||||
"tokenType": 65
|
||||
}
|
||||
|
||||
const nftTokenData01 = {
|
||||
"genesisData": {
|
||||
"type": 65,
|
||||
"ticker": "TV001",
|
||||
"name": "Introduction to NFTs on BCH",
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"documentUri": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"documentHash": "c1731268f4873f1928438abdaf6ffc546d86a1817dc6f3c6bc73fbdfb4664f10",
|
||||
"decimals": 0,
|
||||
"mintBatonIsActive": false,
|
||||
"tokensInCirculationBN": "1",
|
||||
"tokensInCirculationStr": "1",
|
||||
"blockCreated": 740395,
|
||||
"totalBurned": "0",
|
||||
"totalMinted": "1",
|
||||
"parentGroupId": "030563ddd65772d8e9b79b825529ed53c7d27037507b57c528788612b4911107"
|
||||
},
|
||||
"immutableData": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"mutableData": "ipfs://bafybeifzhunfpodsztsj5x4ypopngkxuapbxwaxaxkahaybdktk6joqtlq"
|
||||
"genesisData": {
|
||||
"type": 65,
|
||||
"ticker": "TV001",
|
||||
"name": "Introduction to NFTs on BCH",
|
||||
"tokenId": "eb93f05553ff088bffb0ec687519e83c59e5108c160f7c25a4b6c45109d7e40b",
|
||||
"documentUri": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"documentHash": "c1731268f4873f1928438abdaf6ffc546d86a1817dc6f3c6bc73fbdfb4664f10",
|
||||
"decimals": 0,
|
||||
"mintBatonIsActive": false,
|
||||
"tokensInCirculationBN": "1",
|
||||
"tokensInCirculationStr": "1",
|
||||
"blockCreated": 740395,
|
||||
"totalBurned": "0",
|
||||
"totalMinted": "1",
|
||||
"parentGroupId": "030563ddd65772d8e9b79b825529ed53c7d27037507b57c528788612b4911107"
|
||||
},
|
||||
"immutableData": "ipfs://bafybeibmgilu4lk3uivwyzhtm7jorl5zbsluewqmcsxs6otzrphh33okk4",
|
||||
"mutableData": "ipfs://bafybeifzhunfpodsztsj5x4ypopngkxuapbxwaxaxkahaybdktk6joqtlq"
|
||||
}
|
||||
|
||||
const simpleNftOffer01 = {
|
||||
@@ -126,11 +126,57 @@ const fungibleTokenData01 = {
|
||||
"mutableData": ""
|
||||
}
|
||||
|
||||
const offerMockData = {
|
||||
data: {
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId:
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
buyOrSell: 'sell',
|
||||
rateInBaseUnit: 1000,
|
||||
minUnitsToExchange: 10,
|
||||
numTokens: 0.02,
|
||||
utxoTxid:
|
||||
'241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87',
|
||||
utxoVout: 0,
|
||||
makerAddr: 'address',
|
||||
tokenType: 1,
|
||||
nostrEventId: 'test'
|
||||
}
|
||||
}
|
||||
|
||||
const deserealizeTxMock = {
|
||||
//...
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash:qzy97glp47ut7tstm5g0tlrmkhk742795gkmyc7478']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash:qzy97glp47ut7tstm5g0tlrmkhk742795gkmyc7478']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash:qzy97glp47ut7tstm5g0tlrmkhk742795gkmyc7478']
|
||||
}
|
||||
},
|
||||
//...
|
||||
]
|
||||
}
|
||||
|
||||
export default {
|
||||
nftOffer01,
|
||||
nftTokenData01,
|
||||
simpleNftOffer01,
|
||||
simpleNftTokenData01,
|
||||
fungibleOffer01,
|
||||
fungibleTokenData01
|
||||
fungibleTokenData01,
|
||||
offerMockData,
|
||||
deserealizeTxMock
|
||||
};
|
||||
|
||||
@@ -48,79 +48,70 @@ describe('#offer-use-case', () => {
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if order use cases are not passed in', () => {
|
||||
try {
|
||||
uut = new OfferLib({ adapters })
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
console.log(uut) // linter
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Instance of Order Use Cases must be passed in when instantiating Offer Use Cases library.'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createOffer', () => {
|
||||
// it('should ignore an offer if utxo has been spent', async () => {
|
||||
// const offerObj = {
|
||||
// appId: 'swapTest555',
|
||||
// data: {
|
||||
// messageType: 1,
|
||||
// messageClass: 1,
|
||||
// tokenId:
|
||||
// '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
// buyOrSell: 'sell',
|
||||
// rateInSats: 1000,
|
||||
// minSatsToExchange: 10,
|
||||
// numTokens: 0.02,
|
||||
// utxoTxid:
|
||||
// '241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87',
|
||||
// utxoVout: 0
|
||||
// },
|
||||
// timestamp: '2021-09-20T17:54:26.395Z',
|
||||
// localTimeStamp: '9/20/2021, 10:54:26 AM',
|
||||
// txid: '46f50f2a0cf44e3ed70dfb0618ef3ebfee57aabcf229b5d2d17c07322b54a8d7',
|
||||
// hash: 'zdpuB2X25AZCKo3wpr4sSbw44vqPWJRqcxWQRHZccK5BdtoGD'
|
||||
// }
|
||||
//
|
||||
// // Mock dependencies
|
||||
// // sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
// sandbox.stub(uut, 'categorizeToken').resolves('nft')
|
||||
// sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
//
|
||||
// const result = await uut.createOffer(offerObj)
|
||||
// // console.log('result: ', result)
|
||||
//
|
||||
// assert.equal(result, false)
|
||||
// })
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
await uut.createOffer()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.include(error.message, 'Cannot read properties of undefined')
|
||||
}
|
||||
})
|
||||
|
||||
// it('should create an offer and return the hash', async () => {
|
||||
// const offerObj = {
|
||||
// appId: 'swapTest555',
|
||||
// data: {
|
||||
// messageType: 1,
|
||||
// messageClass: 1,
|
||||
// tokenId:
|
||||
// '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
// buyOrSell: 'sell',
|
||||
// rateInBaseUnit: 1000,
|
||||
// minUnitsToExchange: 10,
|
||||
// numTokens: 0.02,
|
||||
// utxoTxid:
|
||||
// '241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87',
|
||||
// utxoVout: 0,
|
||||
// makerAddr: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
|
||||
// ticker: 'TROUT',
|
||||
// tokenType: 1
|
||||
// },
|
||||
// timestamp: '2021-09-20T17:54:26.395Z',
|
||||
// localTimeStamp: '9/20/2021, 10:54:26 AM',
|
||||
// txid: '46f50f2a0cf44e3ed70dfb0618ef3ebfee57aabcf229b5d2d17c07322b54a8d7',
|
||||
// hash: 'zdpuB2X25AZCKo3wpr4sSbw44vqPWJRqcxWQRHZccK5BdtoGD'
|
||||
// }
|
||||
//
|
||||
// // Mock dependencies
|
||||
// // sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(true)
|
||||
// sandbox.stub(uut, 'categorizeToken').resolves('fungible')
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'getTokenData').resolves({})
|
||||
// sandbox.stub(uut, 'detectNsfw').resolves(false)
|
||||
//
|
||||
// const result = await uut.createOffer(offerObj)
|
||||
// // console.log('result: ', result)
|
||||
//
|
||||
// assert.equal(result, true)
|
||||
// })
|
||||
it('should return false if offer already exist', async () => {
|
||||
const offerObj = mockData.offerMockData
|
||||
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').resolves({})
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isFalse(result)
|
||||
})
|
||||
|
||||
it('should return false for invalid utxo', async () => {
|
||||
const offerObj = mockData.offerMockData
|
||||
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').resolves(null)
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isFalse(result)
|
||||
})
|
||||
|
||||
it('should create offer', async () => {
|
||||
const tokenDataMock = mockData.simpleNftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves({}) // Utxo Status call
|
||||
.onCall(1).resolves(tokenDataMock) // Token Data call
|
||||
.onCall(2).resolves(false) // detectNsfw call
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#categorizeToken', () => {
|
||||
@@ -159,15 +150,29 @@ describe('#offer-use-case', () => {
|
||||
|
||||
assert.equal(result, 'fungible')
|
||||
})
|
||||
|
||||
it('should unknow type', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
const offerData = mockData.fungibleOffer01
|
||||
const tokenData = { genesisData: {} }
|
||||
|
||||
await uut.categorizeToken(offerData, tokenData)
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.include(error.message, 'Unknown token type:')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#removeDuplicateOffers', () => {
|
||||
it('should remove duplicate entries and return true', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'b', remove: async () => {} }
|
||||
{ p2wdbHash: 'a', remove: async () => { } },
|
||||
{ p2wdbHash: 'a', remove: async () => { } },
|
||||
{ p2wdbHash: 'b', remove: async () => { } }
|
||||
])
|
||||
// sandbox.stub(uut.OfferModel, 'remove').resolves()
|
||||
|
||||
@@ -180,8 +185,8 @@ describe('#offer-use-case', () => {
|
||||
it('should return false if there are no duplicate entries', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([
|
||||
{ p2wdbHash: 'a', remove: async () => {} },
|
||||
{ p2wdbHash: 'b', remove: async () => {} }
|
||||
{ p2wdbHash: 'a', remove: async () => { } },
|
||||
{ p2wdbHash: 'b', remove: async () => { } }
|
||||
])
|
||||
// sandbox.stub(uut.OfferModel, 'remove').resolves()
|
||||
|
||||
@@ -190,5 +195,595 @@ describe('#offer-use-case', () => {
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').throws(new Error('test error'))
|
||||
// sandbox.stub(uut.OfferModel, 'remove').resolves()
|
||||
|
||||
await uut.removeDuplicateOffers()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.equal(error.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#removeStaleOffers', () => {
|
||||
it('remove offer with wrong utxoState', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([{ remove: async () => { } }])
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').resolves(false)
|
||||
|
||||
await uut.removeStaleOffers()
|
||||
})
|
||||
|
||||
it('remove offer with wrong txid', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([{ remove: async () => { } }])
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(new Error('txid needs to be a proper transaction ID'))
|
||||
|
||||
await uut.removeStaleOffers()
|
||||
})
|
||||
|
||||
it('remove expired offer ', async () => {
|
||||
const tsMock = new Date()
|
||||
tsMock.setMonth(tsMock.getMonth() - 3)
|
||||
|
||||
const timestamp = tsMock.getTime()
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([{ timestamp, remove: async () => { } }])
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').resolves(true)
|
||||
|
||||
await uut.removeStaleOffers()
|
||||
})
|
||||
|
||||
it('should handle axios error ', async () => {
|
||||
const testErr = new Error()
|
||||
testErr.isAxiosError = true
|
||||
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([{ remove: async () => { } }])
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(testErr)
|
||||
|
||||
await uut.removeStaleOffers()
|
||||
})
|
||||
|
||||
it('should handle error ', async () => {
|
||||
try {
|
||||
const testErr = new Error('unknow error')
|
||||
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'find').resolves([{ remove: async () => { } }])
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(testErr)
|
||||
|
||||
await uut.removeStaleOffers()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.equal(error.message, 'unknow error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findOfferByTxid', () => {
|
||||
it('should throw an error if input is not provided', async () => {
|
||||
try {
|
||||
await uut.findOfferByTxid()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.equal(error.message, 'utxoTxid must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if offer is not found', async () => {
|
||||
try {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'findOne').resolves(null)
|
||||
|
||||
await uut.findOfferByTxid('241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87')
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.equal(error.message, 'offer not found')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return offer', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.OfferModel, 'findOne').resolves(mockData.nftOffer01)
|
||||
|
||||
const result = await uut.findOfferByTxid('241c06bf61384b8623477e419bf4779edbcc7e3bc862f0f179a9ed2967069b87')
|
||||
assert.isObject(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#detectNsfw', () => {
|
||||
it('should return false for wrong cid format', async () => {
|
||||
const result = await uut.detectNsfw({ mutableData: '' })
|
||||
assert.isFalse(result)
|
||||
})
|
||||
|
||||
it('should return true if nft boolean detected', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.axios, 'get').resolves({ data: { nsfw: true } })
|
||||
|
||||
const result = await uut.detectNsfw({ mutableData: 'ipfs://bafybeibqnsmmh6bkf2wwextetki4tly65z4r4qkrrpl5xwgvzdzjley6wm' })
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should return true if nft string detected', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.axios, 'get').resolves({ data: { nsfw: 'true' } })
|
||||
|
||||
const result = await uut.detectNsfw({ mutableData: 'ipfs://bafybeibqnsmmh6bkf2wwextetki4tly65z4r4qkrrpl5xwgvzdzjley6wm' })
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should return false if nfsw property does not exist', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.axios, 'get').resolves({ data: {} })
|
||||
|
||||
const result = await uut.detectNsfw({ mutableData: 'ipfs://bafybeibqnsmmh6bkf2wwextetki4tly65z4r4qkrrpl5xwgvzdzjley6wm' })
|
||||
assert.isFalse(result)
|
||||
})
|
||||
|
||||
it('should return false on error', async () => {
|
||||
// Mock dependencies and force desired code path.
|
||||
sandbox.stub(uut.axios, 'get').throws(new Error('test error'))
|
||||
|
||||
const result = await uut.detectNsfw({ mutableData: 'ipfs://bafybeibqnsmmh6bkf2wwextetki4tly65z4r4qkrrpl5xwgvzdzjley6wm' })
|
||||
assert.isFalse(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#listOffers', () => {
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').throws(new Error('test error'))
|
||||
|
||||
await uut.listOffers()
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should list orders', async () => {
|
||||
const queryMock = {
|
||||
sort () {
|
||||
return this
|
||||
},
|
||||
skip () {
|
||||
return this
|
||||
},
|
||||
limit () { return [] }
|
||||
}
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').returns(queryMock)
|
||||
|
||||
const result = await uut.listOffers()
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#listNftOffers', () => {
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').throws(new Error('test error'))
|
||||
|
||||
await uut.listNftOffers()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should list orders', async () => {
|
||||
const queryMock = {
|
||||
sort () {
|
||||
return this
|
||||
},
|
||||
skip () {
|
||||
return this
|
||||
},
|
||||
limit () { return [] }
|
||||
}
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').returns(queryMock)
|
||||
|
||||
const result = await uut.listNftOffers(1, true)
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#listFungibleOffers', () => {
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').throws(new Error('test error'))
|
||||
|
||||
await uut.listFungibleOffers()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should list orders', async () => {
|
||||
const queryMock = {
|
||||
sort () {
|
||||
return this
|
||||
},
|
||||
skip () {
|
||||
return this
|
||||
},
|
||||
limit () { return [] }
|
||||
}
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'find').returns(queryMock)
|
||||
|
||||
const result = await uut.listFungibleOffers()
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#takeOffer', () => {
|
||||
it('should handle error if input is not provided', async () => {
|
||||
try {
|
||||
await uut.takeOffer()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'eventId must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error for wrong offer status', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns({ offerStatus: 'completed' })
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'offer status is not "posted", so offer is dead and can not be countered.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error for invalid utxo', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns({ offerStatus: 'posted' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').returns(false)
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'UTXO does not exist. Aborting.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle insufficient funds', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns({ offerStatus: 'posted' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').returns(true)
|
||||
sandbox.stub(uut, 'ensureFunds').throws(new Error('test error'))
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error if counter offer cant be calculated', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns({ offerStatus: 'posted' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').returns(true)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves(true)
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Could not calculate the amount of BCH to generate counter offer')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error if counter offer cant be calculated', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns({ offerStatus: 'posted' })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').returns(true)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves(true)
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Could not calculate the amount of BCH to generate counter offer')
|
||||
}
|
||||
})
|
||||
|
||||
it('should take offer', async () => {
|
||||
// Mock data
|
||||
const offerMock = Object.assign({}, mockData.offerMockData.data)
|
||||
offerMock.remove = async () => { }
|
||||
offerMock.offerStatus = 'posted'
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').returns(offerMock)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').returns(true)
|
||||
sandbox.stub(uut, 'ensureFunds').resolves(true)
|
||||
sandbox.stub(uut.adapters.wallet, 'moveBch').resolves({ sats: 1 })
|
||||
|
||||
await uut.takeOffer('eventId')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#ensureFunds', () => {
|
||||
// it('should handle insufficient funds to use p2wdb', async () => {
|
||||
// try {
|
||||
// // Mock dependencies
|
||||
// sandbox.stub(uut.adapters.p2wdb, 'checkForSufficientFunds').resolves(false)
|
||||
//
|
||||
// await uut.ensureFunds(mockData.offerMockData.data)
|
||||
//
|
||||
// assert.fail('Unexpected code path')
|
||||
// } catch (err) {
|
||||
// assert.include(err.message, 'App wallet does not have funds for writing to the P2WDB')
|
||||
// }
|
||||
// })
|
||||
|
||||
it('should throw error if sats needed could no be able to calculated', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.p2wdb, 'checkForSufficientFunds').resolves(true)
|
||||
|
||||
// Mock Input
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
mock.rateInBaseUnit = null
|
||||
|
||||
await uut.ensureFunds(mock)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Could not calculate sats needed!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if app wallet does not have enough bch', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.p2wdb, 'checkForSufficientFunds').resolves(true)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getBalance').resolves(0)
|
||||
|
||||
await uut.ensureFunds(mockData.offerMockData.data)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'App wallet does not control enough BCH to purchase the tokens.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle BUY offer', async () => {
|
||||
try {
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
mock.buyOrSell = 'buy'
|
||||
|
||||
await uut.ensureFunds(mock)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Buy offers are not supported yet.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return true', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.p2wdb, 'checkForSufficientFunds').resolves(true)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'getBalance').resolves(10 * 10 ** 6)
|
||||
|
||||
const result = await uut.ensureFunds(mockData.offerMockData.data)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findOrderByEvent', () => {
|
||||
it('should throw an error if hash is not provided', async () => {
|
||||
try {
|
||||
await uut.findOfferByEvent()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'nostrEventId must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if order is not found!', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'findOne').resolves(null)
|
||||
|
||||
await uut.findOfferByEvent('eventId')
|
||||
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'offer not found')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return offer by eventId', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'findOne').resolves({ toObject: () => { return { hash: 'hash' } } })
|
||||
|
||||
const result = await uut.findOfferByEvent('eventId')
|
||||
assert.isObject(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#flagOffer', () => {
|
||||
it('should throw an error if input is not provided', async () => {
|
||||
try {
|
||||
await uut.flagOffer()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, '"data" property is required')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if offer is not found!', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').resolves(null)
|
||||
|
||||
const input = {
|
||||
data:
|
||||
{
|
||||
nostrEventId: 'eventId'
|
||||
}
|
||||
}
|
||||
await uut.flagOffer(input)
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'not found in the database')
|
||||
}
|
||||
})
|
||||
|
||||
it('should flag offer', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'findOfferByEvent').resolves({ flags: ['a', 'b', 'c'], save: () => { } })
|
||||
|
||||
const input = {
|
||||
data:
|
||||
{
|
||||
nostrEventId: 'eventId'
|
||||
}
|
||||
}
|
||||
const result = await uut.flagOffer(input)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#loadOffers', () => {
|
||||
it('should handle nostr error', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.nostr, 'read').throws(new Error('test error'))
|
||||
|
||||
await uut.loadOffers()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should skip internal function errors ', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.nostr, 'read').resolves([mockData.offerMockData])
|
||||
|
||||
await uut.loadOffers()
|
||||
})
|
||||
|
||||
it('should review and load offers', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.adapters.nostr, 'read').resolves([JSON.stringify(mockData.offerMockData)])
|
||||
|
||||
await uut.loadOffers()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#acceptCounterOffer', () => {
|
||||
it('should return if order is not found!', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByEvent').throws(new Error('test error'))
|
||||
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.equal(result, 'N/A')
|
||||
})
|
||||
|
||||
it('should handle error if counter offer cant be calculated', async () => {
|
||||
try {
|
||||
// Mock Data
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
mock.rateInBaseUnit = null
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByEvent').resolves(mock)
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
console.log('result: ', result)
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Could not calculate the amount of BCH offered in the Counter Offer')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error for wrong transaction output', async () => {
|
||||
try {
|
||||
// Mock Data
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByEvent').resolves(mockData.offerMockData.data)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.BitcoinCash, 'toSatoshi').returns(0)
|
||||
sandbox.stub(uut.adapters.wallet, 'deseralizeTx').resolves(mockData.deserealizeTxMock)
|
||||
|
||||
await uut.acceptCounterOffer({ data: mock })
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'The Counter Offer has an output of ')
|
||||
assert.include(err.message, 'which does not match the required')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle error for wrong transaction output address', async () => {
|
||||
try {
|
||||
// Mock data
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
mock.makerAddr = 'bitcoincash:qzy97glp47ut7tstm5g0tlrmkhk742795gkmyc7477' // Unknow Adress
|
||||
mock.rateInBaseUnit = 0
|
||||
mock.numTokens = 0
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByEvent').resolves(mock)
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.BitcoinCash, 'toSatoshi').returns(0)
|
||||
|
||||
sandbox.stub(uut.adapters.wallet, 'deseralizeTx').resolves(mockData.deserealizeTxMock)
|
||||
|
||||
await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'The Counter Offer has an output address of')
|
||||
assert.include(err.message, 'which does not match the Maker address')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return tx id', async () => {
|
||||
// Mock data
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
mock.makerAddr = mockData.deserealizeTxMock.vout[2].scriptPubKey.addresses[0] // Maker Address
|
||||
mock.rateInBaseUnit = 0
|
||||
mock.numTokens = 0
|
||||
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByEvent').resolves(mock)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.BitcoinCash, 'toSatoshi').returns(0)
|
||||
sandbox.stub(uut.adapters.wallet, 'deseralizeTx').resolves(mockData.deserealizeTxMock)
|
||||
|
||||
//
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.isString(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -62,6 +62,69 @@ describe('#order-use-case', () => {
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
it('should handle insufficient funds', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.adapters.p2wdb, 'checkForSufficientFunds').resolves(false)
|
||||
const orderEntity = {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
buyOrSell: 'sell',
|
||||
rateInSats: 1000,
|
||||
minSatsToExchange: 0,
|
||||
numTokens: 1
|
||||
}
|
||||
|
||||
await uut.ensureFunds(orderEntity)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'App wallet does not have funds for writing to the P2WDB')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle insufficient tokens to satisfy the order', async () => {
|
||||
try {
|
||||
const orderEntity = {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
buyOrSell: 'sell',
|
||||
rateInSats: 1000,
|
||||
minSatsToExchange: 0,
|
||||
numTokens: 1000
|
||||
}
|
||||
|
||||
await uut.ensureFunds(orderEntity)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'App wallet does not have enough tokens to satisfy the SELL order.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle BUY order', async () => {
|
||||
try {
|
||||
const orderEntity = {
|
||||
lokadId: 'SWP',
|
||||
messageType: 1,
|
||||
messageClass: 1,
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
buyOrSell: 'buy',
|
||||
rateInSats: 1000,
|
||||
minSatsToExchange: 0,
|
||||
numTokens: 1
|
||||
}
|
||||
|
||||
await uut.ensureFunds(orderEntity)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Buy orders are not supported yet')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// describe('#moveTokens', () => {
|
||||
@@ -113,12 +176,13 @@ describe('#order-use-case', () => {
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet.bchjs.Util, 'sleep').resolves()
|
||||
sandbox.stub(uut.adapters.wallet, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'initialize').resolves()
|
||||
sandbox.stub(uut.adapters.p2wdb, 'write').resolves('fakeHash')
|
||||
sandbox.stub(uut.adapters.nostr, 'post').resolves('fakeEvenetId')
|
||||
|
||||
const result = await uut.createOrder(entryObj)
|
||||
console.log('result: ', result)
|
||||
|
||||
assert.isString(result)
|
||||
assert.property(result, 'eventId')
|
||||
assert.property(result, 'noteId')
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
@@ -137,4 +201,137 @@ describe('#order-use-case', () => {
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findOrderByEvent', () => {
|
||||
it('should throw an error if hash is not provided', async () => {
|
||||
try {
|
||||
await uut.findOrderByEvent()
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'nostrEventId must be a string')
|
||||
}
|
||||
})
|
||||
it('should throw an error if order is not found!', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.OrderModel, 'findOne').resolves(null)
|
||||
await uut.findOrderByEvent('hash')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'order not found')
|
||||
}
|
||||
})
|
||||
it('should return order by hash', async () => {
|
||||
sandbox.stub(uut.OrderModel, 'findOne').resolves({ toObject: () => { return { hash: 'hash' } } })
|
||||
const result = await uut.findOrderByEvent('hash')
|
||||
assert.isObject(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#removeStaleOrders', () => {
|
||||
it('should remove stale orders', async () => {
|
||||
const spy = sinon.spy() // Spy the function
|
||||
|
||||
// Create 2 orders mock .
|
||||
const ordersMock = new Array(2)
|
||||
ordersMock.fill({ remove: () => { spy.call() } })
|
||||
|
||||
sandbox.stub(uut.OrderModel, 'find').resolves(ordersMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').resolves(false)
|
||||
|
||||
await uut.removeStaleOrders()
|
||||
|
||||
assert.equal(spy.callCount, 2, 'Expected to be called twice.')
|
||||
})
|
||||
it('should handle wrong txid', async () => {
|
||||
const spy = sinon.spy() // Spy the function
|
||||
|
||||
// Create 2 orders mock .
|
||||
const ordersMock = new Array(2)
|
||||
ordersMock.fill({ remove: () => { spy.call() } })
|
||||
|
||||
sandbox.stub(uut.OrderModel, 'find').resolves(ordersMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(new Error('txid needs to be a proper transaction ID'))
|
||||
|
||||
await uut.removeStaleOrders()
|
||||
|
||||
assert.equal(spy.callCount, 2, 'Expected to be called twice.')
|
||||
})
|
||||
it('should skip order on axios error', async () => {
|
||||
const spy = sinon.spy() // Spy the function
|
||||
|
||||
// Create 2 orders mock .
|
||||
const ordersMock = new Array(2)
|
||||
ordersMock.fill({ remove: () => { spy.call() } })
|
||||
|
||||
const axiosError = new Error()
|
||||
axiosError.isAxiosError = true
|
||||
|
||||
sandbox.stub(uut.OrderModel, 'find').resolves(ordersMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(axiosError)
|
||||
|
||||
await uut.removeStaleOrders()
|
||||
|
||||
assert.equal(spy.callCount, 0, 'Expected to no be called.')
|
||||
})
|
||||
it('should handle unknow error', async () => {
|
||||
try {
|
||||
// Create 2 orders mock .
|
||||
const ordersMock = new Array(2)
|
||||
ordersMock.fill({ remove: () => { } })
|
||||
|
||||
sandbox.stub(uut.OrderModel, 'find').resolves(ordersMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(new Error('test error'))
|
||||
|
||||
await uut.removeStaleOrders()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (error) {
|
||||
assert.include(error.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#listOrders', () => {
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.OrderModel, 'find').throws(new Error('test error'))
|
||||
await uut.listOrders()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should list orders', async () => {
|
||||
const queryMock = {
|
||||
sort () {
|
||||
return this
|
||||
},
|
||||
skip () {
|
||||
return this
|
||||
},
|
||||
limit () { return [] }
|
||||
}
|
||||
|
||||
sandbox.stub(uut.OrderModel, 'find').returns(queryMock)
|
||||
const result = await uut.listOrders()
|
||||
assert.isArray(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deleteOrder', () => {
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
sandbox.stub(uut, 'findOrderByEvent').throws(new Error('test error'))
|
||||
await uut.deleteOrder()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should delete orders', async () => {
|
||||
sandbox.stub(uut, 'findOrderByEvent').resolves([])
|
||||
sandbox.stub(uut.adapters.wallet, 'reclaimTokens').resolves('txid')
|
||||
const result = await uut.deleteOrder()
|
||||
assert.isString(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
// Public npm libraries
|
||||
import BCHJS from '@psf/bch-js'
|
||||
|
||||
import BchTokenSweep from 'bch-token-sweep/index'
|
||||
import BchTokenSweep from 'bch-token-sweep'
|
||||
|
||||
// Local libraries
|
||||
import WalletAdapter from '../../src/adapters/wallet'
|
||||
import WalletAdapter from '../../src/adapters/wallet.js'
|
||||
|
||||
// Constants
|
||||
const EMTPY_ADDR_CUTOFF = 15
|
||||
@@ -22,6 +22,7 @@ async function sweepFunds () {
|
||||
// Open the wallet files.
|
||||
const wallet = new WalletAdapter()
|
||||
const walletInfo = await wallet.openWallet()
|
||||
const bchWallet = await wallet.instanceWallet(walletInfo)
|
||||
console.log('walletInfo: ', walletInfo)
|
||||
|
||||
const rootAddr = walletInfo.cashAddress
|
||||
@@ -49,7 +50,7 @@ async function sweepFunds () {
|
||||
const sweeper = new BchTokenSweep(
|
||||
wifToSweep,
|
||||
rootWif,
|
||||
bchjs,
|
||||
bchWallet,
|
||||
550,
|
||||
rootAddr
|
||||
)
|
||||
@@ -68,7 +69,7 @@ async function sweepFunds () {
|
||||
// Wait between loop iterations.
|
||||
await bchjs.Util.sleep(3000)
|
||||
} catch (err) {
|
||||
console.log(`error message with index ${hdIndex}: ${err.message}`)
|
||||
console.log(`error message with index ${hdIndex}: ${err}`)
|
||||
emptyAddrCnt++
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user