@keplr-wallet/background 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -850,3 +850,97 @@ export class EnableVaultsWithCosmosAddressMsg extends Message<
850
850
  return EnableVaultsWithCosmosAddressMsg.type();
851
851
  }
852
852
  }
853
+
854
+ export class RequestSignFigureMarketsAuthMsg extends Message<{
855
+ signedMessage: string;
856
+ signature: StdSignature;
857
+ }> {
858
+ public static type() {
859
+ return "request-sign-figure-markets-auth";
860
+ }
861
+
862
+ constructor(
863
+ public readonly chainId: string,
864
+ public readonly signer: string,
865
+ public readonly message: string
866
+ ) {
867
+ super();
868
+ }
869
+
870
+ validateBasic(): void {
871
+ if (!this.chainId) {
872
+ throw new KeplrError("keyring", 270, "chain id not set");
873
+ }
874
+
875
+ if (!this.signer) {
876
+ throw new KeplrError("keyring", 230, "signer not set");
877
+ }
878
+
879
+ // Validate bech32 address.
880
+ Bech32Address.validate(this.signer);
881
+
882
+ if (!this.message) {
883
+ throw new KeplrError("keyring", 231, "message not set");
884
+ }
885
+ }
886
+
887
+ override approveExternal(): boolean {
888
+ return true;
889
+ }
890
+
891
+ route(): string {
892
+ return ROUTE;
893
+ }
894
+
895
+ type(): string {
896
+ return RequestSignFigureMarketsAuthMsg.type();
897
+ }
898
+ }
899
+
900
+ export class RequestCosmosSignDirectWithMessagesMsg extends Message<{
901
+ readonly txHash: string;
902
+ }> {
903
+ public static type() {
904
+ return "request-cosmos-sign-direct-with-messages";
905
+ }
906
+
907
+ constructor(
908
+ public readonly chainId: string,
909
+ public readonly signer: string,
910
+ // base64 encoded protobuf messages.
911
+ public readonly messages: string[],
912
+ public readonly signDirectWithMessagesOptions: {
913
+ memo?: string;
914
+ sync?: boolean;
915
+ timeoutHeight?: number;
916
+ gasAdjustment?: number;
917
+ }
918
+ ) {
919
+ super();
920
+ }
921
+
922
+ validateBasic(): void {
923
+ if (!this.chainId) {
924
+ throw new KeplrError("keyring", 270, "chain id not set");
925
+ }
926
+
927
+ if (!this.signer) {
928
+ throw new KeplrError("keyring", 230, "signer not set");
929
+ }
930
+
931
+ // Validate bech32 address.
932
+ Bech32Address.validate(this.signer);
933
+ }
934
+
935
+ override approveExternal(): boolean {
936
+ return true;
937
+ }
938
+
939
+ route(): string {
940
+ return ROUTE;
941
+ }
942
+
943
+ type(): string {
944
+ return RequestCosmosSignDirectWithMessagesMsg.type();
945
+ }
946
+ }
@@ -7,11 +7,13 @@ import {
7
7
  DirectSignResponse,
8
8
  KeplrSignOptions,
9
9
  Key,
10
+ StdFee,
10
11
  StdSignature,
11
12
  StdSignDoc,
12
13
  } from "@keplr-wallet/types";
13
14
  import { APP_PORT, Env, KeplrError } from "@keplr-wallet/router";
14
15
  import {
16
+ BaseAccount,
15
17
  Bech32Address,
16
18
  ChainIdHelper,
17
19
  checkAndValidateADR36AminoSignDoc,
@@ -29,16 +31,24 @@ import { InteractionService } from "../interaction";
29
31
  import { Buffer } from "buffer/";
30
32
  import {
31
33
  AuthInfo,
34
+ Fee,
32
35
  SignDoc,
33
36
  SignDocDirectAux,
37
+ SignerInfo,
34
38
  TxBody,
39
+ TxRaw,
35
40
  } from "@keplr-wallet/proto-types/cosmos/tx/v1beta1/tx";
36
41
  import Long from "long";
37
42
  import { PubKeySecp256k1 } from "@keplr-wallet/crypto";
38
43
  import { AnalyticsService } from "../analytics";
39
44
  import { ChainsUIService } from "../chains-ui";
40
- import { Int } from "@keplr-wallet/unit";
45
+ import { Dec, Int } from "@keplr-wallet/unit";
41
46
  import bigInteger from "big-integer";
47
+ import { PubKey } from "@keplr-wallet/proto-types/cosmos/crypto/secp256k1/keys";
48
+ import { SignMode } from "@keplr-wallet/proto-types/cosmos/tx/signing/v1beta1/signing";
49
+ import { Any } from "@keplr-wallet/proto-types/google/protobuf/any";
50
+ import { simpleFetch } from "@keplr-wallet/simple-fetch";
51
+ import { BackgroundTxService } from "../tx";
42
52
 
43
53
  export class KeyRingCosmosService {
44
54
  constructor(
@@ -47,6 +57,7 @@ export class KeyRingCosmosService {
47
57
  protected readonly interactionService: InteractionService,
48
58
  protected readonly chainsUIService: ChainsUIService,
49
59
  protected readonly analyticsService: AnalyticsService,
60
+ protected readonly backgroundTxService: BackgroundTxService,
50
61
  protected readonly msgPrivilegedOrigins: string[],
51
62
  protected readonly msgPrivilegedCosmwasmContractMap: Record<
52
63
  string,
@@ -1050,6 +1061,277 @@ export class KeyRingCosmosService {
1050
1061
  );
1051
1062
  }
1052
1063
 
1064
+ async signDirectWithMessagesSelected(
1065
+ env: Env,
1066
+ origin: string,
1067
+ chainId: string,
1068
+ signer: string,
1069
+ // base64 encoded protobuf messages.
1070
+ messages: string[],
1071
+ signDirectWithMessagesOptions: {
1072
+ memo?: string;
1073
+ sync?: boolean;
1074
+ timeoutHeight?: number;
1075
+ gasAdjustment?: number;
1076
+ }
1077
+ ): Promise<{
1078
+ txHash: string;
1079
+ }> {
1080
+ return await this.signDirectWithMessages(
1081
+ env,
1082
+ origin,
1083
+ this.keyRingService.selectedVaultId,
1084
+ chainId,
1085
+ signer,
1086
+ messages,
1087
+ signDirectWithMessagesOptions
1088
+ );
1089
+ }
1090
+
1091
+ async signDirectWithMessages(
1092
+ env: Env,
1093
+ origin: string,
1094
+ vaultId: string,
1095
+ chainId: string,
1096
+ signer: string,
1097
+ // base64 encoded protobuf messages.
1098
+ messages: string[],
1099
+ signDirectWithMessagesOptions: {
1100
+ memo?: string;
1101
+ sync?: boolean;
1102
+ timeoutHeight?: number;
1103
+ gasAdjustment?: number;
1104
+ }
1105
+ ): Promise<{
1106
+ txHash: string;
1107
+ }> {
1108
+ const useEthereumSign =
1109
+ this.chainsService
1110
+ .getChainInfoOrThrow(chainId)
1111
+ .features?.includes("eth-key-sign") === true;
1112
+
1113
+ const chainIsInjective = chainId.startsWith("injective");
1114
+ const chainIsStratos = chainId.startsWith("stratos");
1115
+ const chainIsCysic = chainId.startsWith("cysic");
1116
+
1117
+ const key = await this.getKey(vaultId, chainId);
1118
+ const bech32Address = key.bech32Address;
1119
+
1120
+ const account = await BaseAccount.fetchFromRest(
1121
+ this.chainsService.getChainInfoOrThrow(chainId).rest,
1122
+ bech32Address,
1123
+ true
1124
+ );
1125
+
1126
+ const anyMessages: Any[] = messages.map((message) => {
1127
+ const bz = Buffer.from(message, "base64");
1128
+ const any = Any.decode(bz);
1129
+ return {
1130
+ typeUrl: any.typeUrl,
1131
+ value: any.value,
1132
+ };
1133
+ });
1134
+
1135
+ const feeCurrency =
1136
+ this.chainsService.getChainInfoOrThrow(chainId).feeCurrencies[0];
1137
+
1138
+ const { gasUsed } = await this.simulateTx(
1139
+ vaultId,
1140
+ chainId,
1141
+ anyMessages,
1142
+ {
1143
+ amount: [
1144
+ {
1145
+ denom: feeCurrency.coinMinimalDenom,
1146
+ amount: "1",
1147
+ },
1148
+ ],
1149
+ },
1150
+ signDirectWithMessagesOptions.memo
1151
+ );
1152
+ const gasAdjustment = signDirectWithMessagesOptions.gasAdjustment || 1.5;
1153
+
1154
+ const gasLimitDec = new Dec(gasUsed).mulTruncate(new Dec(gasAdjustment));
1155
+
1156
+ const signDoc = {
1157
+ bodyBytes: TxBody.encode(
1158
+ TxBody.fromPartial({
1159
+ messages: anyMessages,
1160
+ memo: signDirectWithMessagesOptions.memo,
1161
+ })
1162
+ ).finish(),
1163
+ authInfoBytes: AuthInfo.encode({
1164
+ signerInfos: [
1165
+ {
1166
+ publicKey: {
1167
+ typeUrl: (() => {
1168
+ if (!useEthereumSign) {
1169
+ return "/cosmos.crypto.secp256k1.PubKey";
1170
+ }
1171
+
1172
+ if (chainIsInjective) {
1173
+ return "/injective.crypto.v1beta1.ethsecp256k1.PubKey";
1174
+ }
1175
+
1176
+ if (chainIsStratos) {
1177
+ return "/stratos.crypto.v1.ethsecp256k1.PubKey";
1178
+ }
1179
+
1180
+ if (
1181
+ this.chainsService
1182
+ .getChainInfoOrThrow(chainId)
1183
+ .features?.includes("eth-secp256k1-cosmos")
1184
+ ) {
1185
+ return "/cosmos.evm.crypto.v1.ethsecp256k1.PubKey";
1186
+ }
1187
+ if (
1188
+ this.chainsService
1189
+ .getChainInfoOrThrow(chainId)
1190
+ .features?.includes("eth-secp256k1-initia")
1191
+ ) {
1192
+ return "/initia.crypto.v1beta1.ethsecp256k1.PubKey";
1193
+ }
1194
+
1195
+ if (chainIsCysic) {
1196
+ return "/cysicmint.crypto.v1.ethsecp256k1.PubKey";
1197
+ }
1198
+
1199
+ return "/ethermint.crypto.v1.ethsecp256k1.PubKey";
1200
+ })(),
1201
+ value: PubKey.encode({
1202
+ key: key.pubKey,
1203
+ }).finish(),
1204
+ },
1205
+ modeInfo: {
1206
+ single: {
1207
+ mode: SignMode.SIGN_MODE_DIRECT,
1208
+ },
1209
+ multi: undefined,
1210
+ },
1211
+ sequence: account.getSequence().toString(),
1212
+ },
1213
+ ],
1214
+ fee: Fee.fromPartial({
1215
+ amount: [
1216
+ {
1217
+ denom: feeCurrency.coinMinimalDenom,
1218
+ amount: new Dec(feeCurrency.gasPriceStep?.average ?? 0.025)
1219
+ .mul(gasLimitDec)
1220
+ .roundUp()
1221
+ .toString(),
1222
+ },
1223
+ ],
1224
+ gasLimit: gasLimitDec.roundUp().toString(),
1225
+ }),
1226
+ }).finish(),
1227
+ chainId: chainId,
1228
+ accountNumber: account.getAccountNumber().toString(),
1229
+ };
1230
+
1231
+ const signed = await this.signDirect(
1232
+ env,
1233
+ origin,
1234
+ vaultId,
1235
+ chainId,
1236
+ signer,
1237
+ signDoc,
1238
+ {
1239
+ preferNoSetMemo: !!signDirectWithMessagesOptions.memo,
1240
+ preferNoSetFee: true,
1241
+ }
1242
+ );
1243
+
1244
+ const tx = TxRaw.encode({
1245
+ bodyBytes: signed.signed.bodyBytes,
1246
+ authInfoBytes: signed.signed.authInfoBytes,
1247
+ signatures: [Buffer.from(signed.signature.signature, "base64")],
1248
+ }).finish();
1249
+
1250
+ const txHash = await this.backgroundTxService.sendTx(chainId, tx, "sync", {
1251
+ skipTracingTxResult: true,
1252
+ });
1253
+
1254
+ return {
1255
+ txHash: Buffer.from(txHash).toString("hex"),
1256
+ };
1257
+ }
1258
+
1259
+ async simulateTx(
1260
+ vaultId: string,
1261
+ chainId: string,
1262
+ msgs: Any[],
1263
+ fee: Omit<StdFee, "gas">,
1264
+ memo: string = ""
1265
+ ): Promise<{
1266
+ gasUsed: number;
1267
+ }> {
1268
+ const key = await this.getKey(vaultId, chainId);
1269
+ const bech32Address = key.bech32Address;
1270
+
1271
+ const account = await BaseAccount.fetchFromRest(
1272
+ this.chainsService.getChainInfoOrThrow(chainId).rest,
1273
+ bech32Address,
1274
+ true
1275
+ );
1276
+
1277
+ const unsignedTx = TxRaw.encode({
1278
+ bodyBytes: TxBody.encode(
1279
+ TxBody.fromPartial({
1280
+ messages: msgs,
1281
+ memo: memo,
1282
+ })
1283
+ ).finish(),
1284
+ authInfoBytes: AuthInfo.encode({
1285
+ signerInfos: [
1286
+ SignerInfo.fromPartial({
1287
+ // Pub key is ignored.
1288
+ // It is fine to ignore the pub key when simulating tx.
1289
+ // However, the estimated gas would be slightly smaller because tx size doesn't include pub key.
1290
+ modeInfo: {
1291
+ single: {
1292
+ mode: SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
1293
+ },
1294
+ multi: undefined,
1295
+ },
1296
+ sequence: account.getSequence().toString(),
1297
+ }),
1298
+ ],
1299
+ fee: Fee.fromPartial({
1300
+ amount: fee.amount.map((amount) => {
1301
+ return { amount: amount.amount, denom: amount.denom };
1302
+ }),
1303
+ }),
1304
+ }).finish(),
1305
+ // Because of the validation of tx itself, the signature must exist.
1306
+ // However, since they do not actually verify the signature, it is okay to use any value.
1307
+ signatures: [new Uint8Array(64)],
1308
+ }).finish();
1309
+
1310
+ // TODO: Add response type
1311
+ const result = await simpleFetch<any>(
1312
+ this.chainsService.getChainInfoOrThrow(chainId).rest,
1313
+ "/cosmos/tx/v1beta1/simulate",
1314
+ {
1315
+ method: "POST",
1316
+ headers: {
1317
+ "content-type": "application/json",
1318
+ },
1319
+ body: JSON.stringify({
1320
+ tx_bytes: Buffer.from(unsignedTx).toString("base64"),
1321
+ }),
1322
+ }
1323
+ );
1324
+
1325
+ const gasUsed = parseInt(result.data.gas_info.gas_used);
1326
+ if (Number.isNaN(gasUsed)) {
1327
+ throw new Error(`Invalid integer gas: ${result.data.gas_info.gas_used}`);
1328
+ }
1329
+
1330
+ return {
1331
+ gasUsed,
1332
+ };
1333
+ }
1334
+
1053
1335
  /**
1054
1336
  * Sign a direct-encoded transaction with pre-authorization
1055
1337
  * @dev only sign the transaction, not simulate or broadcast
@@ -1459,6 +1741,70 @@ export class KeyRingCosmosService {
1459
1741
  );
1460
1742
  }
1461
1743
 
1744
+ async signFigureMarketsAuth(
1745
+ _env: Env,
1746
+ origin: string,
1747
+ chainId: string,
1748
+ signer: string,
1749
+ // base64 encoded.
1750
+ message: string
1751
+ ): Promise<{
1752
+ signedMessage: string;
1753
+ signature: StdSignature;
1754
+ }> {
1755
+ const parsed = JSON.parse(Buffer.from(message, "base64").toString());
1756
+ if (
1757
+ parsed.chainId !== this.chainsService.getChainInfoOrThrow(chainId).chainId
1758
+ ) {
1759
+ throw new Error("chain id unmatched");
1760
+ }
1761
+ if (new URL(parsed.domain).origin !== new URL(origin).origin) {
1762
+ throw new Error("domain and origin unmatched");
1763
+ }
1764
+
1765
+ const vaultId = this.keyRingService.selectedVaultId;
1766
+
1767
+ const chainInfo = this.chainsService.getChainInfoOrThrow(chainId);
1768
+ const isEthermintLike = KeyRingService.isEthermintLike(chainInfo);
1769
+ const forceEVMLedger = chainInfo.features?.includes(
1770
+ "force-enable-evm-ledger"
1771
+ );
1772
+
1773
+ const keyInfo = this.keyRingService.getKeyInfo(vaultId);
1774
+ if (!keyInfo) {
1775
+ throw new Error("Null key info");
1776
+ }
1777
+
1778
+ if (isEthermintLike && keyInfo.type === "ledger" && !forceEVMLedger) {
1779
+ KeyRingCosmosService.throwErrorIfEthermintWithLedgerButNotSupported(
1780
+ chainId
1781
+ );
1782
+ }
1783
+
1784
+ const key = await this.getKey(vaultId, chainId);
1785
+ const bech32Prefix =
1786
+ this.chainsService.getChainInfoOrThrow(chainId).bech32Config
1787
+ ?.bech32PrefixAccAddr ?? "";
1788
+ const bech32Address = new Bech32Address(key.address).toBech32(bech32Prefix);
1789
+ if (signer !== bech32Address || parsed.address !== bech32Address) {
1790
+ throw new Error("Signer mismatched");
1791
+ }
1792
+
1793
+ const signature = await this.keyRingService.sign(
1794
+ chainId,
1795
+ vaultId,
1796
+ Buffer.from(message, "base64"),
1797
+ isEthermintLike ? "keccak256" : "sha256"
1798
+ );
1799
+ return {
1800
+ signedMessage: message,
1801
+ signature: encodeSecp256k1Signature(
1802
+ key.pubKey,
1803
+ new Uint8Array([...signature.r, ...signature.s])
1804
+ ),
1805
+ };
1806
+ }
1807
+
1462
1808
  async requestICNSAdr36SignaturesSelected(
1463
1809
  env: Env,
1464
1810
  origin: string,