UNPKG

@tedcryptoorg/cosmos-signer

Version:

Cosmos Signer - A library for signing transactions for Cosmos SDK chains

154 lines (153 loc) 7.3 kB
import { AuthInfo, TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx.js"; import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing"; import { createAuthzAminoConverters, createBankAminoConverters, createDistributionAminoConverters, createGovAminoConverters, createIbcAminoConverters, createStakingAminoConverters, AminoTypes, } from "@cosmjs/stargate"; import { PubKey } from "cosmjs-types/cosmos/crypto/secp256k1/keys"; import { defaultRegistryTypes as defaultStargateTypes } from '@cosmjs/stargate/build/signingstargateclient'; import { fromBase64 } from '@cosmjs/encoding'; import { makeSignDoc as makeAminoSignDoc } from "@cosmjs/amino"; import { makeSignDoc, Registry } from "@cosmjs/proto-signing"; import _ from "lodash"; import Long from "long"; import { createAuthzExecAminoConverters, createAuthzAminoConverters as customCreateAuthzAminoConverters } from "../../converters/Authz"; export class DefaultAdapter { network; wallet; registry; aminoTypes; constructor(network, wallet) { this.network = network; this.wallet = wallet; this.registry = new Registry(defaultStargateTypes); const defaultConverters = { ...createAuthzAminoConverters(), ...createBankAminoConverters(), ...createDistributionAminoConverters(), ...createGovAminoConverters(), ...createStakingAminoConverters(), ...createIbcAminoConverters(), }; const aminoTypes = new AminoTypes(defaultConverters); this.aminoTypes = new AminoTypes({ ...defaultConverters, ...customCreateAuthzAminoConverters(), ...createAuthzExecAminoConverters(this.registry, aminoTypes) }); } async sign(account, messages, fee, memo) { const { chainId } = this.network; const { account_number: accountNumber, sequence, address } = account; let aminoMsgs = undefined; try { aminoMsgs = this.convertToAmino(messages); } catch (e) { console.log(e); } if (aminoMsgs && this.wallet.signAminoSupport()) { const signDoc = makeAminoSignDoc(aminoMsgs, { gas: fee.gasLimit.toString(), amount: fee.amount }, chainId, memo, accountNumber, sequence); const { signature, signed } = await this.wallet.signAmino(address, signDoc); const authInfoBytes = await this.makeAuthInfoBytes(account, fee, SignMode.SIGN_MODE_LEGACY_AMINO_JSON); return { bodyBytes: this.makeBodyBytes(messages, signed.memo), authInfoBytes, signatures: [Buffer.from(signature.signature, "base64")], }; } if (this.wallet.signDirectSupport()) { const authInfoBytes = await this.makeAuthInfoBytes(account, fee, SignMode.SIGN_MODE_DIRECT); const signDoc = makeSignDoc(this.makeBodyBytes(messages, memo), authInfoBytes, chainId, accountNumber); const { signature, signed } = await this.wallet.signDirect(address, signDoc); return { bodyBytes: signed.bodyBytes, authInfoBytes: signed.authInfoBytes, signatures: [fromBase64(signature.signature)], }; } throw new Error('Unable to sign message with this wallet/signer'); } async simulate(account, messages, fee, memo) { return { bodyBytes: this.makeBodyBytes(messages, memo), authInfoBytes: await this.makeAuthInfoBytes(account, fee, SignMode.SIGN_MODE_UNSPECIFIED), signatures: [new Uint8Array()], }; } convertToAmino(messages) { return messages.map((message) => { if (message.typeUrl.startsWith('/cosmos.authz')) { if (!this.network.authzAminoSupport) { throw new Error('This chain does not support amino conversion for Authz messages'); } if (this.network.authzAminoGenericOnly && this.wallet.signDirectSupport()) { throw new Error('This chain does not fully support amino conversion for Authz messages, using signDirect instead'); } } if (message.typeUrl === '/cosmos.authz.v1beta1.MsgExec') { const execTypes = message.value.msgs.map((msg) => msg.typeUrl); const preventedTypes = execTypes.filter((type) => this.network.authzAminoExecPreventTypes.some((prevent) => type.match(_.escapeRegExp(prevent)))); if (preventedTypes.length > 0) { throw new Error(`This chain does not support amino conversion for Authz Exec with message types: ${preventedTypes.join(', ')}`); } } else if (this.network.aminoPreventTypes.some((prevent) => message.typeUrl.match(_.escapeRegExp(prevent)))) { throw new Error(`This chain does not support amino conversion for message type: ${message.typeUrl}`); } let aminoMessage = this.aminoTypes.toAmino(message); if (this.network.authzAminoLiftedValues) { switch (aminoMessage.type) { case 'cosmos-sdk/MsgGrant': aminoMessage = aminoMessage.value; aminoMessage.grant.authorization = aminoMessage.grant.authorization.value; break; case 'cosmos-sdk/MsgRevoke': aminoMessage = aminoMessage.value; break; case 'cosmos-sdk/MsgExec': throw new Error('This chain does not support amino conversion for MsgExec'); } } return aminoMessage; }); } makeBodyBytes(messages, memo) { const anyMsgs = messages.map((m) => this.registry.encodeAsAny(m)); return TxBody.encode(TxBody.fromPartial({ messages: anyMsgs, memo, })).finish(); } async makeAuthInfoBytes(account, fee, mode) { const { sequence } = account; const accountFromSigner = (await this.wallet.getAccounts())[0]; if (!accountFromSigner) { throw new Error("Failed to retrieve account from signer"); } const signerPubkey = accountFromSigner.pubkey; return AuthInfo.encode({ signerInfos: [ { publicKey: { typeUrl: this.pubkeyTypeUrl(account.pub_key), value: PubKey.encode({ key: signerPubkey, }).finish(), }, sequence: Long.fromNumber(sequence, true), modeInfo: { single: { mode } }, }, ], fee, }).finish(); } pubkeyTypeUrl(pub_key) { if (pub_key?.['@type']) return pub_key['@type']; if (this.network.path === 'injective') { return '/injective.crypto.v1beta1.ethsecp256k1.PubKey'; } if (this.network.slip44 === 60) { return '/ethermint.crypto.v1.ethsecp256k1.PubKey'; } return '/cosmos.crypto.secp256k1.PubKey'; } }