Low-level wallet engine for TRON and Ethereum, used by the TronLink browser extension and embeddable in any TypeScript/JavaScript runtime.
@tronlink/core is the wallet primitive layer that powers TronLink. It provides:
- HD account derivation for TRON and EVM chains from a single mnemonic.
- Transaction, message, and EIP-712 typed-data signing for both ecosystems.
- Hardware wallet (Ledger) integration over WebHID for both TRX and ETH apps.
- Address & signature validation utilities.
- A privacy-preserving usage analytics module whose design and guarantees are documented in
PRIVACY.md.
The package is non-custodial and stateless: no keys, mnemonics, or addresses ever leave the host process unless the embedding application explicitly chooses to persist them. The package is published under Apache-2.0 and the source is fully open for audit.
- Installation
- Prerequisites
- Quick Start
- Public API
- Hardware Wallet (Ledger) Support
- Anonymous Analytics Module
- Project Structure
- Build & Development
- Testing
- Package Verification (GPG)
- Versioning & Releases
- Privacy
- License
The package is published on npm: https://www.npmjs.com/package/@tronlink/core
# npm
npm install @tronlink/core
# yarn
yarn add @tronlink/core
# pnpm
pnpm add @tronlink/coreBoth CommonJS and ESM entry points are shipped:
| Module system | Entry |
|---|---|
| CommonJS | dist/commonjs/index.js |
| ESM | dist/esm/index.js |
TypeScript declaration files (.d.ts) are included.
| Tool | Required version |
|---|---|
| Node.js | >= 18.19.0 (recommended 20.x) |
| pnpm | >= 7.32.0 (used for development) |
| TypeScript | >= 4.9.5 (consumers using TS) |
Browser usage requires bundlers that handle Node polyfills (the package depends on buffer, crypto, stream shims; see webpack.config.js for the configuration used internally).
import { BaseWallet, TronWallet, EvmWallet } from '@tronlink/core';
const mnemonic = BaseWallet.generateRandomMnemonic();
// TRON
const tron = new TronWallet();
const tronPath = tron.derivePath({ accountIndex: 0, addressIndex: 0 });
const tronPrivateKey = tron.derivePrivateKey({ mnemonic, path: tronPath });
const tronAddress = tron.getAddressBy({ privateKey: tronPrivateKey });
// EVM
const evm = new EvmWallet();
const evmPath = evm.derivePath({ accountIndex: 0, addressIndex: 0 });
const evmPrivateKey = evm.derivePrivateKey({ mnemonic, path: evmPath });
const evmAddress = evm.getAddressBy({ privateKey: evmPrivateKey });A fresh mnemonic, both private keys, and both addresses are produced entirely in process. Nothing is sent over the network.
import { EvmWallet } from '@tronlink/core';
const wallet = new EvmWallet();
const common = wallet.getCommonConfiguration({
isSupportsEIP1559: true,
chain: 'mainnet',
chainId: 1,
chainName: 'Ethereum',
});
const signed = wallet.signTransaction({
privateKey,
data: { common, unSignedTransaction },
});
// Optional verification
const ok = wallet.verifyEthTransactionSign(unSignedTransaction, signed.rsv, address);import { TronWallet } from '@tronlink/core';
const wallet = new TronWallet();
const signed = wallet.signTransaction({
privateKey,
data: rawTransaction,
});
// Or generic signer with options (multi-sig, permission, signMessageV2 ...)
const sig = wallet.sign({
privateKey,
data: payload,
options: {
nodeInfo,
isMultiSign: false,
permissionId: 0,
isSignMessageV2: true,
},
});// EVM
evmWallet.signTypedData({ privateKey, data: typedData });
// TRON
tronWallet.signTypedData({ privateKey, data: typedData });More end-to-end examples live in the demo/ directory:
demo/create_account.ts— mnemonic & key derivationdemo/evm_signature.ts— EVM tx / message / typed-data signingdemo/tron_signature.ts— TRON tx / message / typed-data signingdemo/user_statistics.ts— anonymous analytics wiring
Run a demo:
cd demo
pnpm install
./node_modules/ts-node/dist/bin.js create_account.tsThe package re-exports the following from src/index.ts:
| Module | Exports |
|---|---|
base_wallet |
BaseWallet, DeviceStatusType |
evm_wallet |
EvmWallet, LedgerEvmSigner, LedgerEthWebHid, LedgerEthHidStatusChecker |
tron_wallet |
TronWallet, LedgerTrxSigner, LedgerTrxWebHid, LedgerTrxHidStatusChecker |
utils |
httpProxy |
userStatistics |
getAndUpdateAddressAssetPrecipitation, getAndUpdateTransactionRecord, checkAndReportXY, plus types (ExternalDependencies, BalanceInfo, TransactionRecord, …) |
| Method | Purpose |
|---|---|
BaseWallet.generateRandomMnemonic() |
Generate a fresh BIP-39 mnemonic. |
| Method | Purpose |
|---|---|
derivePath(opts) |
Build a BIP-44 derivation path. |
derivePrivateKey(opts) |
Derive a private key from { mnemonic, path }. |
getAddressBy(opts) |
Compute the chain-specific address. |
validateAddress(opts) |
Validate an address string. |
signTransaction(opts) |
Sign a chain-specific transaction. |
signMessage(opts) |
Sign a UTF-8 message. |
signTypedData(opts) |
Sign EIP-712 / TIP-712 typed data. |
sign(opts) (TRON) |
Generic signer with SignOptions. |
verifyEthTransactionSign(...) (EVM) |
Verify a signed transaction. |
verifyEthMessageSign(...) (EVM) |
Verify a signed message. |
getCommonConfiguration(opts) (EVM) |
Build an @ethereumjs/common config. |
All signing methods are pure functions of their inputs: they take a private key plus payload, return a signature, and have no side effects on storage or network.
Ledger devices are supported over WebHID for both networks. The connection lifecycle is the same on each chain — connect, read status, sign — only the signer class differs.
import {
LedgerEthWebHid, LedgerEvmSigner, LedgerEthHidStatusChecker,
LedgerTrxWebHid, LedgerTrxSigner, LedgerTrxHidStatusChecker,
} from '@tronlink/core';| Class | Role |
|---|---|
LedgerEthWebHid / LedgerTrxWebHid |
WebHID transport bring-up. |
LedgerEvmSigner / LedgerTrxSigner |
High-level signer over the transport. |
LedgerEthHidStatusChecker / LedgerTrxHidStatusChecker |
Device status polling. |
WebHID is browser-only. Hardware-wallet flows are gated to environments that expose navigator.hid.
The userStatistics module implements non-identifying product telemetry:
- Wallet addresses are never sent. They are mapped, on-device only, to random UUIDs.
- Raw amounts are never sent. They are bucketed locally into a 9-tier logarithmic histogram (
0_1,1_10, …,10m_infinite). - Records are merged locally per
(uid, actionType, tokenAddress, day)before any upload. - Reporting is gated to mainnet via
checkIsNeedReportChain()— testnet and custom-RPC activity are not reported. - Payloads are encrypted in transit by default.
A consumer wires the module by implementing the ExternalDependencies interface and calling the entry points:
import {
getAndUpdateAddressAssetPrecipitation,
getAndUpdateTransactionRecord,
checkAndReportXY,
ExternalDependencies,
} from '@tronlink/core';
const deps: ExternalDependencies = {
getOrCreateUuid,
saveAssetPrecipitation, loadAssetPrecipitation,
setTransactionRecord, loadTransactionRecords,
getAllNeedReportAssetPrecipitation,
getAllNeedReportTransactionRecords,
checkIsNeedReportChain,
getSystemConfig, updateSystemConfig,
getBalanceInfo,
sendStatistics,
getAddressType,
// ...
};
await getAndUpdateAddressAssetPrecipitation(address, nodeId, deps);
await getAndUpdateTransactionRecord(txMeta, deps);
await checkAndReportXY(deps); // batched, encrypted uploadThe privacy guarantees, threat model, and exact on-the-wire payload format are documented separately in PRIVACY.md. Auditors should start there.
tronlink-extension-core/
├── src/
│ ├── base_wallet/ # Mnemonic, BIP-32/39/44, common helpers
│ ├── evm_wallet/ # EVM signing, Ledger ETH integration
│ ├── tron_wallet/ # TRON signing, Ledger TRX integration
│ ├── userStatistics/ # Privacy-preserving analytics
│ │ ├── addressUuidMap.ts # Local-only address ↔ UUID
│ │ ├── userStatistics.ts # Bucketing & record building
│ │ ├── report.ts # Encrypted batch upload
│ │ ├── transactionRecord.ts # Tx record schema
│ │ ├── assetPrecipitation.ts # Daily asset snapshot schema
│ │ ├── actionType.ts # Tracked action enums
│ │ ├── constants.ts # Chains, contract types, tokens
│ │ ├── indexedDB/ # On-device persistence
│ │ ├── types.ts
│ │ └── index.ts
│ ├── utils/ # httpProxy and shared helpers
│ ├── tests/ # Jest unit tests
│ └── index.ts # Package entry
├── demo/ # Runnable usage examples
├── dist/ # Built artifacts (commonjs, esm)
├── webpack.config.js
├── babel.config.js
├── tsconfig*.json
└── package.json
Clone and install:
git clone https://github.com/TronLink/tronlink-extension-core.git
cd tronlink-extension-core
pnpm installAvailable scripts:
| Script | Description |
|---|---|
pnpm build |
Clean + webpack + tsc for both CJS and ESM. |
pnpm build:dev |
Development build variant. |
pnpm start |
Webpack dev server (for in-repo demo iteration). |
pnpm test |
Run the Jest suite with coverage. |
pnpm clean |
Remove dist/. |
Source is formatted with Prettier and type-checked with TypeScript in strict mode. Please run pnpm test before opening a PR.
Tests live in src/tests/ and run on Jest with ts-jest:
pnpm testCoverage is emitted to coverage/ by default. Mainnet code paths are tested against fixed fixtures — no live RPCs or real keys are involved.
Release tarballs published to npm are signed. To verify a downloaded artifact, import the build key and check the signature:
pub: 7B910EA80207596075E6D7BA5D34F7A6550473BA
uid: build_tronlink <build@tronlink.org>
gpg --recv-keys 7B910EA80207596075E6D7BA5D34F7A6550473BA
gpg --verify <package>.tgz.asc <package>.tgzA failed signature check is a security incident — please do not install the package.
This project follows Semantic Versioning. Breaking changes to the public API in src/index.ts are reflected in the major version. The release history is on the GitHub Releases page.
The technical mechanisms by which the TronLink extension avoids collecting device-resident wallet addresses are implemented in this package and documented in PRIVACY.md.
Every claim in that document is anchored to specific files in src/userStatistics/. Independent audits are welcome.
Licensed under the Apache License, Version 2.0.
Copyright (c) TronLink contributors