Skip to content

Commit

Permalink
feat(XDEFI-5747): Add Tron ledger signer (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
samsheff authored Jan 10, 2024
1 parent 7018731 commit ae46027
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 9 deletions.
2 changes: 2 additions & 0 deletions packages/tron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
},
"dependencies": {
"@agrozyme/types-tronweb": "^5.3.2",
"@ledgerhq/hw-app-trx": "^6.28.1",
"@ledgerhq/hw-transport-webhid": "^6.28.1",
"@xdefi-tech/chains-core": "*",
"axios": "^1.6.1",
"bignumber.js": "^9.0.2",
Expand Down
67 changes: 67 additions & 0 deletions packages/tron/src/signers/ledger.signer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Msg } from '@xdefi-tech/chains-core';

import { TronProvider } from '../chain.provider';
import { ChainDataSource } from '../datasource';
import { TRON_MANIFEST } from '../manifests';
import { ChainMsg, MsgBody } from '../msg';

import LedgerSigner from './ledger.signer';
jest.mock('@ledgerhq/hw-transport-webhid', () => ({
create: jest.fn().mockResolvedValue({
close: jest.fn().mockImplementation(),
}),
}));

jest.mock('@ledgerhq/hw-app-trx', () => {
return jest.fn().mockImplementation(() => ({
getAddress: jest.fn().mockResolvedValue({
address: 'TSDmgg8m3AfNniTzz4dyWN44fkGd7otZ4C',
}),
signTransactionHash: jest.fn().mockResolvedValue('SIGNEDTX'),
}));
});

describe('ledger.signer', () => {
let signer: LedgerSigner;
let derivationPath: string;
let provider: TronProvider;
let txInput: MsgBody;
let message: Msg;

beforeEach(() => {
signer = new LedgerSigner();

provider = new TronProvider(new ChainDataSource(TRON_MANIFEST));
derivationPath = "m/44'/195'/0'/0/0";

txInput = {
from: 'TSDmgg8m3AfNniTzz4dyWN44fkGd7otZ4C',
to: 'TN4JsVEuLVBG9Ru7YSjDxkTdoRTychnJkH',
amount: '0.000001',
};

message = provider.createMsg(txInput);
});

it('should get an address from the ledger device', async () => {
expect(await signer.getAddress(derivationPath)).toBe(txInput.from);
});

it('should sign a transaction using a ledger device', async () => {
await signer.sign(message as ChainMsg, derivationPath);

expect(message.signedTransaction).toEqual('SIGNEDTX');
});

it('should return true for a valid address', () => {
expect(signer.verifyAddress(txInput.from, TRON_MANIFEST)).toBe(true);
});

it('should return false for an invalid address', () => {
expect(signer.verifyAddress('invalid-address', TRON_MANIFEST)).toBe(false);
});

it('should fail if private key is requested', async () => {
expect(signer.getPrivateKey(derivationPath)).rejects.toThrowError();
});
});
53 changes: 53 additions & 0 deletions packages/tron/src/signers/ledger.signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Transport from '@ledgerhq/hw-transport-webhid';
import Trx from '@ledgerhq/hw-app-trx';
import { Chain, Signer, SignerDecorator } from '@xdefi-tech/chains-core';
import TronWeb from 'tronweb';

import { ChainMsg } from '../msg';

@SignerDecorator(Signer.SignerType.LEDGER)
export class LedgerSigner extends Signer.Provider {
verifyAddress(address: string, manifest: Chain.Manifest): boolean {
const tronWeb = new TronWeb({
fullHost: manifest.rpcURL,
});

return tronWeb.isAddress(address);
}

async getAddress(derivation: string): Promise<string> {
const transport = await Transport.create();
try {
const trx = new Trx(transport);

const address = await trx.getAddress(derivation);

return address.address;
} catch (e) {
throw e;
} finally {
await transport.close();
}
}

async sign(msg: ChainMsg, derivation: string) {
const transport = await Transport.create();
try {
const trx = new Trx(transport);
const tx = await msg.buildTx();

const signedTx = await trx.signTransactionHash(
derivation,
tx.raw_data_hex
);

msg.sign(signedTx);
} catch (e) {
throw e;
} finally {
await transport.close();
}
}
}

export default LedgerSigner;
4 changes: 2 additions & 2 deletions packages/tron/src/signers/private-key.signer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ describe('tron private-key.signer', () => {
const signer = new PrivateKeySigner(MOCK.privateKey);

it('should return true for a valid address', () => {
expect(signer.verifyAddress(MOCK.address)).toBe(true);
expect(signer.verifyAddress(MOCK.address, TRON_MANIFEST)).toBe(true);
});

it('should return false for an invalid address', () => {
expect(signer.verifyAddress('invalid-address')).toBe(false);
expect(signer.verifyAddress('invalid-address', TRON_MANIFEST)).toBe(false);
});

it('should return the correct address for a valid private key', async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/tron/src/signers/private-key.signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export class PrivateKeySigner extends Signer.Provider {
super(key);
}

verifyAddress(address: string, manifest?: Chain.Manifest): boolean {
verifyAddress(address: string, manifest: Chain.Manifest): boolean {
const tronWeb = new TronWeb({
fullHost: manifest?.rpcURL ? manifest.rpcURL : 'https://api.trongrid.io',
fullHost: manifest.rpcURL,
});

return tronWeb.isAddress(address);
Expand Down
4 changes: 2 additions & 2 deletions packages/tron/src/signers/seed-phrase.signer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ describe('tron seed-phrase.signer', () => {
const signer = new SeedPhraseSigner(MOCK.seedPhrase);

it('should return true for a valid address', () => {
expect(signer.verifyAddress(MOCK.address)).toBe(true);
expect(signer.verifyAddress(MOCK.address, TRON_MANIFEST)).toBe(true);
});

it('should return false for an invalid address', () => {
expect(signer.verifyAddress('invalid-address')).toBe(false);
expect(signer.verifyAddress('invalid-address', TRON_MANIFEST)).toBe(false);
});

it('should return the correct address for a valid private key', async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/tron/src/signers/seed-phrase.signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export class SeedPhraseSigner extends Signer.Provider {
super(key);
}

verifyAddress(address: string, manifest?: Chain.Manifest): boolean {
verifyAddress(address: string, manifest: Chain.Manifest): boolean {
const tronWeb = new TronWeb({
fullHost: manifest?.rpcURL ? manifest.rpcURL : 'https://api.trongrid.io',
fullHost: manifest.rpcURL,
});

return tronWeb.isAddress(address);
Expand Down
49 changes: 48 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3917,6 +3917,16 @@
rxjs "6"
semver "^7.3.5"

"@ledgerhq/devices@^8.2.0":
version "8.2.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.2.0.tgz#ef67bf49628252d1779acaa151b1a941acba790e"
integrity sha512-XROTW2gTmmuy+YPPDjdtKKTQ3mfxrPtKtV+a9QFbj8f5MnjVMV0Zpy1BIB4CyIMsVVi4z6+nI67auT7IlsM3SQ==
dependencies:
"@ledgerhq/errors" "^6.16.1"
"@ledgerhq/logs" "^6.12.0"
rxjs "^7.8.1"
semver "^7.3.5"

"@ledgerhq/errors@^5.34.0", "@ledgerhq/errors@^5.50.0":
version "5.50.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9"
Expand All @@ -3932,6 +3942,11 @@
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.14.0.tgz#0bf253983773ef12eebce2091f463bc719223b37"
integrity sha512-ZWJw2Ti6Dq1Ott/+qYqJdDWeZm16qI3VNG5rFlb0TQ3UcAyLIQZbnnzzdcVVwVeZiEp66WIpINd/pBdqsHVyOA==

"@ledgerhq/errors@^6.16.1":
version "6.16.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.16.1.tgz#df650a9ba105397dee2e8c0ceddf6931c5b25ede"
integrity sha512-4D4wKecGzQpIu7sx03Sg4uE1e8g1oZUndWgw9gw776H8h9ov9c5TxPaldTn2j6orPECAERViLf7LTO4L5pE2Cw==

"@ledgerhq/hw-app-btc@^10.0.0":
version "10.0.0"
resolved "https://registry.npmjs.org/@ledgerhq/hw-app-btc/-/hw-app-btc-10.0.0.tgz"
Expand Down Expand Up @@ -3976,6 +3991,13 @@
"@ledgerhq/logs" "^6.10.1"
bip32-path "^0.4.2"

"@ledgerhq/hw-app-trx@^6.28.1":
version "6.28.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-trx/-/hw-app-trx-6.28.1.tgz#26b32756793dff29be7e1434516e70942d4c71e2"
integrity sha512-2qIWOAMQWkdwyOA4aynyWO6s1w3XCH12USlUf0mI8D5H+oDq09QJhHvxFxQcUdnGTQJ/LgsSe393hxMPTi6W3w==
dependencies:
"@ledgerhq/hw-transport" "^6.30.1"

"@ledgerhq/hw-transport-mocker@^6.27.10":
version "6.27.10"
resolved "https://registry.npmjs.org/@ledgerhq/hw-transport-mocker/-/hw-transport-mocker-6.27.10.tgz"
Expand Down Expand Up @@ -4050,6 +4072,16 @@
"@ledgerhq/hw-transport" "^6.28.1"
"@ledgerhq/logs" "^6.10.1"

"@ledgerhq/hw-transport-webhid@^6.28.1":
version "6.28.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.28.1.tgz#af13c517514451bf60ee83d8e2b402028504af5c"
integrity sha512-m1FzUaaRdMm+KWz+sm4RGjG1axAIYEnIC3PqwFGMtXDjyPVohdWxRJD9B2L/etR4EY67b7AH/MoQ02rpUqCCEA==
dependencies:
"@ledgerhq/devices" "^8.2.0"
"@ledgerhq/errors" "^6.16.1"
"@ledgerhq/hw-transport" "^6.30.1"
"@ledgerhq/logs" "^6.12.0"

"@ledgerhq/hw-transport@^5.25.0", "@ledgerhq/hw-transport@^5.34.0", "@ledgerhq/hw-transport@^5.51.1":
version "5.51.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578"
Expand Down Expand Up @@ -4086,6 +4118,16 @@
"@ledgerhq/errors" "^6.12.3"
events "^3.3.0"

"@ledgerhq/hw-transport@^6.30.1":
version "6.30.1"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.1.tgz#fd3c825f41197aeaf705e3c066f82843eaf48cae"
integrity sha512-Xeeo4nt33g5Fsp3CdsPvcc2Uk7dwYeKRSlSFLWcYAAKprf/PmxgNekhke1eaNU/wLoeLOWhY2Cki8F8w9nLMdQ==
dependencies:
"@ledgerhq/devices" "^8.2.0"
"@ledgerhq/errors" "^6.16.1"
"@ledgerhq/logs" "^6.12.0"
events "^3.3.0"

"@ledgerhq/logs@^5.30.0", "@ledgerhq/logs@^5.50.0":
version "5.50.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186"
Expand All @@ -4096,6 +4138,11 @@
resolved "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz"
integrity sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w==

"@ledgerhq/logs@^6.12.0":
version "6.12.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d"
integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==

"@manypkg/find-root@^1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz"
Expand Down Expand Up @@ -14928,7 +14975,7 @@ rxjs@6:
dependencies:
tslib "^1.9.0"

rxjs@^7.4.0:
rxjs@^7.4.0, rxjs@^7.8.1:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
Expand Down

0 comments on commit ae46027

Please sign in to comment.