From 8361583c574005681aeafb7c1ef8c92c3aea4e9a Mon Sep 17 00:00:00 2001 From: bludnic Date: Mon, 27 Nov 2023 20:24:13 +0000 Subject: [PATCH 01/29] refactor(lisk): rewrite `lisk-utils` to TypeScript --- src/lib/lisk/{lisk-utils.js => lisk-utils.ts} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/lib/lisk/{lisk-utils.js => lisk-utils.ts} (57%) diff --git a/src/lib/lisk/lisk-utils.js b/src/lib/lisk/lisk-utils.ts similarity index 57% rename from src/lib/lisk/lisk-utils.js rename to src/lib/lisk/lisk-utils.ts index a61004a52..e34b5d56f 100644 --- a/src/lib/lisk/lisk-utils.js +++ b/src/lib/lisk/lisk-utils.ts @@ -6,7 +6,7 @@ * Returns Millis timestamp by LSK UNIX timestamp (sec) * @param {number} liskTimestamp */ -export function getMillisTimestamp(liskTimestamp) { +export function getMillisTimestamp(liskTimestamp: number) { return parseInt(liskTimestamp) * 1000 } @@ -14,6 +14,6 @@ export function getMillisTimestamp(liskTimestamp) { * Returns LSK timestamp (UNIX in sec) by Millis timestamp * @param {number} millisTimestamp */ -export function getLiskTimestamp(millisTimestamp) { - return Math.round(parseInt(millisTimestamp) / 1000) +export function getLiskTimestamp(millisTimestamp: number) { + return Math.round(parseInt(millisTimestamp) / 1000) // may be a mistake (use Math.floor instead) } From 15be6ba44f289152871540194c8e6022f29b72e2 Mon Sep 17 00:00:00 2001 From: bludnic Date: Mon, 27 Nov 2023 20:35:04 +0000 Subject: [PATCH 02/29] refactor(lisk): move `getAccount()` to `lisk-utils` --- src/components/ExportKeysForm.vue | 2 +- src/lib/lisk/lisk-api.js | 38 +------------------------------ src/lib/lisk/lisk-constants.ts | 6 +++++ src/lib/lisk/lisk-utils.ts | 34 +++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 38 deletions(-) create mode 100644 src/lib/lisk/lisk-constants.ts diff --git a/src/components/ExportKeysForm.vue b/src/components/ExportKeysForm.vue index c759aee89..e9b75a045 100644 --- a/src/components/ExportKeysForm.vue +++ b/src/components/ExportKeysForm.vue @@ -103,7 +103,7 @@ import { validateMnemonic } from 'bip39' import copyToClipboard from 'copy-to-clipboard' import { getAccountFromPassphrase as getEthAccount } from '@/lib/eth-utils' import { getAccount as getBtcAccount } from '@/lib/bitcoin/btc-base-api' -import { getAccount as getLskAccount } from '@/lib/lisk/lisk-api' +import { getAccount as getLskAccount } from '@/lib/lisk/lisk-utils' import { Cryptos, CryptosInfo } from '@/lib/constants' import QrcodeCapture from '@/components/QrcodeCapture' import QrcodeScannerDialog from '@/components/QrcodeScannerDialog' diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js index 59f9eb6dc..c8070c7b8 100644 --- a/src/lib/lisk/lisk-api.js +++ b/src/lib/lisk/lisk-api.js @@ -4,47 +4,11 @@ import { getMillisTimestamp, getLiskTimestamp } from './lisk-utils' import { bytesToHex } from '@/lib/hex' import * as cryptography from '@liskhq/lisk-cryptography' import * as transactions from '@liskhq/lisk-transactions' -import pbkdf2 from 'pbkdf2' -import sodium from 'sodium-browserify-tweetnacl' -import networks from './networks' import { lsk } from '@/lib/nodes/lsk' - -export const LiskHashSettings = { - SALT: 'adm', - ITERATIONS: 2048, - KEYLEN: 32, - DIGEST: 'sha256' -} +import { getAccount } from './lisk-utils' export const TX_CHUNK_SIZE = 25 -export function getAccount(crypto, passphrase) { - const network = networks[crypto] - const liskSeed = pbkdf2.pbkdf2Sync( - passphrase, - LiskHashSettings.SALT, - LiskHashSettings.ITERATIONS, - LiskHashSettings.KEYLEN, - LiskHashSettings.DIGEST - ) - const keyPair = sodium.crypto_sign_seed_keypair(liskSeed) - const addressHexBinary = cryptography.getAddressFromPublicKey(keyPair.publicKey) - const addressHex = bytesToHex(addressHexBinary) - const address = cryptography.getBase32AddressFromPublicKey(keyPair.publicKey) - // Don't work currently https://github.com/LiskHQ/lisk-sdk/issues/6651 - // const addressLegacy = cryptography.getLegacyAddressFromPublicKey(keyPair.publicKey) - // const addressLegacy = cryptography.getLegacyAddressFromPrivateKey(keyPair.secretKey) - const addressLegacy = 'cryptography.getLegacyAddressFromPublicKey(bytesToHex(keyPair.publicKey))' - return { - network, - keyPair, - address, - addressHexBinary, - addressHex, - addressLegacy - } -} - export default class LiskApi extends LskBaseApi { constructor(passphrase) { super(Cryptos.LSK, passphrase) diff --git a/src/lib/lisk/lisk-constants.ts b/src/lib/lisk/lisk-constants.ts new file mode 100644 index 000000000..b222c07fa --- /dev/null +++ b/src/lib/lisk/lisk-constants.ts @@ -0,0 +1,6 @@ +export const LiskHashSettings = { + SALT: 'adm', + ITERATIONS: 2048, + KEYLEN: 32, + DIGEST: 'sha256' +} as const diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index e34b5d56f..8f284137d 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -2,6 +2,13 @@ * Additional coin-based functions here */ +import * as cryptography from '@liskhq/lisk-cryptography' +import networks from '@/lib/lisk/networks' +import pbkdf2 from 'pbkdf2' +import sodium from 'sodium-browserify-tweetnacl' +import { LiskHashSettings } from './lisk-constants' +import { bytesToHex } from '@/lib/hex' + /** * Returns Millis timestamp by LSK UNIX timestamp (sec) * @param {number} liskTimestamp @@ -17,3 +24,30 @@ export function getMillisTimestamp(liskTimestamp: number) { export function getLiskTimestamp(millisTimestamp: number) { return Math.round(parseInt(millisTimestamp) / 1000) // may be a mistake (use Math.floor instead) } + +export function getAccount(crypto, passphrase) { + const network = networks[crypto] + const liskSeed = pbkdf2.pbkdf2Sync( + passphrase, + LiskHashSettings.SALT, + LiskHashSettings.ITERATIONS, + LiskHashSettings.KEYLEN, + LiskHashSettings.DIGEST + ) + const keyPair = sodium.crypto_sign_seed_keypair(liskSeed) + const addressHexBinary = cryptography.getAddressFromPublicKey(keyPair.publicKey) + const addressHex = bytesToHex(addressHexBinary) + const address = cryptography.getBase32AddressFromPublicKey(keyPair.publicKey) + // Don't work currently https://github.com/LiskHQ/lisk-sdk/issues/6651 + // const addressLegacy = cryptography.getLegacyAddressFromPublicKey(keyPair.publicKey) + // const addressLegacy = cryptography.getLegacyAddressFromPrivateKey(keyPair.secretKey) + const addressLegacy = 'cryptography.getLegacyAddressFromPublicKey(bytesToHex(keyPair.publicKey))' + return { + network, + keyPair, + address, + addressHexBinary, + addressHex, + addressLegacy + } +} From 91fff370ff80048f463f17456571c06d9d0e2272 Mon Sep 17 00:00:00 2001 From: bludnic Date: Mon, 27 Nov 2023 20:55:11 +0000 Subject: [PATCH 03/29] fix(`jest`): fix config name in testing environment --- src/config/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/index.js b/src/config/index.js index 5ef6caca0..6464938b7 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -6,7 +6,7 @@ import torConfig from './tor.json' const configMap = { development: devConfig, production: prodConfig, - testing: testingConfig, + test: testingConfig, tor: torConfig } From c29a2204b60d3acad73df7af7881982f35b467b8 Mon Sep 17 00:00:00 2001 From: bludnic Date: Mon, 27 Nov 2023 20:56:46 +0000 Subject: [PATCH 04/29] fix(`jest`): update old structured `test.json` config --- src/config/test.json | 133 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/src/config/test.json b/src/config/test.json index 590f2bbe7..976551ef5 100644 --- a/src/config/test.json +++ b/src/config/test.json @@ -1,35 +1,132 @@ { - "minApiVersion": "0.5.2", - "server": { - "adm": [ + "adm": { + "minNodeVersion": "0.7.0", + "nodes": [ { - "url": "http://163.172.161.218:36667" + "url": "https://clown.adamant.im" }, { - "url": "http://23.226.231.225:36666/" + "url": "https://lake.adamant.im" }, { - "url": "http://78.47.205.206:36666/" + "url": "https://endless.adamant.im", + "alt_ip": "http://149.102.157.15:36666" }, { - "url": "http://5.161.53.74:36666/" + "url": "https://bid.adamant.im" + }, + { + "url": "https://unusual.adamant.im" + }, + { + "url": "https://debate.adamant.im", + "alt_ip": "http://95.216.161.113:36666" + }, + { + "url": "http://78.47.205.206:36666" + }, + { + "url": "http://5.161.53.74:36666" + }, + { + "url": "http://184.94.215.92:45555" + }, + { + "url": "https://node1.adamant.business" + }, + { + "url": "https://node2.blockchain2fa.io" + } + ], + "services": { + "infoService": [ + { + "url": "https://info.adamant.im", + "alt_ip": "http://88.198.156.44:80" + } + ] + }, + "explorerTx": "https://explorer.adamant.im/tx/${ID}", + "explorer": "https://explorer.adamant.im", + "explorerAddress": "https://explorer.adamant.im/address/${ID}" + }, + "btc": { + "nodes": [ + { + "url": "https://btcnode1.adamant.im", + "alt_ip": "http://176.9.38.204:44099" + }, + { + "url": "https://btcnode2.adamant.im", + "alt_ip": "http://176.9.32.126:44099" } ], - "eth": [ + "explorerTx": "https://explorer.btc.com/btc/transaction/${ID}", + "explorer": "https://explorer.btc.com", + "explorerAddress": "https://explorer.btc.com/btc/address/${ID}" + }, + "dash": { + "nodes": [ + { + "url": "https://dashnode1.adamant.im" + } + ], + "explorerTx": "https://dashblockexplorer.com/tx/${ID}", + "explorer": "https://dashblockexplorer.com", + "explorerAddress": "https://dashblockexplorer.com/address/${ID}" + }, + "doge": { + "nodes": [ + { + "url": "https://dogenode1.adamant.im" + }, + { + "url": "https://dogenode2.adamant.im", + "alt_ip": "http://176.9.32.126:44098" + } + ], + "explorerTx": "https://dogechain.info/tx/${ID}", + "explorer": "https://dogechain.info", + "explorerAddress": "https://dogechain.info/address/${ID}" + }, + "eth": { + "nodes": [ { "url": "https://ethnode1.adamant.im", + "alt_ip": "http://95.216.41.106:44099", + "hasIndex": true + }, + { + "url": "https://ethnode2.adamant.im", + "alt_ip": "http://95.216.114.252:44099", "hasIndex": true } ], - "doge": [{ "url": "https://dogenode1.adamant.im/" }], - "dash": [{ "url": "https://dashnode1.adamant.im" }], - "btc": [{ "url": "https://btcnode2.adamant.im" }], - "lsk": [{ "url": "https://lisknode3.adamant.im" }, { "url": "https://lisknode4.adamant.im" }], - "lskservice": [ - { "url": "https://liskservice3.adamant.im" }, - { "url": "https://liskservice4.adamant.im" } - ], - "infoservice": [{ "url": "https://info.adamant.im" }] + "explorerTx": "https://etherscan.io/tx/${ID}", + "explorer": "https://etherscan.io", + "explorerAddress": "https://etherscan.io/address/${ID}" }, - "env": "test" + "lsk": { + "nodes": [ + { + "url": "https://lisknode3.adamant.im" + }, + { + "url": "https://lisknode4.adamant.im" + } + ], + "explorerTx": "https://liskscan.com/transaction/${ID}", + "services": { + "lskService": [ + { + "url": "https://liskservice3.adamant.im" + }, + { + "url": "https://liskservice4.adamant.im" + } + ] + }, + "explorer": "https://liskscan.com", + "explorerAddress": "https://liskscan.com/account/${ID}" + } } From 35cb37ccfffaf5e720017ade33f94745c7df6df1 Mon Sep 17 00:00:00 2001 From: bludnic Date: Mon, 27 Nov 2023 21:17:52 +0000 Subject: [PATCH 05/29] test(): add unit tests for `lisk-utils` --- .../__snapshots__/lisk-utils.spec.ts.snap | 148 ++++++++++++++++++ src/lib/__tests__/lisk/lisk-utils.spec.ts | 16 ++ 2 files changed, 164 insertions(+) create mode 100644 src/lib/__tests__/lisk/__snapshots__/lisk-utils.spec.ts.snap create mode 100644 src/lib/__tests__/lisk/lisk-utils.spec.ts diff --git a/src/lib/__tests__/lisk/__snapshots__/lisk-utils.spec.ts.snap b/src/lib/__tests__/lisk/__snapshots__/lisk-utils.spec.ts.snap new file mode 100644 index 000000000..f787b5dda --- /dev/null +++ b/src/lib/__tests__/lisk/__snapshots__/lisk-utils.spec.ts.snap @@ -0,0 +1,148 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`lisk-utils > getAccount > should generate account from passphrase 1`] = ` +{ + "address": "lskkjurzk3xb47scma49ukyqupn8vrg2ggyuehk5j", + "addressHex": "a721205101325db197296c291ac08778a5fc7ff1", + "addressHexBinary": { + "data": [ + 167, + 33, + 32, + 81, + 1, + 50, + 93, + 177, + 151, + 41, + 108, + 41, + 26, + 192, + 135, + 120, + 165, + 252, + 127, + 241, + ], + "type": "Buffer", + }, + "addressLegacy": "cryptography.getLegacyAddressFromPublicKey(bytesToHex(keyPair.publicKey))", + "keyPair": { + "publicKey": { + "data": [ + 50, + 235, + 138, + 255, + 40, + 214, + 53, + 186, + 3, + 178, + 187, + 108, + 199, + 133, + 7, + 44, + 12, + 1, + 18, + 58, + 36, + 245, + 110, + 88, + 15, + 249, + 199, + 50, + 6, + 17, + 241, + 29, + ], + "type": "Buffer", + }, + "secretKey": { + "data": [ + 252, + 163, + 131, + 181, + 56, + 88, + 159, + 151, + 39, + 66, + 203, + 135, + 3, + 62, + 247, + 137, + 79, + 255, + 122, + 123, + 109, + 82, + 199, + 75, + 221, + 247, + 228, + 247, + 248, + 102, + 10, + 13, + 50, + 235, + 138, + 255, + 40, + 214, + 53, + 186, + 3, + 178, + 187, + 108, + 199, + 133, + 7, + 44, + 12, + 1, + 18, + 58, + 36, + 245, + 110, + 88, + 15, + 249, + 199, + 50, + 6, + 17, + 241, + 29, + ], + "type": "Buffer", + }, + }, + "network": { + "name": "Lisk", + "port": 8000, + "unit": "LSK", + "wsPort": 8001, + }, +} +`; diff --git a/src/lib/__tests__/lisk/lisk-utils.spec.ts b/src/lib/__tests__/lisk/lisk-utils.spec.ts new file mode 100644 index 000000000..2cf6a52e7 --- /dev/null +++ b/src/lib/__tests__/lisk/lisk-utils.spec.ts @@ -0,0 +1,16 @@ +// @vitest-environment node +// Some crypto libs throw errors when using `jsdom` environment + +import { describe, it, expect } from 'vitest' +import { Cryptos } from '@/lib/constants' +import { getAccount } from '@/lib/lisk/lisk-utils' + +const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' + +describe('lisk-utils', () => { + describe('getAccount', () => { + it('should generate account from passphrase', () => { + expect(getAccount(Cryptos.LSK, passphrase)).toMatchSnapshot() + }) + }) +}) From e11a6ce7cc8581649bdb9162ad8ac04645da2feb Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 28 Nov 2023 20:22:59 +0000 Subject: [PATCH 06/29] fix(`lisk-utils`): Lisk API changed --- src/lib/lisk/lisk-utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 8f284137d..540ca8535 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -35,9 +35,9 @@ export function getAccount(crypto, passphrase) { LiskHashSettings.DIGEST ) const keyPair = sodium.crypto_sign_seed_keypair(liskSeed) - const addressHexBinary = cryptography.getAddressFromPublicKey(keyPair.publicKey) + const addressHexBinary = cryptography.address.getAddressFromPublicKey(keyPair.publicKey) const addressHex = bytesToHex(addressHexBinary) - const address = cryptography.getBase32AddressFromPublicKey(keyPair.publicKey) + const address = cryptography.address.getLisk32AddressFromPublicKey(keyPair.publicKey) // Don't work currently https://github.com/LiskHQ/lisk-sdk/issues/6651 // const addressLegacy = cryptography.getLegacyAddressFromPublicKey(keyPair.publicKey) // const addressLegacy = cryptography.getLegacyAddressFromPrivateKey(keyPair.secretKey) From a1613ac496d6dcb69d18a5dfd8e7ee89a501081b Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 29 Nov 2023 07:13:20 +0000 Subject: [PATCH 07/29] Draft --- package-lock.json | 124 ++++++++++++------ package.json | 4 +- .../lisk/__snapshots__/lisk-api.spec.ts.snap | 50 +++++++ src/lib/__tests__/lisk/lisk-api.spec.ts | 27 ++++ src/lib/lisk/lisk-api.js | 50 +++++-- src/lib/lisk/lisk-utils.ts | 35 +++++ src/lib/lisk/lsk-base-api.js | 8 +- 7 files changed, 241 insertions(+), 57 deletions(-) create mode 100644 src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap create mode 100644 src/lib/__tests__/lisk/lisk-api.spec.ts diff --git a/package-lock.json b/package-lock.json index f08a71fd3..e2d8cc317 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "license": "GPLv3", "dependencies": { "@emoji-mart/data": "^1.1.2", - "@liskhq/lisk-cryptography": "3.2.1", - "@liskhq/lisk-transactions": "5.2.2", + "@liskhq/lisk-cryptography": "4.0.0", + "@liskhq/lisk-transactions": "6.0.0", "@mdi/font": "^7.3.67", "@stablelib/utf8": "^1.0.1", "@zxing/browser": "^0.1.4", @@ -3489,83 +3489,121 @@ } }, "node_modules/@liskhq/lisk-codec": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-codec/-/lisk-codec-0.2.2.tgz", - "integrity": "sha512-TrjZYBrhKLNBuPIG0hIKVzb2UddzmhjWNvQE3RQWuBcLK1BbnSr7Zz7r0B1Ta2Rrd1+gJNjF1U6SkXAcqEkw1A==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-codec/-/lisk-codec-0.4.0.tgz", + "integrity": "sha512-03mgvftrWK5KfjBl/VSceEBtcr2wA4NLGnuBsoxKiwL/Bolfd1f3rdXbQgUKzeY4UJEoDGHN6jRPbXLHH/yUnw==", "dependencies": { - "@liskhq/lisk-utils": "^0.2.1", - "@liskhq/lisk-validator": "^0.6.2" + "@liskhq/lisk-cryptography": "^4.0.0", + "@liskhq/lisk-utils": "^0.4.0", + "@liskhq/lisk-validator": "^0.8.0" }, "engines": { - "node": ">=16.14.1 <=16", + "node": ">=18.12.0 <=18", "npm": ">=8.1.0" } }, "node_modules/@liskhq/lisk-cryptography": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-cryptography/-/lisk-cryptography-3.2.1.tgz", - "integrity": "sha512-n1yoVXSzKj9ZeVMvOmVkpZwyRBcvInu0sXBB7Z7d1bsgAiTSGKJDz6TVGWTwpEkfn0kk79Sie2QBbulaON57YQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-cryptography/-/lisk-cryptography-4.0.0.tgz", + "integrity": "sha512-246hoey+sKx0/2GrECXfsjTmhv5PaD0z/vYpV1zTL4qPOHaDNqbKsSLg/uAt2OfM7et1KoG76jpYzaiifqCJiw==", "dependencies": { + "@liskhq/lisk-passphrase": "^4.0.0", "buffer-reverse": "1.0.1", - "ed2curve": "0.3.0", - "tweetnacl": "1.0.3", - "varuint-bitcoin": "1.1.2" + "hash-wasm": "4.9.0", + "tweetnacl": "1.0.3" }, "engines": { - "node": ">=16.14.1 <=16", + "node": ">=18.12.0 <=18", "npm": ">=8.1.0" }, - "optionalDependencies": { + "peerDependencies": { + "@chainsafe/blst": "0.2.9", "sodium-native": "3.2.1" + }, + "peerDependenciesMeta": { + "@chainsafe/blst": { + "optional": true + }, + "sodium-native": { + "optional": true + } + } + }, + "node_modules/@liskhq/lisk-passphrase": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-passphrase/-/lisk-passphrase-4.0.0.tgz", + "integrity": "sha512-bohEKYtKSqMHyajWqQV3fES5Ejto5gUhAem7fZkLZpgyBIKfZt2xjjdVZRINuvvUixzNBvqpqwF3LQvAJl7SSw==", + "dependencies": { + "bip39": "3.0.3" + }, + "engines": { + "node": ">=18.12.0 <=18", + "npm": ">=8.1.0" + } + }, + "node_modules/@liskhq/lisk-passphrase/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, + "node_modules/@liskhq/lisk-passphrase/node_modules/bip39": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.3.tgz", + "integrity": "sha512-P0dKrz4g0V0BjXfx7d9QNkJ/Txcz/k+hM9TnjqjUaXtuOfAvxXSw2rJw8DX0e3ZPwnK/IgDxoRqf0bvoVCqbMg==", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" } }, "node_modules/@liskhq/lisk-transactions": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-transactions/-/lisk-transactions-5.2.2.tgz", - "integrity": "sha512-jmwTK15y/RjMQCHSJqbDECTZUR/UKxpp4xYiQTqtoxy7TAztrphurnVZXSwtW52/vEVNCSGA8GCHfgCjtBqgAA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-transactions/-/lisk-transactions-6.0.0.tgz", + "integrity": "sha512-HnvPxtoglIR1qHohIXg7K9HCiU0naMcz2+wZUH1jwgDwugCNaypLSTNat1dI7/5MHn8MpohHun0a1tWmJZYH/A==", "dependencies": { - "@liskhq/lisk-codec": "^0.2.2", - "@liskhq/lisk-cryptography": "^3.2.1", - "@liskhq/lisk-validator": "^0.6.2" + "@liskhq/lisk-codec": "^0.4.0", + "@liskhq/lisk-cryptography": "^4.0.0", + "@liskhq/lisk-validator": "^0.8.0" }, "engines": { - "node": ">=16.14.1 <=16", + "node": ">=18.12.0 <=18", "npm": ">=8.1.0" } }, "node_modules/@liskhq/lisk-utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-utils/-/lisk-utils-0.2.1.tgz", - "integrity": "sha512-2vPLoCRVJVYEh8KUePBj/qBrD3i9aM5g3lhimn+RiEweKQSpBBe+BGcBIuNeKtCJtm2A0ChVjSXJV39t7nNh+A==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-utils/-/lisk-utils-0.4.0.tgz", + "integrity": "sha512-rzZZrh36GVSqB65Y4LjaZIe2AVLUH8zXNDNBX0MXM7pI5BhBesDmDpVS2qVLoF7lhyBmUPk9SrARI0wK9/+z9g==", "dependencies": { "lodash.clonedeep": "4.5.0" }, "engines": { - "node": ">=16.14.1 <=16", + "node": ">=18.12.0 <=18", "npm": ">=8.1.0" } }, "node_modules/@liskhq/lisk-validator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-validator/-/lisk-validator-0.6.2.tgz", - "integrity": "sha512-BUqi74EqnrTtpVsjGVgD4et8/nJdA/Oau9tK+SeD2vCrzwA09+AI8Eow5d4arJMcvtjOeNbgX7FApJs8MQ38Xw==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-validator/-/lisk-validator-0.8.0.tgz", + "integrity": "sha512-aChkjwuwBBzvDIQhyCRozOvQ0fTWjzAOLZJi3i53+1op6J3f6LZYG6ffA6q/x6W1/tQbApSoc2GJWrSCjmFpFw==", "dependencies": { - "@liskhq/lisk-cryptography": "^3.2.1", + "@liskhq/lisk-cryptography": "^4.0.0", "ajv": "8.1.0", - "ajv-formats": "2.0.2", + "ajv-formats": "2.1.1", "debug": "4.3.4", - "semver": "7.3.5", + "semver": "7.5.2", "validator": "13.7.0" }, "engines": { - "node": ">=16.14.1 <=16", + "node": ">=18.12.0 <=18", "npm": ">=8.1.0" } }, "node_modules/@liskhq/lisk-validator/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5528,9 +5566,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.0.2.tgz", - "integrity": "sha512-Brah4Uo5/U8v76c6euTwtjVFFaVishwnJrQBYpev1JRh4vjA1F4HY3UzQez41YUCszUCXKagG8v6eVRBHV1gkw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { "ajv": "^8.0.0" }, @@ -10334,6 +10372,11 @@ "node": ">=4" } }, + "node_modules/hash-wasm": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.9.0.tgz", + "integrity": "sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w==" + }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -15111,6 +15154,7 @@ "integrity": "sha512-EgDZ/Z7PxL2kCasKk7wnRkV8W9kvwuIlHuHXAxkQm3FF0MgVsjyLBXGjSRGhjE6u7rhSpk3KaMfFM23bfMysIQ==", "hasInstallScript": true, "optional": true, + "peer": true, "dependencies": { "ini": "^1.3.5", "node-gyp-build": "^4.2.0" diff --git a/package.json b/package.json index 4bb5e1cb2..40414a208 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,8 @@ }, "dependencies": { "@emoji-mart/data": "^1.1.2", - "@liskhq/lisk-cryptography": "3.2.1", - "@liskhq/lisk-transactions": "5.2.2", + "@liskhq/lisk-cryptography": "4.0.0", + "@liskhq/lisk-transactions": "6.0.0", "@mdi/font": "^7.3.67", "@stablelib/utf8": "^1.0.1", "@zxing/browser": "^0.1.4", diff --git a/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap b/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap new file mode 100644 index 000000000..0ae1e7ed4 --- /dev/null +++ b/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap @@ -0,0 +1,50 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`lisk-api > createTransaction 1`] = ` +{ + "hex": { + "asset": { + "amount": "100000000", + "data": "", + "recipientAddress": "a721205101325db197296c291ac08778a5fc7ff1", + }, + "assetID": 0, + "fee": "141000", + "moduleID": 2, + "nonce": "3", + "senderPublicKey": "32eb8aff28d635ba03b2bb6cc785072c0c01123a24f56e580ff9c7320611f11d", + "signatures": [ + "da4c262f70ad97e62c8e6209a65af08b095a450ff9cc0100cbd039777070ce3fe7fbdfc267e5db28a8e66c46642fe515ace88a31da9eebf79432e0d5f7f0e30d", + ], + }, + "txid": "437654f398820a7c01c2231dd5752e8198b2a613b7423e8eba034fcbf2aa8de2", +} +`; + +exports[`lisk-api > lisk.cryptography.getAddressFromBase32Address 1`] = ` +{ + "data": [ + 167, + 33, + 32, + 81, + 1, + 50, + 93, + 177, + 151, + 41, + 108, + 41, + 26, + 192, + 135, + 120, + 165, + 252, + 127, + 241, + ], + "type": "Buffer", +} +`; diff --git a/src/lib/__tests__/lisk/lisk-api.spec.ts b/src/lib/__tests__/lisk/lisk-api.spec.ts new file mode 100644 index 000000000..9a77d5825 --- /dev/null +++ b/src/lib/__tests__/lisk/lisk-api.spec.ts @@ -0,0 +1,27 @@ +// @vitest-environment node +// Some crypto libs throw errors when using `jsdom` environment + +import { describe, expect, it } from 'vitest' +import * as cryptography from '@liskhq/lisk-cryptography' +import LiskApi, { getAccount } from '@/lib/lisk/lisk-api' + +describe('lisk-api', () => { + const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' + const lskAddress = 'lskkjurzk3xb47scma49ukyqupn8vrg2ggyuehk5j' + + it('createTransaction', async () => { + const api = new LiskApi(passphrase) + + const transaction = await api.createTransaction(lskAddress, 1, 0.00141, 3) + + expect(transaction).toMatchSnapshot() + }) + + it('lisk.cryptography.getAddressFromBase32Address', () => { + expect(cryptography.address.getAddressFromLisk32Address(lskAddress)).toMatchSnapshot() + }) + + it('lisk.cryptography.validateBase32Address', () => { + expect(cryptography.address.validateLisk32Address(lskAddress)).toBe(true) + }) +}) diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js index c8070c7b8..981195f0f 100644 --- a/src/lib/lisk/lisk-api.js +++ b/src/lib/lisk/lisk-api.js @@ -1,10 +1,10 @@ import LskBaseApi from './lsk-base-api' import { Cryptos } from '../constants' -import { getMillisTimestamp, getLiskTimestamp } from './lisk-utils' +import { getMillisTimestamp, getLiskTimestamp, createUnsignedTransaction } from './lisk-utils' import { bytesToHex } from '@/lib/hex' import * as cryptography from '@liskhq/lisk-cryptography' import * as transactions from '@liskhq/lisk-transactions' -import { lsk } from '@/lib/nodes/lsk' +// import { lsk } from '@/lib/nodes/lsk' import { getAccount } from './lisk-utils' export const TX_CHUNK_SIZE = 25 @@ -19,6 +19,7 @@ export default class LiskApi extends LskBaseApi { this._addressHexBinary = account.addressHexBinary this._addressHex = account.addressHex this._addressLegacy = account.addressLegacy + this.passphrase = passphrase } /** @@ -93,28 +94,49 @@ export default class LiskApi extends LskBaseApi { * @returns {Promise<{hex: string, txid: string}>} */ createTransaction(address = '', amount = 0, fee, nonce, data = '') { - const liskTx = this._buildTransaction(address, amount, fee, nonce, data).liskTx + console.log(address, amount, fee, nonce, data) + // throw new Error('stop2') + // const unsignedTransaction = this._buildTransaction(address, amount, fee, nonce, data).liskTx // unsignedTx + const unsignedTransaction = createUnsignedTransaction( + address, + this._keyPair.publicKey, + amount, + fee, + nonce, + data + ) // To use transactions.signTransaction, passPhrase is necessary // So we'll use cryptography.signDataWithPrivateKey - const liskTxBytes = transactions.getSigningBytes(this.assetSchema, liskTx) + console.log('liskTx', unsignedTransaction) + + const liskTxBytes = transactions.getSigningBytes(unsignedTransaction, this.assetSchema) + console.log('liskTxBytes', liskTxBytes) const txSignature = cryptography.signDataWithPrivateKey( Buffer.concat([this.networkIdentifier, liskTxBytes]), this._keyPair.secretKey ) - liskTx.signatures[0] = txSignature - const txid = bytesToHex(cryptography.hash(transactions.getBytes(this.assetSchema, liskTx))) + // console.log('chainId', Buffer.from(this.chainId, 'hex')) + // const networkIdTestnet = '15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c'; + // const signedTransaction = transactions.signTransaction( + // unsignedTransaction, + // Buffer.from(networkIdTestnet, 'hex'), + // this._keyPair.secretKey + // ) + // console.log('passed', signedTransaction) + + // const txid = signedTransaction.id // To send Tx to node's core API, we should change data types - liskTx.senderPublicKey = bytesToHex(liskTx.senderPublicKey) - liskTx.nonce = nonce.toString() - liskTx.fee = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) - liskTx.asset.amount = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) - liskTx.asset.recipientAddress = bytesToHex(liskTx.asset.recipientAddress) - liskTx.signatures[0] = bytesToHex(txSignature) - - return Promise.resolve({ hex: liskTx, txid }) + // liskTx.senderPublicKey = bytesToHex(liskTx.senderPublicKey) + // liskTx.nonce = nonce.toString() + // liskTx.fee = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) + // liskTx.asset.amount = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) + // liskTx.asset.recipientAddress = bytesToHex(liskTx.asset.recipientAddress) + // liskTx.signatures[0] = bytesToHex(txSignature) + + return Promise.resolve({ hex: signedTransaction, txid }) } /** @override */ diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 540ca8535..0be5d4d04 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -4,6 +4,7 @@ import * as cryptography from '@liskhq/lisk-cryptography' import networks from '@/lib/lisk/networks' +import * as transactions from '@liskhq/lisk-transactions' import pbkdf2 from 'pbkdf2' import sodium from 'sodium-browserify-tweetnacl' import { LiskHashSettings } from './lisk-constants' @@ -51,3 +52,37 @@ export function getAccount(crypto, passphrase) { addressLegacy } } + +// @todo derivate publicKey from address +export function createUnsignedTransaction(address: string, publicKey, amount, fee, nonce, data = '') { + const amountString = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) + const feeString = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) + const nonceString = nonce.toString() + + // Adjust the values of the unsigned transaction manually + const unsignedTransaction = { + module: 'token', + command: 'transfer', + fee: BigInt(feeString), + nonce: BigInt(nonceString), + senderPublicKey: Buffer.from(publicKey, 'hex'), + params: Buffer.alloc(0), + signatures: [] + } + + // Create the asset for the Token Transfer transaction + const transferParams = { + tokenID: Buffer.from('0000000100000000', 'hex'), + amount: BigInt(amountString), + recipientAddress: cryptography.address.getAddressFromLisk32Address(address), + data + } + + // Add the transaction params to the transaction object + unsignedTransaction.params = transferParams + + // @todo remove + // const minFee = Number(transactions.computeMinFee(this.assetSchema, liskTx)) / this.multiplier + + return unsignedTransaction +} diff --git a/src/lib/lisk/lsk-base-api.js b/src/lib/lisk/lsk-base-api.js index b56e9b8c4..1742986c5 100644 --- a/src/lib/lisk/lsk-base-api.js +++ b/src/lib/lisk/lsk-base-api.js @@ -1,3 +1,4 @@ +import { bytesToHex } from '@/lib/hex' import axios from 'axios' import { getRandomServiceUrl } from '@/config/utils' import { isStringEqualCI } from '@/lib/textHelpers' @@ -47,6 +48,10 @@ export default class LskBaseApi { return 2 } + get chainId() { + return '00000001' + } + /** * Get asset Id * @abstract @@ -77,6 +82,7 @@ export default class LskBaseApi { // Testnet: '15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c' // Mainnet: '4c09e6a781fc4c7bdb936ee815de8f94190f8a7519becd9de2081832be309a99' const networkIdentifier = '4c09e6a781fc4c7bdb936ee815de8f94190f8a7519becd9de2081832be309a99' + console.log('chainID', bytesToHex(Buffer.from('00000001', 'hex'))) return Buffer.from(networkIdentifier, 'hex') } @@ -123,7 +129,7 @@ export default class LskBaseApi { fee: BigInt(feeString), asset: { amount: BigInt(amountString), - recipientAddress: cryptography.getAddressFromBase32Address(address), + recipientAddress: cryptography.address.getAddressFromLisk32Address(address), data // data: 'Sent with ADAMANT Messenger' }, From 40d004ee591bf39f9522a4adcfaf23101dcadd51 Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 29 Nov 2023 10:18:03 +0000 Subject: [PATCH 08/29] Draft2 --- src/components/LoginForm.vue | 3 ++- src/lib/lisk/lisk-api.js | 38 ++++++++++++++++++++++++++++++------ src/lib/lisk/lisk-utils.ts | 10 ++++++++-- src/lib/validateAddress.js | 5 +++-- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 90c08603f..90ed4842d 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -97,7 +97,8 @@ export default defineComponent({ .then(() => { emit('login') }) - .catch(() => { + .catch((err) => { + console.log(err) emit('error', 'login.invalid_passphrase') }) .finally(() => { diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js index 981195f0f..7b7b491de 100644 --- a/src/lib/lisk/lisk-api.js +++ b/src/lib/lisk/lisk-api.js @@ -4,7 +4,7 @@ import { getMillisTimestamp, getLiskTimestamp, createUnsignedTransaction } from import { bytesToHex } from '@/lib/hex' import * as cryptography from '@liskhq/lisk-cryptography' import * as transactions from '@liskhq/lisk-transactions' -// import { lsk } from '@/lib/nodes/lsk' +import { lsk } from '@/lib/nodes/lsk' import { getAccount } from './lisk-utils' export const TX_CHUNK_SIZE = 25 @@ -112,10 +112,17 @@ export default class LiskApi extends LskBaseApi { const liskTxBytes = transactions.getSigningBytes(unsignedTransaction, this.assetSchema) console.log('liskTxBytes', liskTxBytes) - const txSignature = cryptography.signDataWithPrivateKey( - Buffer.concat([this.networkIdentifier, liskTxBytes]), - this._keyPair.secretKey + + const chainID = Buffer.from('00000000', 'hex') + const signedTransaction = transactions.signTransaction( + unsignedTransaction, + this.networkIdentifier, // @todo or chainID? + this._keyPair.secretKey, + this.assetSchema ) + console.log('signedTransaction', signedTransaction) + + // const json = toTransactionJSON(signedTransaction) // console.log('chainId', Buffer.from(this.chainId, 'hex')) // const networkIdTestnet = '15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c'; @@ -126,7 +133,26 @@ export default class LiskApi extends LskBaseApi { // ) // console.log('passed', signedTransaction) - // const txid = signedTransaction.id + const txid = bytesToHex(signedTransaction.id) + + const signeTransactionHex = { + ...signedTransaction, + id: txid, + nonce: Number(signedTransaction.nonce).toString(), + fee: Number(signedTransaction.fee).toString(), + params: { + ...signedTransaction.params, + amount: Number(signedTransaction.params.amount).toString(), + recipientAddress: Buffer.from(signedTransaction.params.recipientAddress).toString('hex'), + tokenID: Buffer.from(signedTransaction.params.tokenID).toString('hex') + }, + senderPublicKey: Buffer.from(signedTransaction.senderPublicKey).toString('hex'), + signatures: signedTransaction.signatures.map((signature) => + Buffer.from(signature).toString('hex') + ) + } + + console.log('txid', txid) // To send Tx to node's core API, we should change data types // liskTx.senderPublicKey = bytesToHex(liskTx.senderPublicKey) @@ -136,7 +162,7 @@ export default class LiskApi extends LskBaseApi { // liskTx.asset.recipientAddress = bytesToHex(liskTx.asset.recipientAddress) // liskTx.signatures[0] = bytesToHex(txSignature) - return Promise.resolve({ hex: signedTransaction, txid }) + return Promise.resolve({ hex: signeTransactionHex, txid }) } /** @override */ diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 0be5d4d04..517c47492 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -55,8 +55,10 @@ export function getAccount(crypto, passphrase) { // @todo derivate publicKey from address export function createUnsignedTransaction(address: string, publicKey, amount, fee, nonce, data = '') { - const amountString = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) - const feeString = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) + const decimals = 8 + + const amountString = transactions.convertLSKToBeddows((+amount).toFixed(decimals)) + const feeString = transactions.convertLSKToBeddows((+fee).toFixed(decimals)) const nonceString = nonce.toString() // Adjust the values of the unsigned transaction manually @@ -86,3 +88,7 @@ export function createUnsignedTransaction(address: string, publicKey, amount, fe return unsignedTransaction } + +export function createOfflineTransaction() { + +} diff --git a/src/lib/validateAddress.js b/src/lib/validateAddress.js index 3886fae01..261ac53ab 100644 --- a/src/lib/validateAddress.js +++ b/src/lib/validateAddress.js @@ -1,6 +1,6 @@ import { isAddress as isEthAddress, isHexStrict } from 'web3-utils' import { isValidAddress as isValidBtcAddress } from './bitcoin/bitcoin-utils' -import { validateBase32Address as isLskAddress } from '@liskhq/lisk-cryptography' +import * as lskCryptography from '@liskhq/lisk-cryptography' import { Cryptos, CryptosInfo, isEthBased } from './constants' /** @@ -18,8 +18,9 @@ export default function validateAddress(crypto, address) { if (crypto === Cryptos.LSK) { // We need to use try-catch https://github.com/LiskHQ/lisk-sdk/issues/6652 try { - return isLskAddress(address) + return lskCryptography.address.validateLisk32Address(address) } catch (e) { + console.log(e) return false } } From 58434f7bf778cf0a246d064e2cfbd38e0fe8812c Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 29 Nov 2023 14:52:20 +0000 Subject: [PATCH 09/29] Refactor --- package-lock.json | 2 + package.json | 2 + src/lib/lisk/lisk-api.js | 23 ++++++++--- src/lib/lisk/lisk-schemas.ts | 74 ++++++++++++++++++++++++++++++++++++ src/lib/lisk/lisk-utils.ts | 53 +++++++++++++++++++++++--- 5 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 src/lib/lisk/lisk-schemas.ts diff --git a/package-lock.json b/package-lock.json index e2d8cc317..7908e38f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,10 @@ "license": "GPLv3", "dependencies": { "@emoji-mart/data": "^1.1.2", + "@liskhq/lisk-codec": "^0.4.0", "@liskhq/lisk-cryptography": "4.0.0", "@liskhq/lisk-transactions": "6.0.0", + "@liskhq/lisk-validator": "^0.8.0", "@mdi/font": "^7.3.67", "@stablelib/utf8": "^1.0.1", "@zxing/browser": "^0.1.4", diff --git a/package.json b/package.json index 40414a208..922bc0bf7 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,10 @@ }, "dependencies": { "@emoji-mart/data": "^1.1.2", + "@liskhq/lisk-codec": "^0.4.0", "@liskhq/lisk-cryptography": "4.0.0", "@liskhq/lisk-transactions": "6.0.0", + "@liskhq/lisk-validator": "^0.8.0", "@mdi/font": "^7.3.67", "@stablelib/utf8": "^1.0.1", "@zxing/browser": "^0.1.4", diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js index 7b7b491de..11387c6cc 100644 --- a/src/lib/lisk/lisk-api.js +++ b/src/lib/lisk/lisk-api.js @@ -1,8 +1,15 @@ +import { TRANSACTION_PARAMS_SCHEMA, TRANSACTION_SCHEMA } from '@/lib/lisk/lisk-schemas' import LskBaseApi from './lsk-base-api' import { Cryptos } from '../constants' -import { getMillisTimestamp, getLiskTimestamp, createUnsignedTransaction } from './lisk-utils' +import { + getMillisTimestamp, + getLiskTimestamp, + createUnsignedTransaction, + encodeTransaction +} from './lisk-utils' import { bytesToHex } from '@/lib/hex' import * as cryptography from '@liskhq/lisk-cryptography' +import * as codec from '@liskhq/lisk-codec' import * as transactions from '@liskhq/lisk-transactions' import { lsk } from '@/lib/nodes/lsk' import { getAccount } from './lisk-utils' @@ -99,7 +106,7 @@ export default class LiskApi extends LskBaseApi { // const unsignedTransaction = this._buildTransaction(address, amount, fee, nonce, data).liskTx // unsignedTx const unsignedTransaction = createUnsignedTransaction( address, - this._keyPair.publicKey, + Buffer.from(this._keyPair.publicKey).toString('hex'), amount, fee, nonce, @@ -110,15 +117,14 @@ export default class LiskApi extends LskBaseApi { // So we'll use cryptography.signDataWithPrivateKey console.log('liskTx', unsignedTransaction) - const liskTxBytes = transactions.getSigningBytes(unsignedTransaction, this.assetSchema) + const liskTxBytes = transactions.getSigningBytes(unsignedTransaction, TRANSACTION_PARAMS_SCHEMA) console.log('liskTxBytes', liskTxBytes) - const chainID = Buffer.from('00000000', 'hex') const signedTransaction = transactions.signTransaction( unsignedTransaction, this.networkIdentifier, // @todo or chainID? this._keyPair.secretKey, - this.assetSchema + TRANSACTION_PARAMS_SCHEMA ) console.log('signedTransaction', signedTransaction) @@ -154,6 +160,11 @@ export default class LiskApi extends LskBaseApi { console.log('txid', txid) + console.log('signeTransactionHex', signeTransactionHex) + console.log('encodeTransaction', encodeTransaction(signedTransaction)) + + // throw new Error('stop') + // To send Tx to node's core API, we should change data types // liskTx.senderPublicKey = bytesToHex(liskTx.senderPublicKey) // liskTx.nonce = nonce.toString() @@ -162,7 +173,7 @@ export default class LiskApi extends LskBaseApi { // liskTx.asset.recipientAddress = bytesToHex(liskTx.asset.recipientAddress) // liskTx.signatures[0] = bytesToHex(txSignature) - return Promise.resolve({ hex: signeTransactionHex, txid }) + return Promise.resolve({ hex: encodeTransaction(signedTransaction), txid }) } /** @override */ diff --git a/src/lib/lisk/lisk-schemas.ts b/src/lib/lisk/lisk-schemas.ts new file mode 100644 index 000000000..eaecdb244 --- /dev/null +++ b/src/lib/lisk/lisk-schemas.ts @@ -0,0 +1,74 @@ +export const TRANSACTION_SCHEMA = { + $id: '/lisk/transaction', + type: 'object', + required: ['module', 'command', 'nonce', 'fee', 'senderPublicKey', 'params'], + properties: { + module: { + dataType: 'string', + fieldNumber: 1, + minLength: 1, + maxLength: 32 + }, + command: { + dataType: 'string', + fieldNumber: 2, + minLength: 1, + maxLength: 32 + }, + nonce: { + dataType: 'uint64', + fieldNumber: 3 + }, + fee: { + dataType: 'uint64', + fieldNumber: 4 + }, + senderPublicKey: { + dataType: 'bytes', + fieldNumber: 5, + minLength: 32, + maxLength: 32 + }, + params: { + dataType: 'bytes', + fieldNumber: 6 + }, + signatures: { + type: 'array', + items: { + dataType: 'bytes' + }, + fieldNumber: 7 + } + } +} + +export const TRANSACTION_PARAMS_SCHEMA = { + $id: '/lisk/transferParams', + title: 'Transfer transaction params', + type: 'object', + required: ['tokenID', 'amount', 'recipientAddress', 'data'], + properties: { + tokenID: { + dataType: 'bytes', + fieldNumber: 1, + minLength: 8, + maxLength: 8 + }, + amount: { + dataType: 'uint64', + fieldNumber: 2 + }, + recipientAddress: { + dataType: 'bytes', + fieldNumber: 3, + format: 'lisk32' + }, + data: { + dataType: 'string', + fieldNumber: 4, + minLength: 0, + maxLength: 64 + } + } +} diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 517c47492..87872d10f 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -2,13 +2,18 @@ * Additional coin-based functions here */ +import { TRANSACTION_PARAMS_SCHEMA, TRANSACTION_SCHEMA } from '@/lib/lisk/lisk-schemas' +import * as codec from '@liskhq/lisk-codec' import * as cryptography from '@liskhq/lisk-cryptography' import networks from '@/lib/lisk/networks' +import { getLisk32AddressFromPublicKey } from '@liskhq/lisk-cryptography/dist-node/address' import * as transactions from '@liskhq/lisk-transactions' +import { Buffer } from 'buffer' import pbkdf2 from 'pbkdf2' import sodium from 'sodium-browserify-tweetnacl' import { LiskHashSettings } from './lisk-constants' import { bytesToHex } from '@/lib/hex' +import * as validator from '@liskhq/lisk-validator' /** * Returns Millis timestamp by LSK UNIX timestamp (sec) @@ -53,10 +58,32 @@ export function getAccount(crypto, passphrase) { } } -// @todo derivate publicKey from address -export function createUnsignedTransaction(address: string, publicKey, amount, fee, nonce, data = '') { +/** + * Creates unsigned LSK transaction. + * + * @param recipientAddress Recipient address (Lisk32 string hex format) + * @param senderPublicKey Sender public key (string hex) + * @param amount Amount of LSK to send + * @param fee Transaction fee + * @param nonce Last nonce + * @param data A.k.a note + */ +export function createUnsignedTransaction( + recipientAddress: string, + senderPublicKey: string, + amount: number | string, + fee: number | string, + nonce: number | string, + data = '' +) { const decimals = 8 + console.log('address', recipientAddress) + console.log('publicKeyHex', senderPublicKey) + console.log('nonce', nonce) + + // throw new Error('stop') + const amountString = transactions.convertLSKToBeddows((+amount).toFixed(decimals)) const feeString = transactions.convertLSKToBeddows((+fee).toFixed(decimals)) const nonceString = nonce.toString() @@ -67,16 +94,18 @@ export function createUnsignedTransaction(address: string, publicKey, amount, fe command: 'transfer', fee: BigInt(feeString), nonce: BigInt(nonceString), - senderPublicKey: Buffer.from(publicKey, 'hex'), + senderPublicKey: Buffer.from(senderPublicKey, 'hex'), params: Buffer.alloc(0), signatures: [] } + validator.validator.validate(TRANSACTION_SCHEMA, unsignedTransaction) + // Create the asset for the Token Transfer transaction const transferParams = { tokenID: Buffer.from('0000000100000000', 'hex'), amount: BigInt(amountString), - recipientAddress: cryptography.address.getAddressFromLisk32Address(address), + recipientAddress: cryptography.address.getAddressFromLisk32Address(recipientAddress), data } @@ -86,9 +115,23 @@ export function createUnsignedTransaction(address: string, publicKey, amount, fe // @todo remove // const minFee = Number(transactions.computeMinFee(this.assetSchema, liskTx)) / this.multiplier + // only tx.params will be validated + transactions.validateTransaction(unsignedTransaction, TRANSACTION_PARAMS_SCHEMA) + + // throw new Error('stop') + return unsignedTransaction } -export function createOfflineTransaction() { +/** + * Encode transaction for later broadcasting to a node. + */ +export function encodeTransaction(signedTransaction: Record) { + const transaction = codec.codec.toJSON(TRANSACTION_SCHEMA, signedTransaction) + const params = codec.codec.toJSON(TRANSACTION_PARAMS_SCHEMA, signedTransaction.params as object) + return { + ...transaction, + params + } } From 91a03959d02f2b5f673c8339787b047f7aab4945 Mon Sep 17 00:00:00 2001 From: bludnic Date: Sat, 9 Dec 2023 01:09:40 +0000 Subject: [PATCH 10/29] feat: migrate Lisk Node API to v4 --- src/lib/lisk/index.ts | 3 + src/lib/lisk/lisk-account.ts | 59 ++++ src/lib/lisk/lisk-api.js | 66 +--- src/lib/lisk/lisk-constants.ts | 6 + src/lib/lisk/lisk-utils.ts | 122 ++++++-- src/lib/lisk/types/lisk.ts | 285 ++++++++++++++++++ src/lib/nodes/lsk/LskClient.ts | 64 ++++ src/lib/nodes/lsk/LskNode.ts | 66 ++-- src/lib/nodes/lsk/types/api.ts | 71 +++++ .../modules/lsk-base/lsk-base-actions.js | 37 +-- src/store/modules/lsk/lsk-actions.js | 64 ++-- src/store/modules/lsk/lsk-getters.js | 15 +- 12 files changed, 676 insertions(+), 182 deletions(-) create mode 100644 src/lib/lisk/index.ts create mode 100644 src/lib/lisk/lisk-account.ts create mode 100644 src/lib/lisk/types/lisk.ts create mode 100644 src/lib/nodes/lsk/types/api.ts diff --git a/src/lib/lisk/index.ts b/src/lib/lisk/index.ts new file mode 100644 index 000000000..767881be6 --- /dev/null +++ b/src/lib/lisk/index.ts @@ -0,0 +1,3 @@ +export * from './types/lisk' +export * from './lisk-constants' +export * from './lisk-account' diff --git a/src/lib/lisk/lisk-account.ts b/src/lib/lisk/lisk-account.ts new file mode 100644 index 000000000..47831757a --- /dev/null +++ b/src/lib/lisk/lisk-account.ts @@ -0,0 +1,59 @@ +import { bytesToHex } from '@/lib/hex' +import { createTransaction } from '@/lib/lisk/lisk-utils.ts' +import { LiskHashSettings } from './lisk-constants' +import { address as liskAddress } from '@liskhq/lisk-cryptography' +import pbkdf2 from 'pbkdf2' +import sodium from 'sodium-browserify-tweetnacl' + +export class LiskAccount { + private readonly publicKey: Buffer + private readonly secretKey: Buffer + private readonly address: Buffer + + constructor(passphrase: string) { + const liskSeed = pbkdf2.pbkdf2Sync( + passphrase, + LiskHashSettings.SALT, + LiskHashSettings.ITERATIONS, + LiskHashSettings.KEYLEN, + LiskHashSettings.DIGEST + ) + const keyPair: { publicKey: Buffer; secretKey: Buffer } = + sodium.crypto_sign_seed_keypair(liskSeed) + + this.publicKey = keyPair.publicKey + this.secretKey = keyPair.secretKey + this.address = liskAddress.getAddressFromPublicKey(keyPair.publicKey) + } + + getLisk32Address() { + return liskAddress.getLisk32AddressFromPublicKey(this.publicKey) + } + + getAddressBuffer() { + return this.address + } + + getAddressHex() { + return bytesToHex(this.address) + } + + async createTransaction( + recipientAddress: string, + amount: number | string, + fee: number | string, + nonce: number | string, + data = '' + ) { + const encodedTransaction = createTransaction( + { publicKey: this.publicKey, secretKey: this.secretKey }, + recipientAddress, + amount, + fee, + nonce, + data + ) + + return encodedTransaction + } +} diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js index 11387c6cc..8e5264e76 100644 --- a/src/lib/lisk/lisk-api.js +++ b/src/lib/lisk/lisk-api.js @@ -1,18 +1,18 @@ -import { TRANSACTION_PARAMS_SCHEMA, TRANSACTION_SCHEMA } from '@/lib/lisk/lisk-schemas' +import { LSK_CHAIN_ID } from './lisk-constants' +import { TRANSACTION_PARAMS_SCHEMA } from './lisk-schemas' +import { Buffer } from 'buffer' import LskBaseApi from './lsk-base-api' import { Cryptos } from '../constants' import { getMillisTimestamp, getLiskTimestamp, createUnsignedTransaction, - encodeTransaction + encodeTransaction, + getAccount } from './lisk-utils' import { bytesToHex } from '@/lib/hex' -import * as cryptography from '@liskhq/lisk-cryptography' -import * as codec from '@liskhq/lisk-codec' import * as transactions from '@liskhq/lisk-transactions' import { lsk } from '@/lib/nodes/lsk' -import { getAccount } from './lisk-utils' export const TX_CHUNK_SIZE = 25 @@ -101,9 +101,6 @@ export default class LiskApi extends LskBaseApi { * @returns {Promise<{hex: string, txid: string}>} */ createTransaction(address = '', amount = 0, fee, nonce, data = '') { - console.log(address, amount, fee, nonce, data) - // throw new Error('stop2') - // const unsignedTransaction = this._buildTransaction(address, amount, fee, nonce, data).liskTx // unsignedTx const unsignedTransaction = createUnsignedTransaction( address, Buffer.from(this._keyPair.publicKey).toString('hex'), @@ -113,66 +110,15 @@ export default class LiskApi extends LskBaseApi { data ) - // To use transactions.signTransaction, passPhrase is necessary - // So we'll use cryptography.signDataWithPrivateKey - console.log('liskTx', unsignedTransaction) - - const liskTxBytes = transactions.getSigningBytes(unsignedTransaction, TRANSACTION_PARAMS_SCHEMA) - console.log('liskTxBytes', liskTxBytes) - const signedTransaction = transactions.signTransaction( unsignedTransaction, - this.networkIdentifier, // @todo or chainID? + Buffer.from(LSK_CHAIN_ID, 'hex'), this._keyPair.secretKey, TRANSACTION_PARAMS_SCHEMA ) - console.log('signedTransaction', signedTransaction) - - // const json = toTransactionJSON(signedTransaction) - - // console.log('chainId', Buffer.from(this.chainId, 'hex')) - // const networkIdTestnet = '15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c'; - // const signedTransaction = transactions.signTransaction( - // unsignedTransaction, - // Buffer.from(networkIdTestnet, 'hex'), - // this._keyPair.secretKey - // ) - // console.log('passed', signedTransaction) const txid = bytesToHex(signedTransaction.id) - const signeTransactionHex = { - ...signedTransaction, - id: txid, - nonce: Number(signedTransaction.nonce).toString(), - fee: Number(signedTransaction.fee).toString(), - params: { - ...signedTransaction.params, - amount: Number(signedTransaction.params.amount).toString(), - recipientAddress: Buffer.from(signedTransaction.params.recipientAddress).toString('hex'), - tokenID: Buffer.from(signedTransaction.params.tokenID).toString('hex') - }, - senderPublicKey: Buffer.from(signedTransaction.senderPublicKey).toString('hex'), - signatures: signedTransaction.signatures.map((signature) => - Buffer.from(signature).toString('hex') - ) - } - - console.log('txid', txid) - - console.log('signeTransactionHex', signeTransactionHex) - console.log('encodeTransaction', encodeTransaction(signedTransaction)) - - // throw new Error('stop') - - // To send Tx to node's core API, we should change data types - // liskTx.senderPublicKey = bytesToHex(liskTx.senderPublicKey) - // liskTx.nonce = nonce.toString() - // liskTx.fee = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) - // liskTx.asset.amount = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) - // liskTx.asset.recipientAddress = bytesToHex(liskTx.asset.recipientAddress) - // liskTx.signatures[0] = bytesToHex(txSignature) - return Promise.resolve({ hex: encodeTransaction(signedTransaction), txid }) } diff --git a/src/lib/lisk/lisk-constants.ts b/src/lib/lisk/lisk-constants.ts index b222c07fa..fa33c9d49 100644 --- a/src/lib/lisk/lisk-constants.ts +++ b/src/lib/lisk/lisk-constants.ts @@ -1,6 +1,12 @@ +import { CryptosInfo } from '@/lib/constants' + export const LiskHashSettings = { SALT: 'adm', ITERATIONS: 2048, KEYLEN: 32, DIGEST: 'sha256' } as const + +export const LSK_CHAIN_ID = '00000000' +export const LSK_TOKEN_ID = '0000000000000000' +export const LSK_DECIMALS = CryptosInfo.LSK.decimals diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 87872d10f..845c8994f 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -2,16 +2,18 @@ * Additional coin-based functions here */ +import { CryptoSymbol } from '@/lib/constants/cryptos' import { TRANSACTION_PARAMS_SCHEMA, TRANSACTION_SCHEMA } from '@/lib/lisk/lisk-schemas' -import * as codec from '@liskhq/lisk-codec' +import { DecodedTransaction, Transaction } from './types/lisk' +import { codec } from '@liskhq/lisk-codec' import * as cryptography from '@liskhq/lisk-cryptography' import networks from '@/lib/lisk/networks' -import { getLisk32AddressFromPublicKey } from '@liskhq/lisk-cryptography/dist-node/address' +import { convertBeddowsToLSK } from '@liskhq/lisk-transactions' import * as transactions from '@liskhq/lisk-transactions' import { Buffer } from 'buffer' import pbkdf2 from 'pbkdf2' import sodium from 'sodium-browserify-tweetnacl' -import { LiskHashSettings } from './lisk-constants' +import { LiskHashSettings, LSK_CHAIN_ID, LSK_DECIMALS, LSK_TOKEN_ID } from './lisk-constants' import { bytesToHex } from '@/lib/hex' import * as validator from '@liskhq/lisk-validator' @@ -20,7 +22,7 @@ import * as validator from '@liskhq/lisk-validator' * @param {number} liskTimestamp */ export function getMillisTimestamp(liskTimestamp: number) { - return parseInt(liskTimestamp) * 1000 + return parseInt(liskTimestamp.toString()) * 1000 } /** @@ -28,10 +30,10 @@ export function getMillisTimestamp(liskTimestamp: number) { * @param {number} millisTimestamp */ export function getLiskTimestamp(millisTimestamp: number) { - return Math.round(parseInt(millisTimestamp) / 1000) // may be a mistake (use Math.floor instead) + return Math.round(parseInt(millisTimestamp.toString()) / 1000) // may be a mistake (use Math.floor instead) } -export function getAccount(crypto, passphrase) { +export function getAccount(crypto: CryptoSymbol, passphrase: string) { const network = networks[crypto] const liskSeed = pbkdf2.pbkdf2Sync( passphrase, @@ -58,6 +60,69 @@ export function getAccount(crypto, passphrase) { } } +export function seedLskAccount(passphrase: string) { + const liskSeed = pbkdf2.pbkdf2Sync( + passphrase, + LiskHashSettings.SALT, + LiskHashSettings.ITERATIONS, + LiskHashSettings.KEYLEN, + LiskHashSettings.DIGEST + ) + const keyPair: { publicKey: Buffer; secretKey: Buffer } = + sodium.crypto_sign_seed_keypair(liskSeed) + + const address = cryptography.address.getLisk32AddressFromPublicKey(keyPair.publicKey) + const addressHexBinary = cryptography.address.getAddressFromPublicKey(keyPair.publicKey) + const addressHex = bytesToHex(addressHexBinary) + + return { + keyPair, + address, + addressHexBinary, + addressHex + } +} + +/** + * Create and sign a transfer transaction. + * Transaction `hex` result is ready for broadcasting to blockchain network. + * + * @param keyPair Sender's `publicKey` and `privateKey` + * @param address Receiver address in Lisk32 format + * @param amount Amount to transfer (LSK, not Beddows) + * @param fee Transaction fee (LSK, not Beddows) + * @param nonce Transaction nonce + * @param data Transaction data field + */ +export function createTransaction( + keyPair: { publicKey: Buffer; secretKey: Buffer }, + address: string, + amount: number | string, + fee: number | string, + nonce: number | string, + data = '' +) { + const unsignedTransaction = createUnsignedTransaction( + address, + Buffer.from(keyPair.publicKey).toString('hex'), + amount, + fee, + nonce, + data + ) + + const signedTransaction = transactions.signTransaction( + unsignedTransaction, + Buffer.from(LSK_CHAIN_ID, 'hex'), + keyPair.secretKey, + TRANSACTION_PARAMS_SCHEMA + ) as unknown as Transaction + + const id = bytesToHex(signedTransaction.id) + + return { hex: encodeTransaction(signedTransaction), id } +} + /** * Creates unsigned LSK transaction. * @@ -76,16 +141,8 @@ export function createUnsignedTransaction( nonce: number | string, data = '' ) { - const decimals = 8 - - console.log('address', recipientAddress) - console.log('publicKeyHex', senderPublicKey) - console.log('nonce', nonce) - - // throw new Error('stop') - - const amountString = transactions.convertLSKToBeddows((+amount).toFixed(decimals)) - const feeString = transactions.convertLSKToBeddows((+fee).toFixed(decimals)) + const amountString = transactions.convertLSKToBeddows((+amount).toFixed(LSK_DECIMALS)) + const feeString = transactions.convertLSKToBeddows((+fee).toFixed(LSK_DECIMALS)) const nonceString = nonce.toString() // Adjust the values of the unsigned transaction manually @@ -95,7 +152,7 @@ export function createUnsignedTransaction( fee: BigInt(feeString), nonce: BigInt(nonceString), senderPublicKey: Buffer.from(senderPublicKey, 'hex'), - params: Buffer.alloc(0), + params: Buffer.alloc(0) as unknown, // @todo fix type signatures: [] } @@ -103,7 +160,7 @@ export function createUnsignedTransaction( // Create the asset for the Token Transfer transaction const transferParams = { - tokenID: Buffer.from('0000000100000000', 'hex'), + tokenID: Buffer.from(LSK_TOKEN_ID, 'hex'), amount: BigInt(amountString), recipientAddress: cryptography.address.getAddressFromLisk32Address(recipientAddress), data @@ -112,26 +169,31 @@ export function createUnsignedTransaction( // Add the transaction params to the transaction object unsignedTransaction.params = transferParams - // @todo remove - // const minFee = Number(transactions.computeMinFee(this.assetSchema, liskTx)) / this.multiplier - - // only tx.params will be validated + // Only `transaction.params` will be validated transactions.validateTransaction(unsignedTransaction, TRANSACTION_PARAMS_SCHEMA) - // throw new Error('stop') - return unsignedTransaction } /** * Encode transaction for later broadcasting to a node. */ -export function encodeTransaction(signedTransaction: Record) { - const transaction = codec.codec.toJSON(TRANSACTION_SCHEMA, signedTransaction) - const params = codec.codec.toJSON(TRANSACTION_PARAMS_SCHEMA, signedTransaction.params as object) +export function encodeTransaction(transaction: DecodedTransaction | Transaction) { + let encodedParams + if (!Buffer.isBuffer(transaction.params)) { + encodedParams = codec.encode(TRANSACTION_PARAMS_SCHEMA, transaction.params) + } else { + encodedParams = transaction.params + } - return { + const encodedTransaction = codec.encode(TRANSACTION_SCHEMA, { ...transaction, - params - } + params: encodedParams + }) + + return encodedTransaction.toString('hex') +} + +export function estimateFee() { + return convertBeddowsToLSK('164000') } diff --git a/src/lib/lisk/types/lisk.ts b/src/lib/lisk/types/lisk.ts new file mode 100644 index 000000000..200a27ef1 --- /dev/null +++ b/src/lib/lisk/types/lisk.ts @@ -0,0 +1,285 @@ +/* + * Copyright © 2020 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ + +// These types are provided by `@liskhq/lisk-api-client` +// @source https://github.com/LiskHQ/lisk-sdk/blob/development/elements/lisk-api-client/src/types.ts +import { Schema } from '@liskhq/lisk-codec' + +export interface Defer { + promise: Promise + resolve: (result: T) => void + reject: (error?: Error) => void +} + +export interface JSONRPCNotification { + readonly id: never + readonly jsonrpc: string + readonly method: string + readonly params?: T +} + +export interface JSONRPCError { + code: number + message: string + data?: string | number | boolean | Record +} + +export interface JSONRPCResponse { + readonly id: number + readonly jsonrpc: string + readonly method: never + readonly params: never + readonly error?: JSONRPCError + readonly result?: T +} + +export type JSONRPCMessage = JSONRPCNotification | JSONRPCResponse + +export type EventCallback> = (event?: T) => void | Promise + +export interface Channel { + connect: () => Promise + disconnect: () => Promise + invoke: (actionName: string, params?: Record) => Promise + subscribe: (eventName: string, cb: EventCallback) => void +} + +export interface RegisteredSchemas { + block: Schema + header: Schema + asset: Schema + transaction: Schema + event: Schema +} + +export interface ModuleMetadata { + id: string + name: string + endpoints: { + name: string + request?: Schema + response: Schema + }[] + events: { + name: string + data: Schema + }[] + commands: { + id: string + name: string + params: Schema + }[] + assets: { + version: number + data: Schema + }[] + stores: { + key: string + data: Schema + }[] +} + +export interface GenesisConfig { + [key: string]: unknown + readonly bftBatchSize: number + readonly chainID: string + readonly blockTime: number + readonly maxTransactionsSize: number +} + +export interface NodeInfo { + readonly version: string + readonly networkVersion: string + readonly chainID: string + readonly lastBlockID: string + readonly height: number + readonly genesisHeight: number + readonly finalizedHeight: number + readonly syncing: boolean + readonly unconfirmedTransactions: number + readonly genesis: GenesisConfig + readonly network: { + readonly port: number + readonly hostIp?: string + readonly seedPeers: { + readonly ip: string + readonly port: number + }[] + readonly blacklistedIPs?: string[] + readonly fixedPeers?: string[] + readonly whitelistedPeers?: { + readonly ip: string + readonly port: number + }[] + } +} + +export interface MultiSignatureKeys { + readonly mandatoryKeys: Buffer[] + readonly optionalKeys: Buffer[] + readonly numberOfSignatures: number +} + +export interface NetworkStats { + [key: string]: unknown + readonly outgoing: { + count: number + connects: number + disconnects: number + } + readonly incoming: { + count: number + connects: number + disconnects: number + } + readonly banning: { + count: number + bannedPeers: { + [key: string]: { + lastBanTime: number + banCount: number + } + } + } + readonly totalConnectedPeers: number + readonly totalDisconnectedPeers: number + readonly totalErrors: number + readonly totalRemovedPeers: number + readonly totalMessagesReceived: { + [key: string]: number + } + readonly totalRequestsReceived: { + [key: string]: number + } + readonly totalPeersDiscovered: number + readonly startTime: number +} + +export interface PeerInfo { + readonly ipAddress: string + readonly port: number + readonly chainID?: Buffer + readonly networkVersion?: string + readonly nonce?: string + readonly options?: { [key: string]: unknown } +} + +type Primitive = string | number | bigint | boolean | null | undefined +type Replaced = T extends TReplace | TKeep + ? T extends TReplace + ? TWith | Exclude + : T + : { + [P in keyof T]: Replaced + } + +export type JSONObject = Replaced + +export interface BlockHeader { + readonly version: number + readonly height: number + readonly generatorAddress: Buffer + readonly previousBlockID: Buffer + readonly timestamp: number + readonly maxHeightPrevoted: number + readonly maxHeightGenerated: number + readonly aggregateCommit: { + readonly height: number + readonly aggregationBits: Buffer + readonly certificateSignature: Buffer + } + readonly validatorsHash: Buffer + readonly stateRoot: Buffer + readonly transactionRoot: Buffer + readonly assetRoot: Buffer + readonly eventRoot: Buffer + readonly signature: Buffer + readonly id: Buffer +} + +export type BlockHeaderJSON = JSONObject + +export interface BlockAsset { + module: string + data: Buffer +} + +export type BlockAssetJSON = JSONObject +export type DecodedBlockAsset> = Omit & { data: T } +export type DecodedBlockAssetJSON> = Omit & { + data: T +} + +export interface Transaction { + readonly module: string + readonly command: string + readonly senderPublicKey: Buffer + readonly nonce: bigint + readonly fee: bigint + readonly params: Buffer + readonly signatures: ReadonlyArray + readonly id: Buffer +} + +export type TransactionJSON = JSONObject +export type DecodedTransaction> = Omit & { + params: T +} +export type DecodedTransactionJSON> = Omit< + TransactionJSON, + 'params' +> & { params: T } + +export interface Block { + header: BlockHeader + transactions: Transaction[] + assets: BlockAsset[] +} + +export interface DecodedBlock { + header: BlockHeader + transactions: DecodedTransaction[] + assets: DecodedBlockAsset[] +} + +export interface BlockJSON { + header: BlockHeaderJSON + transactions: TransactionJSON[] + assets: BlockAssetJSON[] +} + +export interface DecodedBlockJSON { + header: BlockHeaderJSON + transactions: DecodedTransactionJSON[] + assets: DecodedBlockAssetJSON[] +} + +export interface Event { + readonly module: string + /** + * several events can be emitted from each module, e.g. + * token module transfer event + * nft module transfer event + * + * name of event + */ + readonly name: string + readonly topics: Buffer[] + readonly index: number + readonly data: Buffer +} + +export type EventJSON = JSONObject +export type DecodedEventJSON> = Omit & { data: T } diff --git a/src/lib/nodes/lsk/LskClient.ts b/src/lib/nodes/lsk/LskClient.ts index 84d6dd585..75ae8b15e 100644 --- a/src/lib/nodes/lsk/LskClient.ts +++ b/src/lib/nodes/lsk/LskClient.ts @@ -1,3 +1,6 @@ +import { convertBeddowsToLSK } from '@liskhq/lisk-transactions' +import { LSK_TOKEN_ID } from '@/lib/lisk' +import { RpcMethod, RpcResults } from './types/api' import { LskNode } from './LskNode' import { Client } from '../abstract.client' @@ -9,4 +12,65 @@ export class LskClient extends Client { void this.watchNodeStatusChange() } + + private async invoke( + method: M, + params?: RpcResults[M]['params'] + ): Promise { + const node = this.useFastest ? this.getFastestNode() : this.getRandomNode() + if (!node) { + // All nodes seem to be offline: let's refresh the statuses + this.checkHealth() + // But there's nothing we can do right now + throw new Error('No online nodes at the moment') + } + + return node.invoke(method, params) + } + + /** + * @param address LSK address (e.g.: "lskfan97j6phfdc3odtoorfabxdqtdymah55wnkk9") + */ + async getNonce(address: string) { + const { nonce } = await this.invoke('auth_getAuthAccount', { address }) + + return nonce + } + + /** + * @param address LSK address (e.g.: "lskfan97j6phfdc3odtoorfabxdqtdymah55wnkk9") + */ + async getBalance(address: string) { + const { availableBalance } = await this.invoke('token_getBalance', { + tokenID: LSK_TOKEN_ID, + address + }) + + return convertBeddowsToLSK(availableBalance) + } + + async getHeight() { + const { height } = await this.invoke('system_getNodeInfo') + + return height + } + + async sendTransaction(transaction: string, dryRun?: boolean) { + if (dryRun) { + const result = await this.invoke('txpool_dryRunTransaction', { transaction }) + + if ('errorMessage' in result) { + throw new Error(result.errorMessage) + } + + console.debug(`txpool_dryRunTransaction: Dry run transaction`, transaction, result) + + return { + transactionId: 'debug_tx_id' + } + } + + const { transactionId } = await this.invoke('txpool_postTransaction', { transaction }) + return transactionId + } } diff --git a/src/lib/nodes/lsk/LskNode.ts b/src/lib/nodes/lsk/LskNode.ts index 5e00ffeec..5d6fa035c 100644 --- a/src/lib/nodes/lsk/LskNode.ts +++ b/src/lib/nodes/lsk/LskNode.ts @@ -1,6 +1,9 @@ import axios, { AxiosInstance } from 'axios' import { Node } from '@/lib/nodes/abstract.node' import { NODE_LABELS } from '@/lib/nodes/constants' +import { RpcMethod, RpcResults } from './types/api' +import { JSONRPCResponse } from '@/lib/lisk' +import { v4 as uuid } from 'uuid' /** * Encapsulates a node. Provides methods to send API-requests @@ -13,32 +16,55 @@ export class LskNode extends Node { super(url, NODE_LABELS.LskNode) this.client = axios.create({ baseURL: url }) - this.client.interceptors.response.use(null, (error) => { - if (error.response && Number(error.response.status) >= 500) { - console.error(`Request to ${url} failed.`, error) - } - // Lisk is spamming with 404 in console, when there is no LSK account - // There is no way to disable 404 logging for Chrome - if (error.response && Number(error.response.status) === 404) { - if ( - error.response.data && - error.response.data.errors && - error.response.data.errors[0] && - error.response.data.errors[0].message && - error.response.data.errors[0].message.includes('was not found') - ) { - return error.response - } - } - return Promise.reject(error) - }) + // Don't fetch node info if user disabled it + if (this.active) { + void this.fetchNodeInfo() + } void this.startHealthcheck() } + /** + * Performs an RPC request to the Lisk node. + * + * @docs https://lisk.com/documentation/beta/api/lisk-node-rpc.html + */ + async invoke( + method: M, + params?: RpcResults[M]['params'] + ): Promise { + return this.client + .post>('/rpc', { + jsonrpc: '2.0', + id: uuid(), + method, + params + }) + .then((res) => { + const { error, result } = res.data + + if (error) { + throw new Error(error.message, { cause: error.code }) // @todo lisk api error + } + + return result! + }) + } + + private async fetchNodeInfo(): Promise<{ version: string; height: number }> { + const { version, height } = await this.invoke('system_getNodeInfo') + this.version = version + this.height = height + + return { + version, + height + } + } + protected async checkHealth() { const time = Date.now() - const height = await this.client.get('/api/node/info').then((res) => res.data.data.height) + const { height } = await this.invoke('system_getNodeInfo') return { height, diff --git a/src/lib/nodes/lsk/types/api.ts b/src/lib/nodes/lsk/types/api.ts new file mode 100644 index 000000000..52128eea5 --- /dev/null +++ b/src/lib/nodes/lsk/types/api.ts @@ -0,0 +1,71 @@ +import { MultiSignatureKeys, NodeInfo } from '@/lib/lisk' + +export type RpcResults = { + system_getNodeInfo: { + params: undefined + result: NodeInfo + } + auth_getAuthAccount: { + params: { + /** + * LSK address, e.g. "lskfan97j6phfdc3odtoorfabxdqtdymah55wnkk9" + */ + address: string + } + result: MultiSignatureKeys & { + /** + * Nonce, numeric string + */ + nonce: string + } + } + token_getBalance: { + params: { + /** + * Numeric string. Must be 16 chars length. + * + * ID of the LSK token: "0000000000000000" + */ + tokenID: string + /** + * LSK address, e.g. "lskfan97j6phfdc3odtoorfabxdqtdymah55wnkk9" + */ + address: string + } + result: { + availableBalance: string + lockedBalances: [] // @todo array of string? + } + } + txpool_postTransaction: { + params: { + /** + * Encoded transaction data + */ + transaction: string + } + result: { + transactionId: string + } + } + txpool_dryRunTransaction: { + params: { + /** + * Encoded transaction data + */ + transaction: string + } + result: + | { + result: 1 + events: unknown[] // maybe type better + } + | { + result: -1 + events: [] + errorMessage: string + } + } +} + +export type RpcMethod = keyof RpcResults diff --git a/src/store/modules/lsk-base/lsk-base-actions.js b/src/store/modules/lsk-base/lsk-base-actions.js index 67bedf290..5f442991b 100644 --- a/src/store/modules/lsk-base/lsk-base-actions.js +++ b/src/store/modules/lsk-base/lsk-base-actions.js @@ -1,7 +1,9 @@ import BigNumber from '@/lib/bignumber' +import { LiskAccount } from '../../../lib/lisk' import LskBaseApi from '../../../lib/lisk/lsk-base-api' import { storeCryptoAddress } from '../../../lib/store-crypto-address' import * as tf from '../../../lib/transactionsFetching' +import { lsk } from '../../../lib/nodes/lsk' const DEFAULT_CUSTOM_ACTIONS = () => ({}) @@ -22,15 +24,18 @@ function createActions(options) { const Api = options.apiCtor || LskBaseApi const { customActions = DEFAULT_CUSTOM_ACTIONS, fetchRetryTimeout } = options - /** @type {LskBaseApi} */ + /** @type {LiskAccount | null} */ let api = null + let account = null return { afterLogin: { root: true, handler(context, passphrase) { api = new Api(passphrase) - context.commit('address', api.address) + account = new LiskAccount(passphrase) + + context.commit('address', account.getLisk32Address()) context.dispatch('updateStatus') context.dispatch('storeAddress') } @@ -41,6 +46,7 @@ function createActions(options) { root: true, handler(context) { api = null + account = null context.commit('reset') } }, @@ -52,7 +58,9 @@ function createActions(options) { const passphrase = context.rootGetters.getPassPhrase if (passphrase) { api = new Api(passphrase) - context.commit('address', api.address) + + account = new LiskAccount(passphrase) + context.commit('address', account.getLisk32Address()) context.dispatch('updateStatus') context.dispatch('storeAddress') } @@ -63,18 +71,13 @@ function createActions(options) { storeCryptoAddress(state.crypto, state.address) }, - updateStatus(context) { - if (!api) return - api.getBalance().then((balance) => context.commit('status', { balance })) - }, - sendTokens(context, { amount, admAddress, address, comments, fee, textData, replyToId }) { - if (!api) return + if (!account) return address = address.trim() const crypto = context.state.crypto - return api + return account .createTransaction(address, amount, fee, context.state.nonce, textData) .then((tx) => { if (!admAddress) return tx.hex @@ -94,7 +97,7 @@ function createActions(options) { .then((success) => (success ? tx.hex : Promise.reject(new Error('adm_message')))) }) .then((rawTx) => - api.sendTransaction(rawTx).then( + lsk.sendTransaction(rawTx).then( (hash) => ({ hash }), (error) => ({ error }) ) @@ -126,16 +129,6 @@ function createActions(options) { }) }, - /** - * Calculates fee for a Tx - * @param {object} context Vuex action context - * @ - */ - calculateFee(context, payload) { - if (!api) return - return api.getFee(payload.address, payload.amount, payload.nonce, payload.data) - }, - /** * Retrieves transaction details * @param {object} context Vuex action context @@ -308,7 +301,7 @@ function createActions(options) { ) }, - ...customActions(() => api) + ...customActions(() => account) } } diff --git a/src/store/modules/lsk/lsk-actions.js b/src/store/modules/lsk/lsk-actions.js index cda157ab1..30a82a37d 100644 --- a/src/store/modules/lsk/lsk-actions.js +++ b/src/store/modules/lsk/lsk-actions.js @@ -1,27 +1,24 @@ import { FetchStatus } from '@/lib/constants' import baseActions from '../lsk-base/lsk-base-actions' import LskApi from '../../../lib/lisk/lisk-api' +import { lsk } from '../../../lib/nodes/lsk' const TX_FETCH_INTERVAL = 10 * 1000 -const customActions = (getApi) => ({ +const customActions = (getAccount) => ({ updateBalance: { root: true, - async handler({ commit }, payload = {}) { + async handler({ commit, state }, payload = {}) { if (payload.requestedByUser) { commit('setBalanceStatus', FetchStatus.Loading) } try { - const api = getApi() - const account = await api.getAccount() + const balance = await lsk.getBalance(state.address) + const nonce = await lsk.getNonce(state.address) - if (account) { - commit('status', { balance: account.balance, nonce: account.nonce }) - commit('setBalanceStatus', FetchStatus.Success) - } else { - commit('setBalanceStatus', FetchStatus.Error) - } + commit('status', { balance, nonce }) + commit('setBalanceStatus', FetchStatus.Success) } catch (err) { commit('setBalanceStatus', FetchStatus.Error) console.log(err) @@ -29,41 +26,32 @@ const customActions = (getApi) => ({ } }, - updateStatus(context) { - const api = getApi() + async updateStatus({ commit, dispatch }) { + const account = getAccount() + if (!account) return - if (!api) return - api - .getAccount() - .then((account) => { - if (account) { - context.commit('status', { balance: account.balance, nonce: account.nonce }) - context.commit('setBalanceStatus', FetchStatus.Success) - } else { - context.commit('setBalanceStatus', FetchStatus.Error) - } - }) - .catch((err) => { - context.commit('setBalanceStatus', FetchStatus.Error) + const address = account.getLisk32Address() - throw err - }) + try { + const balance = await lsk.getBalance(address) + const nonce = await lsk.getNonce(address) + + commit('status', { balance, nonce }) + commit('setBalanceStatus', FetchStatus.Success) + } catch (err) { + commit('setBalanceStatus', FetchStatus.Error) - // Not needed - // api.getFeeRate().then(rate => context.commit('feeRate', rate)) + throw err + } // Last block height - context.dispatch('updateHeight') + dispatch('updateHeight') }, - updateHeight({ commit }) { - const api = getApi() - if (!api) return - api.getHeight().then((height) => { - if (height) { - commit('height', height) - } - }) + async updateHeight({ commit }) { + const height = await lsk.getHeight() + + commit('height', height) }, /** diff --git a/src/store/modules/lsk/lsk-getters.js b/src/store/modules/lsk/lsk-getters.js index 35da8b2d7..e9b0ca933 100644 --- a/src/store/modules/lsk/lsk-getters.js +++ b/src/store/modules/lsk/lsk-getters.js @@ -1,20 +1,11 @@ +import { estimateFee } from '../../../lib/lisk/lisk-utils' import baseGetters from '../lsk-base/lsk-base-getters' -import lskActions from './lsk-actions' -import validateAddress from '@/lib/validateAddress' export default { ...baseGetters, - fee: (state) => (amount, recipientAddress, data) => { - recipientAddress = validateAddress(state.crypto, recipientAddress) - ? recipientAddress - : state.address - return lskActions.calculateFee(null, { - address: recipientAddress, - amount, - nonce: state.nonce, - data: data || '' - }) + fee: () => (_amount, _recipientAddress, _data) => { + return estimateFee() }, height(state) { From 990ea0a4b71d6eea8912d4d430795791fdd56749 Mon Sep 17 00:00:00 2001 From: bludnic Date: Sat, 9 Dec 2023 01:10:47 +0000 Subject: [PATCH 11/29] chore(package.json): add missing types --- package-lock.json | 17 +++++++++++++++++ package.json | 2 ++ 2 files changed, 19 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7908e38f2..7a07ecda6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,6 +94,8 @@ "@types/emoji-mart": "^3.0.12", "@types/eslint": "^8.44.7", "@types/marked": "^5.0.2", + "@types/pbkdf2": "^3.1.2", + "@types/uuid": "^9.0.7", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "@vitejs/plugin-vue": "^4.5.0", @@ -4639,6 +4641,15 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/plist": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", @@ -4709,6 +4720,12 @@ "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, "node_modules/@types/verror": { "version": "1.10.6", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", diff --git a/package.json b/package.json index 922bc0bf7..dc67bcd8c 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,8 @@ "@types/emoji-mart": "^3.0.12", "@types/eslint": "^8.44.7", "@types/marked": "^5.0.2", + "@types/pbkdf2": "^3.1.2", + "@types/uuid": "^9.0.7", "@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/parser": "^6.11.0", "@vitejs/plugin-vue": "^4.5.0", From ad46845b122aeb02790893ee5f4b1dad3185d087 Mon Sep 17 00:00:00 2001 From: bludnic Date: Sat, 9 Dec 2023 01:19:31 +0000 Subject: [PATCH 12/29] feat(SendFundsForm): add `dryRun` checkbox for debugging --- src/components/SendFundsForm.vue | 10 ++++++++-- src/store/modules/lsk-base/lsk-base-actions.js | 9 ++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/SendFundsForm.vue b/src/components/SendFundsForm.vue index 43efc43f1..2d4b23a6a 100644 --- a/src/components/SendFundsForm.vue +++ b/src/components/SendFundsForm.vue @@ -136,6 +136,7 @@ :label="$t('transfer.increase_fee')" color="grey darken-1" /> +
@@ -301,7 +302,11 @@ export default { fetchAddress: null, // fn throttle increaseFee: false, showWarningOnPartnerAddressDialog: false, - warningOnPartnerInfo: {} + warningOnPartnerInfo: {}, + + // Debugging section + dryRun: false, + debug: !!localStorage.getItem('DEBUG') }), computed: { className: () => 'send-funds-form', @@ -711,7 +716,8 @@ export default { fee: this.transferFee, increaseFee: this.increaseFee, textData: this.textData, - replyToId: this.replyToId + replyToId: this.replyToId, + dryRun: this.dryRun }) } }, diff --git a/src/store/modules/lsk-base/lsk-base-actions.js b/src/store/modules/lsk-base/lsk-base-actions.js index 5f442991b..d14607b67 100644 --- a/src/store/modules/lsk-base/lsk-base-actions.js +++ b/src/store/modules/lsk-base/lsk-base-actions.js @@ -71,7 +71,10 @@ function createActions(options) { storeCryptoAddress(state.crypto, state.address) }, - sendTokens(context, { amount, admAddress, address, comments, fee, textData, replyToId }) { + sendTokens( + context, + { amount, admAddress, address, comments, fee, textData, replyToId, dryRun } + ) { if (!account) return address = address.trim() @@ -80,7 +83,7 @@ function createActions(options) { return account .createTransaction(address, amount, fee, context.state.nonce, textData) .then((tx) => { - if (!admAddress) return tx.hex + if (!admAddress || dryRun) return tx.hex const msgPayload = { address: admAddress, @@ -97,7 +100,7 @@ function createActions(options) { .then((success) => (success ? tx.hex : Promise.reject(new Error('adm_message')))) }) .then((rawTx) => - lsk.sendTransaction(rawTx).then( + lsk.sendTransaction(rawTx, dryRun).then( (hash) => ({ hash }), (error) => ({ error }) ) From 3a898c2d427bca0447ef2609856f13fc8329f647 Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 12 Dec 2023 19:55:22 +0000 Subject: [PATCH 13/29] feat: migrate lisk service from v2 to v3 --- .../transactions/LskTransaction.vue | 14 +-- src/lib/lisk/lisk-constants.ts | 1 + src/lib/lisk/lisk-utils.ts | 4 +- src/lib/nodes/index.ts | 5 + src/lib/nodes/lsk-indexer/LskIndexer.ts | 64 ++++++++++ src/lib/nodes/lsk-indexer/LskIndexerClient.ts | 65 ++++++++++ src/lib/nodes/lsk-indexer/index.ts | 8 ++ .../types/api/common/pagination.ts | 5 + .../nodes/lsk-indexer/types/api/endpoints.ts | 24 ++++ .../types/api/index-status/index-status.ts | 14 +++ .../transactions/normalized-transaction.ts | 16 +++ .../api/transactions/transaction-params.ts | 63 ++++++++++ .../types/api/transactions/transaction.ts | 34 +++++ src/lib/nodes/lsk-indexer/utils.ts | 32 +++++ .../modules/lsk-base/lsk-base-actions.js | 118 +++++++++--------- 15 files changed, 397 insertions(+), 70 deletions(-) create mode 100644 src/lib/nodes/lsk-indexer/LskIndexer.ts create mode 100644 src/lib/nodes/lsk-indexer/LskIndexerClient.ts create mode 100644 src/lib/nodes/lsk-indexer/index.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/common/pagination.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/endpoints.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/index-status/index-status.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/transactions/normalized-transaction.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/transactions/transaction-params.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/transactions/transaction.ts create mode 100644 src/lib/nodes/lsk-indexer/utils.ts diff --git a/src/components/transactions/LskTransaction.vue b/src/components/transactions/LskTransaction.vue index 85140a7b3..63abd68ed 100644 --- a/src/components/transactions/LskTransaction.vue +++ b/src/components/transactions/LskTransaction.vue @@ -80,18 +80,14 @@ export default { return getExplorerTxUrl(Cryptos.LSK, this.id) }, confirmations() { - const { height, confirmations } = this.transaction + const { height } = this.transaction + const currentHeight = this.$store.getters[`${this.cryptoKey}/height`] - let result = confirmations - if (height) { - // Calculate actual confirmations count based on the tx block height and the last block height. - const c = this.$store.getters[`${this.cryptoKey}/height`] - height + 1 - if (c > 0 && (c > result || !result)) { - result = c - } + if (height === undefined || currentHeight === 0) { + return 0 } - return result + return currentHeight - height + 1 }, admTx() { const admTx = {} diff --git a/src/lib/lisk/lisk-constants.ts b/src/lib/lisk/lisk-constants.ts index fa33c9d49..d2d4a4950 100644 --- a/src/lib/lisk/lisk-constants.ts +++ b/src/lib/lisk/lisk-constants.ts @@ -10,3 +10,4 @@ export const LiskHashSettings = { export const LSK_CHAIN_ID = '00000000' export const LSK_TOKEN_ID = '0000000000000000' export const LSK_DECIMALS = CryptosInfo.LSK.decimals +export const LSK_TXS_PER_PAGE = 25 // transactions per page diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 845c8994f..b14da32be 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -21,7 +21,7 @@ import * as validator from '@liskhq/lisk-validator' * Returns Millis timestamp by LSK UNIX timestamp (sec) * @param {number} liskTimestamp */ -export function getMillisTimestamp(liskTimestamp: number) { +export function getMillisTimestamp(liskTimestamp: number | string) { return parseInt(liskTimestamp.toString()) * 1000 } @@ -29,7 +29,7 @@ export function getMillisTimestamp(liskTimestamp: number) { * Returns LSK timestamp (UNIX in sec) by Millis timestamp * @param {number} millisTimestamp */ -export function getLiskTimestamp(millisTimestamp: number) { +export function getLiskTimestamp(millisTimestamp: number | string) { return Math.round(parseInt(millisTimestamp.toString()) / 1000) // may be a mistake (use Math.floor instead) } diff --git a/src/lib/nodes/index.ts b/src/lib/nodes/index.ts index 9be8d5dcf..680aa053b 100644 --- a/src/lib/nodes/index.ts +++ b/src/lib/nodes/index.ts @@ -4,6 +4,7 @@ import { dash } from './dash' import { doge } from './doge' import { eth } from './eth' import { lsk } from './lsk' +import { lskIndexer } from './lsk-indexer' export const nodes = { adm, @@ -13,3 +14,7 @@ export const nodes = { eth, lsk } + +export const services = { + lskIndexer +} diff --git a/src/lib/nodes/lsk-indexer/LskIndexer.ts b/src/lib/nodes/lsk-indexer/LskIndexer.ts new file mode 100644 index 000000000..49906c99a --- /dev/null +++ b/src/lib/nodes/lsk-indexer/LskIndexer.ts @@ -0,0 +1,64 @@ +import axios, { AxiosInstance } from 'axios' +import { Node } from '@/lib/nodes/abstract.node' +import { NODE_LABELS } from '@/lib/nodes/constants' +import { Endpoints } from './types/api/endpoints' + +/** + * Encapsulates a node. Provides methods to send API-requests + * to the node and verify is status (online/offline, version, ping, etc.) + */ +export class LskIndexer extends Node { + client: AxiosInstance + + constructor(url: string) { + super(url, NODE_LABELS.LskIndexer) + + this.client = axios.create({ baseURL: `${url}/api/v3` }) + + // Don't fetch node info if user disabled it + if (this.active) { + void this.fetchServiceInfo() + } + void this.startHealthcheck() + } + + /** + * Performs a request to the Lisk service. + * + * @docs https://lisk.com/documentation/api/lisk-service-http.html + */ + async request( + endpoint: E, + params?: Endpoints[E]['params'] + ): Promise { + const [method, path] = endpoint.split(' ') + + return this.client + .request({ + url: path, + method, + params: method === 'GET' ? params : undefined, + data: method === 'POST' ? params : undefined + }) + .then((res) => res.data) + } + + private async fetchServiceInfo(): Promise<{ height: number }> { + const { data } = await this.request('GET /index/status') + this.height = data.numBlocksIndexed + + return { + height: data.numBlocksIndexed + } + } + + protected async checkHealth() { + const time = Date.now() + const { height } = await this.fetchServiceInfo() + + return { + height, + ping: Date.now() - time + } + } +} diff --git a/src/lib/nodes/lsk-indexer/LskIndexerClient.ts b/src/lib/nodes/lsk-indexer/LskIndexerClient.ts new file mode 100644 index 000000000..391748d0a --- /dev/null +++ b/src/lib/nodes/lsk-indexer/LskIndexerClient.ts @@ -0,0 +1,65 @@ +import { normalizeTransaction } from './utils' +import { TransactionParams } from './types/api/transactions/transaction-params' +import { Endpoints } from './types/api/endpoints' +import { LskIndexer } from './LskIndexer' +import { Client } from '../abstract.client' + +export class LskIndexerClient extends Client { + constructor(endpoints: string[] = [], minNodeVersion = '0.0.0') { + super('lsk') + this.nodes = endpoints.map((endpoint) => new LskIndexer(endpoint)) + this.minNodeVersion = minNodeVersion + + void this.watchNodeStatusChange() + } + + private async request( + endpoint: E, + params?: Endpoints[E]['params'] + ): Promise { + const node = this.useFastest ? this.getFastestNode() : this.getRandomNode() + if (!node) { + // All nodes seem to be offline: let's refresh the statuses + this.checkHealth() + // But there's nothing we can do right now + throw new Error('No online nodes at the moment') + } + + return node.request(endpoint, params) + } + + /** + * Query transactions history + * + * @param params Query params + * @param address Owner address + */ + async getTransactions(params: TransactionParams, address: string) { + const { data: transactions } = await this.request('GET /transactions', { + ...params, + moduleCommand: 'token:transfer' + }) + + return transactions.map((transaction) => normalizeTransaction(transaction, address)) + } + + /** + * Returns a single transaction by ID + * + * @param transactionID + * @param address + */ + async getTransaction(transactionID: string, address: string) { + const { data: transactions } = await this.request('GET /transactions', { + transactionID + }) + + const transaction = transactions[0] + + if (!transaction) { + throw new Error(`LskIndexer: Transaction ID:${transactionID} not found`) + } + + return normalizeTransaction(transaction, address) + } +} diff --git a/src/lib/nodes/lsk-indexer/index.ts b/src/lib/nodes/lsk-indexer/index.ts new file mode 100644 index 000000000..8226a992b --- /dev/null +++ b/src/lib/nodes/lsk-indexer/index.ts @@ -0,0 +1,8 @@ +import config from '@/config' +import { Service } from '@/types/wallets' +import { LskIndexerClient } from './LskIndexerClient' + +const endpoints = (config.lsk.services.lskService as Service[]).map((endpoint) => endpoint.url) +export const lskIndexer = new LskIndexerClient(endpoints) + +export default lskIndexer diff --git a/src/lib/nodes/lsk-indexer/types/api/common/pagination.ts b/src/lib/nodes/lsk-indexer/types/api/common/pagination.ts new file mode 100644 index 000000000..8e9a35632 --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/common/pagination.ts @@ -0,0 +1,5 @@ +export type Pagination = { + count: number + offset: number + total: number +} diff --git a/src/lib/nodes/lsk-indexer/types/api/endpoints.ts b/src/lib/nodes/lsk-indexer/types/api/endpoints.ts new file mode 100644 index 000000000..8a22aa6ea --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/endpoints.ts @@ -0,0 +1,24 @@ +import { IndexStatus } from './index-status/index-status' +import { TransactionParams } from './transactions/transaction-params' +import { Pagination } from './common/pagination' +import { Transaction } from './transactions/transaction' + +export type Endpoints = { + /** + * @see https://lisk.com/documentation/api/lisk-service-http.html#/Transactions/get_transactions + */ + 'GET /transactions': { + params?: TransactionParams + result: { + data: Transaction[] + meta: Pagination + } + } + /** + * @see https://lisk.com/documentation/api/lisk-service-http.html#/Index%20Status/get_index_status + */ + 'GET /index/status': { + params: undefined + result: IndexStatus + } +} diff --git a/src/lib/nodes/lsk-indexer/types/api/index-status/index-status.ts b/src/lib/nodes/lsk-indexer/types/api/index-status/index-status.ts new file mode 100644 index 000000000..351603fd2 --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/index-status/index-status.ts @@ -0,0 +1,14 @@ +export type IndexStatus = { + data: { + genesisHeight: number + lastBlockHeight: number + lastIndexedBlockHeight: number + chainLength: number + numBlocksIndexed: number + percentageIndexed: number + isIndexingInProgress: boolean + } + meta: { + lastUpdate: number + } +} diff --git a/src/lib/nodes/lsk-indexer/types/api/transactions/normalized-transaction.ts b/src/lib/nodes/lsk-indexer/types/api/transactions/normalized-transaction.ts new file mode 100644 index 000000000..eba72917e --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/transactions/normalized-transaction.ts @@ -0,0 +1,16 @@ +export type NormalizedTransaction = { + id: string + hash: string + fee: number + status: 'CONFIRMED' | 'REGISTERED' + data: string + timestamp: number // block timestamp + direction: 'from' | 'to' + senderId: string + recipientId: string + amount: number + height: number // block height + nonce: string + module: 'token' + command: 'transfer' +} diff --git a/src/lib/nodes/lsk-indexer/types/api/transactions/transaction-params.ts b/src/lib/nodes/lsk-indexer/types/api/transactions/transaction-params.ts new file mode 100644 index 000000000..49c12284a --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/transactions/transaction-params.ts @@ -0,0 +1,63 @@ +export type TransactionParams = { + /** + * Transaction ID to query + */ + transactionID?: string + /** + * Combination of moduleName:commandName + */ + moduleCommand?: string + /** + * Lisk account address + */ + senderAddress?: string + /** + * Resolves for both senderAddress and recipientAddress + */ + address?: string + /** + * Lisk account address. + */ + recipientAddress?: string + /** + * Chain ID for the receiving chain in the case of cross-chain token transfers. + */ + receivingChainID?: string + /** + * Block ID to query + */ + blockID?: string + /** + * Query by height or a height range. Can be expressed as an interval i.e. `1:20` or `1:` or `:20`. Specified values are inclusive. + */ + height?: string + /** + * Query by timestamp or a timestamp range. Can be expressed as an interval i.e. `1000000:2000000` or `1000000:` or `:2000000`. Specified values are inclusive. + */ + timestamp?: string + /** + * Query transactions by their executionStatus. + * Accepted values: pending, successful, failed. + */ + executionStatus?: 'pending' | 'successful' | 'failed' + /** + * Query by nonce. Nonce is only allowed if senderAddress is supplied as a parameter. + */ + nonce?: string + /** + * Limit to apply to the query results. + */ + limit?: number + /** + * Offset to apply to the query results. + */ + offset?: number + /** + * Fields to sort results by. + */ + sort?: 'timestamp:asc' | 'timestamp:desc' | 'height:asc' | 'height:desc' + /** + * Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision. + */ + order?: 'index:asc' | 'index:desc' +} diff --git a/src/lib/nodes/lsk-indexer/types/api/transactions/transaction.ts b/src/lib/nodes/lsk-indexer/types/api/transactions/transaction.ts new file mode 100644 index 000000000..92f01e2e0 --- /dev/null +++ b/src/lib/nodes/lsk-indexer/types/api/transactions/transaction.ts @@ -0,0 +1,34 @@ +export type Transaction = { + id: string + moduleCommand: string + nonce: string + fee: string + minFee: string + size: number + block: { + id: string + height: number + timestamp: number + isFinal: boolean + } + sender: { + address: string + publicKey: string + name: string + } + params: { + amount: string + recipientAddress: string + tokenID: string + data: string + } + executionStatus: string + index: number + meta: { + recipient: { + address: string + publicKey: string + name: string + } + } +} diff --git a/src/lib/nodes/lsk-indexer/utils.ts b/src/lib/nodes/lsk-indexer/utils.ts new file mode 100644 index 000000000..5ba605e9a --- /dev/null +++ b/src/lib/nodes/lsk-indexer/utils.ts @@ -0,0 +1,32 @@ +import { getMillisTimestamp } from '@/lib/lisk/lisk-utils' +import { convertBeddowsToLSK } from '@liskhq/lisk-transactions' +import { Transaction } from './types/api/transactions/transaction' +import { NormalizedTransaction } from './types/api/transactions/normalized-transaction' + +/** + * @param transaction + * @param ownerAddress LSK address + */ +export function normalizeTransaction( + transaction: Transaction, + ownerAddress: string +): NormalizedTransaction { + const direction = transaction.sender.address === ownerAddress ? 'from' : 'to' + + return { + id: transaction.id, + hash: transaction.id, + fee: Number(convertBeddowsToLSK(transaction.fee)), + status: 'CONFIRMED', + data: transaction.params.data, + timestamp: getMillisTimestamp(transaction.block.timestamp), // block timestamp + direction, + senderId: transaction.sender.address, + recipientId: transaction.params.recipientAddress, + amount: Number(convertBeddowsToLSK(transaction.params.amount)), + height: transaction.block.height, + nonce: transaction.nonce, + module: 'token', + command: 'transfer' + } +} diff --git a/src/store/modules/lsk-base/lsk-base-actions.js b/src/store/modules/lsk-base/lsk-base-actions.js index d14607b67..a42f7e47a 100644 --- a/src/store/modules/lsk-base/lsk-base-actions.js +++ b/src/store/modules/lsk-base/lsk-base-actions.js @@ -1,9 +1,11 @@ import BigNumber from '@/lib/bignumber' -import { LiskAccount } from '../../../lib/lisk' +import { LiskAccount, LSK_TXS_PER_PAGE } from '../../../lib/lisk' +import { getLiskTimestamp } from '../../../lib/lisk/lisk-utils' import LskBaseApi from '../../../lib/lisk/lsk-base-api' import { storeCryptoAddress } from '../../../lib/store-crypto-address' import * as tf from '../../../lib/transactionsFetching' import { lsk } from '../../../lib/nodes/lsk' +import lskIndexer from '../../../lib/nodes/lsk-indexer' const DEFAULT_CUSTOM_ACTIONS = () => ({}) @@ -160,7 +162,7 @@ function createActions(options) { let tx = null try { - tx = await api.getTransaction(payload.hash) + tx = await lskIndexer.getTransaction(payload.hash, context.state.address) } catch (e) { /* empty */ } @@ -229,79 +231,77 @@ function createActions(options) { * Retrieves new transactions: those that follow the most recently retrieved one. * @param {any} context Vuex action context */ - async getNewTransactions(context) { - if (!api) return - const options = {} + async getNewTransactions({ state, commit, dispatch }) { // Magic here helps to refresh Tx list when browser deletes it - if (Object.keys(context.state.transactions).length < context.state.transactionsCount) { - context.state.transactionsCount = 0 - context.state.maxTimestamp = -1 - context.state.minTimestamp = Infinity - context.commit('bottom', false) - } - if (context.state.maxTimestamp > 0) { - options.fromTimestamp = context.state.maxTimestamp - options.sort = 'timestamp:asc' - } else { - // First time we fetch txs — get newest - options.sort = 'timestamp:desc' + if (Object.keys(state.transactions).length < state.transactionsCount) { + state.transactionsCount = 0 + state.maxTimestamp = -1 + state.minTimestamp = Infinity + commit('bottom', false) } - context.commit('areRecentLoading', true) - return api.getTransactions(options).then( - (transactions) => { - context.commit('areRecentLoading', false) - if (transactions && transactions.length > 0) { - context.commit('transactions', { transactions, updateTimestamps: true }) - // get new transactions until we fetch the newest one - if (options.fromTimestamp && transactions.length === api.TX_CHUNK_SIZE) { - this.dispatch(`${context.state.crypto.toLowerCase()}/getNewTransactions`) - } + commit('areRecentLoading', true) + try { + const timestamp = state.maxTimestamp + ? `${getLiskTimestamp(state.maxTimestamp)}:` + : undefined + + const transactions = await lskIndexer.getTransactions({ + address: state.address, + // First time we fetch txs — get newest first + sort: state.maxTimestamp > 0 ? 'timestamp:asc' : 'timestamp:desc', + timestamp, + limit: LSK_TXS_PER_PAGE + }) + commit('areRecentLoading', false) + + if (transactions.length > 0) { + commit('transactions', { transactions, updateTimestamps: true }) + + if (timestamp && transactions.length === LSK_TXS_PER_PAGE) { + dispatch(`${state.crypto.toLowerCase()}/getNewTransactions`) } - }, - (error) => { - context.commit('areRecentLoading', false) - return Promise.reject(error) } - ) + } catch (err) { + commit('areRecentLoading', false) + throw err + } }, /** * Retrieves old transactions: those that preceded the oldest among the retrieved ones. * @param {any} context Vuex action context */ - async getOldTransactions(context) { - if (!api) return + async getOldTransactions({ state, commit }) { // If we already have the most old transaction for this address, no need to request anything - if (context.state.bottomReached) return Promise.resolve() + if (state.bottomReached) return - const options = {} - if (context.state.minTimestamp < Infinity) { - options.toTimestamp = context.state.minTimestamp - } - options.sort = 'timestamp:desc' - - context.commit('areOlderLoading', true) - - return api.getTransactions(options).then( - (transactions) => { - context.commit('areOlderLoading', false) + commit('areOlderLoading', true) + try { + const timestamp = + state.minTimestamp < Infinity ? `:${getLiskTimestamp(state.minTimestamp)}` : undefined + + const transactions = await lskIndexer.getTransactions({ + address: state.address, + sort: 'timestamp:desc', + timestamp, + limit: LSK_TXS_PER_PAGE + }) + commit('areOlderLoading', false) - if (transactions && transactions.length > 0) { - context.commit('transactions', { transactions, updateTimestamps: true }) - } + if (transactions.length > 0) { + commit('transactions', { transactions, updateTimestamps: true }) + } - // Successful but empty response means, that the oldest transaction for the current - // address has been received already - if (transactions && transactions.length === 0) { - context.commit('bottom', true) - } - }, - (error) => { - context.commit('areOlderLoading', false) - return Promise.reject(error) + // Successful but empty response means, that the oldest transaction for the current + // address has been received already + if (transactions.length === 0) { + commit('bottom', true) } - ) + } catch (err) { + commit('areOlderLoading', false) + throw err + } }, ...customActions(() => account) From c6fd58b8214a44c46bb0d63939aff579197e1002 Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 12 Dec 2023 19:56:01 +0000 Subject: [PATCH 14/29] chore: update package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a07ecda6..740b8fa27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3534,9 +3534,9 @@ } }, "node_modules/@liskhq/lisk-passphrase": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@liskhq/lisk-passphrase/-/lisk-passphrase-4.0.0.tgz", - "integrity": "sha512-bohEKYtKSqMHyajWqQV3fES5Ejto5gUhAem7fZkLZpgyBIKfZt2xjjdVZRINuvvUixzNBvqpqwF3LQvAJl7SSw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@liskhq/lisk-passphrase/-/lisk-passphrase-4.1.0.tgz", + "integrity": "sha512-1VdpEp+OGiyCF0Fjtgcc+SLlHeIflzya49GzIXcXjFNf19VgkUu/eIDM8XHxSdj/5KzpL+yD07OYCh+LdoAkfA==", "dependencies": { "bip39": "3.0.3" }, From 5ab5383fc5cfdc592ad0a2ed6fecafd98ba45dde Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 12 Dec 2023 20:05:32 +0000 Subject: [PATCH 15/29] chore(`sodium-browserify-tweetnacl`): add missing types --- src/lib/lisk/lisk-utils.ts | 3 +-- src/sodium.d.ts | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 src/sodium.d.ts diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index b14da32be..74e9c271e 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -68,8 +68,7 @@ export function seedLskAccount(passphrase: string) { LiskHashSettings.KEYLEN, LiskHashSettings.DIGEST ) - const keyPair: { publicKey: Buffer; secretKey: Buffer } = - sodium.crypto_sign_seed_keypair(liskSeed) + const keyPair = sodium.crypto_sign_seed_keypair(liskSeed) const address = cryptography.address.getLisk32AddressFromPublicKey(keyPair.publicKey) const addressHexBinary = cryptography.address.getAddressFromPublicKey(keyPair.publicKey) diff --git a/src/sodium.d.ts b/src/sodium.d.ts new file mode 100644 index 000000000..2f6932c76 --- /dev/null +++ b/src/sodium.d.ts @@ -0,0 +1,7 @@ +declare module 'sodium-browserify-tweetnacl' { + type KeyPair = { + publicKey: Buffer + secretKey: Buffer + } + declare function crypto_sign_seed_keypair(seed: Buffer): KeyPair +} From d9e2bedab41674b35b99d415cfa1f538b2cb2c7c Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 12 Dec 2023 21:02:26 +0000 Subject: [PATCH 16/29] fix(lsk): fix tx id --- src/lib/nodes/lsk/LskClient.ts | 4 +--- src/store/modules/lsk-base/lsk-base-actions.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/nodes/lsk/LskClient.ts b/src/lib/nodes/lsk/LskClient.ts index 75ae8b15e..83dc61d31 100644 --- a/src/lib/nodes/lsk/LskClient.ts +++ b/src/lib/nodes/lsk/LskClient.ts @@ -65,9 +65,7 @@ export class LskClient extends Client { console.debug(`txpool_dryRunTransaction: Dry run transaction`, transaction, result) - return { - transactionId: 'debug_tx_id' - } + return 'debug_tx_id' // transactionId } const { transactionId } = await this.invoke('txpool_postTransaction', { transaction }) diff --git a/src/store/modules/lsk-base/lsk-base-actions.js b/src/store/modules/lsk-base/lsk-base-actions.js index a42f7e47a..af150521f 100644 --- a/src/store/modules/lsk-base/lsk-base-actions.js +++ b/src/store/modules/lsk-base/lsk-base-actions.js @@ -92,7 +92,7 @@ function createActions(options) { amount: BigNumber(amount).toFixed(), comments, crypto, - hash: tx.txid, + hash: tx.id, replyToId } From 2780f1d0ce56fef14bf0c4959b7307f562ce1475 Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 13 Dec 2023 10:42:12 +0000 Subject: [PATCH 17/29] fix(LskClient): `getBalance()` must return a number --- src/lib/nodes/lsk/LskClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/nodes/lsk/LskClient.ts b/src/lib/nodes/lsk/LskClient.ts index 83dc61d31..b57afe878 100644 --- a/src/lib/nodes/lsk/LskClient.ts +++ b/src/lib/nodes/lsk/LskClient.ts @@ -46,7 +46,7 @@ export class LskClient extends Client { address }) - return convertBeddowsToLSK(availableBalance) + return Number(convertBeddowsToLSK(availableBalance)) } async getHeight() { From ab1bee929b59136bd5f56cb8722a3f61aaa670bb Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 13 Dec 2023 12:58:09 +0000 Subject: [PATCH 18/29] feat(lisk-utils): add `estimateFee()` for LSK --- src/lib/__tests__/lisk/lisk-utils.spec.ts | 28 ++++++++- src/lib/lisk/lisk-account.ts | 20 ++++++- src/lib/lisk/lisk-constants.ts | 12 ++++ src/lib/lisk/lisk-utils.ts | 71 +++++++++++++++++++++-- 4 files changed, 125 insertions(+), 6 deletions(-) diff --git a/src/lib/__tests__/lisk/lisk-utils.spec.ts b/src/lib/__tests__/lisk/lisk-utils.spec.ts index 2cf6a52e7..eb80fac98 100644 --- a/src/lib/__tests__/lisk/lisk-utils.spec.ts +++ b/src/lib/__tests__/lisk/lisk-utils.spec.ts @@ -1,9 +1,11 @@ // @vitest-environment node // Some crypto libs throw errors when using `jsdom` environment +import { LSK_MIN_REQUIRED_FEE } from '@/lib/lisk' +import { convertBeddowsToLSK } from '@liskhq/lisk-transactions' import { describe, it, expect } from 'vitest' import { Cryptos } from '@/lib/constants' -import { getAccount } from '@/lib/lisk/lisk-utils' +import { estimateFee, getAccount } from '@/lib/lisk/lisk-utils' const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' @@ -13,4 +15,28 @@ describe('lisk-utils', () => { expect(getAccount(Cryptos.LSK, passphrase)).toMatchSnapshot() }) }) + + describe('estimateFee', () => { + const MIN_FEE = convertBeddowsToLSK(LSK_MIN_REQUIRED_FEE.toString()) + + it('should return default fee', () => { + expect(estimateFee()).toBe(MIN_FEE) + }) + + it('should meet minimum required fee', () => { + expect( + estimateFee({ + amount: '0.01' + }) + ).toBe(MIN_FEE) + }) + + it('should calculate fee including `data`', () => { + const data = 'hello' + const messageFee = data.length * 1000 + const expectedFee = LSK_MIN_REQUIRED_FEE + messageFee + + expect(estimateFee({ data })).toBe(convertBeddowsToLSK(expectedFee.toString())) + }) + }) }) diff --git a/src/lib/lisk/lisk-account.ts b/src/lib/lisk/lisk-account.ts index 47831757a..0c17c50cf 100644 --- a/src/lib/lisk/lisk-account.ts +++ b/src/lib/lisk/lisk-account.ts @@ -1,5 +1,5 @@ import { bytesToHex } from '@/lib/hex' -import { createTransaction } from '@/lib/lisk/lisk-utils.ts' +import { createTransaction, estimateFee } from '@/lib/lisk/lisk-utils.ts' import { LiskHashSettings } from './lisk-constants' import { address as liskAddress } from '@liskhq/lisk-cryptography' import pbkdf2 from 'pbkdf2' @@ -56,4 +56,22 @@ export class LiskAccount { return encodedTransaction } + + /** + * Estimate transaction fee by LSK amount. + * + * @param amount LSK amount + * @param data Transaction data field + */ + estimateFee(amount?: string, data?: string) { + return estimateFee({ + amount, + keyPair: { + publicKey: this.publicKey.toString('hex'), + secretKey: this.secretKey.toString('hex') + }, + recipientAddress: this.address.toString('hex'), + data + }) + } } diff --git a/src/lib/lisk/lisk-constants.ts b/src/lib/lisk/lisk-constants.ts index d2d4a4950..885261a65 100644 --- a/src/lib/lisk/lisk-constants.ts +++ b/src/lib/lisk/lisk-constants.ts @@ -10,4 +10,16 @@ export const LiskHashSettings = { export const LSK_CHAIN_ID = '00000000' export const LSK_TOKEN_ID = '0000000000000000' export const LSK_DECIMALS = CryptosInfo.LSK.decimals +export const LSK_MIN_REQUIRED_FEE = 164000 // in beddows export const LSK_TXS_PER_PAGE = 25 // transactions per page + +// Used for estimating fee +// Generated from passphrase: joy mouse injury soft decade bid rough about alarm wreck season sting +export const LSK_DEMO_ACCOUNT = { + address: 'lskkjurzk3xb47scma49ukyqupn8vrg2ggyuehk5j', + keyPair: { + publicKey: '32eb8aff28d635ba03b2bb6cc785072c0c01123a24f56e580ff9c7320611f11d', + secretKey: + 'fca383b538589f972742cb87033ef7894fff7a7b6d52c74bddf7e4f7f8660a0d32eb8aff28d635ba03b2bb6cc785072c0c01123a24f56e580ff9c7320611f11d' + } +} diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 74e9c271e..42cb97ca9 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -8,12 +8,19 @@ import { DecodedTransaction, Transaction } from './types/lisk' import { codec } from '@liskhq/lisk-codec' import * as cryptography from '@liskhq/lisk-cryptography' import networks from '@/lib/lisk/networks' -import { convertBeddowsToLSK } from '@liskhq/lisk-transactions' +import { computeMinFee, convertBeddowsToLSK, convertLSKToBeddows } from '@liskhq/lisk-transactions' import * as transactions from '@liskhq/lisk-transactions' import { Buffer } from 'buffer' import pbkdf2 from 'pbkdf2' import sodium from 'sodium-browserify-tweetnacl' -import { LiskHashSettings, LSK_CHAIN_ID, LSK_DECIMALS, LSK_TOKEN_ID } from './lisk-constants' +import { + LiskHashSettings, + LSK_CHAIN_ID, + LSK_DECIMALS, + LSK_DEMO_ACCOUNT, + LSK_MIN_REQUIRED_FEE, + LSK_TOKEN_ID +} from './lisk-constants' import { bytesToHex } from '@/lib/hex' import * as validator from '@liskhq/lisk-validator' @@ -193,6 +200,62 @@ export function encodeTransaction(transaction: DecodedTransaction | Transaction) return encodedTransaction.toString('hex') } -export function estimateFee() { - return convertBeddowsToLSK('164000') +type EstimateFeeParams = { + /** + * LSK amount + */ + amount?: string + /** + * Sender's `publicKey` and `privateKey` + */ + keyPair?: { publicKey: string; secretKey: string } + /** + * Recipient address in Lisk32 format + */ + recipientAddress?: string + /** + * Transaction data field + */ + data?: string +} + +/** + * Estimate transaction fee by LSK amount. + * + * @param params Transaction params + */ +export function estimateFee(params?: EstimateFeeParams) { + const { + amount = '1', + keyPair = LSK_DEMO_ACCOUNT.keyPair, + recipientAddress = LSK_DEMO_ACCOUNT.address, + data = '' + } = params || {} + + const unsignedTransaction = { + module: 'token', + command: 'transfer', + fee: BigInt(0), + nonce: BigInt(0), + senderPublicKey: Buffer.from(keyPair.publicKey, 'hex'), + params: { + tokenID: Buffer.from(LSK_TOKEN_ID, 'hex'), + amount: BigInt(convertLSKToBeddows(amount)), + recipientAddress: cryptography.address.getAddressFromLisk32Address(recipientAddress), + data + }, + signatures: [] + } + + const signedTransaction = transactions.signTransaction( + unsignedTransaction, + Buffer.from(LSK_CHAIN_ID, 'hex'), + Buffer.from(keyPair.secretKey, 'hex'), + TRANSACTION_PARAMS_SCHEMA + ) + + const minFee = computeMinFee(signedTransaction, TRANSACTION_PARAMS_SCHEMA) + const fee = minFee < LSK_MIN_REQUIRED_FEE ? LSK_MIN_REQUIRED_FEE : minFee + + return convertBeddowsToLSK(fee.toString()) } From 4544330cf067b63d9d2867d43ebb96fe682b46c6 Mon Sep 17 00:00:00 2001 From: bludnic Date: Wed, 13 Dec 2023 13:17:06 +0000 Subject: [PATCH 19/29] feat(tests, lisk-api): remove unused imports --- src/lib/__tests__/lisk/lisk-api.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/__tests__/lisk/lisk-api.spec.ts b/src/lib/__tests__/lisk/lisk-api.spec.ts index 9a77d5825..70297b2b7 100644 --- a/src/lib/__tests__/lisk/lisk-api.spec.ts +++ b/src/lib/__tests__/lisk/lisk-api.spec.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest' import * as cryptography from '@liskhq/lisk-cryptography' -import LiskApi, { getAccount } from '@/lib/lisk/lisk-api' +import LiskApi from '@/lib/lisk/lisk-api' describe('lisk-api', () => { const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' From f006da46c6442ec68dec6948ff881e7a0376788e Mon Sep 17 00:00:00 2001 From: bludnic Date: Thu, 14 Dec 2023 15:26:32 +0000 Subject: [PATCH 20/29] refactor(`lisk-account`): remove redundant types --- src/lib/lisk/lisk-account.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/lisk/lisk-account.ts b/src/lib/lisk/lisk-account.ts index 0c17c50cf..0f7c5efa0 100644 --- a/src/lib/lisk/lisk-account.ts +++ b/src/lib/lisk/lisk-account.ts @@ -18,8 +18,7 @@ export class LiskAccount { LiskHashSettings.KEYLEN, LiskHashSettings.DIGEST ) - const keyPair: { publicKey: Buffer; secretKey: Buffer } = - sodium.crypto_sign_seed_keypair(liskSeed) + const keyPair = sodium.crypto_sign_seed_keypair(liskSeed) this.publicKey = keyPair.publicKey this.secretKey = keyPair.secretKey From a4593ac892d9bfcd3301c4a40d212896f4658928 Mon Sep 17 00:00:00 2001 From: bludnic Date: Thu, 14 Dec 2023 15:33:08 +0000 Subject: [PATCH 21/29] refactor: remove `lisk-api` (use `lisk-account` instead) --- .../lisk/__snapshots__/lisk-api.spec.ts.snap | 50 ---- src/lib/__tests__/lisk/lisk-api.spec.ts | 27 --- src/lib/lisk/lisk-api.js | 205 ---------------- src/lib/lisk/lsk-base-api.js | 221 ------------------ .../modules/lsk-base/lsk-base-actions.js | 10 +- src/store/modules/lsk/lsk-actions.js | 2 - 6 files changed, 1 insertion(+), 514 deletions(-) delete mode 100644 src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap delete mode 100644 src/lib/__tests__/lisk/lisk-api.spec.ts delete mode 100644 src/lib/lisk/lisk-api.js delete mode 100644 src/lib/lisk/lsk-base-api.js diff --git a/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap b/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap deleted file mode 100644 index 0ae1e7ed4..000000000 --- a/src/lib/__tests__/lisk/__snapshots__/lisk-api.spec.ts.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`lisk-api > createTransaction 1`] = ` -{ - "hex": { - "asset": { - "amount": "100000000", - "data": "", - "recipientAddress": "a721205101325db197296c291ac08778a5fc7ff1", - }, - "assetID": 0, - "fee": "141000", - "moduleID": 2, - "nonce": "3", - "senderPublicKey": "32eb8aff28d635ba03b2bb6cc785072c0c01123a24f56e580ff9c7320611f11d", - "signatures": [ - "da4c262f70ad97e62c8e6209a65af08b095a450ff9cc0100cbd039777070ce3fe7fbdfc267e5db28a8e66c46642fe515ace88a31da9eebf79432e0d5f7f0e30d", - ], - }, - "txid": "437654f398820a7c01c2231dd5752e8198b2a613b7423e8eba034fcbf2aa8de2", -} -`; - -exports[`lisk-api > lisk.cryptography.getAddressFromBase32Address 1`] = ` -{ - "data": [ - 167, - 33, - 32, - 81, - 1, - 50, - 93, - 177, - 151, - 41, - 108, - 41, - 26, - 192, - 135, - 120, - 165, - 252, - 127, - 241, - ], - "type": "Buffer", -} -`; diff --git a/src/lib/__tests__/lisk/lisk-api.spec.ts b/src/lib/__tests__/lisk/lisk-api.spec.ts deleted file mode 100644 index 70297b2b7..000000000 --- a/src/lib/__tests__/lisk/lisk-api.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -// @vitest-environment node -// Some crypto libs throw errors when using `jsdom` environment - -import { describe, expect, it } from 'vitest' -import * as cryptography from '@liskhq/lisk-cryptography' -import LiskApi from '@/lib/lisk/lisk-api' - -describe('lisk-api', () => { - const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' - const lskAddress = 'lskkjurzk3xb47scma49ukyqupn8vrg2ggyuehk5j' - - it('createTransaction', async () => { - const api = new LiskApi(passphrase) - - const transaction = await api.createTransaction(lskAddress, 1, 0.00141, 3) - - expect(transaction).toMatchSnapshot() - }) - - it('lisk.cryptography.getAddressFromBase32Address', () => { - expect(cryptography.address.getAddressFromLisk32Address(lskAddress)).toMatchSnapshot() - }) - - it('lisk.cryptography.validateBase32Address', () => { - expect(cryptography.address.validateLisk32Address(lskAddress)).toBe(true) - }) -}) diff --git a/src/lib/lisk/lisk-api.js b/src/lib/lisk/lisk-api.js deleted file mode 100644 index 8e5264e76..000000000 --- a/src/lib/lisk/lisk-api.js +++ /dev/null @@ -1,205 +0,0 @@ -import { LSK_CHAIN_ID } from './lisk-constants' -import { TRANSACTION_PARAMS_SCHEMA } from './lisk-schemas' -import { Buffer } from 'buffer' -import LskBaseApi from './lsk-base-api' -import { Cryptos } from '../constants' -import { - getMillisTimestamp, - getLiskTimestamp, - createUnsignedTransaction, - encodeTransaction, - getAccount -} from './lisk-utils' -import { bytesToHex } from '@/lib/hex' -import * as transactions from '@liskhq/lisk-transactions' -import { lsk } from '@/lib/nodes/lsk' - -export const TX_CHUNK_SIZE = 25 - -export default class LiskApi extends LskBaseApi { - constructor(passphrase) { - super(Cryptos.LSK, passphrase) - const account = getAccount(Cryptos.LSK, passphrase) - this._network = account.network - this._keyPair = account.keyPair - this._address = account.address - this._addressHexBinary = account.addressHexBinary - this._addressHex = account.addressHex - this._addressLegacy = account.addressLegacy - this.passphrase = passphrase - } - - /** - * Get asset Id - * @override - */ - get assetId() { - return 0 - } - - /** - * Get asset schema - * @override - */ - get assetSchema() { - return { - $id: 'lisk/transfer-asset', - title: 'Transfer transaction asset', - type: 'object', - required: ['amount', 'recipientAddress', 'data'], - properties: { - amount: { - dataType: 'uint64', - fieldNumber: 1 - }, - recipientAddress: { - dataType: 'bytes', - fieldNumber: 2, - minLength: 20, - maxLength: 20 - }, - data: { - dataType: 'string', - fieldNumber: 3, - minLength: 0, - maxLength: 64 - } - } - } - } - - /** @override */ - getAccount() { - return this._get(`/api/accounts/${this.addressHex}`, {}).then((data) => { - const account = {} - if (data && data.data && data.data.token && data.data.token.balance) { - account.balance = data.data.token.balance / this.multiplier - } - if (data && data.data && data.data.sequence && data.data.sequence.nonce) { - account.nonce = data.data.sequence.nonce - } - return account - }) - } - - /** @override */ - getHeight() { - return this._get('/api/node/info').then((data) => { - return Number(data.data.height) || 0 - }) - } - - /** - * Creates a transfer transaction hex (signed JSON tx object) and ID - * Signed JSON tx object is ready for broadcasting to blockchain network - * @override - * @param {string} address receiver address in Base32 format - * @param {number} amount amount to transfer (coins, not satoshis) - * @param {number} fee transaction fee (coins, not satoshis) - * @param {number} nonce transaction nonce - * @param {string} data transaction data field - * @returns {Promise<{hex: string, txid: string}>} - */ - createTransaction(address = '', amount = 0, fee, nonce, data = '') { - const unsignedTransaction = createUnsignedTransaction( - address, - Buffer.from(this._keyPair.publicKey).toString('hex'), - amount, - fee, - nonce, - data - ) - - const signedTransaction = transactions.signTransaction( - unsignedTransaction, - Buffer.from(LSK_CHAIN_ID, 'hex'), - this._keyPair.secretKey, - TRANSACTION_PARAMS_SCHEMA - ) - - const txid = bytesToHex(signedTransaction.id) - - return Promise.resolve({ hex: encodeTransaction(signedTransaction), txid }) - } - - /** @override */ - sendTransaction(signedTx) { - return lsk - .getClient() - .post('/api/transactions', signedTx) - .then((response) => { - return response.data.data.transactionId - }) - } - - /** @override */ - getTransaction(txid) { - return this._getService('/api/v2/transactions/', { transactionId: txid }).then((data) => { - if (data && data.data[0]) { - return this._mapTransaction(data.data[0]) - } - }) - } - - /** @override */ - getTransactions(options = {}) { - const url = '/api/v2/transactions/' - options.moduleAssetId = `${this.moduleId}:${this.assetId}` - options.limit = TX_CHUNK_SIZE - options.address = this.address - // options.includePending = true // workaround on Lisk Gateway bug v0.5.0 https://github.com/LiskHQ/lisk-service/issues/883 - if (options.toTimestamp || options.fromTimestamp) { - options.toTimestamp = options.toTimestamp || Date.now() - options.fromTimestamp = options.fromTimestamp || 0 - options.timestamp = `${getLiskTimestamp(options.fromTimestamp) + 1}:${ - getLiskTimestamp(options.toTimestamp) - 1 - }` - } - delete options.toTimestamp - delete options.fromTimestamp - // additional options: offset, height, and others - - return ( - this._getService(url, options) - .then((transactions) => { - if (transactions && transactions.data) { - const mappedTxs = transactions.data.map((tx) => this._mapTransaction(tx)) - return mappedTxs - } - }) - // Unfortunately, Lisk Service returns 404 for empty results - // https://github.com/LiskHQ/lisk-service/issues/698 - .catch(() => { - return [] - }) - ) - } - - /** @override */ - _mapTransaction(tx) { - const mapped = super._mapTransaction({ - ...tx - }) - - mapped.amount /= this.multiplier - mapped.fee /= this.multiplier - mapped.timestamp = getMillisTimestamp(mapped.timestamp) - - return mapped - } - - /** Executes a GET request to the node's core API */ - _get(url, params) { - return lsk - .getClient() - .get(url, { params }) - .then((response) => response.data) - } - - /** Executes a GET request to the Lisk Service API */ - _getService(url, params) { - return this._getServiceClient() - .get(url, { params }) - .then((response) => response.data) - } -} diff --git a/src/lib/lisk/lsk-base-api.js b/src/lib/lisk/lsk-base-api.js deleted file mode 100644 index 1742986c5..000000000 --- a/src/lib/lisk/lsk-base-api.js +++ /dev/null @@ -1,221 +0,0 @@ -import { bytesToHex } from '@/lib/hex' -import axios from 'axios' -import { getRandomServiceUrl } from '@/config/utils' -import { isStringEqualCI } from '@/lib/textHelpers' -import * as cryptography from '@liskhq/lisk-cryptography' -import * as transactions from '@liskhq/lisk-transactions' -import { CryptosInfo } from '../constants' - -export const TX_DEFAULT_FEE = 0.0016 - -const createServiceClient = (url) => { - const client = axios.create({ baseURL: url }) - client.interceptors.response.use(null, (error) => { - if (error.response && Number(error.response.status) >= 500) { - console.error(`Request to ${url} failed.`, error) - } - return Promise.reject(error) - }) - return client -} - -export default class LskBaseApi { - /** - * Constructor - * @abstract - */ - constructor(crypto, _passphrase) { - this._clients = {} - this._crypto = crypto - this._network = undefined - this._keyPair = undefined - this._address = undefined - } - - get decimals() { - return CryptosInfo[this._crypto].decimals - } - - get multiplier() { - return 1e8 - } - - /** - * Get Token/Send module Id - * @returns {number} - */ - get moduleId() { - return 2 - } - - get chainId() { - return '00000001' - } - - /** - * Get asset Id - * @abstract - * @returns {number} - */ - get assetId() { - return undefined - } - - get address() { - return this._address - } - - get addressHex() { - return this._addressHex - } - - /** - * Get asset schema - * @abstract - * @returns {object} JSON schema - */ - get assetSchema() { - return {} - } - - get networkIdentifier() { - // Testnet: '15f0dacc1060e91818224a94286b13aa04279c640bd5d6f193182031d133df7c' - // Mainnet: '4c09e6a781fc4c7bdb936ee815de8f94190f8a7519becd9de2081832be309a99' - const networkIdentifier = '4c09e6a781fc4c7bdb936ee815de8f94190f8a7519becd9de2081832be309a99' - console.log('chainID', bytesToHex(Buffer.from('00000001', 'hex'))) - return Buffer.from(networkIdentifier, 'hex') - } - - /** - * Retrieves current balance and nonce - * @abstract - * @returns {Promise} - */ - getAccount() { - return Promise.resolve(0) - } - - /** - * Returns last block height - * @abstract - * @returns {Promise} - */ - getHeight() { - return Promise.resolve(0) - } - - /** - * Calculates fee for a Tx - * @abstract - * @returns {BigInt} - */ - getFee(address = '', amount = 0, nonce, data = '') { - const tx = this._buildTransaction(address, amount, TX_DEFAULT_FEE, nonce, data) - return tx && tx.minFee ? tx.minFee : TX_DEFAULT_FEE - } - - /** - * Creates an LSK-based transaction as an object with specific types - * @returns {object} - */ - _buildTransaction(address, amount, fee, nonce, data = '') { - const amountString = transactions.convertLSKToBeddows((+amount).toFixed(this.decimals)) - const feeString = transactions.convertLSKToBeddows((+fee).toFixed(this.decimals)) - const nonceString = nonce.toString() - const liskTx = { - moduleID: this.moduleId, - assetID: this.assetId, - nonce: BigInt(nonceString), - fee: BigInt(feeString), - asset: { - amount: BigInt(amountString), - recipientAddress: cryptography.address.getAddressFromLisk32Address(address), - data - // data: 'Sent with ADAMANT Messenger' - }, - signatures: [] - } - liskTx.senderPublicKey = this._keyPair.publicKey - const minFee = Number(transactions.computeMinFee(this.assetSchema, liskTx)) / this.multiplier - - return { liskTx, minFee } - } - - /** - * Creates a transfer transaction hex (signed JSON tx object) and ID - * Signed JSON tx object is ready for broadcasting to blockchain network - * @abstract - * @param {string} address receiver address in Base32 format - * @param {number} amount amount to transfer (coins, not satoshis) - * @param {number} fee transaction fee (coins, not satoshis) - * @param {number} nonce transaction nonce - * @param {string} data transaction data field - * @returns {Promise<{hex: string, txid: string}>} - */ - createTransaction(_address = '', _amount = 0, _fee, _nonce, _data) { - return Promise.resolve({ hex: undefined, txid: undefined }) - } - - /** - * Broadcasts the specified transaction to the network. - * @abstract - * @param {string} txHex raw transaction as a HEX literal - */ - sendTransaction(_txHex) { - return Promise.resolve('') - } - - /** - * Retrieves transaction details - * @abstract - * @param {*} txid transaction ID - * @returns {Promise} - */ - getTransaction(_txid) { - return Promise.resolve(null) - } - - /** - * Retrieves transactions for the specified address - * @abstract - * @param {any} options crypto-specific options - * @returns {Promise<{hasMore: boolean, items: Array}>} - */ - getTransactions(_options) { - return Promise.resolve({ hasMore: false, items: [] }) - } - - /** Picks a Lisk Service client for a random API endpoint */ - _getServiceClient() { - const url = getRandomServiceUrl(this._crypto.toLowerCase(), 'lskService') - if (!this._clients[url]) { - this._clients[url] = createServiceClient(url) - } - return this._clients[url] - } - - _mapTransaction(tx) { - const direction = isStringEqualCI(tx.sender.address, this._address) ? 'from' : 'to' - // no confirmations field - // additional data: asset, receivedAt, blockId, height, type, recipientPublicKey, senderSecondPublicKey - return { - id: tx.id, - hash: tx.id, - fee: tx.fee, - status: tx.height ? 'CONFIRMED' : 'REGISTERED', - data: tx.asset.data, - timestamp: tx.block.timestamp, - direction, - senderId: tx.sender.address, - recipientId: tx.asset.recipient.address, - amount: tx.asset.amount, - confirmations: tx.confirmations, - height: tx.height, - nonce: tx.nonce, - moduleId: tx.moduleAssetId.split(':')[0], - assetId: tx.moduleAssetId.split(':')[1], - moduleName: tx.moduleAssetName.split(':')[0], - assetName: tx.moduleAssetName.split(':')[1] - } - } -} diff --git a/src/store/modules/lsk-base/lsk-base-actions.js b/src/store/modules/lsk-base/lsk-base-actions.js index af150521f..d09a3d3a3 100644 --- a/src/store/modules/lsk-base/lsk-base-actions.js +++ b/src/store/modules/lsk-base/lsk-base-actions.js @@ -1,7 +1,6 @@ import BigNumber from '@/lib/bignumber' import { LiskAccount, LSK_TXS_PER_PAGE } from '../../../lib/lisk' import { getLiskTimestamp } from '../../../lib/lisk/lisk-utils' -import LskBaseApi from '../../../lib/lisk/lsk-base-api' import { storeCryptoAddress } from '../../../lib/store-crypto-address' import * as tf from '../../../lib/transactionsFetching' import { lsk } from '../../../lib/nodes/lsk' @@ -11,7 +10,6 @@ const DEFAULT_CUSTOM_ACTIONS = () => ({}) /** * @typedef {Object} Options - * @property {function} apiCtor class to use for interaction with the crypto API * @property {function(LskBaseApi, object): Promise} getNewTransactions function to get the new transactions list (second arg is a Vuex context) * @property {function(LskBaseApi, object): Promise} getOldTransactions function to get the old transactions list (second arg is a Vuex context) * @property {function(function(): LskBaseApi): object} customActions function to create custom actions for the current crypto (optional) @@ -23,18 +21,15 @@ const DEFAULT_CUSTOM_ACTIONS = () => ({}) * @param {Options} options config options */ function createActions(options) { - const Api = options.apiCtor || LskBaseApi const { customActions = DEFAULT_CUSTOM_ACTIONS, fetchRetryTimeout } = options /** @type {LiskAccount | null} */ - let api = null let account = null return { afterLogin: { root: true, handler(context, passphrase) { - api = new Api(passphrase) account = new LiskAccount(passphrase) context.commit('address', account.getLisk32Address()) @@ -47,7 +42,6 @@ function createActions(options) { reset: { root: true, handler(context) { - api = null account = null context.commit('reset') } @@ -59,9 +53,8 @@ function createActions(options) { handler(context) { const passphrase = context.rootGetters.getPassPhrase if (passphrase) { - api = new Api(passphrase) - account = new LiskAccount(passphrase) + context.commit('address', account.getLisk32Address()) context.dispatch('updateStatus') context.dispatch('storeAddress') @@ -140,7 +133,6 @@ function createActions(options) { * @param {{hash: string, force: boolean, timestamp: number, amount: number}} payload hash and timestamp of the transaction to fetch */ async getTransaction(context, payload) { - if (!api) return if (!payload.hash) return let existing = context.state.transactions[payload.hash] diff --git a/src/store/modules/lsk/lsk-actions.js b/src/store/modules/lsk/lsk-actions.js index 30a82a37d..d0bd6f0c6 100644 --- a/src/store/modules/lsk/lsk-actions.js +++ b/src/store/modules/lsk/lsk-actions.js @@ -1,6 +1,5 @@ import { FetchStatus } from '@/lib/constants' import baseActions from '../lsk-base/lsk-base-actions' -import LskApi from '../../../lib/lisk/lisk-api' import { lsk } from '../../../lib/nodes/lsk' const TX_FETCH_INTERVAL = 10 * 1000 @@ -79,7 +78,6 @@ const customActions = (getAccount) => ({ export default { ...baseActions({ - apiCtor: LskApi, customActions, fetchRetryTimeout: TX_FETCH_INTERVAL }) From be180daab874d3a956d6212a8ec0dead1f3a0aa5 Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 19 Dec 2023 13:39:28 +0000 Subject: [PATCH 22/29] Merge pull request #58 from Adamant-im/dev Release 2.2.0 --- adamant-wallets | 2 +- src/config/development.json | 34 ++- src/config/production.json | 34 ++- src/config/tor.json | 17 +- src/lib/constants/cryptos/data.json | 414 ++++++++++++++-------------- 5 files changed, 259 insertions(+), 242 deletions(-) diff --git a/adamant-wallets b/adamant-wallets index c81f215b2..6742dab5c 160000 --- a/adamant-wallets +++ b/adamant-wallets @@ -1 +1 @@ -Subproject commit c81f215b290da5279b3cbf5b7266d354f628a776 +Subproject commit 6742dab5ce9b08f6e85d6d21351f954d89f7b68e diff --git a/src/config/development.json b/src/config/development.json index d8da6c858..3bc1d7f87 100644 --- a/src/config/development.json +++ b/src/config/development.json @@ -1,7 +1,7 @@ { "adm": { "explorerTx": "https://explorer.adamant.im/tx/${ID}", - "minNodeVersion": "0.7.0", + "minNodeVersion": "0.8.0", "nodes": [ { "url": "https://clown.adamant.im" @@ -33,17 +33,21 @@ "url": "http://184.94.215.92:45555" }, { - "url": "https://node1.adamant.business" + "url": "https://node1.adamant.business", + "alt_ip": "http://194.233.75.29:45555" }, { "url": "https://node2.blockchain2fa.io" + }, + { + "url": "https://sunshine.adamant.im" } ], "services": { "infoService": [ { "url": "https://info.adamant.im", - "alt_ip": "http://88.198.156.44:80" + "alt_ip": "http://88.198.156.44:44099" } ] }, @@ -69,7 +73,12 @@ "explorerTx": "https://dashblockexplorer.com/tx/${ID}", "nodes": [ { - "url": "https://dashnode1.adamant.im" + "url": "https://dashnode1.adamant.im", + "alt_ip": "http://45.85.147.224:44099" + }, + { + "url": "https://dashnode2.adamant.im", + "alt_ip": "http://207.180.210.95:44099" } ], "explorer": "https://dashblockexplorer.com", @@ -79,7 +88,8 @@ "explorerTx": "https://dogechain.info/tx/${ID}", "nodes": [ { - "url": "https://dogenode1.adamant.im" + "url": "https://dogenode1.adamant.im", + "alt_ip": "http://5.9.99.62:44099" }, { "url": "https://dogenode2.adamant.im", @@ -110,23 +120,19 @@ "explorerTx": "https://liskscan.com/transaction/${ID}", "nodes": [ { - "url": "https://lisknode3.adamant.im" - }, - { - "url": "https://lisknode4.adamant.im" + "url": "https://lisknode5.adamant.im", + "alt_ip": "http://38.242.243.29:44099" } ], "services": { "lskService": [ { - "url": "https://liskservice3.adamant.im" - }, - { - "url": "https://liskservice4.adamant.im" + "url": "https://liskservice5.adamant.im", + "alt_ip": "http://38.242.243.29:44098" } ] }, "explorer": "https://liskscan.com", "explorerAddress": "https://liskscan.com/account/${ID}" } -} +} \ No newline at end of file diff --git a/src/config/production.json b/src/config/production.json index 976551ef5..a4255fe34 100644 --- a/src/config/production.json +++ b/src/config/production.json @@ -1,6 +1,6 @@ { "adm": { - "minNodeVersion": "0.7.0", + "minNodeVersion": "0.8.0", "nodes": [ { "url": "https://clown.adamant.im" @@ -32,17 +32,21 @@ "url": "http://184.94.215.92:45555" }, { - "url": "https://node1.adamant.business" + "url": "https://node1.adamant.business", + "alt_ip": "http://194.233.75.29:45555" }, { "url": "https://node2.blockchain2fa.io" + }, + { + "url": "https://sunshine.adamant.im" } ], "services": { "infoService": [ { "url": "https://info.adamant.im", - "alt_ip": "http://88.198.156.44:80" + "alt_ip": "http://88.198.156.44:44099" } ] }, @@ -68,7 +72,12 @@ "dash": { "nodes": [ { - "url": "https://dashnode1.adamant.im" + "url": "https://dashnode1.adamant.im", + "alt_ip": "http://45.85.147.224:44099" + }, + { + "url": "https://dashnode2.adamant.im", + "alt_ip": "http://207.180.210.95:44099" } ], "explorerTx": "https://dashblockexplorer.com/tx/${ID}", @@ -78,7 +87,8 @@ "doge": { "nodes": [ { - "url": "https://dogenode1.adamant.im" + "url": "https://dogenode1.adamant.im", + "alt_ip": "http://5.9.99.62:44099" }, { "url": "https://dogenode2.adamant.im", @@ -109,24 +119,20 @@ "lsk": { "nodes": [ { - "url": "https://lisknode3.adamant.im" - }, - { - "url": "https://lisknode4.adamant.im" + "url": "https://lisknode5.adamant.im", + "alt_ip": "http://38.242.243.29:44099" } ], "explorerTx": "https://liskscan.com/transaction/${ID}", "services": { "lskService": [ { - "url": "https://liskservice3.adamant.im" - }, - { - "url": "https://liskservice4.adamant.im" + "url": "https://liskservice5.adamant.im", + "alt_ip": "http://38.242.243.29:44098" } ] }, "explorer": "https://liskscan.com", "explorerAddress": "https://liskscan.com/account/${ID}" } -} +} \ No newline at end of file diff --git a/src/config/tor.json b/src/config/tor.json index a1ffc9241..fe67a6427 100644 --- a/src/config/tor.json +++ b/src/config/tor.json @@ -1,6 +1,6 @@ { "adm": { - "minNodeVersion": "0.7.0", + "minNodeVersion": "0.8.0", "nodes": [ { "url": "http://37g5to2z6bdoeegun4hms2hvkfbdxh4rcon4e3p267wtfkh4ji2ns6id.onion" @@ -46,6 +46,9 @@ "nodes": [ { "url": "http://ldod53womnlwjmd4noq5yuq26xx3mmbrr4uogkfymuakhofjcuqeygad.onion" + }, + { + "url": "http://eq2blda45h4kakbuq3g4stcgatiwqaefgmxzfsfuiahpdbt2pvbbepad.onion" } ], "explorerTx": "https://dashblockexplorer.com/tx/${ID}", @@ -83,24 +86,18 @@ "lsk": { "nodes": [ { - "url": "http://nl2c4vcsdrd6je3ebt4236yst55mbso72ojmksrmlyoikapgnu647tid.onion" - }, - { - "url": "http://qpjxvkathebm7oso3dk5hb4cggbeecqvcpcthuxlpovtn66plr6f4wyd.onion" + "url": "http://4bsg7dijnpkiu6wylaad5fiom4iep73oz3o2rojkwnuteaawd2w3o2ad.onion" } ], "explorerTx": "https://liskscan.com/transaction/${ID}", "services": { "lskService": [ { - "url": "http://xqrymra5go2nb3cslb3c3iv4weeuicybcxpjee4ay2indk2hgo7rw4qd.onion" - }, - { - "url": "http://s2q6phc22icy73zjccjq66555gwtvyuyrodmadwhjdhmvyehb3qrcsid.onion" + "url": "http://cf6vf6xylvhza3t3ewffa7cmr2bqgaus5jwghiktjleids5ovdhoazad.onion" } ] }, "explorer": "https://liskscan.com", "explorerAddress": "https://liskscan.com/account/${ID}" } -} +} \ No newline at end of file diff --git a/src/lib/constants/cryptos/data.json b/src/lib/constants/cryptos/data.json index 3ffb39a3d..faa8a082f 100644 --- a/src/lib/constants/cryptos/data.json +++ b/src/lib/constants/cryptos/data.json @@ -1,63 +1,4 @@ { - "BNB": { - "symbol": "BNB", - "name": "Binance Coin", - "decimals": 18, - "contractId": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", - "createCoin": false, - "cryptoTransferDecimals": 6, - "defaultGasLimit": 58000, - "status": "active", - "mainCoin": "ETH", - "type": "ERC20", - "fees": "ethereum" - }, - "BUSD": { - "symbol": "BUSD", - "name": "Binance USD", - "decimals": 18, - "contractId": "0x4fabb145d64652a948d72533023f6e7a623c7c53", - "createCoin": false, - "cryptoTransferDecimals": 6, - "defaultGasLimit": 58000, - "status": "active", - "mainCoin": "ETH", - "type": "ERC20", - "fees": "ethereum" - }, - "BTC": { - "symbol": "BTC", - "name": "Bitcoin", - "qrPrefix": "bitcoin", - "minBalance": 0.00001, - "regexAddress": "^bc1[a-zA-Z0-9]{39,59}$|^[13][1-9A-HJ-NP-Za-km-z]{25,34}$", - "decimals": 8, - "minTransferAmount": 0.00000546, - "nodes": [ - { - "url": "https://btcnode1.adamant.im", - "alt_ip": "http://176.9.38.204:44099" - }, - { - "url": "https://btcnode2.adamant.im", - "alt_ip": "http://176.9.32.126:44099" - } - ], - "createCoin": true, - "cryptoTransferDecimals": 8, - "defaultFee": 0.00003153, - "defaultVisibility": true, - "txFetchInfo": { - "newPendingInterval": 10000, - "oldPendingInterval": 3000, - "registeredInterval": 40000, - "newPendingAttempts": 20, - "oldPendingAttempts": 4 - }, - "txConsistencyMaxTime": 10800000, - "defaultOrdinalLevel": 10, - "explorerTx": "https://explorer.btc.com/btc/transaction/${ID}" - }, "ADM": { "symbol": "ADM", "name": "ADAMANT Messenger", @@ -96,10 +37,14 @@ "url": "http://184.94.215.92:45555" }, { - "url": "https://node1.adamant.business" + "url": "https://node1.adamant.business", + "alt_ip": "http://194.233.75.29:45555" }, { "url": "https://node2.blockchain2fa.io" + }, + { + "url": "https://sunshine.adamant.im" } ], "createCoin": true, @@ -114,51 +59,66 @@ "defaultOrdinalLevel": 0, "explorerTx": "https://explorer.adamant.im/tx/${ID}" }, - "DAI": { - "symbol": "DAI", - "name": "Dai", - "decimals": 18, - "contractId": "0x6b175474e89094c44da98b954eedeac495271d0f", + "BZZ": { + "symbol": "BZZ", + "name": "Swarm", + "decimals": 16, + "contractId": "0x19062190B1925b5b6689D7073fDfC8c2976EF8Cb", "createCoin": false, "cryptoTransferDecimals": 6, "defaultVisibility": true, "defaultGasLimit": 58000, - "defaultOrdinalLevel": 50, + "defaultOrdinalLevel": 95, "status": "active", "mainCoin": "ETH", "type": "ERC20", "fees": "ethereum" }, - "DOGE": { - "symbol": "DOGE", - "name": "Dogecoin", - "qrPrefix": "doge", - "regexAddress": "^[A|D|9][A-Z0-9]([0-9a-zA-Z]{9,})$", + "BNB": { + "symbol": "BNB", + "name": "Binance Coin", + "decimals": 18, + "contractId": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52", + "createCoin": false, + "cryptoTransferDecimals": 6, + "defaultGasLimit": 58000, + "status": "active", + "mainCoin": "ETH", + "type": "ERC20", + "fees": "ethereum" + }, + "BTC": { + "symbol": "BTC", + "name": "Bitcoin", + "qrPrefix": "bitcoin", + "minBalance": 0.00001, + "regexAddress": "^bc1[a-zA-Z0-9]{39,59}$|^[13][1-9A-HJ-NP-Za-km-z]{25,34}$", "decimals": 8, - "minTransferAmount": 1, + "minTransferAmount": 0.00000546, "nodes": [ { - "url": "https://dogenode1.adamant.im" + "url": "https://btcnode1.adamant.im", + "alt_ip": "http://176.9.38.204:44099" }, { - "url": "https://dogenode2.adamant.im", - "alt_ip": "http://176.9.32.126:44098" + "url": "https://btcnode2.adamant.im", + "alt_ip": "http://176.9.32.126:44099" } ], "createCoin": true, "cryptoTransferDecimals": 8, - "fixedFee": 1, + "defaultFee": 0.00003153, "defaultVisibility": true, "txFetchInfo": { - "newPendingInterval": 5000, + "newPendingInterval": 10000, "oldPendingInterval": 3000, - "registeredInterval": 20000, + "registeredInterval": 40000, "newPendingAttempts": 20, "oldPendingAttempts": 4 }, - "txConsistencyMaxTime": 900000, - "defaultOrdinalLevel": 70, - "explorerTx": "https://dogechain.info/tx/${ID}" + "txConsistencyMaxTime": 10800000, + "defaultOrdinalLevel": 10, + "explorerTx": "https://explorer.btc.com/btc/transaction/${ID}" }, "DASH": { "symbol": "DASH", @@ -170,7 +130,12 @@ "minTransferAmount": 0.00002, "nodes": [ { - "url": "https://dashnode1.adamant.im" + "url": "https://dashnode1.adamant.im", + "alt_ip": "http://45.85.147.224:44099" + }, + { + "url": "https://dashnode2.adamant.im", + "alt_ip": "http://207.180.210.95:44099" } ], "createCoin": true, @@ -188,54 +153,80 @@ "defaultOrdinalLevel": 80, "explorerTx": "https://dashblockexplorer.com/tx/${ID}" }, - "BZZ": { - "symbol": "BZZ", - "name": "Swarm", - "decimals": 16, - "contractId": "0x19062190B1925b5b6689D7073fDfC8c2976EF8Cb", + "BUSD": { + "symbol": "BUSD", + "name": "Binance USD", + "decimals": 18, + "contractId": "0x4fabb145d64652a948d72533023f6e7a623c7c53", "createCoin": false, "cryptoTransferDecimals": 6, - "defaultVisibility": true, "defaultGasLimit": 58000, - "defaultOrdinalLevel": 95, "status": "active", "mainCoin": "ETH", "type": "ERC20", "fees": "ethereum" }, - "ETH": { - "symbol": "ETH", - "name": "Ethereum", - "qrPrefix": "ethereum", - "regexAddress": "^0x[0-9a-fA-F]{40}$", - "decimals": 18, + "DOGE": { + "symbol": "DOGE", + "name": "Dogecoin", + "qrPrefix": "doge", + "regexAddress": "^[A|D|9][A-Z0-9]([0-9a-zA-Z]{9,})$", + "decimals": 8, + "minTransferAmount": 1, "nodes": [ { - "url": "https://ethnode1.adamant.im", - "alt_ip": "http://95.216.41.106:44099", - "hasIndex": true + "url": "https://dogenode1.adamant.im", + "alt_ip": "http://5.9.99.62:44099" }, { - "url": "https://ethnode2.adamant.im", - "alt_ip": "http://95.216.114.252:44099", - "hasIndex": true + "url": "https://dogenode2.adamant.im", + "alt_ip": "http://176.9.32.126:44098" } ], "createCoin": true, - "cryptoTransferDecimals": 6, + "cryptoTransferDecimals": 8, + "fixedFee": 1, "defaultVisibility": true, - "defaultGasLimit": 22000, - "defaultGasPriceGwei": 30, "txFetchInfo": { - "newPendingInterval": 4000, + "newPendingInterval": 5000, "oldPendingInterval": 3000, - "registeredInterval": 5000, + "registeredInterval": 20000, "newPendingAttempts": 20, "oldPendingAttempts": 4 }, - "txConsistencyMaxTime": 1200000, - "defaultOrdinalLevel": 20, - "explorerTx": "https://etherscan.io/tx/${ID}" + "txConsistencyMaxTime": 900000, + "defaultOrdinalLevel": 70, + "explorerTx": "https://dogechain.info/tx/${ID}" + }, + "FLUX": { + "symbol": "FLUX", + "name": "Flux", + "decimals": 8, + "contractId": "0x720CD16b011b987Da3518fbf38c3071d4F0D1495", + "createCoin": false, + "cryptoTransferDecimals": 6, + "defaultVisibility": true, + "defaultGasLimit": 58000, + "defaultOrdinalLevel": 90, + "status": "active", + "mainCoin": "ETH", + "type": "ERC20", + "fees": "ethereum" + }, + "DAI": { + "symbol": "DAI", + "name": "Dai", + "decimals": 18, + "contractId": "0x6b175474e89094c44da98b954eedeac495271d0f", + "createCoin": false, + "cryptoTransferDecimals": 6, + "defaultVisibility": true, + "defaultGasLimit": 58000, + "defaultOrdinalLevel": 50, + "status": "active", + "mainCoin": "ETH", + "type": "ERC20", + "fees": "ethereum" }, "ENS": { "symbol": "ENS", @@ -263,6 +254,34 @@ "type": "ERC20", "fees": "ethereum" }, + "LSK": { + "symbol": "LSK", + "name": "Lisk", + "qrPrefix": "lisk", + "minBalance": 0.05, + "regexAddress": "^lsk[a-z2-9]{38}$", + "decimals": 8, + "nodes": [ + { + "url": "https://lisknode5.adamant.im", + "alt_ip": "http://38.242.243.29:44099" + } + ], + "createCoin": true, + "cryptoTransferDecimals": 8, + "defaultFee": 0.00164, + "defaultVisibility": true, + "txFetchInfo": { + "newPendingInterval": 3000, + "oldPendingInterval": 3000, + "registeredInterval": 5000, + "newPendingAttempts": 15, + "oldPendingAttempts": 4 + }, + "txConsistencyMaxTime": 60000, + "defaultOrdinalLevel": 60, + "explorerTx": "https://liskscan.com/transaction/${ID}" + }, "INJ": { "symbol": "INJ", "name": "Injective", @@ -278,26 +297,45 @@ "type": "ERC20", "fees": "ethereum" }, - "FLUX": { - "symbol": "FLUX", - "name": "Flux", - "decimals": 8, - "contractId": "0x720CD16b011b987Da3518fbf38c3071d4F0D1495", - "createCoin": false, + "ETH": { + "symbol": "ETH", + "name": "Ethereum", + "qrPrefix": "ethereum", + "regexAddress": "^0x[0-9a-fA-F]{40}$", + "decimals": 18, + "nodes": [ + { + "url": "https://ethnode1.adamant.im", + "alt_ip": "http://95.216.41.106:44099", + "hasIndex": true + }, + { + "url": "https://ethnode2.adamant.im", + "alt_ip": "http://95.216.114.252:44099", + "hasIndex": true + } + ], + "createCoin": true, "cryptoTransferDecimals": 6, "defaultVisibility": true, - "defaultGasLimit": 58000, - "defaultOrdinalLevel": 90, - "status": "active", - "mainCoin": "ETH", - "type": "ERC20", - "fees": "ethereum" + "defaultGasLimit": 22000, + "defaultGasPriceGwei": 30, + "txFetchInfo": { + "newPendingInterval": 4000, + "oldPendingInterval": 3000, + "registeredInterval": 5000, + "newPendingAttempts": 20, + "oldPendingAttempts": 4 + }, + "txConsistencyMaxTime": 1200000, + "defaultOrdinalLevel": 20, + "explorerTx": "https://etherscan.io/tx/${ID}" }, - "MANA": { - "symbol": "MANA", - "name": "Decentraland", + "LINK": { + "symbol": "LINK", + "name": "Chainlink", "decimals": 18, - "contractId": "0x0f5d2fb29fb7d3cfee444a200298f468908cc942", + "contractId": "0x514910771af9ca656af840dff83e8264ecf986ca", "createCoin": false, "cryptoTransferDecimals": 6, "defaultGasLimit": 58000, @@ -306,11 +344,11 @@ "type": "ERC20", "fees": "ethereum" }, - "LINK": { - "symbol": "LINK", - "name": "Chainlink", + "MANA": { + "symbol": "MANA", + "name": "Decentraland", "decimals": 18, - "contractId": "0x514910771af9ca656af840dff83e8264ecf986ca", + "contractId": "0x0f5d2fb29fb7d3cfee444a200298f468908cc942", "createCoin": false, "cryptoTransferDecimals": 6, "defaultGasLimit": 58000, @@ -360,59 +398,29 @@ "type": "ERC20", "fees": "ethereum" }, - "LSK": { - "symbol": "LSK", - "name": "Lisk", - "qrPrefix": "lisk", - "minBalance": 0.05, - "regexAddress": "^lsk[a-z2-9]{38}$", - "decimals": 8, - "nodes": [ - { - "url": "https://lisknode3.adamant.im" - }, - { - "url": "https://lisknode4.adamant.im" - } - ], - "createCoin": true, - "cryptoTransferDecimals": 8, - "defaultFee": 0.00142, - "defaultVisibility": true, - "txFetchInfo": { - "newPendingInterval": 3000, - "oldPendingInterval": 3000, - "registeredInterval": 5000, - "newPendingAttempts": 15, - "oldPendingAttempts": 4 - }, - "txConsistencyMaxTime": 60000, - "defaultOrdinalLevel": 60, - "explorerTx": "https://liskscan.com/transaction/${ID}" - }, - "SKL": { - "symbol": "SKL", - "name": "SKALE", + "REN": { + "symbol": "REN", + "name": "Ren", "decimals": 18, - "contractId": "0x00c83aecc790e8a4453e5dd3b0b4b3680501a7a7", + "contractId": "0x408e41876cccdc0f92210600ef50372656052a38", "createCoin": false, "cryptoTransferDecimals": 6, - "defaultVisibility": true, "defaultGasLimit": 58000, - "defaultOrdinalLevel": 85, "status": "active", "mainCoin": "ETH", "type": "ERC20", "fees": "ethereum" }, - "REN": { - "symbol": "REN", - "name": "Ren", + "SKL": { + "symbol": "SKL", + "name": "SKALE", "decimals": 18, - "contractId": "0x408e41876cccdc0f92210600ef50372656052a38", + "contractId": "0x00c83aecc790e8a4453e5dd3b0b4b3680501a7a7", "createCoin": false, "cryptoTransferDecimals": 6, + "defaultVisibility": true, "defaultGasLimit": 58000, + "defaultOrdinalLevel": 85, "status": "active", "mainCoin": "ETH", "type": "ERC20", @@ -444,24 +452,26 @@ "type": "ERC20", "fees": "ethereum" }, - "TUSD": { - "symbol": "TUSD", - "name": "TrueUSD", - "decimals": 18, - "contractId": "0x0000000000085d4780b73119b644ae5ecd22b376", + "USDC": { + "symbol": "USDC", + "name": "USD Coin", + "decimals": 6, + "contractId": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "createCoin": false, "cryptoTransferDecimals": 6, + "defaultVisibility": true, "defaultGasLimit": 58000, + "defaultOrdinalLevel": 40, "status": "active", "mainCoin": "ETH", "type": "ERC20", "fees": "ethereum" }, - "UNI": { - "symbol": "UNI", - "name": "Uniswap", + "USDP": { + "symbol": "USDP", + "name": "PAX Dollar", "decimals": 18, - "contractId": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "contractId": "0x8e870d67f660d95d5be530380d0ec0bd388289e1", "createCoin": false, "cryptoTransferDecimals": 6, "defaultGasLimit": 58000, @@ -470,26 +480,24 @@ "type": "ERC20", "fees": "ethereum" }, - "USDC": { - "symbol": "USDC", - "name": "USD Coin", - "decimals": 6, - "contractId": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "UNI": { + "symbol": "UNI", + "name": "Uniswap", + "decimals": 18, + "contractId": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", "createCoin": false, "cryptoTransferDecimals": 6, - "defaultVisibility": true, "defaultGasLimit": 58000, - "defaultOrdinalLevel": 40, "status": "active", "mainCoin": "ETH", "type": "ERC20", "fees": "ethereum" }, - "USDP": { - "symbol": "USDP", - "name": "PAX Dollar", + "TUSD": { + "symbol": "TUSD", + "name": "TrueUSD", "decimals": 18, - "contractId": "0x8e870d67f660d95d5be530380d0ec0bd388289e1", + "contractId": "0x0000000000085d4780b73119b644ae5ecd22b376", "createCoin": false, "cryptoTransferDecimals": 6, "defaultGasLimit": 58000, @@ -511,19 +519,6 @@ "type": "ERC20", "fees": "ethereum" }, - "WOO": { - "symbol": "WOO", - "name": "WOO Network", - "decimals": 18, - "contractId": "0x4691937a7508860f876c9c0a2a617e7d9e945d4b", - "createCoin": false, - "cryptoTransferDecimals": 6, - "defaultGasLimit": 58000, - "status": "active", - "mainCoin": "ETH", - "type": "ERC20", - "fees": "ethereum" - }, "USDT": { "symbol": "USDT", "name": "Tether", @@ -554,6 +549,19 @@ "type": "ERC20", "fees": "ethereum" }, + "WOO": { + "symbol": "WOO", + "name": "WOO Network", + "decimals": 18, + "contractId": "0x4691937a7508860f876c9c0a2a617e7d9e945d4b", + "createCoin": false, + "cryptoTransferDecimals": 6, + "defaultGasLimit": 58000, + "status": "active", + "mainCoin": "ETH", + "type": "ERC20", + "fees": "ethereum" + }, "XCN": { "symbol": "XCN", "name": "Onyxcoin", @@ -569,4 +577,4 @@ "type": "ERC20", "fees": "ethereum" } -} +} \ No newline at end of file From fc335a82fc78854166002664e5853f16b595aad5 Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 19 Dec 2023 14:14:31 +0000 Subject: [PATCH 23/29] feat(SendsFunds): include `amount` and `data` in LSK fee calculation --- src/lib/lisk/lisk-utils.ts | 4 ++-- src/store/modules/lsk/lsk-getters.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/lisk/lisk-utils.ts b/src/lib/lisk/lisk-utils.ts index 42cb97ca9..c781cdd82 100644 --- a/src/lib/lisk/lisk-utils.ts +++ b/src/lib/lisk/lisk-utils.ts @@ -204,7 +204,7 @@ type EstimateFeeParams = { /** * LSK amount */ - amount?: string + amount?: string | number /** * Sender's `publicKey` and `privateKey` */ @@ -240,7 +240,7 @@ export function estimateFee(params?: EstimateFeeParams) { senderPublicKey: Buffer.from(keyPair.publicKey, 'hex'), params: { tokenID: Buffer.from(LSK_TOKEN_ID, 'hex'), - amount: BigInt(convertLSKToBeddows(amount)), + amount: BigInt(convertLSKToBeddows(amount.toString())), recipientAddress: cryptography.address.getAddressFromLisk32Address(recipientAddress), data }, diff --git a/src/store/modules/lsk/lsk-getters.js b/src/store/modules/lsk/lsk-getters.js index e9b0ca933..2402f86a2 100644 --- a/src/store/modules/lsk/lsk-getters.js +++ b/src/store/modules/lsk/lsk-getters.js @@ -4,8 +4,8 @@ import baseGetters from '../lsk-base/lsk-base-getters' export default { ...baseGetters, - fee: () => (_amount, _recipientAddress, _data) => { - return estimateFee() + fee: () => (amount, _recipientAddress, data) => { + return estimateFee({ amount, data }) }, height(state) { From 7f2f8982e8aca16becfad7b54728a79adb45668d Mon Sep 17 00:00:00 2001 From: bludnic Date: Tue, 19 Dec 2023 16:14:10 +0000 Subject: [PATCH 24/29] feat(LSK): apply additional fee of 0.05 LSK if account doesn't exist --- src/components/SendFundsForm.vue | 54 ++++++++++++++++++- src/lib/lisk/lisk-constants.ts | 3 +- src/lib/lisk/lisk-utils.ts | 10 +++- src/lib/nodes/lsk-indexer/LskIndexer.ts | 6 ++- src/lib/nodes/lsk-indexer/LskIndexerClient.ts | 25 ++++++++- .../nodes/lsk-indexer/types/api/endpoints.ts | 9 ++++ .../types/api/token/account-exists.request.ts | 26 +++++++++ .../api/token/account-exists.response.ts | 6 +++ src/lib/validateAddress.js | 1 - src/store/modules/lsk/lsk-getters.js | 4 +- 10 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 src/lib/nodes/lsk-indexer/types/api/token/account-exists.request.ts create mode 100644 src/lib/nodes/lsk-indexer/types/api/token/account-exists.response.ts diff --git a/src/components/SendFundsForm.vue b/src/components/SendFundsForm.vue index 2d4b23a6a..bc93b95a1 100644 --- a/src/components/SendFundsForm.vue +++ b/src/components/SendFundsForm.vue @@ -186,6 +186,8 @@