diff --git a/web/cashtab/package-lock.json b/web/cashtab/package-lock.json
--- a/web/cashtab/package-lock.json
+++ b/web/cashtab/package-lock.json
@@ -28,6 +28,7 @@
"buffer": "^6.0.3",
"camelcase": "^6.2.1",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
+ "chronik-client": "^0.4.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
@@ -52,6 +53,7 @@
"localforage": "^1.9.0",
"lodash.isempty": "^4.4.0",
"lodash.isequal": "^4.5.0",
+ "long": "^5.2.0",
"mini-css-extract-plugin": "^2.4.5",
"minimal-slp-wallet": "^3.3.1",
"postcss": "^8.4.4",
@@ -2988,6 +2990,60 @@
"node": ">= 8"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
"node_modules/@psf/bch-js": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.21.0.tgz",
@@ -3871,6 +3927,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -6274,6 +6335,66 @@
"node": ">=6.0"
}
},
+ "node_modules/chronik-client": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/chronik-client/-/chronik-client-0.4.0.tgz",
+ "integrity": "sha512-VgxGHPYN3rlRcn7g1gEh562Tj/2dEDlXQnbhU3qQYgfFIRnBt80TP4Rq2raek7nUy+TIen0d4JK7zxoSK5Kauw==",
+ "dependencies": {
+ "@types/ws": "^8.2.1",
+ "axios": "^0.21.1",
+ "isomorphic-ws": "^4.0.1",
+ "long": "^5.2.0",
+ "protobufjs": "^6.11.2",
+ "ws": "^8.3.0"
+ }
+ },
+ "node_modules/chronik-client/node_modules/axios": {
+ "version": "0.21.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+ "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+ "dependencies": {
+ "follow-redirects": "^1.14.0"
+ }
+ },
+ "node_modules/chronik-client/node_modules/follow-redirects": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+ "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/chronik-client/node_modules/ws": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
+ "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/ci-info": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
@@ -10695,6 +10816,14 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
+ "node_modules/isomorphic-ws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "peerDependencies": {
+ "ws": "*"
+ }
+ },
"node_modules/istanbul-lib-coverage": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
@@ -13028,6 +13157,11 @@
"triple-beam": "^1.3.0"
}
},
+ "node_modules/long": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz",
+ "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -15463,6 +15597,36 @@
"node": ">=4.0.0"
}
},
+ "node_modules/protobufjs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+ "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "bin": {
+ "pbjs": "bin/pbjs",
+ "pbts": "bin/pbts"
+ }
+ },
+ "node_modules/protobufjs/node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -22066,6 +22230,60 @@
}
}
},
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
"@psf/bch-js": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.21.0.tgz",
@@ -22722,6 +22940,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
+ "@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
+ },
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -24572,6 +24795,40 @@
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
},
+ "chronik-client": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/chronik-client/-/chronik-client-0.4.0.tgz",
+ "integrity": "sha512-VgxGHPYN3rlRcn7g1gEh562Tj/2dEDlXQnbhU3qQYgfFIRnBt80TP4Rq2raek7nUy+TIen0d4JK7zxoSK5Kauw==",
+ "requires": {
+ "@types/ws": "^8.2.1",
+ "axios": "^0.21.1",
+ "isomorphic-ws": "^4.0.1",
+ "long": "^5.2.0",
+ "protobufjs": "^6.11.2",
+ "ws": "^8.3.0"
+ },
+ "dependencies": {
+ "axios": {
+ "version": "0.21.4",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+ "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+ "requires": {
+ "follow-redirects": "^1.14.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz",
+ "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ=="
+ },
+ "ws": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.6.0.tgz",
+ "integrity": "sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==",
+ "requires": {}
+ }
+ }
+ },
"ci-info": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz",
@@ -27864,6 +28121,12 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
+ "isomorphic-ws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+ "requires": {}
+ },
"istanbul-lib-coverage": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
@@ -29603,6 +29866,11 @@
"triple-beam": "^1.3.0"
}
},
+ "long": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz",
+ "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w=="
+ },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -31270,6 +31538,33 @@
"retry": "^0.10.0"
}
},
+ "protobufjs": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz",
+ "integrity": "sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "dependencies": {
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ }
+ }
+ },
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
diff --git a/web/cashtab/package.json b/web/cashtab/package.json
--- a/web/cashtab/package.json
+++ b/web/cashtab/package.json
@@ -23,6 +23,7 @@
"buffer": "^6.0.3",
"camelcase": "^6.2.1",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
+ "chronik-client": "^0.4.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
@@ -47,6 +48,7 @@
"localforage": "^1.9.0",
"lodash.isempty": "^4.4.0",
"lodash.isequal": "^4.5.0",
+ "long": "^5.2.0",
"mini-css-extract-plugin": "^2.4.5",
"minimal-slp-wallet": "^3.3.1",
"postcss": "^8.4.4",
diff --git a/web/cashtab/src/components/Common/Notifications.js b/web/cashtab/src/components/Common/Notifications.js
--- a/web/cashtab/src/components/Common/Notifications.js
+++ b/web/cashtab/src/components/Common/Notifications.js
@@ -144,6 +144,33 @@
});
};
+const xecReceivedNotificationWebsocket = (
+ xecAmount,
+ cashtabSettings,
+ fiatPrice,
+) => {
+ const notificationStyle = getDeviceNotificationStyle();
+ notification.success({
+ message: 'eCash received',
+ description: (
+
+ + {xecAmount.toLocaleString()} {currency.ticker}{' '}
+ {cashtabSettings &&
+ cashtabSettings.fiatCurrency &&
+ `(${
+ currency.fiatCurrencies[cashtabSettings.fiatCurrency]
+ .symbol
+ }${(xecAmount * fiatPrice).toFixed(
+ currency.cashDecimals,
+ )} ${cashtabSettings.fiatCurrency.toUpperCase()})`}
+
+ ),
+ duration: currency.notificationDurationShort,
+ icon: ,
+ style: notificationStyle,
+ });
+};
+
const eTokenReceivedNotification = (
currency,
receivedSlpTicker,
@@ -202,6 +229,7 @@
tokenIconSubmitSuccess,
sendTokenNotification,
xecReceivedNotification,
+ xecReceivedNotificationWebsocket,
eTokenReceivedNotification,
errorNotification,
messageSignedNotification,
diff --git a/web/cashtab/src/hooks/useWallet.js b/web/cashtab/src/hooks/useWallet.js
--- a/web/cashtab/src/hooks/useWallet.js
+++ b/web/cashtab/src/hooks/useWallet.js
@@ -13,6 +13,7 @@
addNewHydratedUtxos,
removeConsumedUtxos,
areAllUtxosIncludedInIncrementallyHydratedUtxos,
+ parseChronikTx,
} from 'utils/cashMethods';
import { isValidCashtabSettings, isValidContactList } from 'utils/validation';
import localforage from 'localforage';
@@ -21,10 +22,17 @@
import isEqual from 'lodash.isequal';
import {
xecReceivedNotification,
+ xecReceivedNotificationWebsocket,
eTokenReceivedNotification,
} from 'components/Common/Notifications';
+
+import { ChronikClient } from 'chronik-client';
+// For XEC, eCash chain:
+const chronik = new ChronikClient('https://chronik.be.cash/xec');
+
const useWallet = () => {
const [wallet, setWallet] = useState(false);
+ const [chronikWebsocket, setChronikWebsocket] = useState(null);
const [contactList, setContactList] = useState(false);
const [cashtabSettings, setCashtabSettings] = useState(false);
const [fiatPrice, setFiatPrice] = useState(null);
@@ -1207,6 +1215,101 @@
}
};
+ // Parse chronik ws message for incoming tx notifications
+ const processChronikWsMsg = async msg => {
+ console.log(`processChronikWsMsg called with`, msg);
+ // get txid info
+ const txid = msg.txid;
+ console.log(`txid`, txid);
+ const txDetails = await chronik.tx(txid);
+ console.log(`txDetails`, txDetails);
+ // parse tx for notification
+ const parsedChronikTx = parseChronikTx(txDetails, [
+ BCH.Address.toHash160(wallet.Path245.cashAddress),
+ BCH.Address.toHash160(wallet.Path145.cashAddress),
+ BCH.Address.toHash160(wallet.Path1899.cashAddress),
+ ]);
+ console.log(`parsedChronikTx`, parsedChronikTx);
+ if (parsedChronikTx.incoming) {
+ if (parsedChronikTx.isEtokenTx) {
+ //notification
+ console.log(`eToken notification would be fired here`);
+ } else {
+ xecReceivedNotificationWebsocket(
+ parsedChronikTx.xecAmount,
+ cashtabSettings,
+ fiatPrice,
+ );
+ }
+ }
+
+ // incoming? notification + recalc utxo set. outgoing? recalc utxo set
+ };
+
+ // Chronik websockets
+ const initializeWebsocket = async wallet => {
+ console.log(`initializeWebsocket called with`, wallet.name);
+ console.log(
+ `initializeWebsocket called with chronikWebsocket in state`,
+ chronikWebsocket,
+ );
+ // Because wallet is set to `false` before it is loaded, do nothing if you find this case
+ if (!wallet) {
+ return setChronikWebsocket(null);
+ }
+
+ // Initialize if not in state
+ let ws = chronikWebsocket;
+ if (ws === null) {
+ ws = chronik.ws({
+ onMessage: async msg => {
+ processChronikWsMsg(msg);
+ },
+ onReconnect: e => {
+ // Fired before a reconnect attempt is made:
+ console.log(
+ 'Reconnecting websocket, disconnection cause: ',
+ e,
+ );
+ },
+ });
+ // Wait for WS to be connected:
+ await ws.waitForOpen();
+ console.log(`websocket open`);
+ }
+
+ // Real method will need to iterate through wallet object
+ const hash160Array = [
+ BCH.Address.toHash160(wallet.Path245.cashAddress), // will be .hash160
+ BCH.Address.toHash160(wallet.Path145.cashAddress),
+ BCH.Address.toHash160(wallet.Path1899.cashAddress),
+ ];
+ console.log(`hash160Array`, hash160Array);
+
+ // Unsubscribe to any active subscriptions
+ const previousWebsocketSubscriptions = ws._subs;
+ console.log(
+ `previousWebsocketSubscriptions`,
+ previousWebsocketSubscriptions,
+ );
+ if (previousWebsocketSubscriptions.length > 0) {
+ for (let i = 0; i < previousWebsocketSubscriptions.length; i += 1) {
+ const unsubHash160 =
+ previousWebsocketSubscriptions[i].scriptPayload;
+ ws.unsubscribe('p2pkh', unsubHash160);
+ console.log(`ws.unsubscribe('p2pkh', ${unsubHash160})`);
+ }
+ }
+
+ // Subscribe to addresses of current wallet
+ for (let i = 0; i < hash160Array.length; i += 1) {
+ ws.subscribe('p2pkh', hash160Array[i]);
+ console.log(`ws.subscribe('p2pkh', ${hash160Array[i]})`);
+ }
+
+ return setChronikWebsocket(ws);
+ };
+
useEffect(async () => {
handleUpdateWallet(setWallet);
await loadContactList();
@@ -1214,6 +1317,10 @@
initializeFiatPriceApi(initialSettings.fiatCurrency);
}, []);
+ useEffect(async () => {
+ await initializeWebsocket(wallet);
+ }, [wallet.mnemonic]);
+
return {
BCH,
wallet,
diff --git a/web/cashtab/src/utils/__mocks__/chronikWebsocketTxs.js b/web/cashtab/src/utils/__mocks__/chronikWebsocketTxs.js
new file mode 100644
--- /dev/null
+++ b/web/cashtab/src/utils/__mocks__/chronikWebsocketTxs.js
@@ -0,0 +1,316 @@
+export const lambdaHash160s = [
+ '58549b5b93428fac88e36617456cd99a411bd0eb',
+ '438a162355ef683062a7fde9d08dd720397aaee8',
+ '76458db0ed96fe9863fc1ccec9fa2cfab884b0f6',
+];
+
+export const lambdaIncomingXecTx = {
+ txid: 'ac83faac54059c89c41dea4c3d6704e4f74fb82e4ad2fb948e640f1d19b760de',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: '783428349b7b040b473ca9720ddbb2eda6fe28db16883ae47f3113b7a0977915',
+ outIdx: 1,
+ },
+ inputScript:
+ '48304502210094c497d6a0ce9ca6d79819467a1bb3953084b2e003ac7edac3b4f0634800baab02205729e229bd96d3a35cece712e3e9ec2d3f610a43d7712928f806983f209fbd72412103318d0e1109f32debc66952d0e3ec21b1cf96575ea4c2a97a6535628f7f8b10e6',
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ value: {
+ low: 517521,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ },
+ ],
+ outputs: [
+ {
+ value: { low: 4200, high: 0, unsigned: false },
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ },
+ {
+ value: {
+ low: 512866,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ },
+ ],
+ lockTime: 0,
+ timeFirstSeen: {
+ low: 1652811898,
+ high: 0,
+ unsigned: false,
+ },
+ network: 'XEC',
+};
+export const lambdaIncomingEtokenTx = {
+ txid: '46cf8bf009dbc6da45045c23af878cd2fd6dd3d3f62bf524d675e75959d5fdbd',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: '51c18b220c2ff1d3ead60c3031316f15ed1c7fa43fbfe563c8227e107f218751',
+ outIdx: 1,
+ },
+ inputScript:
+ '473044022004db23a179194d5e2d8446159859a3e55521239c807f14d4666c772d1493a7d402206d6ea22a4fb8ef20cd6159d200a7292a3ff0181c8d596e7a3e1b9027e6912103412103318d0e1109f32debc66952d0e3ec21b1cf96575ea4c2a97a6535628f7f8b10e6',
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ value: {
+ low: 3891539,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ },
+ {
+ prevOut: {
+ txid: '66f0663e79f6a7fa3bf0834a16b48cb86fa42076c0df25ae89b402d5ee97c311',
+ outIdx: 2,
+ },
+ inputScript:
+ '483045022100c45951e15402b907c419f8a80bd76d374521faf885327ba3e55021345c2eb41902204cdb84e0190a5f671dd049b6b656f6b9e8b57254ec0123308345d5a634802acd412103318d0e1109f32debc66952d0e3ec21b1cf96575ea4c2a97a6535628f7f8b10e6',
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ slpToken: {
+ amount: {
+ low: 240,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ ],
+ outputs: [
+ {
+ value: {
+ low: 0,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript:
+ '6a04534c500001010453454e44204bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c308000000000000000c0800000000000000e4',
+ },
+ {
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ slpToken: {
+ amount: {
+ low: 12,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ {
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ slpToken: {
+ amount: {
+ low: 228,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ {
+ value: {
+ low: 3889721,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ },
+ ],
+ lockTime: 0,
+ slpTxData: {
+ slpMeta: {
+ tokenType: 'FUNGIBLE',
+ txType: 'SEND',
+ tokenId:
+ '4bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c3',
+ },
+ },
+ timeFirstSeen: {
+ low: 1652822000,
+ high: 0,
+ unsigned: false,
+ },
+ network: 'XEC',
+};
+
+export const lambdaOutgoingXecTx = {
+ txid: 'b82a67f929d256c9beb04a850ad735f3b322156cc9df2e37cadc130cc4fab660',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: 'bb161d20f884ce45374fa3f9f1452290a2e52e93c8b552f559fad8ccd1ca33cc',
+ outIdx: 5,
+ },
+ inputScript:
+ '473044022054a6b2065a0b0bbe70048e782aa9be048cc8bee0a241d08d0b98fcd74505a90202201ed5224f34c9ff73dc0c581390247686af521476a977a58e55ed33c4afd177c2412102c237f49dd4c812f27b09d69d4c8a4da12744fda8ad63ce151fed2a3f41fd8795',
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ value: {
+ low: 4400000,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ },
+ ],
+ outputs: [
+ {
+ value: {
+ low: 22200,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ },
+ {
+ value: {
+ low: 4377345,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ },
+ ],
+ lockTime: 0,
+ timeFirstSeen: {
+ low: 1652823464,
+ high: 0,
+ unsigned: false,
+ },
+ network: 'XEC',
+};
+
+export const lambdaOutgoingEtokenTx = {
+ txid: '3d60d2d130eee3e45e6a2d0e88e2ecae82d70c1ed1afc8f62ca9c8564d38108d',
+ version: 2,
+ inputs: [
+ {
+ prevOut: {
+ txid: 'bf7a7d1a063751d8f9c67e88523b3e6ffe8bb133e54ebf3cf500b859adfe16e0',
+ outIdx: 1,
+ },
+ inputScript:
+ '473044022047077b516d8554aba4deb36c66b789b5136bf16657bf1675ae866fd8a62834f5022035a7bd45422e0d0c343ac832a5efb0c05269ebe591ea400a33c23849cfa7c3a0412102c237f49dd4c812f27b09d69d4c8a4da12744fda8ad63ce151fed2a3f41fd8795',
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ value: {
+ low: 450747149,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ },
+ {
+ prevOut: {
+ txid: '66f0663e79f6a7fa3bf0834a16b48cb86fa42076c0df25ae89b402d5ee97c311',
+ outIdx: 1,
+ },
+ inputScript:
+ '47304402203ba0eff663f253805a4ae75fecf5886d7dbaf6369c9e6f0bbf5c114184223fa202207992c5f1a8cb69b552b1af54a75bbab341bfcf90591e535282bd9409981d8464412102c237f49dd4c812f27b09d69d4c8a4da12744fda8ad63ce151fed2a3f41fd8795',
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ sequenceNo: 4294967295,
+ slpToken: {
+ amount: {
+ low: 69,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ ],
+ outputs: [
+ {
+ value: {
+ low: 0,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript:
+ '6a04534c500001010453454e44204bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c3080000000000000011080000000000000034',
+ },
+ {
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a9144e532257c01b310b3b5c1fd947c79a72addf852388ac',
+ slpToken: {
+ amount: {
+ low: 17,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ {
+ value: {
+ low: 546,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ slpToken: {
+ amount: {
+ low: 52,
+ high: 0,
+ unsigned: true,
+ },
+ isMintBaton: false,
+ },
+ },
+ {
+ value: {
+ low: 450745331,
+ high: 0,
+ unsigned: false,
+ },
+ outputScript: '76a91476458db0ed96fe9863fc1ccec9fa2cfab884b0f688ac',
+ },
+ ],
+ lockTime: 0,
+ slpTxData: {
+ slpMeta: {
+ tokenType: 'FUNGIBLE',
+ txType: 'SEND',
+ tokenId:
+ '4bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c3',
+ },
+ },
+ timeFirstSeen: {
+ low: 1652823534,
+ high: 0,
+ unsigned: false,
+ },
+ network: 'XEC',
+};
diff --git a/web/cashtab/src/utils/__tests__/cashMethods.test.js b/web/cashtab/src/utils/__tests__/cashMethods.test.js
--- a/web/cashtab/src/utils/__tests__/cashMethods.test.js
+++ b/web/cashtab/src/utils/__tests__/cashMethods.test.js
@@ -24,6 +24,8 @@
getUtxoCount,
areAllUtxosIncludedInIncrementallyHydratedUtxos,
convertEcashtoEtokenAddr,
+ parseChronikTx,
+ addTypeToLongsInChronikTxDetails,
} from 'utils/cashMethods';
import { currency } from 'components/Common/Ticker';
import {
@@ -156,6 +158,14 @@
incrementallyHydratedUtxosAfterProcessingOneMissing,
} from '../__mocks__/incrementalUtxoMocks';
+import {
+ lambdaHash160s,
+ lambdaIncomingXecTx,
+ lambdaIncomingEtokenTx,
+ lambdaOutgoingXecTx,
+ lambdaOutgoingEtokenTx,
+} from '../__mocks__/chronikWebsocketTxs';
+
describe('Correctly executes cash utility functions', () => {
it(`Correctly converts smallest base unit to smallest decimal for cashDecimals = 2`, () => {
expect(fromSmallestDenomination(1, 2)).toBe(0.01);
@@ -873,4 +883,66 @@
it(`flattenContactList returns an empty array for invalid input`, () => {
expect(flattenContactList(false)).toStrictEqual([]);
});
+ it(`Successfully parses an incoming XEC tx`, () => {
+ expect(
+ parseChronikTx(
+ addTypeToLongsInChronikTxDetails(lambdaIncomingXecTx),
+ lambdaHash160s,
+ ),
+ ).toStrictEqual({
+ incoming: true,
+ xecAmount: '42',
+ isEtokenTx: false,
+ });
+ });
+ it(`Successfully parses an incoming eToken tx`, () => {
+ expect(
+ parseChronikTx(
+ addTypeToLongsInChronikTxDetails(lambdaIncomingEtokenTx),
+ lambdaHash160s,
+ ),
+ ).toStrictEqual({
+ incoming: true,
+ xecAmount: '5.46',
+ isEtokenTx: true,
+ slpMeta: {
+ tokenId:
+ '4bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c3',
+ tokenType: 'FUNGIBLE',
+ txType: 'SEND',
+ },
+ etokenAmount: '12',
+ });
+ });
+ it(`Successfully parses an outgoing XEC tx`, () => {
+ expect(
+ parseChronikTx(
+ addTypeToLongsInChronikTxDetails(lambdaOutgoingXecTx),
+ lambdaHash160s,
+ ),
+ ).toStrictEqual({
+ incoming: false,
+ xecAmount: '222',
+ isEtokenTx: false,
+ });
+ });
+ it(`Successfully parses an outgoing eToken tx`, () => {
+ expect(
+ parseChronikTx(
+ addTypeToLongsInChronikTxDetails(lambdaOutgoingEtokenTx),
+ lambdaHash160s,
+ ),
+ ).toStrictEqual({
+ incoming: false,
+ xecAmount: '5.46',
+ isEtokenTx: true,
+ slpMeta: {
+ tokenId:
+ '4bd147fc5d5ff26249a9299c46b80920c0b81f59a60e05428262160ebee0b0c3',
+ tokenType: 'FUNGIBLE',
+ txType: 'SEND',
+ },
+ etokenAmount: '17',
+ });
+ });
});
diff --git a/web/cashtab/src/utils/cashMethods.js b/web/cashtab/src/utils/cashMethods.js
--- a/web/cashtab/src/utils/cashMethods.js
+++ b/web/cashtab/src/utils/cashMethods.js
@@ -7,6 +7,7 @@
} from 'utils/validation';
import BigNumber from 'bignumber.js';
import cashaddr from 'ecashaddrjs';
+import Long from 'long';
export function parseOpReturn(hexStr) {
if (
@@ -975,3 +976,124 @@
incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch = true;
return incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch;
};
+
+export const parseChronikTx = (tx, walletHash160s) => {
+ // Determine if incoming or outgoing tx
+ // Determine if XEC tx
+ // Determine how much XEC sent or received
+ // Determine how much eToken sent or received
+ // edge cases: minting token txs, airdrops, OP_RETURN msgs
+ const { inputs, outputs } = tx;
+ // Assign defaults
+ let incoming = true;
+ let xecAmount = new BigNumber(0);
+ let etokenAmount = new BigNumber(0);
+ const isEtokenTx = 'slpTxData' in tx && typeof tx.slpTxData !== 'undefined';
+
+ // Iterate over inputs to see if this is a sent tx
+ for (let i = 0; i < inputs.length; i += 1) {
+ const thisInput = inputs[i];
+ const thisInputSendingHash160 = thisInput.outputScript;
+ for (let j = 0; j < walletHash160s.length; j += 1) {
+ const thisWalletHash160 = walletHash160s[j];
+ if (thisInputSendingHash160.includes(thisWalletHash160)) {
+ // Then this is an outgoing tx
+ incoming = false;
+ }
+ }
+ }
+ // Iterate over outputs to get the amount sent
+ for (let i = 0; i < outputs.length; i += 1) {
+ const thisOutput = outputs[i];
+ const thisOutputReceivedAtHash160 = thisOutput.outputScript;
+ // Find amounts at your wallet's addresses
+ for (let j = 0; j < walletHash160s.length; j += 1) {
+ const thisWalletHash160 = walletHash160s[j];
+ if (thisOutputReceivedAtHash160.includes(thisWalletHash160)) {
+ // If incoming tx, this is amount received by the user's wallet
+ // if !incoming, then this is a change amount
+ const thisOutputAmount = new BigNumber(thisOutput.value);
+ xecAmount = incoming
+ ? xecAmount.plus(thisOutputAmount)
+ : xecAmount.minus(thisOutputAmount);
+
+ // Parse token qty if token tx
+ // Note: edge case this is a token tx that sends XEC to recipient but token somewhere else
+ if (isEtokenTx) {
+ try {
+ const thisEtokenAmount = new BigNumber(
+ thisOutput.slpToken.amount,
+ );
+
+ etokenAmount = incoming
+ ? etokenAmount.plus(thisEtokenAmount)
+ : etokenAmount.minus(thisEtokenAmount);
+ } catch (err) {
+ // edge case described above; in this case there is no value, so do nothing
+ etokenAmount.plus(new BigNumber(0));
+ }
+ }
+ }
+ }
+ // Output amounts not at your wallet are sent amounts if !incoming
+ if (!incoming) {
+ const thisOutputAmount = new BigNumber(thisOutput.value);
+ xecAmount = xecAmount.plus(thisOutputAmount);
+ if (isEtokenTx) {
+ try {
+ const thisEtokenAmount = new BigNumber(
+ thisOutput.slpToken.amount,
+ );
+
+ etokenAmount = etokenAmount.plus(thisEtokenAmount);
+ } catch (err) {
+ // edge case where tx is token but this particular output is not
+ etokenAmount.plus(new BigNumber(0));
+ }
+ }
+ }
+ }
+ // Convert from sats to XEC
+ xecAmount = xecAmount.shiftedBy(-2);
+ // Convert from long to string
+ xecAmount = xecAmount.toString();
+ etokenAmount = etokenAmount.toString();
+ if (isEtokenTx) {
+ const { slpMeta } = tx.slpTxData;
+ return {
+ incoming,
+ xecAmount,
+ isEtokenTx,
+ etokenAmount,
+ slpMeta,
+ };
+ }
+ return { incoming, xecAmount, isEtokenTx };
+};
+
+// Chronik api calls return typed Longs, but this typing is lost in copy pasting, storing in mocks, or retrieving from local storage
+export const addTypeToLong = untypedLong => {
+ try {
+ const { low, high, unsigned } = untypedLong;
+ return new Long(low, high, unsigned);
+ } catch (err) {
+ // Not a valid untyped long number
+ return false;
+ }
+};
+
+export const addTypeToLongsInChronikTxDetails = chronikTxDetails => {
+ const { inputs, outputs } = chronikTxDetails;
+ for (let i = 0; i < inputs.length; i += 1) {
+ inputs[i].value = addTypeToLong(inputs[i].value);
+ }
+ for (let i = 0; i < outputs.length; i += 1) {
+ outputs[i].value = addTypeToLong(outputs[i].value);
+ if ('slpToken' in outputs[i]) {
+ outputs[i].slpToken.amount = addTypeToLong(
+ outputs[i].slpToken.amount,
+ );
+ }
+ }
+ return chronikTxDetails;
+};