-
Notifications
You must be signed in to change notification settings - Fork 89
feat: support tron with native wallet #750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
21477bf
chore: version packages to 1.62.12-tron-support.0
NeOMakinG 6b22beb
chore: temporarily skip build for publish
NeOMakinG 5b368ce
chore: version packages to 1.62.12-tron-alpha.0
NeOMakinG b7fa895
chore: version packages to 1.62.12-tron-alpha.1
NeOMakinG 6624af5
fix: rename adapter to tronAdapter to avoid property collision with S…
NeOMakinG 844e640
chore: version packages to 1.62.14-tron-adapter-fix.0
NeOMakinG 4536ed9
fix: resolve TypeScript error in GridPlus ethereum transaction data
NeOMakinG 8dec9a0
fix: restore proper build scripts in package.json files
NeOMakinG 6fc3d0b
chore: version packages to 1.62.14-tron-adapter-fix.1
NeOMakinG 0791b70
chore: fix clean scripts and version to 1.62.14-tron-adapter-fix.2
NeOMakinG 2011f88
chore: update gitHead in package.json files
NeOMakinG 516d886
fix: convert Uint8Array to Buffer before toString in TRON address der…
NeOMakinG b1bf1e9
fix: use slice(1) instead of slice(2) for TRON public key
NeOMakinG cdfb59c
chore: bump version to 1.62.14-tron-adapter-fix.4
NeOMakinG 4119922
fix: lint
NeOMakinG d71a3fe
fix: reset packages
NeOMakinG f441d6e
fix: reset gridplus
NeOMakinG 99f5a85
fix: reset yarn
NeOMakinG 822f49a
chore: bump version to 1.62.15-tron.1
gomesalexandre 5cd6b3e
chore: reset package.json and lerna.json to origin/master
gomesalexandre 6ff6cda
Merge remote-tracking branch 'origin/master' into tron-support-native
gomesalexandre caa3b9d
chore(release): publish 1.62.14
gomesalexandre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import { addressNListToBIP32, slip44ByCoin } from "./utils"; | ||
| import { BIP32Path, HDWallet, HDWalletInfo, PathDescription } from "./wallet"; | ||
|
|
||
| export interface TronGetAddress { | ||
| addressNList: BIP32Path; | ||
| showDisplay?: boolean; | ||
| pubKey?: string; | ||
| } | ||
|
|
||
| export interface TronSignTx { | ||
| addressNList: BIP32Path; | ||
| /** Raw transaction data in hex format */ | ||
| rawDataHex: string; | ||
| } | ||
|
|
||
| export interface TronSignedTx { | ||
| serialized: string; | ||
| signature: string; | ||
| } | ||
|
|
||
| export interface TronTxSignature { | ||
| signature: string; | ||
| } | ||
|
|
||
| export interface TronGetAccountPaths { | ||
| accountIdx: number; | ||
| } | ||
|
|
||
| export interface TronAccountPath { | ||
| addressNList: BIP32Path; | ||
| } | ||
|
|
||
| export interface TronWalletInfo extends HDWalletInfo { | ||
| readonly _supportsTronInfo: boolean; | ||
|
|
||
| /** | ||
| * Returns a list of bip32 paths for a given account index in preferred order | ||
| * from most to least preferred. | ||
| */ | ||
| tronGetAccountPaths(msg: TronGetAccountPaths): Array<TronAccountPath>; | ||
|
|
||
| /** | ||
| * Returns the "next" account path, if any. | ||
| */ | ||
| tronNextAccountPath(msg: TronAccountPath): TronAccountPath | undefined; | ||
| } | ||
|
|
||
| export interface TronWallet extends TronWalletInfo, HDWallet { | ||
| readonly _supportsTron: boolean; | ||
|
|
||
| tronGetAddress(msg: TronGetAddress): Promise<string | null>; | ||
| tronGetAddresses?(msgs: TronGetAddress[]): Promise<string[]>; | ||
| tronSignTx(msg: TronSignTx): Promise<TronSignedTx | null>; | ||
| tronSendTx?(msg: TronSignTx): Promise<TronTxSignature | null>; | ||
| } | ||
|
|
||
| export function tronDescribePath(path: BIP32Path): PathDescription { | ||
| const pathStr = addressNListToBIP32(path); | ||
| const unknown: PathDescription = { | ||
| verbose: pathStr, | ||
| coin: "Tron", | ||
| isKnown: false, | ||
| }; | ||
|
|
||
| if (path.length != 5) return unknown; | ||
| if (path[0] != 0x80000000 + 44) return unknown; | ||
| if (path[1] != 0x80000000 + slip44ByCoin("Tron")) return unknown; | ||
| if ((path[2] & 0x80000000) >>> 0 !== 0x80000000) return unknown; | ||
| if (path[3] !== 0) return unknown; | ||
| if (path[4] !== 0) return unknown; | ||
|
|
||
| const index = path[2] & 0x7fffffff; | ||
| return { | ||
| verbose: `Tron Account #${index}`, | ||
| accountIdx: index, | ||
| wholeAccount: true, | ||
| coin: "Tron", | ||
| isKnown: true, | ||
| }; | ||
| } | ||
|
|
||
| // The standard BIP44 derivation path for Tron is: m/44'/195'/<account>'/0/0 | ||
| // https://github.com/tronprotocol/tips/issues/102 | ||
| export function tronGetAccountPaths(msg: TronGetAccountPaths): Array<TronAccountPath> { | ||
| const slip44 = slip44ByCoin("Tron"); | ||
| return [{ addressNList: [0x80000000 + 44, 0x80000000 + slip44, 0x80000000 + msg.accountIdx, 0, 0] }]; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
packages/hdwallet-native/src/crypto/isolation/adapters/tron.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import { keccak256 } from "@ethersproject/keccak256"; | ||
| import * as core from "@shapeshiftoss/hdwallet-core"; | ||
| import * as bs58check from "bs58check"; | ||
|
|
||
| import { Isolation } from "../.."; | ||
| import { SecP256K1 } from "../core"; | ||
|
|
||
| const TRON_ADDRESS_PREFIX = 0x41; | ||
|
|
||
| export class TronAdapter { | ||
| protected readonly nodeAdapter: Isolation.Adapters.BIP32; | ||
|
|
||
| constructor(nodeAdapter: Isolation.Adapters.BIP32) { | ||
| this.nodeAdapter = nodeAdapter; | ||
| } | ||
|
|
||
| /** | ||
| * Get TRON address from BIP32 path | ||
| * Address generation: | ||
| * 1. Get uncompressed public key (65 bytes) | ||
| * 2. Remove first byte (0x04) | ||
| * 3. Hash with Keccak256 | ||
| * 4. Take last 20 bytes | ||
| * 5. Prepend 0x41 | ||
| * 6. Base58check encode | ||
| */ | ||
| async getAddress(addressNList: core.BIP32Path): Promise<string> { | ||
| const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList)); | ||
| const publicKey = SecP256K1.UncompressedPoint.from(nodeAdapter.getPublicKey()); | ||
|
|
||
| // Remove the 0x04 prefix from uncompressed public key | ||
| const publicKeyBytes = publicKey.slice(1); | ||
|
|
||
| // Hash with Keccak256 | ||
| const hash = keccak256("0x" + Buffer.from(publicKeyBytes).toString("hex")); | ||
|
|
||
| // Take last 20 bytes and prepend 0x41 (TRON prefix) | ||
| const addressBytes = Buffer.concat([Buffer.from([TRON_ADDRESS_PREFIX]), Buffer.from(hash.slice(-40), "hex")]); | ||
|
|
||
| // Base58check encode | ||
| return bs58check.encode(addressBytes); | ||
| } | ||
|
|
||
| /** | ||
| * Sign TRON transaction | ||
| * Transaction signing: | ||
| * 1. Get raw transaction hex | ||
| * 2. Hash with SHA256 (NOT Keccak256) | ||
| * 3. Sign with secp256k1 | ||
| * 4. Return hex signature | ||
| */ | ||
| async signTransaction(rawDataHex: string, addressNList: core.BIP32Path): Promise<string> { | ||
| const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList)); | ||
| const txBuf = Buffer.from(rawDataHex, "hex"); | ||
|
|
||
| // TRON uses SHA256 for transaction signing | ||
| const recoverableSig = await SecP256K1.RecoverableSignature.signCanonically(nodeAdapter.node, "sha256", txBuf); | ||
|
|
||
| const sig = SecP256K1.RecoverableSignature.sig(recoverableSig); | ||
| const recoveryParam = SecP256K1.RecoverableSignature.recoveryParam(recoverableSig); | ||
|
|
||
| // Concatenate signature (64 bytes) + recovery param (1 byte) | ||
| const fullSig = core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]); | ||
|
|
||
| return fullSig.toString("hex"); | ||
| } | ||
| } | ||
|
|
||
| export default TronAdapter; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
gomesalexandre marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import * as core from "@shapeshiftoss/hdwallet-core"; | ||
|
|
||
| import { Isolation } from "./crypto"; | ||
| import { TronAdapter } from "./crypto/isolation/adapters/tron"; | ||
| import { NativeHDWalletBase } from "./native"; | ||
|
|
||
| export function MixinNativeTronWalletInfo<TBase extends core.Constructor<core.HDWalletInfo>>(Base: TBase) { | ||
| // eslint-disable-next-line @typescript-eslint/no-shadow | ||
| return class MixinNativeTronWalletInfo extends Base implements core.TronWalletInfo { | ||
| readonly _supportsTronInfo = true; | ||
|
|
||
| tronGetAccountPaths(msg: core.TronGetAccountPaths): Array<core.TronAccountPath> { | ||
| return core.tronGetAccountPaths(msg); | ||
| } | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| tronNextAccountPath(msg: core.TronAccountPath): core.TronAccountPath | undefined { | ||
| throw new Error("Method not implemented"); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| export function MixinNativeTronWallet<TBase extends core.Constructor<NativeHDWalletBase>>(Base: TBase) { | ||
| // eslint-disable-next-line @typescript-eslint/no-shadow | ||
| return class MixinNativeTronWallet extends Base { | ||
| readonly _supportsTron = true; | ||
|
|
||
| tronAdapter: TronAdapter | undefined; | ||
|
|
||
| async tronInitializeWallet(masterKey: Isolation.Core.BIP32.Node): Promise<void> { | ||
| const nodeAdapter = await Isolation.Adapters.BIP32.create(masterKey); | ||
| this.tronAdapter = new TronAdapter(nodeAdapter); | ||
| } | ||
|
|
||
| tronWipe() { | ||
| this.tronAdapter = undefined; | ||
| } | ||
|
|
||
| async tronGetAddress(msg: core.TronGetAddress): Promise<string | null> { | ||
| return this.needsMnemonic(!!this.tronAdapter, () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| return this.tronAdapter!.getAddress(msg.addressNList); | ||
| }); | ||
| } | ||
|
|
||
| async tronSignTx(msg: core.TronSignTx): Promise<core.TronSignedTx | null> { | ||
| return this.needsMnemonic(!!this.tronAdapter, async () => { | ||
| const address = await this.tronGetAddress({ | ||
| addressNList: msg.addressNList, | ||
| showDisplay: false, | ||
| }); | ||
|
|
||
| if (!address) throw new Error("Failed to get TRON address"); | ||
|
|
||
| const signature = await this.tronAdapter!.signTransaction(msg.rawDataHex, msg.addressNList); | ||
|
|
||
| // Serialized transaction = rawDataHex + signature | ||
| const serialized = msg.rawDataHex + signature; | ||
|
|
||
| return { | ||
| serialized, | ||
| signature, | ||
| }; | ||
| }); | ||
| } | ||
| }; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.