Skip to content

Commit

Permalink
new module for ledger related utils
Browse files Browse the repository at this point in the history
  • Loading branch information
dssei committed Aug 16, 2024
1 parent 35c62d0 commit 8f40a33
Show file tree
Hide file tree
Showing 13 changed files with 1,288 additions and 11 deletions.
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
63 changes: 63 additions & 0 deletions packages/ledger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# @sei-js/ledger

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

## Usage
```typescript
import {
coin,
StargateClient,
StdFee
} from "@cosmjs/stargate";
import {
AminoMsg,
} from "@cosmjs/amino";

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

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

const {app} = await createTransportAndApp();
const {nativeAddress} = await getAddresses(app, path);
const account = await client.getAccount(nativeAddress.address);
if (!account) {
throw new Error("Account not found");
}

const aminoMsg: AminoMsg = {
type: "cosmos-sdk/MsgDelegate",
value: {
delegator_address: nativeAddress.address,
validator_address: validatorAddress,
amount: coin("1000", "usei"),
},
};

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

const signDoc = createSignDoc(aminoMsg, fee, chainId, memo, account);
const broadcastResponse = await signAndBroadcast(app, path, signDoc, client, nativeAddress);

console.log("Broadcast result:", broadcastResponse);
};

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",
"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"
}
}
}
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 './wallet';
1 change: 1 addition & 0 deletions packages/ledger/src/wallet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./utils"
91 changes: 91 additions & 0 deletions packages/ledger/src/wallet/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
Account,
AminoTypes,
createDefaultAminoConverters,
defaultRegistryTypes,
StargateClient,
StdFee
} from "@cosmjs/stargate";
import Transport from "@ledgerhq/hw-transport-node-hid";
import {
AminoMsg,
AminoSignResponse,
encodeSecp256k1Pubkey,
encodeSecp256k1Signature,
makeSignDoc,
serializeSignDoc,
StdSignDoc,
} from "@cosmjs/amino";
import {fromBase64, fromHex} from "@cosmjs/encoding";
import {TxRaw} from "cosmjs-types/cosmos/tx/v1beta1/tx";
import {encodePubkey, makeAuthInfoBytes, Registry, TxBodyEncodeObject} from "@cosmjs/proto-signing";
import {Secp256k1Signature} from "@cosmjs/crypto";
import {Int53} from "@cosmjs/math";
import {SignMode} from "cosmjs-types/cosmos/tx/signing/v1beta1/signing";
import {SeiApp} from "@zondax/ledger-sei";

export const createTransportAndApp = async () => {
const transport = await Transport.create();
const app = new SeiApp(transport);
return {transport, app};
};

export const getAddresses = async (app: SeiApp, path: string) => {
const evmAddress = await app.getEVMAddress(path);
const nativeAddress = await app.getCosmosAddress(path);
return {evmAddress, nativeAddress};
};

export const createSignDoc = (aminoMsg: AminoMsg, fee: StdFee, chainId: string, memo: string, account: Account) => {
return makeSignDoc(
[aminoMsg],
fee,
chainId,
memo,
account.accountNumber.toString(),
account.sequence.toString(),
);
};

export const signAndBroadcast = async (app: SeiApp, path: string, signDoc: StdSignDoc, client: StargateClient, nativeAddress: {
address: string,
pubKey: string
}) => {
const signature = await app.signCosmos(path, Buffer.from(serializeSignDoc(signDoc)));
const sig = new Secp256k1Signature(signature.r, signature.s).toFixedLength();
const aminoSignResponse: AminoSignResponse = {
signed: signDoc,
signature: encodeSecp256k1Signature(fromHex(nativeAddress.pubKey), sig),
};

const registry = new Registry(defaultRegistryTypes);
const aminoTypes = new AminoTypes(createDefaultAminoConverters());
const signedTxBody = {
messages: aminoSignResponse.signed.msgs.map((msg) => aminoTypes.fromAmino(msg)),
memo: aminoSignResponse.signed.memo,
};
const signedTxBodyEncodeObject: TxBodyEncodeObject = {
typeUrl: "/cosmos.tx.v1beta1.TxBody",
value: signedTxBody,
};
const signedTxBodyBytes = registry.encode(signedTxBodyEncodeObject);
const signedGasLimit = Int53.fromString(aminoSignResponse.signed.fee.gas).toNumber();
const signedSequence = Int53.fromString(aminoSignResponse.signed.sequence).toNumber();
const pubkey = encodePubkey(encodeSecp256k1Pubkey(fromHex(nativeAddress.pubKey)));
const signedAuthInfoBytes = makeAuthInfoBytes(
[{pubkey, sequence: signedSequence}],
aminoSignResponse.signed.fee.amount,
signedGasLimit,
aminoSignResponse.signed.fee.granter,
aminoSignResponse.signed.fee.payer,
SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
);
const txRaw = TxRaw.fromPartial({
bodyBytes: signedTxBodyBytes,
authInfoBytes: signedAuthInfoBytes,
signatures: [fromBase64(aminoSignResponse.signature.signature)],
});

return await client.broadcastTx(TxRaw.encode(txRaw).finish());
};

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

0 comments on commit 8f40a33

Please sign in to comment.