Skip to content

Commit

Permalink
Merge branch 'injective-ledger' into release-v2.18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tombeynon committed Dec 26, 2024
2 parents c068c27 + 97e65f7 commit 1a5ec3c
Show file tree
Hide file tree
Showing 45 changed files with 2,369 additions and 1,134 deletions.
1,894 changes: 1,521 additions & 373 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"dependencies": {
"@bugsnag/js": "^7.16.1",
"@bugsnag/plugin-react": "^7.16.1",
"@cosmjs/proto-signing": "^0.28.1",
"@cosmjs/stargate": "^0.28.1",
"@keplr-wallet/wc-client": "^0.11.3",
"@terra-money/terra.js": "^3.1.8",
"@cosmjs/proto-signing": "^0.32.4",
"@cosmjs/stargate": "^0.32.4",
"@injectivelabs/sdk-ts": "^1.14.33",
"@terra-money/terra.js": "^3.1.10",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^15.0.7",
"@testing-library/user-event": "^13.5.0",
Expand Down Expand Up @@ -73,12 +73,16 @@
"devDependencies": {
"@parcel/packager-raw-url": "^2.12.0",
"@parcel/transformer-webmanifest": "^2.12.0",
"assert": "^2.1.0",
"crypto-browserify": "^3.12.0",
"eslint": "^9.4.0",
"events": "^3.3.0",
"https-browserify": "^1.0.0",
"parcel": "^2.12.0",
"path-browserify": "^1.0.1",
"stream-browserify": "^3.0.0",
"stream-http": "^3.2.0",
"url": "^0.11.4",
"vm-browserify": "^1.1.2"
}
}
129 changes: 59 additions & 70 deletions src/adapters/DefaultSigningAdapter.mjs
Original file line number Diff line number Diff line change
@@ -1,51 +1,25 @@
import _ from 'lodash'
import Long from "long";

import {
defaultRegistryTypes as defaultStargateTypes,
AminoTypes,
createBankAminoConverters,
createDistributionAminoConverters,
createFreegrantAminoConverters,
createGovAminoConverters,
createIbcAminoConverters,
createStakingAminoConverters,
} from "@cosmjs/stargate";
import { makeSignDoc, Registry } from "@cosmjs/proto-signing";
import { makeSignDoc } from "@cosmjs/proto-signing";
import { makeSignDoc as makeAminoSignDoc } from "@cosmjs/amino";
import { fromBase64 } from '@cosmjs/encoding'
import { PubKey } from "cosmjs-types/cosmos/crypto/secp256k1/keys.js";
import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing.js";
import { AuthInfo, Fee, TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx.js";

import { createAuthzAminoConverters, createAuthzExecAminoConverters } from '../converters/Authz.mjs'

export default class DefaultSigningAdapter {
constructor(network, signerProvider) {
this.network = network;
this.signerProvider = signerProvider;

this.registry = new Registry(defaultStargateTypes);
const defaultConverters = {
...createAuthzAminoConverters(),
...createBankAminoConverters(),
...createDistributionAminoConverters(),
...createGovAminoConverters(),
...createStakingAminoConverters(this.network.prefix),
...createIbcAminoConverters(),
...createFreegrantAminoConverters(),
}
let aminoTypes = new AminoTypes(defaultConverters)
this.aminoTypes = new AminoTypes({...defaultConverters, ...createAuthzExecAminoConverters(this.registry, aminoTypes)})
}

async sign(account, messages, memo, fee){
const { chainId } = this.network
const { account_number: accountNumber, sequence, address } = account
const txBodyBytes = this.makeBodyBytes(messages, memo)
let aminoMsgs
try {
aminoMsgs = this.convertToAmino(messages)
aminoMsgs = messages.map(message => this.toAmino(message))
} catch (e) { console.log(e) }
if(aminoMsgs && this.signerProvider.signAminoSupport()){
// Sign as amino if possible for Ledger and Keplr support
Expand All @@ -66,6 +40,7 @@ export default class DefaultSigningAdapter {
amount: fee.amount,
gasLimit: fee.gas,
}, SignMode.SIGN_MODE_DIRECT)
const txBodyBytes = this.makeBodyBytes(messages, memo)
const signDoc = makeSignDoc(txBodyBytes, authInfoBytes, chainId, accountNumber);
const { signature, signed } = await this.signerProvider.signDirect(address, signDoc);
return {
Expand All @@ -89,50 +64,68 @@ export default class DefaultSigningAdapter {
}
}

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.signerProvider.signDirectSupport()){
throw new Error('This chain does not fully support amino conversion for Authz messages, using signDirect instead')
}
toProto(message){
return message.toProto()
}

toAmino(message){
this.checkAminoSupport(message)
let aminoMessage = message.toAmino()
if(this.network.authzAminoLiftedValues){
aminoMessage = this.liftAuthzAmino(aminoMessage)
}
return aminoMessage
}

checkAminoSupport(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(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}`)
if(this.network.authzAminoGenericOnly && this.signerProvider.signDirectSupport()){
throw new Error('This chain does not fully support amino conversion for Authz messages, using signDirect instead')
}
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')
}
}
if(message.typeUrl === '/cosmos.authz.v1beta1.MsgExec'){
const execTypes = message.params.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(', ')}`)
}
return aminoMessage
})
}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}`)
}
}

makeBodyBytes(messages, memo){
const anyMsgs = messages.map((m) => this.registry.encodeAsAny(m));
liftAuthzAmino(aminoMessage){
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, timeoutHeight){
const protoMsgs = messages.map(message => this.toProto(message));

const txBody = {
messages: protoMsgs,
memo: memo,
}

if (timeoutHeight) {
txBody.timeoutHeight = timeoutHeight.toString()
}

return TxBody.encode(
TxBody.fromPartial({
messages: anyMsgs,
memo: memo,
})
TxBody.fromPartial(txBody)
).finish()
}

Expand Down Expand Up @@ -163,10 +156,6 @@ export default class DefaultSigningAdapter {
pubkeyTypeUrl(pub_key){
if(pub_key && 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'
}
Expand Down
92 changes: 92 additions & 0 deletions src/adapters/InjectiveSigningAdapter.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import _ from 'lodash'
import {
createWeb3Extension,
createTxRawEIP712,
getEip712TypedData
} from '@injectivelabs/sdk-ts'
import {
BigNumberInBase,
DEFAULT_BLOCK_TIMEOUT_HEIGHT
} from '@injectivelabs/utils'
import { SignMode } from "cosmjs-types/cosmos/tx/signing/v1beta1/signing.js";

import DefaultSigningAdapter from "./DefaultSigningAdapter.mjs";

export default class InjectiveSigningAdapter extends DefaultSigningAdapter {
async sign(account, messages, memo, fee){
if(!this.signerProvider.isLedger()){
return super.sign(account, messages, memo, fee)
}

if(!this.signerProvider.signEIP712){
throw new Error('Unable to sign message with this wallet/signer')
}

const { chainId } = this.network
const ethereumChainId = 1
const { account_number: accountNumber, sequence, address } = account

const latestBlock = await this.network.restClient.getLatestBlock()
const latestHeight = latestBlock.block.header.height
const timeoutHeight = new BigNumberInBase(latestHeight).plus(
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
)

const injMessages = messages.map((m) => m.toInjective())

const eip712TypedData = getEip712TypedData({
msgs: injMessages,
fee,
tx: {
memo: memo,
accountNumber: accountNumber.toString(),
sequence: sequence.toString(),
timeoutHeight: timeoutHeight.toFixed(),
chainId,
},
ethereumChainId,
})

const { signature, signed } = await this.signerProvider.signEIP712(
chainId,
address,
eip712TypedData,
{
chain_id: chainId,
timeout_height: timeoutHeight.toFixed(),
account_number: accountNumber.toString(),
sequence: sequence.toString(),
fee,
msgs: injMessages.map((m) => m.toEip712()),
memo: memo || '',
}
)

const txRaw = {
authInfoBytes: await this.makeAuthInfoBytes(account, {
amount: signed.fee.amount,
gasLimit: signed.fee.gas,
}, SignMode.SIGN_MODE_LEGACY_AMINO_JSON),
bodyBytes: this.makeBodyBytes(messages, signed.memo, timeoutHeight),
}

const web3Extension = createWeb3Extension({
ethereumChainId,
})
const txRawEip712 = createTxRawEIP712(txRaw, web3Extension)

const signatureBuff = Buffer.from(
signature.signature,
'base64',
)
txRawEip712.signatures = [signatureBuff]

return txRawEip712
}

pubkeyTypeUrl(pub_key){
if(pub_key && pub_key['@type']) return pub_key['@type']

return '/injective.crypto.v1beta1.ethsecp256k1.PubKey'
}
}
25 changes: 20 additions & 5 deletions src/adapters/TerraSigningAdapter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@ import { Msg } from '@terra-money/terra.js';
import DefaultSigningAdapter from "./DefaultSigningAdapter.mjs";

export default class TerraSigningAdapter extends DefaultSigningAdapter {
convertToAmino(messages){
return super.convertToAmino(messages).map(message => {
// Terra uses custom Amino message types, use Terra.js to convert them
return Msg.fromAmino(message).toAmino(true)
})
toAmino(message){
this.checkAminoSupport(message)
// Terra uses custom Amino message types, use Terra.js to convert them
let aminoMessage = Msg.fromAmino(message.toAmino()).toAmino(true)
if(this.network.authzAminoLiftedValues){
aminoMessage = this.liftAuthzAmino(aminoMessage)
}
return aminoMessage
}

liftAuthzAmino(aminoMessage){
switch (aminoMessage.type) {
case 'msgauth/MsgGrantAuthorization':
throw new Error('This chain does not support amino conversion for MsgGrant')
case 'cosmos-sdk/MsgRevoke':
throw new Error('This chain does not support amino conversion for MsgRevoke')
case 'cosmos-sdk/MsgExec':
throw new Error('This chain does not support amino conversion for MsgExec')
}
return aminoMessage;
}
}
Loading

0 comments on commit 1a5ec3c

Please sign in to comment.