UNPKG

@tronlink/core

Version:

The library serves as a core module within TronLink Extension, which provides low-level wallet functionality for both Tron and Ethereum networks, primary features includes account generation and transaction signing

262 lines (245 loc) 8.85 kB
// @ts-ignore import { TronWeb, utils as TronWebUtils } from 'tronweb'; import { InvalidParameterError, SignError } from '../../base_wallet/error'; import { LedgerSigner } from '../../base_wallet/ledger/LedgerSigner'; import { LedgerSignParams } from '../../base_wallet/types'; import { getTransactionContractType } from '../../utils'; import { LedgerTrxWebHid } from './LedgerTrxWebHid'; import exchangeList, { ExchangeItem } from './config/exchanges'; import tokenList, { TokenItem } from './config/tokens'; export class LedgerTrxSigner extends LedgerSigner { async ledgerSign(params: LedgerSignParams) { let isSignMessageV2; let transaction; let path; let tronWebInstance; let options; ({ data: transaction, path, options } = params); ({ isSignMessageV2, tronWebInstance } = options || { isSignMessageV2: false }); let contractType; const ledgerWebHid = new LedgerTrxWebHid(); try { if (typeof transaction === 'string') { const tx = isSignMessageV2 ? Buffer.from(transaction).toString('hex') : transaction; return await ledgerWebHid.signPersonalMessage(tx, path); } contractType = getTransactionContractType(transaction); const tokenInfo: string[] = []; let extra: any = {}; switch (contractType) { case 'TransferContract': { //Transfer if (transaction.raw_data.data) { extra.note = transaction.raw_data.data; } break; } case 'TransferAssetContract': { const ID = TronWeb.toUtf8(transaction.raw_data.contract[0].parameter.value.asset_name); // get token info extra = await getTokenExtraInfo( transaction.raw_data.contract[0].parameter.value.asset_name, tronWebInstance, ); if (transaction.raw_data.data) { extra.note = transaction.raw_data.data; } const tokenObj = await getLedgerTokenInfo(ID); if (tokenObj.message) { tokenInfo.push(tokenObj.message); } break; } case 'WitnessCreateContract': case 'ProposalCreateContract': case 'ProposalApproveContract': case 'ProposalDeleteContract': case 'TriggerSmartContract': case 'AccountPermissionUpdateContract': { extra = transaction.extra || {}; break; } case 'ExchangeCreateContract': { const token1 = await getTokenExtraInfo( transaction.raw_data.contract[0].parameter.value.first_token_id, tronWebInstance, ); const token2 = await getTokenExtraInfo( transaction.raw_data.contract[0].parameter.value.second_token_id, tronWebInstance, ); if (token1 !== undefined && token2 !== undefined) { extra = { token1: token1.token_name, decimals1: token1.decimals, token2: token2.token_name, decimals2: token2.decimals, }; if (token1.id !== 0) { tokenInfo.push(getLedgerTokenInfo(token1.id).message); } if (token2.id !== 0) { tokenInfo.push(getLedgerTokenInfo(token2.id).message); } } break; } case 'ExchangeInjectContract': { //ExchangeInjectContract const exchangeDepositID = transaction.raw_data.contract[0].parameter.value.exchange_id; const exchangeDeposit = getLedgerExchangeInfo(exchangeDepositID); const exchangeDepositToken = getLedgerTokenInfo( TronWeb.toUtf8(transaction.raw_data.contract[0].parameter.value.token_id), ); // get exchange info extra = { pair: exchangeDeposit.pair, // @ts-ignore token: exchangeDepositToken.token_name, // @ts-ignore decimals: exchangeDepositToken.decimals, }; if (exchangeDepositToken.id !== 0) { tokenInfo.push(exchangeDepositToken.message); } break; } case 'ExchangeWithdrawContract': { //ExchangeWithdrawContract const exchangeWithdrawID = transaction.raw_data.contract[0].parameter.value.exchange_id; const exchangeWithdraw = getLedgerExchangeInfo(exchangeWithdrawID); const exchangeWithdrawToken = getLedgerTokenInfo( TronWeb.toUtf8(transaction.raw_data.contract[0].parameter.value.token_id), ); // get exchange info extra = { pair: exchangeWithdraw.pair, // @ts-ignore token: exchangeWithdrawToken.token_name, // @ts-ignore decimals: exchangeWithdrawToken.decimals, }; if (exchangeWithdrawToken.id !== 0) { tokenInfo.push(exchangeWithdrawToken.message); } break; } case 'ExchangeTransactionContract': { //ExchangeTransactionContract const exchangeID = transaction.raw_data.contract[0].parameter.value.exchange_id; const exchange = getLedgerExchangeInfo(exchangeID); // get exchange info extra = { pair: exchange.pair, decimals1: exchange.decimals1, decimals2: exchange.decimals2, action: transaction.raw_data.contract[0].parameter.value.token_id === exchange.firstToken ? 'Sell' : 'Buy', }; tokenInfo.push(exchange.message); break; } } extra.hash = transaction.txID; const signedResponse = await ledgerWebHid.signTransaction( { hex: transaction.raw_data_hex, info: tokenInfo, }, path, ); if (Array.isArray(transaction.signature)) { if (!transaction.signature.includes(signedResponse)) transaction.signature.push(signedResponse); } else { transaction.signature = [signedResponse]; } return transaction; } catch (error: any) { const stakeV2Contracts = [ 'FreezeBalanceV2Contract', 'UnfreezeBalanceV2Contract', 'WithdrawExpireUnfreezeContract', 'DelegateResourceContract', 'UnDelegateResourceContract', 'CancelAllUnfreezeV2Contract', ]; if (/Too many bytes to encode/.test(error.message)) { return await signHash(ledgerWebHid, transaction, path); } else if (error.toString().includes('0x6a80') && stakeV2Contracts.includes(contractType)) { return await signHash(ledgerWebHid, transaction, path); } else { throw new SignError(error.message); } } } async ledgerSignTypedData(params: LedgerSignParams) { try { const { data, path } = params; const ledgerWebHid = new LedgerTrxWebHid(); const TypedDataEncoder = TronWebUtils._TypedDataEncoder; const messageDigest = TypedDataEncoder.hash(data.domain, data.types, data.message).replace( '0x', '', ); return await ledgerWebHid.signTransactionHash(path, messageDigest); } catch (e: any) { throw new SignError(e.message); } } } async function signHash(ledgerWebHid: LedgerTrxWebHid, transaction: any, path: string) { try { const signedResponse = await ledgerWebHid.signTransactionHash(path, transaction.txID); if (Array.isArray(transaction.signature)) { if (!transaction.signature.includes(signedResponse)) transaction.signature.push(signedResponse); } else { transaction.signature = [signedResponse]; } return transaction; } catch (e: any) { throw e.message; } } const getTokenExtraInfo = async (ID: any, tronWebInstance: any) => { let tokenID = ID; if (typeof tokenID != 'number') { tokenID = TronWeb.toUtf8(tokenID); if (tokenID === '') return { id: 0, decimals: 6, token_name: 'TRX' }; else tokenID = parseInt(tokenID); } const { id, precision, name } = await tronWebInstance.trx.getTokenByID(tokenID); return { id, decimals: precision, token_name: name }; }; const getLedgerTokenInfo = (ID: any): TokenItem => { let tokenID = ID; if (typeof tokenID != 'number') { if (tokenID === '') tokenID = 0; else tokenID = parseInt(tokenID); } return ( tokenList.find((o: any) => o.id === tokenID) ?? { id: 0, message: '', } ); }; const getLedgerExchangeInfo = (ID: any): ExchangeItem => { let exchangeID = ID; if (typeof exchangeID !== 'number') { exchangeID = parseInt(exchangeID); } return ( exchangeList.find((o: any) => o.id === exchangeID) ?? { id: 0, message: '', firstToken: '', pair: '', decimals1: 0, decimals2: 0, } ); };