Skip to content
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

new module for ledger related utils #193

Merged
merged 6 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/silver-bugs-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sei-js/ledger': minor
---

Packge init + helper functions to work with Ledger in Cosmos Stargate client
2 changes: 2 additions & 0 deletions packages/ledger/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
Empty file added packages/ledger/.gitkeep
Empty file.
13 changes: 13 additions & 0 deletions packages/ledger/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
src
node_modules
docs
coverage/

.gitkeep
jest.config.ts
tsconfig.json

yarn-error.log

.eslintignore
eslintrc.json
52 changes: 52 additions & 0 deletions packages/ledger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# @sei-js/ledger

## Installation
```bash
yarn add @sei-js/ledger
```

## Usage
```typescript
import {
coins,
SigningStargateClient,
StdFee
} from "@cosmjs/stargate";

import {
createTransportAndApp,
getAddresses,
SeiLedgerOfflineAminoSigner
} from "@sei-js/ledger";

const testApp = async () => {
const validatorAddress = "seivaloper1sq7x0r2mf3gvwr2l9amtlye0yd3c6dqa4th95v";
const rpcUrl = "https://rpc-testnet.sei-apis.com/";
const memo = "Delegation";
const path = "m/44'/60'/0'/0/0";

const {app} = await createTransportAndApp();
const {nativeAddress} = await getAddresses(app, path);
const ledgerSigner = new SeiLedgerOfflineAminoSigner(app, path)
const signingStargateClient = await SigningStargateClient.connectWithSigner(rpcUrl, ledgerSigner)

const msgDelegate = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: {
delegatorAddress: nativeAddress.address,
validatorAddress: validatorAddress,
amount: coins(500, "usei"),
},
};

const fee: StdFee = {
amount: [{denom: "usei", amount: "20000"}],
gas: "200000",
};

const result = await signingStargateClient.signAndBroadcast(nativeAddress.address, [msgDelegate], fee, memo)
console.log("Broadcast result:", result);
};

testApp();
```
3 changes: 3 additions & 0 deletions packages/ledger/eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["../../eslint.base.json", "eslint:recommended", "plugin:@typescript-eslint/recommended"]
}
8 changes: 8 additions & 0 deletions packages/ledger/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
modulePathIgnorePatterns: ['<rootDir>/dist/']
};
50 changes: 50 additions & 0 deletions packages/ledger/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@sei-js/ledger",
"version": "1.0.0",
"description": "TypeScript library for SEI Ledger app helper functions",
"main": "./dist/cjs/src/index.js",
"module": "./dist/esm/src/index.js",
"types": "./dist/types/src/index.d.ts",
"sideEffects": false,
"files": [
"dist"
],
"scripts": {
"prebuild": "rimraf dist",
"build": "yarn build:cjs && yarn build:esm && yarn build:prettier && yarn build:types",
"build:cjs": "tsc --outDir dist/cjs --module commonjs",
"build:esm": "tsc --outDir dist/esm --module esnext",
"build:types": "tsc --project ./tsconfig.declaration.json",
"build:prettier": "prettier --write 'dist/**/*.js'",
"docs": "typedoc --out docs",
"test": "jest --passWithNoTests",
"lint": "eslint --ext .ts"
},
"homepage": "https://github.com/sei-protocol/sei-js#readme",
"keywords": [
"sei",
"javascript",
"typescript",
"ledger"
],
"repository": "[email protected]:sei-protocol/sei-js.git",
"license": "MIT",
"private": false,
"publishConfig": {
"access": "public"
},
"dependencies": {
"@cosmjs/stargate": "^0.32.4",
"@ledgerhq/hw-transport-node-hid": "^6.29.3",
"@zondax/ledger-sei": "^1.0.0"
},
"peerDependencies": {},
"devDependencies": {},
"exports": {
".": {
"import": "./dist/esm/src/index.js",
"require": "./dist/cjs/src/index.js",
"types": "./dist/types/src/index.d.ts"
}
}
}
2 changes: 2 additions & 0 deletions packages/ledger/src/cosmos/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./seiLedgerOfflineAminoSigner"
export * from "./utils"
50 changes: 50 additions & 0 deletions packages/ledger/src/cosmos/seiLedgerOfflineAminoSigner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
AminoSignResponse,
encodeSecp256k1Signature,
OfflineAminoSigner,
serializeSignDoc,
StdSignDoc
} from '@cosmjs/amino';
import {fromHex } from '@cosmjs/encoding';
import { AccountData } from '@cosmjs/proto-signing';
import { Secp256k1Signature } from '@cosmjs/crypto';
import { SeiApp } from '@zondax/ledger-sei';

/**
* A signer implementation that uses a Ledger device to sign transactions
*/
export class SeiLedgerOfflineAminoSigner implements OfflineAminoSigner {
private readonly path: string;
private readonly app: SeiApp;

/**
* Creates a new SeiLedgerOfflineAminoSigner
* @param app Ledger Sei app instance
* @param path hd derivation path (e.g. "m/44'/60'/0'/0/0")
*/
constructor(app: SeiApp, path: string) {
this.path = path;
this.app = app;
}

public async getAccounts(): Promise<readonly AccountData[]> {
const nativeAddress = await this.app.getCosmosAddress(this.path);
return [
{
address: nativeAddress.address,
algo: "secp256k1",
pubkey: fromHex(nativeAddress.pubKey)
},
];
}

public async signAmino(signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> {
const signature = await this.app.signCosmos(this.path, Buffer.from(serializeSignDoc(signDoc)));
const sig = new Secp256k1Signature(signature.r, signature.s).toFixedLength();
const nativeAddress = await this.app.getCosmosAddress(this.path);
return {
signed: signDoc,
signature: encodeSecp256k1Signature(fromHex(nativeAddress.pubKey), sig),
};
}
}
28 changes: 28 additions & 0 deletions packages/ledger/src/cosmos/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Transport from '@ledgerhq/hw-transport-node-hid';
import { SeiApp } from '@zondax/ledger-sei';

/**
* Creates a transport and app instance
*
* @returns {Promise<{transport: Transport, app: SeiApp}>} transport and app instances
*/
export const createTransportAndApp = async () => {
const transport = await Transport.create();
const app = new SeiApp(transport);
return {transport, app};
};

/**
* Get the EVM and Cosmos addresses from the Ledger device
* @param app Ledger Sei app instance
* @param path hd derivation path (e.g. "m/44'/60'/0'/0/0")
*
* @returns {Promise<{evmAddress: string, nativeAddress: string}>} EVM and Cosmos address objects containing
* address and public key
*/
export const getAddresses = async (app: SeiApp, path: string) => {
const evmAddress = await app.getEVMAddress(path);
const nativeAddress = await app.getCosmosAddress(path);
return {evmAddress, nativeAddress};
};

1 change: 1 addition & 0 deletions packages/ledger/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './cosmos';
10 changes: 10 additions & 0 deletions packages/ledger/tsconfig.declaration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist/types",
"declaration": true,
"emitDeclarationOnly": true,
"isolatedModules": false,
"preserveConstEnums": false
}
}
13 changes: 13 additions & 0 deletions packages/ledger/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"include": ["./src/**/*" ],
"compilerOptions": {
"outDir": "./dist/types",
"rootDir": "./",
},
"typedocOptions": {
"readme": "./README.md",
"name": "registry",
"entryPoints": ["src/index.ts"]
}
}
Loading
Loading