add keplr support for evmos

This commit is contained in:
liangping 2022-09-03 12:48:02 +08:00
parent 5aac799522
commit db4b27c98e
5 changed files with 110 additions and 93 deletions

View File

@ -6,13 +6,21 @@ module.exports = {
extends: [ extends: [
'@vue/airbnb', '@vue/airbnb',
'plugin:vue/recommended', 'plugin:vue/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/typescript', 'plugin:import/typescript',
// 'prettier/@typescript-eslint',
'plugin:@typescript-eslint/recommended',
// "plugin:prettier/recommended", // "plugin:prettier/recommended",
// "prettier/@typescript-eslint"
], ],
parser: 'vue-eslint-parser',
parserOptions: { parserOptions: {
parser: '@babel/eslint-parser', parser: '@typescript-eslint/parser',
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: false,
modules: true,
experimentalObjectRestSpread: true,
},
}, },
rules: { rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
@ -23,10 +31,12 @@ module.exports = {
'linebreak-style': 'off', 'linebreak-style': 'off',
// camelcase: ['error', { properties: 'never', ignoreDestructuring: true, ignoreImports: true }], // camelcase: ['error', { properties: 'never', ignoreDestructuring: true, ignoreImports: true }],
'arrow-parens': ['error', 'as-needed'], 'arrow-parens': ['error', 'as-needed'],
'vue/multiline-html-element-content-newline': 'off', 'vue/new-line-between-multi-line-property': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/multi-word-component-names': 0, 'vue/multi-word-component-names': 0,
'vue/no-mutating-props': 0, 'vue/no-mutating-props': 0,
'vue/v-slot-style': 0, 'vue/v-slot-style': 0,
'@typescript-eslint/no-empty-function': 1,
camelcase: 0, camelcase: 0,
'vuejs-accessibility/click-events-have-key-events': 'off', 'vuejs-accessibility/click-events-have-key-events': 'off',
'vuejs-accessibility/mouse-events-have-key-events': 'off', 'vuejs-accessibility/mouse-events-have-key-events': 'off',

View File

@ -11,37 +11,22 @@ import {
createIbcAminoConverters, createIbcAminoConverters,
createStakingAminoConverters, createStakingAminoConverters,
} from '@cosmjs/stargate' } from '@cosmjs/stargate'
import { import { EncodeObject } from '@cosmjs/proto-signing'
EncodeObject, TxBodyEncodeObject, makeAuthInfoBytes,
} from '@cosmjs/proto-signing'
import { LedgerSigner } from '@cosmjs/ledger-amino' import { LedgerSigner } from '@cosmjs/ledger-amino'
import TransportWebUSB from '@ledgerhq/hw-transport-webusb' import TransportWebUSB from '@ledgerhq/hw-transport-webusb'
import TransportWebBLE from '@ledgerhq/hw-transport-web-ble' import TransportWebBLE from '@ledgerhq/hw-transport-web-ble'
import { import {
StdFee, StdFee,
} from '@cosmjs/amino' } from '@cosmjs/amino'
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'
import { import {
generateMessageWithMultipleTransactions, generateMessageWithMultipleTransactions,
generateTypes, generateTypes,
generateFee, generateFee,
createEIP712, createEIP712,
} from '@tharsis/eip712' } from '@tharsis/eip712'
import { import { createTxRawEIP712, signatureToWeb3Extension, Chain } from '@tharsis/transactions'
createTxRawEIP712, signatureToWeb3Extension, Chain, import { createTransactionWithMultipleMessages } from '@tharsis/proto'
} from '@tharsis/transactions' import { fromBech32, toBase64 } from '@cosmjs/encoding'
import {
createTransactionWithMultipleMessages,
} from '@tharsis/proto'
import {
fromBase64, fromBech32, fromHex, toBase64,
} from '@cosmjs/encoding'
// import { generateEndpointBroadcast, generatePostBodyBroadcast } from '@tharsis/provider'
import { SignMode } from 'cosmjs-types/cosmos/tx/signing/v1beta1/signing'
import * as eth from '@tharsis/proto/dist/proto/ethermint/crypto/v1/ethsecp256k1/keys' // /ethermint/crypto/v1/ethsecp256k1/keys'
import { PubKey } from 'cosmjs-types/cosmos/crypto/secp256k1/keys'
import { Int53 } from '@cosmjs/math'
import { Any } from 'cosmjs-types/google/protobuf/any'
import EthereumLedgerSigner from './EthereumLedgerSigner' import EthereumLedgerSigner from './EthereumLedgerSigner'
import { defaultMessageAdapter } from './MessageAdapter' import { defaultMessageAdapter } from './MessageAdapter'
@ -152,52 +137,6 @@ export class SigningEthermintClient {
return Promise.resolve(rawTx) return Promise.resolve(rawTx)
} }
} }
export function encodePubkey(pubkey: string): Any {
// const value = new eth.ethermint.crypto.v1.ethsecp256k1.PubKey({ key: fromBase64(pubkey) })
// return Any.fromPartial({
// typeUrl: '/ethermint.crypto.v1.ethsecp256k1.PubKey',
// value: value.serializeBinary(),
// })
return Any.fromPartial({
typeUrl: '/ethermint.crypto.v1.ethsecp256k1.PubKey',
value: PubKey.encode({
key: fromBase64(pubkey),
}).finish(),
})
}
function makeRawTx(sender, messages, memo, fee, signature, registry): TxRaw {
const pubkey = encodePubkey(sender.pubkey)
const signedTxBody = {
messages,
memo,
}
const signedTxBodyEncodeObject: TxBodyEncodeObject = {
typeUrl: '/cosmos.tx.v1beta1.TxBody',
value: signedTxBody,
}
const signedTxBodyBytes = registry.encode(signedTxBodyEncodeObject)
const signedGasLimit = Int53.fromString(fee.gas).toNumber()
// const signedSequence = sender.sequence
const signedAuthInfoBytes = makeAuthInfoBytes(
[{ pubkey, sequence: sender.sequence }],
fee.amount,
signedGasLimit,
SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
)
const rawTx = TxRaw.fromPartial({
bodyBytes: signedTxBodyBytes,
authInfoBytes: signedAuthInfoBytes,
signatures: [fromHex(signature)],
})
return rawTx
}
// export function createAnyMessage(messages: readonly EncodeObject[]) {
// return messages.map(x => x.)
// }
export declare type SigningClient = SigningStargateClient | SigningEthermintClient; export declare type SigningClient = SigningStargateClient | SigningEthermintClient;

View File

@ -0,0 +1,59 @@
import { fromBase64 } from '@cosmjs/encoding'
import { Int53 } from '@cosmjs/math'
import {
EncodeObject, makeAuthInfoBytes, makeSignDoc, OfflineSigner,
} from '@cosmjs/proto-signing'
import {
SignerData, SigningStargateClient, SigningStargateClientOptions, StdFee,
} from '@cosmjs/stargate'
import { PubKey } from 'cosmjs-types/cosmos/crypto/secp256k1/keys'
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'
import { Any } from 'cosmjs-types/google/protobuf/any'
export default class SigningKeplerEthermintClient {
private signer
private client
static async offline(signer: OfflineSigner, options?: SigningStargateClientOptions): Promise<SigningKeplerEthermintClient> {
const instance = new SigningKeplerEthermintClient()
instance.client = await SigningStargateClient.offline(signer, options)
instance.signer = signer
return Promise.resolve(instance)
}
async sign(signerAddress: string, messages: readonly EncodeObject[], fee: StdFee, memo: string, explicitSignerData?: SignerData): Promise<Uint8Array> {
const account = await this.signer.getAccounts()
const acc = account.find(x => x.address === signerAddress)
if (!acc) {
throw new Error('The signer address dose not exsits in Ledger!')
}
// Custom typeUrl for EVMOS
const pubk = Any.fromPartial({
typeUrl: '/ethermint.crypto.v1.ethsecp256k1.PubKey',
value: PubKey.encode({
key: acc.pubkey,
}).finish(),
})
const txBodyEncodeObject = {
typeUrl: '/cosmos.tx.v1beta1.TxBody',
value: {
messages,
memo,
},
}
const txBodyBytes = this.client.registry.encode(txBodyEncodeObject)
const gasLimit = Int53.fromString(fee.gas).toNumber()
const authInfoBytes = makeAuthInfoBytes([{ pubkey: pubk, sequence: explicitSignerData.sequence }], fee.amount, gasLimit)
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, explicitSignerData.chainId, explicitSignerData.accountNumber)
const { signature, signed } = await this.signer.signDirect(signerAddress, signDoc)
// returns txBytes for broadcast
return Promise.resolve(TxRaw.encode({
bodyBytes: signed.bodyBytes,
authInfoBytes: signed.authInfoBytes,
signatures: [fromBase64(signature.signature)],
}).finish())
}
}

View File

@ -20,8 +20,9 @@ import { $themeColors } from '@themeConfig'
// import { SigningStargateClient } from '@cosmjs/stargate' // import { SigningStargateClient } from '@cosmjs/stargate'
// import PingWalletClient from './data/signing' // import PingWalletClient from './data/signing'
import { SigningStargateClient } from '@cosmjs/stargate' import { SigningStargateClient } from '@cosmjs/stargate'
import { getSigningClient } from './client/PingWalletClient.ts' import { getSigningClient } from './client/SigningEthermintClient.ts'
import { EthereumLedgerSigner } from './client/EthereumLedgerSigner.ts' import EthereumLedgerSigner from './client/EthereumLedgerSigner.ts'
import SigningKeplerEthermintClient from './client/SigningKeplrEthermintClient'
dayjs.extend(localeData) dayjs.extend(localeData)
dayjs.extend(duration) dayjs.extend(duration)
@ -231,6 +232,34 @@ function getHdPath(address) {
return stringToPath(hdPath) return stringToPath(hdPath)
} }
function isEvmosBasedChain(chainId) {
const re = /[_]{1}[\d]{4}[\\-]{1}[\d]+$/g
return re.test(chainId)
}
export async function sign(device, chainId, signerAddress, messages, fee, memo, signerData) {
const hdpath = getHdPath(signerAddress)
let client
if (device.startsWith('ledger')) {
client = await getSigningClient(device, hdpath)
} else {
if (!window.getOfflineSigner || !window.keplr) {
throw new Error('Please install keplr extension')
}
await window.keplr.enable(chainId)
const signer = window.getOfflineSigner(chainId)
if (isEvmosBasedChain(chainId)) {
client = await SigningKeplerEthermintClient.offline(signer)
} else {
client = await SigningStargateClient.offline(signer)
}
}
const coinType = Number(hdpath[1])
const addr = device.startsWith('ledger') && coinType !== 60 ? toSignAddress(signerAddress) : signerAddress
return client.sign(addr, messages, fee, memo, signerData)
}
// import address from ledger
async function getLedgerAppName(coinType, device, hdpath) { async function getLedgerAppName(coinType, device, hdpath) {
let ledgerAppName = 'Cosmos' let ledgerAppName = 'Cosmos'
switch (coinType) { switch (coinType) {
@ -252,27 +281,6 @@ async function getLedgerAppName(coinType, device, hdpath) {
return new LedgerSigner(transport, { hdPaths: [hdpath], ledgerAppName }) return new LedgerSigner(transport, { hdPaths: [hdpath], ledgerAppName })
} }
export async function sign(device, chainId, signerAddress, messages, fee, memo, signerData) {
const hdpath = getHdPath(signerAddress)
let client
if (device.startsWith('ledger')) {
client = await getSigningClient(device, hdpath)
} else {
if (!window.getOfflineSigner || !window.keplr) {
throw new Error('Please install keplr extension')
}
await window.keplr.enable(chainId)
const signer = window.getOfflineSignerOnlyAmino(chainId)
client = await SigningStargateClient.offline(signer)
}
// let transport
// let signer
// const hdpath = getHdPath(signerAddress)
const coinType = Number(hdpath[1])
const addr = device.startsWith('ledger') && coinType !== 60 ? toSignAddress(signerAddress) : signerAddress
return client.sign(addr, messages, fee, memo, signerData)
}
export async function getLedgerAddress(transport = 'blu', hdPath = "m/44'/118/0'/0/0") { export async function getLedgerAddress(transport = 'blu', hdPath = "m/44'/118/0'/0/0") {
const protocol = transport === 'usb' ? await TransportWebUSB.create() : await TransportWebBLE.create() const protocol = transport === 'usb' ? await TransportWebUSB.create() : await TransportWebBLE.create()
// extract Cointype from from HDPath // extract Cointype from from HDPath
@ -280,6 +288,7 @@ export async function getLedgerAddress(transport = 'blu', hdPath = "m/44'/118/0'
const signer = await getLedgerAppName(coinType, protocol, stringToPath(hdPath)) const signer = await getLedgerAppName(coinType, protocol, stringToPath(hdPath))
return signer.getAccounts() return signer.getAccounts()
} }
/// end import address from ledger
export function toDuration(value) { export function toDuration(value) {
return dayjs.duration(value).humanize() return dayjs.duration(value).humanize()

View File

@ -651,7 +651,7 @@ export default {
}, },
created() { created() {
this.$http.getAuthAccount(this.address).then(acc => { this.$http.getAuthAccount(this.address).then(acc => {
this.account = acc this.account = acc.account
this.initial() this.initial()
this.$http.getTxsBySender(this.address).then(res => { this.$http.getTxsBySender(this.address).then(res => {
this.transactions = res this.transactions = res