From 5b79f8ad3958cd8383e6b18ff6b12d271e23d313 Mon Sep 17 00:00:00 2001 From: Pouria Date: Mon, 7 Feb 2022 06:53:41 +0330 Subject: [PATCH 1/5] add stellar functionality --- README.md | 7 + data/ProkeyCoinsInfo.json | 34 ++ package.json | 5 +- .../prokey/src/ProkeyBaseBlockChain.ts | 38 +- .../servers/prokey/src/stellar/Stellar.ts | 87 ++++ .../prokey/src/stellar/StellarModels.ts | 164 +++++++ src/coins/CoinInfo.ts | 24 +- src/device/ICoinCommand.ts | 15 +- src/device/StellarCommands.ts | 209 +++++++++ src/models/CoinInfoModel.ts | 18 +- src/models/Prokey.ts | 14 + src/wallet/BaseWallet.ts | 35 +- src/wallet/StellarWallet.ts | 404 ++++++++++++++++++ 13 files changed, 1006 insertions(+), 48 deletions(-) create mode 100644 src/blockchain/servers/prokey/src/stellar/Stellar.ts create mode 100644 src/blockchain/servers/prokey/src/stellar/StellarModels.ts create mode 100644 src/device/StellarCommands.ts create mode 100644 src/wallet/StellarWallet.ts diff --git a/README.md b/README.md index a553048..97eb404 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,13 @@ It is recommended to add this repository main branch to your project as a submod After adding, you need to run 'npm install' in the root directory of this module in order to install all prerequisites. +## Warning! +if you get any error about node-gyp or sodium-native. try deleting this two files + + C:\Users\{YourUserName}\AppData\Local\node-gyp + + C:\Users\{YourUserName}\.node-gyp + ## Supported Coins - Bitcoin based coins Like Bitcoin, Litecoin, Bitcoincash, Doge, Bitcoin Gold - Ethereum diff --git a/data/ProkeyCoinsInfo.json b/data/ProkeyCoinsInfo.json index 6f57fb9..3675204 100644 --- a/data/ProkeyCoinsInfo.json +++ b/data/ProkeyCoinsInfo.json @@ -35789,5 +35789,39 @@ "https://wallet.prokey.io" ] } + ], + "stellar": [ + { + "name": "Stellar", + "shortcut": "XLM", + "slip44": 148, + "decimals": 0, + "priority": 9, + "on_device": "Stellar", + "test": false, + "support": { + "optimum": "1.8.0" + }, + "tx_url": "https://stellarchain.io/tx/{hash}", + "wallets": [ + "https://wallet.prokey.io" + ] + }, + { + "name": "Stellar Testnet", + "shortcut": "tXlm", + "slip44": 148, + "decimals": 0, + "priority": 100, + "on_device": "Stellar Testnet", + "test": true, + "support": { + "optimum": "1.8.0" + }, + "tx_url": "http://testnet.stellarchain.io/transactions/{hash}", + "wallets": [ + "https://wallet.prokey.io" + ] + } ] } diff --git a/package.json b/package.json index aacefed..5a9602c 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,10 @@ "rxjs": "~6.2.0", "typescript-http-client": "^0.10.1", "websocket": "^1.0.34", - "crypto-js": "3.1.9-1" + "crypto-js": "3.1.9-1", + "websocket": "^1.0.34", + "sodium-native": "^3.2.1", + "stellar-base": "^6.0.1" }, "scripts": { "build": "tsc", diff --git a/src/blockchain/servers/prokey/src/ProkeyBaseBlockChain.ts b/src/blockchain/servers/prokey/src/ProkeyBaseBlockChain.ts index e011536..ecc7e98 100644 --- a/src/blockchain/servers/prokey/src/ProkeyBaseBlockChain.ts +++ b/src/blockchain/servers/prokey/src/ProkeyBaseBlockChain.ts @@ -3,39 +3,31 @@ import { Request, import {RequestAddressInfo} from "../../../../models/GenericWalletModel"; export abstract class ProkeyBaseBlockChain { - _url = 'https://blocks.prokey.org/'; + _baseUrl = 'https://blocks.prokey.org/'; - constructor(url?: string) { - if (url) { - this._url = url; - } - } - -// These functions must be implemented in child classes + // These functions must be implemented in child classes public abstract GetAddressInfo(reqAddresses: Array | RequestAddressInfo); public abstract GetTransactions(hash: string); public abstract GetLatestTransactions(trs: Array, count : number, offset: number); public abstract BroadCastTransaction(data: any); + constructor(baseUrl: string = 'https://blocks.prokey.org/') { + this._baseUrl = baseUrl; + } /** * This is a private helper function to GET data from server * @param toServer URL + data * @param changeJson a callback for adjust json before casting */ protected async GetFromServer(toServer: string, changeJson?: (json: string) => string) { - const client = newHttpClient(); - const request = new Request(this._url + toServer, { method: 'GET' }); + const request = new Request(this._baseUrl + toServer, {method: 'GET'}); let json = await client.execute(request); - if (changeJson) { - json = changeJson(json); - } - - return JSON.parse(json) as T; - } + return this.handleJsonResponse(changeJson, json); + } /** * This is a private helper function to POST data to server @@ -43,11 +35,19 @@ export abstract class ProkeyBaseBlockChain { * @param body Request Body * @returns Response data from server */ - protected async PostToServer(toServer: string, body: any): Promise { + protected async PostToServer(toServer: string, body: any, changeJson?: (json: string) => string): Promise { const client = newHttpClient(); - const request = new Request(this._url + toServer, {body: body, method: 'POST'}); + const request = new Request(this._baseUrl + toServer, {body: body, method: 'POST'}); + + let json = await client.execute(request); + return this.handleJsonResponse(changeJson, json); + } - return JSON.parse(await client.execute(request)); + private handleJsonResponse(changeJson: ((json: string) => string) | undefined, json: string) { + if (changeJson) { + json = changeJson(json); + } + return JSON.parse(json) as T; } } diff --git a/src/blockchain/servers/prokey/src/stellar/Stellar.ts b/src/blockchain/servers/prokey/src/stellar/Stellar.ts new file mode 100644 index 0000000..e295cfb --- /dev/null +++ b/src/blockchain/servers/prokey/src/stellar/Stellar.ts @@ -0,0 +1,87 @@ +import {ProkeyBaseBlockChain} from "../ProkeyBaseBlockChain"; +import {RequestAddressInfo} from "../../../../../models/GenericWalletModel"; +import { + StellarAccountInfo, + StellarFee, + StellarTransactionOperationResponse, + StellarTransactionResponse +} from "./StellarModels"; +import {MyConsole} from "../../../../../utils/console"; + +export class StellarBlockchain extends ProkeyBaseBlockChain { + _coinName: string; + + constructor(coinName: string = "Xlm") + { + super(); + this._coinName = coinName; + MyConsole.Info(this._coinName); + } + + /** + * broadcast transaction over network + * @param data stellar signed transaction (base64 format) + * @returns object response of transaction + */ + public async BroadCastTransaction(data: string): Promise { + return await this.PostToServer(`Transaction/advanced-send/${this._coinName}/`, {"SignedTransactionBlob": data}); + } + + /** + * get account information from network + * @param reqAddress address + * @returns StellarAccountInfo account info + */ + public async GetAddressInfo(reqAddress: RequestAddressInfo) : Promise { + try { + return await this.GetFromServer(`address/${this._coinName}/${reqAddress.address}`); + } catch (error) { + return null; + } + } + + /** + * get account transaction list from network + * @param accountAddress address of account + * @param limit number of transactions + * @param cursor used for pagination when you want next page + * @returns StellarAccountInfo list of transactions + */ + public async GetAccountTransactions(accountAddress: string, limit: number = 10, cursor?: string): Promise { + let serverResponse = await this.GetFromServer(`address/transactions/${this._coinName}/${accountAddress}/${limit}`); + if (serverResponse != null && serverResponse.transactions != null) + { + return serverResponse; + } + return null; + } + + /** + * get operations of a specific transaction + * @param transactionId transaction id + * @returns StellarTransactionOperationResponse list of operations + */ + public async GetTransactionOperations(transactionId: string): Promise { + let queryUrl = `transaction/${this._coinName}/${transactionId}`; + let serverResponse = await this.GetFromServer(queryUrl); + if (serverResponse != null && serverResponse.operations != null) + { + return serverResponse; + } + return null; + } + + /** + * get network fee detail + * @returns StellarFee + */ + public async GetCurrentFee(): Promise { + return await this.GetFromServer(`transaction/fee/${this._coinName}`); + } + + GetLatestTransactions(trs: Array, count: number, offset: number) { + } + + GetTransactions(hash: string) { + } +} diff --git a/src/blockchain/servers/prokey/src/stellar/StellarModels.ts b/src/blockchain/servers/prokey/src/stellar/StellarModels.ts new file mode 100644 index 0000000..201c52e --- /dev/null +++ b/src/blockchain/servers/prokey/src/stellar/StellarModels.ts @@ -0,0 +1,164 @@ +/* + * This is part of PROKEY HARDWARE WALLET project + * Copyright (C) Prokey.io + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +export interface StellarFee { + "last_ledger": string, + "last_ledger_base_fee": string, + "ledger_capacity_usage": string, + "min_accepted_fee": string, + "mode_accepted_fee": string, + "p10_accepted_fee": string, + "p20_accepted_fee": string, + "p30_accepted_fee": string, + "p40_accepted_fee": string, + "p50_accepted_fee": string, + "p60_accepted_fee": string, + "p70_accepted_fee": string, + "p80_accepted_fee": string, + "p90_accepted_fee": string, + "p95_accepted_fee": string, + "p99_accepted_fee": string, + "fee_charged": FeeState, + "max_fee": FeeState +} + +export interface StellarAccountInfo { + account_id: string, + sequence: number, + subentry_count: number, + inflation_destination: string, + home_domain: string, + thresholds: Thresholds, + flags: Flags, + balances: Balance[], + signers: Signer[], + num_sponsoring: number, + num_sponsored: number, + Data: object +} + +export interface StellarTransactionResponse { + "transactions": StellarTransaction[], + "nextPageCursor": string, + "prevPageCursor": string +} + +export interface StellarTransaction { + "hash": string, + "ledger": number, + "created_at": string, + "source_account": string, + "fee_account": string, + "successful": boolean, + "paging_token": string, + "source_account_sequence": number, + "fee_charged": number, + "max_fee": number, + "operation_count": number, + "envelope_xdr": string, + "result_xdr": string, + "result_meta_xdr": string, + "signatures": string[], + "fee_bump_transaction": FeeBumpTransaction, + "inner_transaction": InnerTransaction, + "memo_type": string, + "memo": string, + "memo_bytes": string +} + +export interface StellarTransactionOperationResponse { + "operations": StellarTransactionOperation[], + "nextPageCursor": string, + "prevPageCursor": string +} + +export interface StellarTransactionOperation { + "id": number, + "source_account": string, + "paging_token": string, + "type": string, + "type_i": number, + "created_at": string, + "transaction_hash": string, + "transaction_successful": boolean + "asset_type": string, + "from": string, + "to": string, + "amount": string, + "name": string, + "value": string, + "funder": string, + "account": string, + "starting_balance": string +} + +interface FeeState { + "max": string; + "min": string; + "mode": string; + "p10": string; + "p20": string; + "p30": string; + "p40": string; + "p50": string; + "p60": string; + "p70": string; + "p80": string; + "p90": string; + "p95": string; + "p99": string; +} + +export interface FeeBumpTransaction { + "hash": string, + signatures: string[] +} + +export interface InnerTransaction { + "hash": string, + signatures: string, + max_fee: number +} + +export interface Thresholds { + low_threshold: number, + med_threshold: number, + high_threshold: number +} + +export interface Flags { + auth_required: boolean, + auth_revocable: boolean, + auth_immutable: boolean +} + +export interface Balance { + asset_type: string, + asset_code: string, + asset_issuer: string, + limit: string, + balance: string, + buying_liabilities: string, + selling_liabilities: string, + is_authorized: boolean, + is_authorized_to_maintain_liabilities: boolean +} + +export interface Signer { + key: string, + type: string, + weight: number +} diff --git a/src/coins/CoinInfo.ts b/src/coins/CoinInfo.ts index fceeb74..957a35c 100644 --- a/src/coins/CoinInfo.ts +++ b/src/coins/CoinInfo.ts @@ -1,9 +1,9 @@ /* * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io - * + * * Hadi Robati, hadi@prokey.io - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -95,6 +95,9 @@ export class CoinInfo { case CoinBaseType.NEM: c = ProkeyCoinInfoModel.NEM; break; + case CoinBaseType.Stellar: + c = ProkeyCoinInfoModel.stellar; + break; } let f = coinName.toLowerCase(); @@ -141,6 +144,11 @@ export class CoinInfo { ci.id = `nem_${ci.shortcut}`; break; + case CoinBaseType.Stellar: + ci = c.find(obj => obj.name.toLowerCase() == f || obj.shortcut.toLowerCase() == f); + + ci.id = `stellar_${ci.shortcut}`; + break; default: ci = c.find(obj => obj.name.toLowerCase() == f || obj.shortcut.toLowerCase() == f); @@ -248,6 +256,18 @@ export class CoinInfo { } }); + //! For all Stellar base coins + ProkeyCoinInfoModel.stellar.forEach(stellar => { + //! Check the version + if(compareVersions(firmwareVersion, stellar.support.optimum) >= 0) { + list.push({ + ...stellar, + coinBaseType: CoinBaseType.Stellar, + id: `stellar_${stellar.shortcut}`, + }) + } + }); + //! Sort the list by Priority list.sort((a, b) => { if (a.priority > b.priority) diff --git a/src/device/ICoinCommand.ts b/src/device/ICoinCommand.ts index dae3538..1d5baab 100644 --- a/src/device/ICoinCommand.ts +++ b/src/device/ICoinCommand.ts @@ -1,9 +1,9 @@ /* * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io - * + * * Hadi Robati, hadi@prokey.io - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -23,6 +23,7 @@ import * as ProkeyResponses from '../models/Prokey'; import { BitcoinTx } from '../models/BitcoinTx'; import { EthereumTx } from '../models/EthereumTx'; import { RippleTransaction } from '../models/Responses-V6'; +import {StellarSignTransactionRequest} from "../models/Prokey"; import {NEMSignTxMessage} from "../models/Prokey"; export interface ICoinCommands { @@ -41,7 +42,7 @@ export interface ICoinCommands { GetAddresses( device: Device, path: Array | string>, - ): Promise; + ProkeyResponses.NEMSignedTx | + string>; SignMessage( device: Device, @@ -84,7 +87,7 @@ export interface ICoinCommands { coinName?: string ): Promise; - + VerifyMessage( device: Device, address: string, diff --git a/src/device/StellarCommands.ts b/src/device/StellarCommands.ts new file mode 100644 index 0000000..5eb382b --- /dev/null +++ b/src/device/StellarCommands.ts @@ -0,0 +1,209 @@ +import {ICoinCommands} from "./ICoinCommand"; +import {RippleCoinInfoModel, StellarCoinInfoModel} from "../models/CoinInfoModel"; +import {CoinBaseType, CoinInfo} from "../coins/CoinInfo"; +import {Device} from "./Device"; +import { + MessageSignature, + PublicKey, + StellarAddress, + StellarSignedTx, + StellarSignTransactionRequest, StellarTxOpRequest, + Success +} from "../models/Prokey"; +import {GeneralErrors, GeneralResponse} from "../models/GeneralResponse"; +import * as PathUtil from "../utils/pathUtils"; +import * as ProkeyResponses from "../models/Prokey"; +import {MyConsole} from "../utils/console"; +import {StrKey} from "stellar-base"; +import * as Utility from "../utils/utils"; +import { ByteArrayToHexString } from "../utils/utils" + +export class StellarCommands implements ICoinCommands { + private readonly _coinInfo: StellarCoinInfoModel; + + constructor(coinName: string) { + this._coinInfo = CoinInfo.Get(coinName, CoinBaseType.Stellar); + } + + /** + * Get stellar account address based on given path + * @param device Prokey device instance + * @param path BIP path + * @param showOnProkey true means show the address on device display + * @returns StellarAddress stellar unique address + */ + public async GetAddress(device: Device, path: Array | string, showOnProkey: boolean = true): Promise { + if (device == null || path == null) { + return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); + } + let address_n: Array; + try { + address_n = this.GetAddressArray(path); + } + catch (e) { + return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); + } + + let param = { + address_n: address_n, + show_display: showOnProkey, + } + + return await device.SendMessage('StellarGetAddress', param, 'StellarAddress'); + } + + /** + * Get a list of account addresses based on given paths + * @param device Prokey device instance + * @param paths List of BIP paths + * @constructor + */ + public async GetAddresses(device: Device, paths: Array | string>): Promise> { + let stellarAddresses: Array = new Array(); + for (const path of paths) { + stellarAddresses.push(await this.GetAddress(device, path, false)); + } + return stellarAddresses; + } + + /** + * Get Coin Info + */ + public GetCoinInfo(): StellarCoinInfoModel { + return this._coinInfo; + } + + /** + * Get Public key + * @param device The prokey device + * @param path BIP path + * @param showOnProkey true means show the public key on prokey display + */ + public async GetPublicKey(device: Device, path: Array | string, showOnProkey: boolean = true): Promise { + if (device == null || path == null) { + return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); + } + let address_n: Array; + try { + address_n = this.GetAddressArray(path); + } + catch (e) { + return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); + } + let param = { + address_n: address_n, + show_display: showOnProkey, + } + + return await device.SendMessage('GetPublicKey', param, 'PublicKey'); + } + + /** + * Sign Message + * @param device Prokey device instance + * @param path array of BIP32/44 Path + * @param message message to be signed + * @param coinName coin name + */ + public async SignMessage(device: Device, path: Array, message: Uint8Array, coinName?: string): Promise { + let scriptType = PathUtil.GetScriptType(path); + + let res = await device.SendMessage('SignMessage', { + address_n: path, + message: message, + coin_name: coinName || 'Stellar', + script_type: scriptType, + }, 'MessageSignature'); + + if (res.signature) { + res.signature = Utility.ByteArrayToHexString(res.signature); + } + + return res; + } + + /** + * Verify Message + * @param device Prokey device instance + * @param address address + * @param message message + * @param signature signature data + * @param coinName coin name + */ + public async VerifyMessage(device: Device, address: string, message: Uint8Array, signature: Uint8Array, coinName?: string): Promise { + return await device.SendMessage('VerifyMessage', { + address: address, + signature: signature, + message: message, + coin_name: coinName || 'Stellar', + }, 'Success'); + } + + /** + * sign transaction + * @param device + * @param transactionForSign a model that containg a transaction model for device and sdk for create signed transaction + * @constructor + */ + public async SignTransaction(device: Device, transactionForSign: StellarSignTransactionRequest): Promise { + var OnFailure = (reason: any) => { + device.RemoveOnFailureCallBack(OnFailure); + + throw new Error(`Signing transaction failed: ${reason.message}`); + }; + + MyConsole.Info("StellarSignTx", transactionForSign); + + if (!transactionForSign) { + let e: GeneralResponse = { + success: false, + errorCode: GeneralErrors.INVALID_PARAM, + errorMessage: "StellarCommands::SignTransaction->parameter transaction cannot be null", + } + + throw e; + } + let firstOperationRequest = await device.SendMessage('StellarSignTx', transactionForSign.signTxMessage, 'StellarTxOpRequest'); + MyConsole.Info("operation request", firstOperationRequest); + + for (let i=0; i < transactionForSign.operations.length - 1; i++) { + let operation = transactionForSign.operations[i]; + let operationRequest = await device.SendMessage(operation.type, operation, 'StellarTxOpRequest'); + MyConsole.Info("operation request", operationRequest); + } + + let operation = transactionForSign.operations[transactionForSign.operations.length - 1]; + let signResponse = await device.SendMessage(operation.type, operation, 'StellarSignedTx'); + return await StellarCommands.prepareTransactionForBroadcast(transactionForSign, signResponse); + } + + /** + * get byte array of path if its serialized + * @param path + * @returns Array account BIP path + */ + public GetAddressArray(path: Array | string) : Array { + if (typeof path == "string") { + return PathUtil.getHDPath(path); + } else { + return path; + } + } + + /** + * prepare signed transaction for sending over network + * @param transactionForSign stellar transaction model + * @param signResponse device sign response + * @private + */ + private static async prepareTransactionForBroadcast(transactionForSign: StellarSignTransactionRequest, signResponse: StellarSignedTx) { + let transactionModel = transactionForSign.transactionModel; + let stringSignature = ByteArrayToHexString(signResponse.signature); + let decodedPublicKey = StrKey.encodeEd25519PublicKey(Buffer.from(signResponse.public_key)); + transactionModel.addSignature( + decodedPublicKey, + Buffer.from(stringSignature, 'hex').toString('base64') + ); + return transactionModel.toEnvelope().toXDR().toString("base64"); + } +} diff --git a/src/models/CoinInfoModel.ts b/src/models/CoinInfoModel.ts index f0a4f7c..92af3c2 100644 --- a/src/models/CoinInfoModel.ts +++ b/src/models/CoinInfoModel.ts @@ -1,7 +1,7 @@ /* * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -28,7 +28,8 @@ export type GeneralCoinInfoModel = BitcoinBaseCoinInfoModel | RippleCoinInfoModel | OmniCoinInfoModel | MiscCoinInfoModel | - NemCoinInfoModel; + NemCoinInfoModel | + StellarCoinInfoModel; export interface BaseCoinInfoModel { name: string, @@ -47,8 +48,8 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { blocktime: number, //cashAddrPrefix?: string, label: string, // this is human readable format, could be different from "name" - - + + dust_limit: number, force_bip143: boolean, fork_id?: number, @@ -58,7 +59,7 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { minfee_kb: number, segwit: boolean, // signed_message_header: in Network - + //xPubMagic: number, //xPubMagicSegwitNative?: number, //xPubMagicSegwit?: number, @@ -71,7 +72,7 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { blocks?: number, decimals: number, on_device: string, - + tx_url: string, timestamp: boolean, priority: number, @@ -138,3 +139,8 @@ export interface NemCoinInfoModel extends BaseCoinInfoModel { tx_url: string, priority: number, } + +export interface StellarCoinInfoModel extends BaseCoinInfoModel { + on_device: string, + tx_url: string, +} diff --git a/src/models/Prokey.ts b/src/models/Prokey.ts index b9eabcd..0f3bf5c 100644 --- a/src/models/Prokey.ts +++ b/src/models/Prokey.ts @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +import {Transaction} from "stellar-base"; export type CipheredKeyValue = { value: string, @@ -446,6 +447,19 @@ export type NEMSignTxMessage = { } // Stellar types +export type StellarDecoratedSignature = { + hint: string, + signature: string +} + +export type StellarTxOpRequest = { +} + +export type StellarSignTransactionRequest = { + signTxMessage: StellarSignTxMessage, + operations: StellarOperationMessage[] + transactionModel: Transaction +} export type StellarAddress = { address: string, diff --git a/src/wallet/BaseWallet.ts b/src/wallet/BaseWallet.ts index 55fbeb9..cf4f8ca 100644 --- a/src/wallet/BaseWallet.ts +++ b/src/wallet/BaseWallet.ts @@ -1,9 +1,9 @@ /* * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io - * + * * Hadi Robati, hadi@prokey.io - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -20,11 +20,12 @@ import { Device } from "../device/Device"; import { - BitcoinBaseCoinInfoModel, - EthereumBaseCoinInfoModel, - Erc20BaseCoinInfoModel, - OmniCoinInfoModel, - RippleCoinInfoModel + BitcoinBaseCoinInfoModel, + EthereumBaseCoinInfoModel, + Erc20BaseCoinInfoModel, + OmniCoinInfoModel, + RippleCoinInfoModel, + StellarCoinInfoModel } from '../models/CoinInfoModel' import { CoinBaseType, CoinInfo } from '../coins/CoinInfo' import { ICoinCommands } from '../device/ICoinCommand' @@ -54,6 +55,7 @@ import { CardanoSignedTx, NEMSignedTx, NEMSignTxMessage, + StellarSignTransactionRequest, Success } from "../models/Prokey"; @@ -68,6 +70,7 @@ import { BitcoinTx } from '../models/BitcoinTx'; import { EthereumTx } from '../models/EthereumTx'; import { RippleCommands } from "../device/RippleCommands"; import { RippleSignedTx, RippleTransaction } from "../models/Responses-V6"; +import {StellarCommands} from "../device/StellarCommands"; /** * This is the base class for all implemented wallets @@ -82,9 +85,9 @@ export abstract class BaseWallet { * @param _coinName Coin name, Check /data/ProkeyCoinsInfo.json * @param _coinType Coin type BitcoinBase | EthereumBase | ERC20 | NEM | OMNI | OTHERS */ - constructor(private _device: Device, - coinName: string, - coinType: CoinBaseType, + constructor(private _device: Device, + coinName: string, + coinType: CoinBaseType, chainOrPropertyNumber?: number, coinInfo?: BitcoinBaseCoinInfoModel | EthereumBaseCoinInfoModel | Erc20BaseCoinInfoModel | OmniCoinInfoModel | RippleCoinInfoModel) { if (_device == null) @@ -113,6 +116,10 @@ export abstract class BaseWallet { this._commands = new RippleCommands(coinName); break; + case CoinBaseType.Stellar: + this._commands = new StellarCommands(coinName); + break; + case CoinBaseType.NEM: this._commands = new NemCommands(coinName); break; @@ -126,7 +133,7 @@ export abstract class BaseWallet { /** * Get CoinInfo */ - public GetCoinInfo(): BitcoinBaseCoinInfoModel | EthereumBaseCoinInfoModel | Erc20BaseCoinInfoModel | OmniCoinInfoModel | RippleCoinInfoModel { + public GetCoinInfo(): BitcoinBaseCoinInfoModel | EthereumBaseCoinInfoModel | Erc20BaseCoinInfoModel | OmniCoinInfoModel | RippleCoinInfoModel | StellarCoinInfoModel { return this._coinInfo; } @@ -175,13 +182,13 @@ export abstract class BaseWallet { * @param tx transaction to be signed by device */ public async SignTransaction - (tx: BitcoinTx | EthereumTx | RippleTransaction | NEMSignTxMessage): Promise + (tx: BitcoinTx | EthereumTx | RippleTransaction | NEMSignTxMessage | StellarSignTransactionRequest): Promise { return await this._commands.SignTransaction(this._device, tx) as T; } /** - * Sign Message + * Sign Message * @param path BIP32 Path to sign the message * @param message Message to be signed * @param coinName Optional, Only for Bitcoin based coins @@ -197,7 +204,7 @@ export abstract class BaseWallet { * @param message Signed message * @param signature Signature * @param coinName Optional, Only for Bitcoin based coins - * @returns + * @returns */ public async VerifyMessage(address: string, message: string, signature: string, coinName?: string): Promise { const messageBytes = Util.StringToUint8Array(message); diff --git a/src/wallet/StellarWallet.ts b/src/wallet/StellarWallet.ts new file mode 100644 index 0000000..8153c0c --- /dev/null +++ b/src/wallet/StellarWallet.ts @@ -0,0 +1,404 @@ +import {BaseWallet} from "./BaseWallet"; +import {Device} from "../device/Device"; +import {CoinBaseType} from "../coins/CoinInfo"; +import {StellarCoinInfoModel} from "../models/CoinInfoModel"; +import { + AddressModel, + StellarAddress, StellarOperationMessage, StellarPaymentOp, + StellarSignTransactionRequest, + StellarSignTxMessage +} from "../models/Prokey"; +import {StellarBlockchain} from "../blockchain/servers/prokey/src/stellar/Stellar"; +import { + StellarAccountInfo, + StellarFee, StellarTransactionOperationResponse, + StellarTransactionResponse +} from "../blockchain/servers/prokey/src/stellar/StellarModels"; +import { + Account, + Asset, + Keypair, + Memo, MemoType, + Operation, + TransactionBuilder, xdr +} from "stellar-base"; +import * as PathUtil from "../utils/pathUtils"; + +const BigNumber = require('bignumber.js'); + +var WAValidator = require('multicoin-address-validator'); + +export class StellarWallet extends BaseWallet { + private readonly STELLAR_BASE_RESERVE = 0.5; + private readonly _networkPublicPassphrase = "Public Global Stellar Network ; September 2015"; + private readonly _networkTestPassphrase = "Test SDF Network ; September 2015"; + + _block_chain: StellarBlockchain; + _accounts: Array; + + constructor(device: Device, coinName: string) { + super(device, coinName, CoinBaseType.Stellar); + this._block_chain = new StellarBlockchain(this.GetCoinInfo().shortcut); + this._accounts = new Array(); + } + + public IsAddressValid(address: string): boolean { + return WAValidator.validate(address, "xlm"); + } + + /** + * discover network for collecting this device accounts information + * @param accountFindCallBack + * @returns Array an array of accounts information + */ + public async StartDiscovery(accountFindCallBack?: (accountInfo: StellarAccountInfo) => void): Promise> { + return new Promise>(async (resolve, reject) => { + let accountNumber = 0; + do { + let account = await this.GetAccountInfo(accountNumber); + if (account == null) { + return resolve(this._accounts); + } + this._accounts.push(account); + if (accountFindCallBack) { + accountFindCallBack(account); + } + accountNumber++; + } while (true); + }); + } + + /** + * get account info + * @param accountNumber account number in device + * @returns StellarAccountInfo account information + */ + public async GetAccountInfo(accountNumber: number): Promise { + let path = this.GetCoinPath(accountNumber); + let address = await this.GetAddress(path.path, false); + return await this.GetAccountInfoByAddress(address.address); + } + + public async GetAccountInfoByAddress(accountAddress: string): Promise { + return await this._block_chain.GetAddressInfo({address: accountAddress}) + } + + public async GetAccountTransactions(account: string, limit?: number, cursor?: string): Promise { + return await this._block_chain.GetAccountTransactions(account, limit, cursor); + } + + public async GetTransactionOperations(transactionId: string): Promise { + return await this._block_chain.GetTransactionOperations(transactionId); + } + + public async GetCurrentFee(): Promise { + return await this._block_chain.GetCurrentFee(); + } + + /** + * prepare payment transaction request object for sign in device and broadcast over network + * @param toAccount receiver account + * @param amount + * @param accountNumber user account number in device + * @param selectedFee user selected fee + * @returns StellarSignTransactionRequest + */ + public async GenerateTransaction(toAccount: string, amount: number, accountNumber: number, selectedFee: string, memoType: MemoType = "none", memoValue: string = ""): Promise { + // TODO: add memo latter + // Check balance + let balance = this.GetAccountBalance(accountNumber); + this.validateBalance(balance, accountNumber, amount, selectedFee); + let path = this.GetCoinPath(accountNumber).path; + const accountObject = this.GetAccount(accountNumber); + // fetch account for valid sequence + const updatedAccount = await this.GetAccountInfoByAddress(accountObject.account_id); + + if (!updatedAccount) { + throw new Error("your account is not valid"); + } + + let account = new Account(accountObject.account_id, updatedAccount.sequence.toString()); + const stellarTransactionModel = new TransactionBuilder(account, { + fee: selectedFee, + networkPassphrase: this.getNetworkPassphrase() + }) + .addOperation( + // this operation funds the new account with XLM + Operation.payment({ + destination: toAccount, + asset: Asset.native(), + amount: amount.toString() + }) + ) + .addMemo(StellarWallet.getMemo(memoType, memoValue)) + .setTimeout(180) // wait 3 min for transaction + .build(); + + return this.transformTransaction(path, stellarTransactionModel); + } + + /** + * prepare create account transaction request object for sign in device and broadcast over network + * @param toAccount requested account for creation + * @param amount + * @param accountNumber user account number in device + * @param selectedFee user selected fee + * @returns StellarSignTransactionRequest + */ + public async GenerateCreateAccountTransaction(toAccount: string, amount: number, accountNumber: number, selectedFee: string, memoType: MemoType = "none", memoValue: string = ""): Promise { + let balance = this.GetAccountBalance(accountNumber); + let path = this.GetCoinPath(accountNumber).path; + + this.validateBalance(balance, accountNumber, amount, selectedFee); + const accountObject = this.GetAccount(accountNumber); + // fetch account for valid sequence + const updatedAccount = await this.GetAccountInfoByAddress(accountObject.account_id); + + if (!updatedAccount) { + throw new Error("your account is not valid"); + } + let account = new Account(accountObject.account_id, updatedAccount.sequence.toString()); + const stellarTransactionModel = new TransactionBuilder(account, { + fee: selectedFee, + networkPassphrase: this.getNetworkPassphrase() + }) + .addOperation( + Operation.createAccount({ + destination: toAccount, + startingBalance: amount.toString() + }) + ) + .addMemo(StellarWallet.getMemo(memoType, memoValue)) + .setTimeout(180) // wait 3 min for transaction + .build(); + return this.transformTransaction(path, stellarTransactionModel); + } + + /** + * transform stellar sdk transaction to prokey transaction object + * @param path BIP path + * @param transaction stellar sdk transaction + */ + public transformTransaction(path: Array, transaction): StellarSignTransactionRequest { + const amounts = ['amount', 'sendMax', 'destAmount', 'startingBalance', 'limit']; + const assets = ['asset', 'sendAsset', 'destAsset', 'selling', 'buying', 'line']; + + const operations = transaction.operations.map((o, i) => { + const operation = {...o}; + + if (operation.signer) { + operation.signer = this.transformSigner(operation.signer); + } + + if (operation.path) { + operation.path = operation.path.map(this.transformAsset); + } + + if (typeof operation.price === 'string') { + const xdrOperation = transaction.tx.operations()[i]; + operation.price = { + n: xdrOperation.body().value().price().n(), + d: xdrOperation.body().value().price().d(), + }; + } + + amounts.forEach(field => { + if (typeof operation[field] === 'string') { + operation[field] = this.transformAmount(operation[field]); + } + }); + + assets.forEach(field => { + if (operation[field]) { + operation[field] = this.transformAsset(operation[field]); + } + }); + + if (operation.type === 'allowTrust') { + const allowTrustAsset = new Asset(operation.assetCode, operation.trustor); + operation.asset = this.transformAsset(allowTrustAsset); + } + + if (operation.type === 'manageData' && operation.value) { + // stringify is not necessary, Buffer is also accepted + operation.value = operation.value.toString('hex'); + } + + return this.transformType(operation); + }); + + let signTxMessage: StellarSignTxMessage = { + address_n: path, + source_account: transaction.source, + fee: transaction.fee, // todo: if multi operations transaction implemented this value must changes(exm: fee * operationNumber) + sequence_number: transaction.sequence, + network_passphrase: transaction.networkPassphrase, + num_operations: operations.length, + }; + this.transformTimebounds(signTxMessage, transaction.timeBounds); + this.transformMemo(signTxMessage, transaction.memo); + + return { + signTxMessage: signTxMessage, + operations: operations, + transactionModel: transaction + } + } + + public async SendTransaction(tx: string): Promise { + return await this._block_chain.BroadCastTransaction(tx); + } + + public GetAccountBalance(accountNumber: number): number { + let account = this.GetAccount(accountNumber); + let nativeBalance = account.balances.find(balance => balance.asset_type === "native"); + if (nativeBalance) { + return +nativeBalance.balance; + } + return 0; + } + + public GetAccountReserveBalance(accountNumber: number): number { + let account = this.GetAccount(accountNumber); + return (2 + account.subentry_count + account.num_sponsoring - account.num_sponsored) * this.STELLAR_BASE_RESERVE; + } + + private static getMemo(memoType: MemoType, memoValue: string): Memo { + if (memoValue == "") { + return Memo.none(); + } + switch (memoType) { + case "hash": + return Memo.hash(memoValue); + case "id": + return Memo.id(memoValue) + case "none": + return Memo.none(); + case "return": + return Memo.return(memoValue); + case "text": + return Memo.text(memoValue); + } + } + + private validateBalance(balance: number, accountNumber: number, amount: number, selectedFee: string) { + balance = balance - this.GetAccountReserveBalance(accountNumber) - amount - (+selectedFee); + if (balance < 0) + throw new Error(`Insufficient balance you need to hold ${this.GetAccountBalance(accountNumber)} XLM in your account.`); + } + + private getNetworkPassphrase() { + return this.GetCoinInfo().test ? this._networkTestPassphrase : this._networkPublicPassphrase; + } + + private GetAccount(accountNumber: number) { + if (accountNumber >= this._accounts.length) { + throw new Error('Account number is wrong'); + } + return this._accounts[accountNumber]; + } + + public GetCoinPath(accountNumber: number): AddressModel { + return PathUtil.GetBipPath( + CoinBaseType.Ripple, + accountNumber, + super.GetCoinInfo() + ); + } + + public transformSigner(signer) { + let type = 0; + let key; + const {weight} = signer; + if (typeof signer.ed25519PublicKey === 'string') { + const keyPair = Keypair.fromPublicKey(signer.ed25519PublicKey); + key = keyPair.rawPublicKey().toString('hex'); + } + if (signer.preAuthTx instanceof Buffer) { + type = 1; + key = signer.preAuthTx.toString('hex'); + } + if (signer.sha256Hash instanceof Buffer) { + type = 2; + key = signer.sha256Hash.toString('hex'); + } + return { + type, + key, + weight, + }; + } + + public transformAsset(asset) { + if (asset.isNative()) { + return { + type: 0, + code: asset.getCode(), + }; + } + return { + type: asset.getAssetType() === 'credit_alphanum4' ? 1 : 2, + code: asset.getCode(), + issuer: asset.getIssuer(), + }; + } + + public transformAmount(amount) { + return new BigNumber(amount).times(10000000).toString(); + } + + public transformType(operation): StellarOperationMessage | null { + let operationMessage: StellarOperationMessage; + switch (operation.type) { + case 'payment': + return { + type: 'StellarPaymentOp', + destination_account: operation.destination, + asset: operation.asset, + amount: operation.amount, + } + case 'createAccount': + return { + type: 'StellarCreateAccountOp', + new_account: operation.destination, + starting_balance: operation.startingBalance + } + default: + return null; + } + } + + public transformMemo(signMessage: StellarSignTxMessage, memo: Memo) { + if (memo && memo.value) { + switch (memo.type) { + case 'text': + signMessage.memo_type = 1; + signMessage.memo_text = memo.value.toString(); + break; + case 'id': + signMessage.memo_type = 2; + signMessage.memo_id = memo.value.toString(); + break; + case 'hash': + + signMessage.memo_type = 3; + signMessage.memo_hash = Buffer.from(memo.value).toString('hex'); + break; + case 'return': + signMessage.memo_type = 4; + signMessage.memo_hash = Buffer.from(memo.value).toString('hex'); + break; + default: + signMessage.memo_type = 0; + } + } else { + signMessage.memo_type = 0; + } + } + + public transformTimebounds(signMessage: StellarSignTxMessage, timebounds) { + if (!timebounds) return undefined; + signMessage.timebounds_start = Number.parseInt(timebounds.minTime, 10); + signMessage.timebounds_end = Number.parseInt(timebounds.maxTime, 10); + } +} From aae8fd6cadd1108aee32503b960e106248a47d98 Mon Sep 17 00:00:00 2001 From: Pouria Date: Thu, 10 Feb 2022 05:27:18 +0330 Subject: [PATCH 2/5] add stellar functionality --- src/models/CoinInfoModel.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/models/CoinInfoModel.ts b/src/models/CoinInfoModel.ts index 92af3c2..df5db8a 100644 --- a/src/models/CoinInfoModel.ts +++ b/src/models/CoinInfoModel.ts @@ -1,7 +1,7 @@ /* * This is part of PROKEY HARDWARE WALLET project * Copyright (C) Prokey.io - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -48,8 +48,8 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { blocktime: number, //cashAddrPrefix?: string, label: string, // this is human readable format, could be different from "name" - - + + dust_limit: number, force_bip143: boolean, fork_id?: number, @@ -59,7 +59,7 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { minfee_kb: number, segwit: boolean, // signed_message_header: in Network - + //xPubMagic: number, //xPubMagicSegwitNative?: number, //xPubMagicSegwit?: number, @@ -72,7 +72,7 @@ export interface BitcoinBaseCoinInfoModel extends BaseCoinInfoModel { blocks?: number, decimals: number, on_device: string, - + tx_url: string, timestamp: boolean, priority: number, From 9e9a4de5d2c12087d735e65d81fa8c4ab9a84a0b Mon Sep 17 00:00:00 2001 From: Pouria Date: Thu, 10 Feb 2022 05:27:53 +0330 Subject: [PATCH 3/5] add stellar functionality --- src/wallet/StellarWallet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/StellarWallet.ts b/src/wallet/StellarWallet.ts index 8153c0c..f6382df 100644 --- a/src/wallet/StellarWallet.ts +++ b/src/wallet/StellarWallet.ts @@ -300,7 +300,7 @@ export class StellarWallet extends BaseWallet { public GetCoinPath(accountNumber: number): AddressModel { return PathUtil.GetBipPath( - CoinBaseType.Ripple, + CoinBaseType.Stellar, accountNumber, super.GetCoinInfo() ); From 37b4574e976ffbac5605f3547b3ca37458b69398 Mon Sep 17 00:00:00 2001 From: Hadi Date: Wed, 16 Feb 2022 13:13:22 +0800 Subject: [PATCH 4/5] Update StellarCommands.ts --- src/device/StellarCommands.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/device/StellarCommands.ts b/src/device/StellarCommands.ts index 5eb382b..b68ee21 100644 --- a/src/device/StellarCommands.ts +++ b/src/device/StellarCommands.ts @@ -36,6 +36,7 @@ export class StellarCommands implements ICoinCommands { if (device == null || path == null) { return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); } + let address_n: Array; try { address_n = this.GetAddressArray(path); @@ -83,6 +84,7 @@ export class StellarCommands implements ICoinCommands { if (device == null || path == null) { return Promise.reject({ success: false, errorCode: GeneralErrors.INVALID_PARAM }); } + let address_n: Array; try { address_n = this.GetAddressArray(path); @@ -90,6 +92,7 @@ export class StellarCommands implements ICoinCommands { catch (e) { return Promise.reject({ success: false, errorCode: GeneralErrors.PATH_NOT_VALID }); } + let param = { address_n: address_n, show_display: showOnProkey, @@ -163,6 +166,7 @@ export class StellarCommands implements ICoinCommands { throw e; } + let firstOperationRequest = await device.SendMessage('StellarSignTx', transactionForSign.signTxMessage, 'StellarTxOpRequest'); MyConsole.Info("operation request", firstOperationRequest); @@ -198,6 +202,7 @@ export class StellarCommands implements ICoinCommands { */ private static async prepareTransactionForBroadcast(transactionForSign: StellarSignTransactionRequest, signResponse: StellarSignedTx) { let transactionModel = transactionForSign.transactionModel; + let stringSignature = ByteArrayToHexString(signResponse.signature); let decodedPublicKey = StrKey.encodeEd25519PublicKey(Buffer.from(signResponse.public_key)); transactionModel.addSignature( From a4b8fce45f75ed7d3b1785aeb660fadd24c587ee Mon Sep 17 00:00:00 2001 From: Pouria Date: Tue, 22 Feb 2022 09:01:49 +0330 Subject: [PATCH 5/5] fix stellar issues --- README.md | 2 +- data/ProkeyCoinsInfo.json | 4 +- .../prokey/src/stellar/StellarModels.ts | 154 +++++++++--------- src/device/StellarCommands.ts | 12 +- 4 files changed, 83 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 97eb404..3ee4f88 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ It is recommended to add this repository main branch to your project as a submod After adding, you need to run 'npm install' in the root directory of this module in order to install all prerequisites. -## Warning! +## Note if you get any error about node-gyp or sodium-native. try deleting this two files C:\Users\{YourUserName}\AppData\Local\node-gyp diff --git a/data/ProkeyCoinsInfo.json b/data/ProkeyCoinsInfo.json index 3675204..a8caeba 100644 --- a/data/ProkeyCoinsInfo.json +++ b/data/ProkeyCoinsInfo.json @@ -35809,8 +35809,8 @@ }, { "name": "Stellar Testnet", - "shortcut": "tXlm", - "slip44": 148, + "shortcut": "tXLM", + "slip44": 1, "decimals": 0, "priority": 100, "on_device": "Stellar Testnet", diff --git a/src/blockchain/servers/prokey/src/stellar/StellarModels.ts b/src/blockchain/servers/prokey/src/stellar/StellarModels.ts index 201c52e..4ea38c0 100644 --- a/src/blockchain/servers/prokey/src/stellar/StellarModels.ts +++ b/src/blockchain/servers/prokey/src/stellar/StellarModels.ts @@ -15,24 +15,24 @@ */ export interface StellarFee { - "last_ledger": string, - "last_ledger_base_fee": string, - "ledger_capacity_usage": string, - "min_accepted_fee": string, - "mode_accepted_fee": string, - "p10_accepted_fee": string, - "p20_accepted_fee": string, - "p30_accepted_fee": string, - "p40_accepted_fee": string, - "p50_accepted_fee": string, - "p60_accepted_fee": string, - "p70_accepted_fee": string, - "p80_accepted_fee": string, - "p90_accepted_fee": string, - "p95_accepted_fee": string, - "p99_accepted_fee": string, - "fee_charged": FeeState, - "max_fee": FeeState + last_ledger: string, + last_ledger_base_fee: string, + ledger_capacity_usage: string, + min_accepted_fee: string, + mode_accepted_fee: string, + p10_accepted_fee: string, + p20_accepted_fee: string, + p30_accepted_fee: string, + p40_accepted_fee: string, + p50_accepted_fee: string, + p60_accepted_fee: string, + p70_accepted_fee: string, + p80_accepted_fee: string, + p90_accepted_fee: string, + p95_accepted_fee: string, + p99_accepted_fee: string, + fee_charged: FeeState, + max_fee: FeeState } export interface StellarAccountInfo { @@ -51,84 +51,84 @@ export interface StellarAccountInfo { } export interface StellarTransactionResponse { - "transactions": StellarTransaction[], - "nextPageCursor": string, - "prevPageCursor": string + transactions: StellarTransaction[], + nextPageCursor: string, + prevPageCursor: string } export interface StellarTransaction { - "hash": string, - "ledger": number, - "created_at": string, - "source_account": string, - "fee_account": string, - "successful": boolean, - "paging_token": string, - "source_account_sequence": number, - "fee_charged": number, - "max_fee": number, - "operation_count": number, - "envelope_xdr": string, - "result_xdr": string, - "result_meta_xdr": string, - "signatures": string[], - "fee_bump_transaction": FeeBumpTransaction, - "inner_transaction": InnerTransaction, - "memo_type": string, - "memo": string, - "memo_bytes": string + hash: string, + ledger: number, + created_at: string, + source_account: string, + fee_account: string, + successful: boolean, + paging_token: string, + source_account_sequence: number, + fee_charged: number, + max_fee: number, + operation_count: number, + envelope_xdr: string, + result_xdr: string, + result_meta_xdr: string, + signatures: string[], + fee_bump_transaction: FeeBumpTransaction, + inner_transaction: InnerTransaction, + memo_type: string, + memo: string, + memo_bytes: string } export interface StellarTransactionOperationResponse { - "operations": StellarTransactionOperation[], - "nextPageCursor": string, - "prevPageCursor": string + operations: StellarTransactionOperation[], + nextPageCursor: string, + prevPageCursor: string } export interface StellarTransactionOperation { - "id": number, - "source_account": string, - "paging_token": string, - "type": string, - "type_i": number, - "created_at": string, - "transaction_hash": string, - "transaction_successful": boolean - "asset_type": string, - "from": string, - "to": string, - "amount": string, - "name": string, - "value": string, - "funder": string, - "account": string, - "starting_balance": string + id: number, + source_account: string, + paging_token: string, + type: string, + type_i: number, + created_at: string, + transaction_hash: string, + transaction_successful: boolean + asset_type: string, + from: string, + to: string, + amount: string, + name: string, + value: string, + funder: string, + account: string, + starting_balance: string } interface FeeState { - "max": string; - "min": string; - "mode": string; - "p10": string; - "p20": string; - "p30": string; - "p40": string; - "p50": string; - "p60": string; - "p70": string; - "p80": string; - "p90": string; - "p95": string; - "p99": string; + max: string; + min: string; + mode: string; + p10: string; + p20: string; + p30: string; + p40: string; + p50: string; + p60: string; + p70: string; + p80: string; + p90: string; + p95: string; + p99: string; } export interface FeeBumpTransaction { - "hash": string, + hash: string, signatures: string[] } export interface InnerTransaction { - "hash": string, + hash: string, signatures: string, max_fee: number } diff --git a/src/device/StellarCommands.ts b/src/device/StellarCommands.ts index b68ee21..c611adf 100644 --- a/src/device/StellarCommands.ts +++ b/src/device/StellarCommands.ts @@ -7,7 +7,8 @@ import { PublicKey, StellarAddress, StellarSignedTx, - StellarSignTransactionRequest, StellarTxOpRequest, + StellarSignTransactionRequest, + StellarTxOpRequest, Success } from "../models/Prokey"; import {GeneralErrors, GeneralResponse} from "../models/GeneralResponse"; @@ -16,7 +17,6 @@ import * as ProkeyResponses from "../models/Prokey"; import {MyConsole} from "../utils/console"; import {StrKey} from "stellar-base"; import * as Utility from "../utils/utils"; -import { ByteArrayToHexString } from "../utils/utils" export class StellarCommands implements ICoinCommands { private readonly _coinInfo: StellarCoinInfoModel; @@ -149,12 +149,6 @@ export class StellarCommands implements ICoinCommands { * @constructor */ public async SignTransaction(device: Device, transactionForSign: StellarSignTransactionRequest): Promise { - var OnFailure = (reason: any) => { - device.RemoveOnFailureCallBack(OnFailure); - - throw new Error(`Signing transaction failed: ${reason.message}`); - }; - MyConsole.Info("StellarSignTx", transactionForSign); if (!transactionForSign) { @@ -203,7 +197,7 @@ export class StellarCommands implements ICoinCommands { private static async prepareTransactionForBroadcast(transactionForSign: StellarSignTransactionRequest, signResponse: StellarSignedTx) { let transactionModel = transactionForSign.transactionModel; - let stringSignature = ByteArrayToHexString(signResponse.signature); + let stringSignature = Utility.ByteArrayToHexString(signResponse.signature); let decodedPublicKey = StrKey.encodeEd25519PublicKey(Buffer.from(signResponse.public_key)); transactionModel.addSignature( decodedPublicKey,