@keplr-wallet/stores 0.12.299 → 0.12.300-rc.0

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.
Files changed (103) hide show
  1. package/build/account/babylon.js +19 -7
  2. package/build/account/babylon.js.map +1 -1
  3. package/build/account/base.js +7 -5
  4. package/build/account/base.js.map +1 -1
  5. package/build/account/cosmos.js +94 -54
  6. package/build/account/cosmos.js.map +1 -1
  7. package/build/account/cosmwasm.js +10 -2
  8. package/build/account/cosmwasm.js.map +1 -1
  9. package/build/account/secret.js +10 -2
  10. package/build/account/secret.js.map +1 -1
  11. package/build/account/store.js +4 -4
  12. package/build/account/store.js.map +1 -1
  13. package/build/chain/base.d.ts +122 -1
  14. package/build/chain/base.js +601 -40
  15. package/build/chain/base.js.map +1 -1
  16. package/build/chain/types.d.ts +27 -1
  17. package/build/common/utils/index.d.ts +2 -2
  18. package/build/common/utils/index.js.map +1 -1
  19. package/build/lsm/currency-registrar.js +7 -7
  20. package/build/lsm/currency-registrar.js.map +1 -1
  21. package/build/query/balances.js +24 -11
  22. package/build/query/balances.js.map +1 -1
  23. package/build/query/chain-query.js +2 -2
  24. package/build/query/chain-query.js.map +1 -1
  25. package/build/query/chain-rpc-query.js +2 -2
  26. package/build/query/chain-rpc-query.js.map +1 -1
  27. package/build/query/cosmos/balance/balances.js +8 -4
  28. package/build/query/cosmos/balance/balances.js.map +1 -1
  29. package/build/query/cosmos/balance/spendable.js +3 -2
  30. package/build/query/cosmos/balance/spendable.js.map +1 -1
  31. package/build/query/cosmos/ibc/channel.js +3 -2
  32. package/build/query/cosmos/ibc/channel.js.map +1 -1
  33. package/build/query/cosmos/ibc/client-state.js +2 -2
  34. package/build/query/cosmos/ibc/client-state.js.map +1 -1
  35. package/build/query/cosmos/ibc/denom-trace.js +6 -8
  36. package/build/query/cosmos/ibc/denom-trace.js.map +1 -1
  37. package/build/query/cosmos/staking/babylon-btc-delegation-reward.js +3 -2
  38. package/build/query/cosmos/staking/babylon-btc-delegation-reward.js.map +1 -1
  39. package/build/query/cosmos/staking/delegations.js +5 -4
  40. package/build/query/cosmos/staking/delegations.js.map +1 -1
  41. package/build/query/cosmos/staking/initia-delegations.js +8 -6
  42. package/build/query/cosmos/staking/initia-delegations.js.map +1 -1
  43. package/build/query/cosmos/staking/initia-unbonding-delegations.js +6 -5
  44. package/build/query/cosmos/staking/initia-unbonding-delegations.js.map +1 -1
  45. package/build/query/cosmos/staking/initia-validators.js +3 -3
  46. package/build/query/cosmos/staking/initia-validators.js.map +1 -1
  47. package/build/query/cosmos/staking/pool.js +3 -3
  48. package/build/query/cosmos/staking/pool.js.map +1 -1
  49. package/build/query/cosmos/staking/rewards.js +13 -10
  50. package/build/query/cosmos/staking/rewards.js.map +1 -1
  51. package/build/query/cosmos/staking/unbonding-delegations.js +3 -3
  52. package/build/query/cosmos/staking/unbonding-delegations.js.map +1 -1
  53. package/build/query/cosmos/staking/validators.js +2 -3
  54. package/build/query/cosmos/staking/validators.js.map +1 -1
  55. package/build/query/cosmwasm/contract-query.js +2 -2
  56. package/build/query/cosmwasm/contract-query.js.map +1 -1
  57. package/build/query/cosmwasm/cw20-balance.js +7 -4
  58. package/build/query/cosmwasm/cw20-balance.js.map +1 -1
  59. package/build/query/cosmwasm/neutron/staking-rewards.js +10 -8
  60. package/build/query/cosmwasm/neutron/staking-rewards.js.map +1 -1
  61. package/build/query/noble/swap/simulate-swap.js +1 -1
  62. package/build/query/noble/swap/simulate-swap.js.map +1 -1
  63. package/build/query/osmosis/txfees/fee-tokens/index.js +8 -4
  64. package/build/query/osmosis/txfees/fee-tokens/index.js.map +1 -1
  65. package/build/query/secret-wasm/secret20-balance.js +7 -5
  66. package/build/query/secret-wasm/secret20-balance.js.map +1 -1
  67. package/build/token-factory/currency-registrar.js +1 -1
  68. package/build/token-factory/currency-registrar.js.map +1 -1
  69. package/package.json +11 -11
  70. package/src/account/babylon.ts +21 -7
  71. package/src/account/base.ts +7 -5
  72. package/src/account/cosmos.ts +121 -66
  73. package/src/account/cosmwasm.ts +12 -3
  74. package/src/account/secret.ts +12 -3
  75. package/src/account/store.ts +4 -4
  76. package/src/chain/base.ts +816 -34
  77. package/src/chain/types.ts +27 -1
  78. package/src/common/utils/index.ts +2 -2
  79. package/src/lsm/currency-registrar.ts +9 -7
  80. package/src/query/balances.ts +30 -13
  81. package/src/query/chain-query.ts +6 -2
  82. package/src/query/chain-rpc-query.ts +6 -2
  83. package/src/query/cosmos/balance/balances.ts +8 -4
  84. package/src/query/cosmos/balance/spendable.ts +3 -3
  85. package/src/query/cosmos/ibc/channel.ts +5 -2
  86. package/src/query/cosmos/ibc/client-state.ts +2 -2
  87. package/src/query/cosmos/ibc/denom-trace.ts +7 -7
  88. package/src/query/cosmos/staking/babylon-btc-delegation-reward.ts +5 -2
  89. package/src/query/cosmos/staking/delegations.ts +9 -4
  90. package/src/query/cosmos/staking/initia-delegations.ts +14 -7
  91. package/src/query/cosmos/staking/initia-unbonding-delegations.ts +12 -6
  92. package/src/query/cosmos/staking/initia-validators.ts +5 -3
  93. package/src/query/cosmos/staking/pool.ts +3 -3
  94. package/src/query/cosmos/staking/rewards.ts +13 -10
  95. package/src/query/cosmos/staking/unbonding-delegations.ts +7 -3
  96. package/src/query/cosmos/staking/validators.ts +4 -3
  97. package/src/query/cosmwasm/contract-query.ts +4 -2
  98. package/src/query/cosmwasm/cw20-balance.ts +8 -5
  99. package/src/query/cosmwasm/neutron/staking-rewards.ts +10 -8
  100. package/src/query/noble/swap/simulate-swap.ts +1 -1
  101. package/src/query/osmosis/txfees/fee-tokens/index.ts +8 -4
  102. package/src/query/secret-wasm/secret20-balance.ts +7 -5
  103. package/src/token-factory/currency-registrar.ts +1 -1
package/src/chain/base.ts CHANGED
@@ -594,6 +594,25 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
594
594
  protected registeredStarkentCurrencies: ERC20Currency[] = [];
595
595
  @observable.shallow
596
596
  protected registeredBitcoinCurrencies: AppCurrency[] = [];
597
+ @observable.shallow
598
+ protected registeredEvmCurrencies: AppCurrency[] = [];
599
+
600
+ @observable.shallow
601
+ protected registeredCurrenciesNoReaction: AppCurrency[] = [];
602
+
603
+ @computed
604
+ protected get unknownDenomMap(): Map<
605
+ string,
606
+ { denom: string; reaction: boolean }
607
+ > {
608
+ return new Map();
609
+ }
610
+
611
+ @observable.shallow
612
+ protected registrationInProgressCurrencyMap: Map<string, boolean> = new Map();
613
+
614
+ @observable.shallow
615
+ protected availableModules: ChainInfoModule[];
597
616
 
598
617
  constructor(
599
618
  embedded: M,
@@ -602,12 +621,23 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
602
621
  }
603
622
  ) {
604
623
  this._embedded = embedded;
624
+ this.availableModules = this.detectAvailableModules();
605
625
 
606
626
  makeObservable(this);
607
627
 
608
628
  keepAlive(this, "cosmosCurrencyMap");
609
629
  keepAlive(this, "starknetCurrencyMap");
610
630
  keepAlive(this, "bitcoinCurrencyMap");
631
+ keepAlive(this, "evmCurrencyMap");
632
+ }
633
+
634
+ protected detectAvailableModules(): ChainInfoModule[] {
635
+ const modules: ChainInfoModule[] = [];
636
+ if ("bitcoin" in this._embedded) modules.push("bitcoin");
637
+ if ("starknet" in this._embedded) modules.push("starknet");
638
+ if ("evm" in this._embedded) modules.push("evm");
639
+ if ("cosmos" in this._embedded) modules.push("cosmos");
640
+ return modules;
611
641
  }
612
642
 
613
643
  get embedded(): M {
@@ -617,13 +647,47 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
617
647
  @action
618
648
  setEmbeddedModularChainInfo(embedded: M) {
619
649
  this._embedded = embedded;
650
+ this.availableModules = this.detectAvailableModules();
620
651
  }
621
652
 
622
653
  get chainId(): string {
623
654
  return this._embedded.chainId;
624
655
  }
625
656
 
626
- getCurrencies(module: ChainInfoModule): AppCurrency[] {
657
+ get stakeCurrency(): Currency | undefined {
658
+ return "cosmos" in this._embedded
659
+ ? this._embedded.cosmos.stakeCurrency
660
+ : undefined;
661
+ }
662
+
663
+ get feeCurrencies(): FeeCurrency[] | undefined {
664
+ if ("cosmos" in this._embedded) {
665
+ return this._embedded.cosmos.feeCurrencies;
666
+ }
667
+ if ("evm" in this._embedded) {
668
+ return this._embedded.evm.feeCurrencies;
669
+ }
670
+ if ("starknet" in this._embedded) {
671
+ const feeContractAddress = this._embedded.starknet.strkContractAddress;
672
+ const feeCurrency = this.getCurrenciesByModule("starknet").find(
673
+ (cur) => cur.coinMinimalDenom === `erc20:${feeContractAddress}`
674
+ );
675
+ if (feeCurrency) {
676
+ return [feeCurrency];
677
+ }
678
+ }
679
+ if ("bitcoin" in this._embedded) {
680
+ return [this._embedded.bitcoin.currencies[0]];
681
+ }
682
+ }
683
+
684
+ getCurrencies(): AppCurrency[] {
685
+ return this.availableModules
686
+ .map((module) => this.getCurrenciesByModule(module))
687
+ .flat();
688
+ }
689
+
690
+ getCurrenciesByModule(module: ChainInfoModule): AppCurrency[] {
627
691
  switch (module) {
628
692
  case "cosmos":
629
693
  if (!("cosmos" in this._embedded)) {
@@ -649,12 +713,569 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
649
713
  return this._embedded.bitcoin.currencies.concat(
650
714
  this.registeredBitcoinCurrencies
651
715
  );
716
+ case "evm":
717
+ if (!("evm" in this._embedded)) {
718
+ throw new Error(
719
+ `No evm native module for this chain: ${this.chainId}`
720
+ );
721
+ }
722
+
723
+ return this._embedded.evm.currencies.concat(
724
+ this.registeredEvmCurrencies
725
+ );
652
726
 
653
727
  default:
654
728
  throw new Error(`Unknown module: ${module}`);
655
729
  }
656
730
  }
657
731
 
732
+ /**
733
+ * Currency를 반환한다.
734
+ * 만약 해당 Currency가 없다면 unknownDenomMap에 추가한다.
735
+ * @param coinMinimalDenom
736
+ */
737
+ findCurrency(coinMinimalDenom: string): AppCurrency | undefined {
738
+ const normalizedDenom = DenomHelper.normalizeDenom(coinMinimalDenom);
739
+
740
+ const currency = this.findCurrencyByModule(normalizedDenom);
741
+ if (currency) {
742
+ return currency;
743
+ }
744
+
745
+ this.availableModules.forEach((module) => {
746
+ // 동적으로 토큰을 발견하고 등록하는 절차는 cosmos, evm 모듈에서만 지원
747
+ if (module === "cosmos" || module === "evm") {
748
+ this.addUnknownDenomsImpl({
749
+ module,
750
+ coinMinimalDenoms: [normalizedDenom],
751
+ reaction: true,
752
+ });
753
+ }
754
+ });
755
+
756
+ // Unknown denom can be registered synchronously in some cases.
757
+ // For this case, re-try to get currency.
758
+ return this.findCurrencyByModule(normalizedDenom);
759
+ }
760
+
761
+ findCurrencyByModule(denom: string) {
762
+ for (const module of this.availableModules) {
763
+ const currency = this.getCurrencyMapByModule(module).get(denom);
764
+
765
+ if (currency) {
766
+ return currency;
767
+ }
768
+
769
+ if (module === "cosmos" || module === "evm") {
770
+ const noReactionCurrency = this.currencyMapNoReaction.get(denom);
771
+ if (noReactionCurrency) {
772
+ return noReactionCurrency;
773
+ }
774
+ }
775
+ }
776
+
777
+ return undefined;
778
+ }
779
+
780
+ /**
781
+ * findCurrency와 비슷하지만 해당하는 currency가 존재하지 않을 경우 raw currency를 반환한다.
782
+ * @param coinMinimalDenom
783
+ */
784
+ forceFindCurrency(coinMinimalDenom: string): AppCurrency {
785
+ const normalizedCoinMinimalDenom =
786
+ DenomHelper.normalizeDenom(coinMinimalDenom);
787
+
788
+ const currency = this.findCurrency(coinMinimalDenom);
789
+
790
+ if (currency) {
791
+ return currency;
792
+ }
793
+
794
+ return this.getRawCurrency(normalizedCoinMinimalDenom);
795
+ }
796
+
797
+ /**
798
+ * 최적화를 위해 reaction 없이 currency를 조회한다.
799
+ * 현재는 IBC asset에서만 사용되고 있어 Cosmos 모듈만 지원한다.
800
+ * @param coinMinimalDenom
801
+ */
802
+ findCurrencyWithoutReaction(
803
+ coinMinimalDenom: string
804
+ ): AppCurrency | undefined {
805
+ const normalizedCoinMinimalDenom =
806
+ DenomHelper.normalizeDenom(coinMinimalDenom);
807
+
808
+ const currency = this.findCurrencyByModule(normalizedCoinMinimalDenom);
809
+
810
+ if (currency) {
811
+ return currency;
812
+ }
813
+
814
+ this.availableModules.forEach((module) => {
815
+ if (module === "cosmos" || module === "evm") {
816
+ this.addUnknownDenomsImpl({
817
+ module,
818
+ coinMinimalDenoms: [normalizedCoinMinimalDenom],
819
+ reaction: false,
820
+ });
821
+ }
822
+ });
823
+
824
+ // Unknown denom can be registered synchronously in some cases.
825
+ // For this case, re-try to get currency.
826
+ return this.findCurrencyByModule(normalizedCoinMinimalDenom);
827
+ }
828
+
829
+ findCurrencyAsync(
830
+ coinMinimalDenom: string
831
+ ): Promise<AppCurrency | undefined> {
832
+ const normalizedCoinMinimalDenom =
833
+ DenomHelper.normalizeDenom(coinMinimalDenom);
834
+
835
+ const currency = this.findCurrencyByModule(normalizedCoinMinimalDenom);
836
+ if (currency) {
837
+ return Promise.resolve(currency);
838
+ }
839
+
840
+ this.availableModules.forEach((module) => {
841
+ if (module === "cosmos" || module === "evm") {
842
+ this.addUnknownDenomsImpl({
843
+ module,
844
+ coinMinimalDenoms: [normalizedCoinMinimalDenom],
845
+ reaction: true,
846
+ });
847
+ }
848
+ });
849
+
850
+ let disposal: IReactionDisposer | undefined;
851
+
852
+ return new Promise<AppCurrency | undefined>((resolve) => {
853
+ disposal = autorun(() => {
854
+ const registration = this.registrationInProgressCurrencyMap.get(
855
+ normalizedCoinMinimalDenom
856
+ );
857
+ if (!registration) {
858
+ const result = this.findCurrencyByModule(normalizedCoinMinimalDenom);
859
+ resolve(result);
860
+ }
861
+ });
862
+ }).finally(() => {
863
+ if (disposal) {
864
+ disposal();
865
+ }
866
+ });
867
+ }
868
+
869
+ forceFindCurrencyWithoutReaction(coinMinimalDenom: string): AppCurrency {
870
+ const normalizedCoinMinimalDenom =
871
+ DenomHelper.normalizeDenom(coinMinimalDenom);
872
+
873
+ const currency = this.findCurrencyWithoutReaction(
874
+ normalizedCoinMinimalDenom
875
+ );
876
+ if (currency) {
877
+ return currency;
878
+ }
879
+
880
+ return this.getRawCurrency(normalizedCoinMinimalDenom);
881
+ }
882
+
883
+ /**
884
+ * raw currency를 반환한다. ref을 유지하기 위해서 cache를 사용한다.
885
+ */
886
+ protected getRawCurrency(denom: string): AppCurrency {
887
+ if (forceFindCurrencyCache.has(denom)) {
888
+ return forceFindCurrencyCache.get(denom)!;
889
+ }
890
+
891
+ const currency = {
892
+ coinMinimalDenom: denom,
893
+ coinDenom: denom,
894
+ coinDecimals: 0,
895
+ };
896
+
897
+ forceFindCurrencyCache.set(denom, currency);
898
+
899
+ return currency;
900
+ }
901
+
902
+ addUnknownDenoms({
903
+ module,
904
+ coinMinimalDenoms,
905
+ }: {
906
+ module: ChainInfoModule;
907
+ coinMinimalDenoms: string[];
908
+ }) {
909
+ this.addUnknownDenomsImpl({
910
+ module,
911
+ coinMinimalDenoms,
912
+ reaction: true,
913
+ });
914
+ }
915
+
916
+ addUnknownDenomsWithoutReaction({
917
+ module,
918
+ coinMinimalDenoms,
919
+ }: {
920
+ module: ChainInfoModule;
921
+ coinMinimalDenoms: string[];
922
+ }) {
923
+ this.addUnknownDenomsImpl({
924
+ module,
925
+ coinMinimalDenoms,
926
+ reaction: false,
927
+ });
928
+ }
929
+
930
+ /**
931
+ * 해당되는 denom의 currency를 모를 때 이 메소드를 사용해서 등록을 요청할 수 있다.
932
+ *
933
+ * - balance 쿼리 결과 등에서 모르는 denom이 나타날 때
934
+ * - IBC denom을 동적으로 등록해야 할 때
935
+ * ---
936
+ * - action 데코레이터가 사용되지 않은 이유: action 내부에서 autorun을 생성하면, autorun의 첫 실행이 action이 끝날 때까지 지연된다.
937
+ * autorun을 사용하는 startCurrencyRegistration 내부에서 불필요한 지연이 발생할 수 있어 action 없이 구현하여 autorun을 즉시 실행한다.
938
+ * 하지만 이 메소드를 action 안에서 호출하게 되는 경우가 있다면 여전히 즉시 실행되지 않으므로 고려가 필요하다.
939
+ */
940
+ protected addUnknownDenomsImpl({
941
+ module,
942
+ coinMinimalDenoms,
943
+ reaction,
944
+ }: {
945
+ module: ChainInfoModule;
946
+ coinMinimalDenoms: string[];
947
+ reaction: boolean;
948
+ }) {
949
+ for (const coinMinimalDenom of coinMinimalDenoms) {
950
+ const normalizedDenom = DenomHelper.normalizeDenom(coinMinimalDenom);
951
+
952
+ // 이미 등록 중인 경우 reaction 모드만 업데이트하고 스킵
953
+ if (this.shouldSkipUnknownDenom(normalizedDenom, reaction)) {
954
+ continue;
955
+ }
956
+
957
+ // 이미 currency가 존재하면 스킵
958
+ if (
959
+ reaction &&
960
+ this.getCurrencyMapByModule(module).has(normalizedDenom)
961
+ ) {
962
+ continue;
963
+ }
964
+
965
+ // noreaction 맵 확인
966
+ if (!reaction && this.currencyMapNoReaction.has(normalizedDenom)) {
967
+ continue;
968
+ }
969
+
970
+ // unknownDenomMap에 등록 및 등록진행중 상태로 변경
971
+ const isNewUnknownDenom = this.addToUnknownDenomMapAndReturnIsNew(
972
+ normalizedDenom,
973
+ reaction
974
+ );
975
+
976
+ // 새로운 unknown denom인 경우에만 autorun 시작
977
+ if (isNewUnknownDenom) {
978
+ this.startCurrencyRegistration(module, normalizedDenom);
979
+ }
980
+ }
981
+ }
982
+
983
+ // module별 currency map 제공
984
+ protected getCurrencyMapByModule(
985
+ module: ChainInfoModule
986
+ ): Map<string, AppCurrency> {
987
+ switch (module) {
988
+ case "cosmos":
989
+ return this.cosmosCurrencyMap;
990
+ case "starknet":
991
+ return this.starknetCurrencyMap;
992
+ case "bitcoin":
993
+ return this.bitcoinCurrencyMap;
994
+ case "evm":
995
+ return this.evmCurrencyMap;
996
+ default:
997
+ throw new Error(`Unknown module: ${module}`);
998
+ }
999
+ }
1000
+
1001
+ /**
1002
+ * 이미 등록 중인 unknown denom인지 체크하고, reaction 업데이트
1003
+ */
1004
+ protected shouldSkipUnknownDenom(
1005
+ normalizedDenom: string,
1006
+ reaction: boolean
1007
+ ): boolean {
1008
+ const prior = this.unknownDenomMap.get(normalizedDenom);
1009
+ if (!prior) {
1010
+ return false;
1011
+ }
1012
+
1013
+ // 같은 reaction 모드면 스킵
1014
+ if (prior.reaction === reaction) {
1015
+ return true;
1016
+ }
1017
+
1018
+ // noReaction을 reaction으로 변경하는 경우
1019
+ if (reaction) {
1020
+ // 로직상 reaction은 reactive할 필요가 없기 때문에 직접 변경
1021
+ prior.reaction = reaction;
1022
+ }
1023
+
1024
+ return true;
1025
+ }
1026
+
1027
+ /**
1028
+ * unknownDenomMap에 등록 및 등록진행중 상태로 변경
1029
+ * @returns 새로 추가된 경우 true, 이미 있던 경우 false
1030
+ */
1031
+ protected addToUnknownDenomMapAndReturnIsNew(
1032
+ denom: string,
1033
+ reaction: boolean
1034
+ ): boolean {
1035
+ const prior = this.unknownDenomMap.get(denom);
1036
+ if (prior) {
1037
+ return false; // 이미 존재하면 새로 추가하지 않음
1038
+ }
1039
+
1040
+ runInAction(() => {
1041
+ this.unknownDenomMap.set(denom, {
1042
+ denom: denom,
1043
+ reaction,
1044
+ });
1045
+ this.registrationInProgressCurrencyMap.set(denom, true);
1046
+ });
1047
+
1048
+ return true;
1049
+ }
1050
+
1051
+ protected startCurrencyRegistration(
1052
+ module: ChainInfoModule,
1053
+ denom: string
1054
+ ): void {
1055
+ let i = 0;
1056
+ let disposed = false;
1057
+
1058
+ const disposer = autorun(() => {
1059
+ i++;
1060
+
1061
+ // autorun의 첫 실행에서 즉시 dispose하면 disposer가 아직 undefined이므로
1062
+ // setTimeout으로 다음 회차로 안전하게 연기
1063
+ const dispose = () => {
1064
+ disposed = true;
1065
+
1066
+ if (i === 1) {
1067
+ // 첫 실행: 다음 회차에 dispose
1068
+ setTimeout(() => {
1069
+ disposer?.();
1070
+ }, 1);
1071
+ } else {
1072
+ // 두번째 이후: 즉시 dispose
1073
+ disposer?.();
1074
+ }
1075
+ };
1076
+
1077
+ // 이미 dispose된 경우 리턴
1078
+ if (disposed) {
1079
+ return;
1080
+ }
1081
+
1082
+ const generator = this.currencyRegistry.getCurrencyRegistrar(
1083
+ this.chainId,
1084
+ denom
1085
+ );
1086
+
1087
+ if (generator) {
1088
+ this.handleCurrencyRegistration({
1089
+ module,
1090
+ denom,
1091
+ generator,
1092
+ dispose,
1093
+ });
1094
+ } else {
1095
+ // registrar가 없으면 등록 불가능
1096
+ if (this.registrationInProgressCurrencyMap.get(denom)) {
1097
+ runInAction(() => {
1098
+ this.registrationInProgressCurrencyMap.delete(denom);
1099
+ });
1100
+ }
1101
+ dispose();
1102
+ }
1103
+ });
1104
+ }
1105
+
1106
+ /**
1107
+ * currency 등록 진행
1108
+ */
1109
+ protected handleCurrencyRegistration(args: {
1110
+ module: ChainInfoModule;
1111
+ denom: string;
1112
+ generator: { value: AppCurrency | undefined; done: boolean };
1113
+ dispose: () => void;
1114
+ }): void {
1115
+ const { module, denom, generator, dispose } = args;
1116
+ const currency = generator.value;
1117
+
1118
+ runInAction(() => {
1119
+ // 진행 중 상태 업데이트
1120
+ if (!generator.done) {
1121
+ this.registrationInProgressCurrencyMap.set(denom, true);
1122
+ }
1123
+
1124
+ // currency가 있으면 등록
1125
+ if (currency) {
1126
+ const unknownDenom = this.findAndRemoveUnknownDenom(
1127
+ denom,
1128
+ generator.done
1129
+ );
1130
+
1131
+ // reaction 모드에 따라 적절한 메서드로 추가
1132
+ if (!unknownDenom || unknownDenom.reaction) {
1133
+ this.addOrReplaceCurrency(module, currency);
1134
+ } else {
1135
+ this.addOrReplaceCurrencyNoReaction(module, currency);
1136
+ }
1137
+ }
1138
+
1139
+ // 완료되면 진행 중 상태 제거
1140
+ if (generator.done) {
1141
+ this.registrationInProgressCurrencyMap.delete(denom);
1142
+ }
1143
+ });
1144
+
1145
+ // 완료되면 dispose
1146
+ if (generator.done) {
1147
+ dispose();
1148
+ }
1149
+ }
1150
+
1151
+ /**
1152
+ * unknownDenomMap에서 해당 denom 찾고, 완료되었으면 제거
1153
+ * @returns 찾은 unknownDenom 정보 (없으면 undefined)
1154
+ */
1155
+ protected findAndRemoveUnknownDenom(
1156
+ denom: string,
1157
+ shouldRemove: boolean
1158
+ ):
1159
+ | {
1160
+ denom: string;
1161
+ reaction: boolean;
1162
+ }
1163
+ | undefined {
1164
+ const unknownDenom = this.unknownDenomMap.get(denom);
1165
+ if (!unknownDenom) {
1166
+ return undefined;
1167
+ }
1168
+
1169
+ if (shouldRemove) {
1170
+ this.unknownDenomMap.delete(denom);
1171
+ }
1172
+ return unknownDenom;
1173
+ }
1174
+
1175
+ @action
1176
+ protected addOrReplaceCurrency(
1177
+ module: ChainInfoModule,
1178
+ currency: AppCurrency
1179
+ ) {
1180
+ const currencyMap = this.getCurrencyMapByModule(module);
1181
+ const normalizedCoinMinimalDenom = DenomHelper.normalizeDenom(
1182
+ currency.coinMinimalDenom
1183
+ );
1184
+
1185
+ const newRegisteredCurrencies =
1186
+ this.getRegisteredCurrencies(module).slice();
1187
+
1188
+ if (currencyMap.has(normalizedCoinMinimalDenom)) {
1189
+ const index = newRegisteredCurrencies.findIndex(
1190
+ (cur) => cur.coinMinimalDenom === normalizedCoinMinimalDenom
1191
+ );
1192
+ if (index >= 0) {
1193
+ const prev = newRegisteredCurrencies[index];
1194
+ if (
1195
+ // If same, do nothing
1196
+ sortedJsonByKeyStringify(prev) !== sortedJsonByKeyStringify(currency)
1197
+ ) {
1198
+ newRegisteredCurrencies.splice(index, 1, currency);
1199
+ this.replaceRegisteredCurrencies(module, newRegisteredCurrencies);
1200
+ }
1201
+ }
1202
+ } else {
1203
+ newRegisteredCurrencies.push(currency);
1204
+ this.replaceRegisteredCurrencies(module, newRegisteredCurrencies);
1205
+ }
1206
+ }
1207
+
1208
+ @action
1209
+ protected addOrReplaceCurrencyNoReaction(
1210
+ module: ChainInfoModule,
1211
+ currency: AppCurrency
1212
+ ) {
1213
+ if (module !== "cosmos" && module !== "evm") {
1214
+ return;
1215
+ }
1216
+
1217
+ const normalizedCoinMinimalDenom = DenomHelper.normalizeDenom(
1218
+ currency.coinMinimalDenom
1219
+ );
1220
+ const currencyMap = this.getCurrencyMapByModule(module);
1221
+ const newRegisteredCurrencies = this.registeredCurrenciesNoReaction.slice();
1222
+
1223
+ if (currencyMap.has(normalizedCoinMinimalDenom)) {
1224
+ const index = newRegisteredCurrencies.findIndex(
1225
+ (cur) => cur.coinMinimalDenom === normalizedCoinMinimalDenom
1226
+ );
1227
+ if (index >= 0) {
1228
+ const prev = newRegisteredCurrencies[index];
1229
+ if (
1230
+ // If same, do nothing
1231
+ sortedJsonByKeyStringify(prev) !== sortedJsonByKeyStringify(currency)
1232
+ ) {
1233
+ newRegisteredCurrencies.splice(index, 1, currency);
1234
+ this.registeredCurrenciesNoReaction = newRegisteredCurrencies;
1235
+ }
1236
+ }
1237
+ } else {
1238
+ newRegisteredCurrencies.push(currency);
1239
+ this.registeredCurrenciesNoReaction = newRegisteredCurrencies;
1240
+ }
1241
+ }
1242
+
1243
+ protected getRegisteredCurrencies(module: ChainInfoModule): AppCurrency[] {
1244
+ switch (module) {
1245
+ case "cosmos":
1246
+ return this.registeredCosmosCurrencies;
1247
+ case "starknet":
1248
+ return this.registeredStarkentCurrencies;
1249
+ case "bitcoin":
1250
+ return this.registeredBitcoinCurrencies;
1251
+ case "evm":
1252
+ return this.registeredEvmCurrencies;
1253
+ default:
1254
+ throw new Error(`Unknown module: ${module}`);
1255
+ }
1256
+ }
1257
+ protected replaceRegisteredCurrencies(
1258
+ module: ChainInfoModule,
1259
+ currencies: AppCurrency[]
1260
+ ) {
1261
+ switch (module) {
1262
+ case "cosmos":
1263
+ this.registeredCosmosCurrencies = currencies;
1264
+ break;
1265
+ case "starknet":
1266
+ this.registeredStarkentCurrencies = currencies as ERC20Currency[];
1267
+ break;
1268
+ case "bitcoin":
1269
+ this.registeredBitcoinCurrencies = currencies;
1270
+ break;
1271
+ case "evm":
1272
+ this.registeredEvmCurrencies = currencies;
1273
+ break;
1274
+ default:
1275
+ throw new Error(`Unknown module: ${module}`);
1276
+ }
1277
+ }
1278
+
658
1279
  @action
659
1280
  addCurrencies(module: ChainInfoModule, ...currencies: AppCurrency[]) {
660
1281
  if (currencies.length === 0) {
@@ -708,6 +1329,20 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
708
1329
  }
709
1330
  }
710
1331
  break;
1332
+ case "evm":
1333
+ if (!("evm" in this._embedded)) {
1334
+ throw new Error(`No evm module for this chain: ${this.chainId}`);
1335
+ }
1336
+
1337
+ for (const currency of currencies) {
1338
+ const normalizedCoinMinimalDenom = DenomHelper.normalizeDenom(
1339
+ currency.coinMinimalDenom
1340
+ );
1341
+ if (!this.evmCurrencyMap.has(normalizedCoinMinimalDenom)) {
1342
+ this.registeredEvmCurrencies.push(currency);
1343
+ }
1344
+ }
1345
+ break;
711
1346
  default:
712
1347
  throw new Error(`Unknown module: ${module}`);
713
1348
  }
@@ -755,6 +1390,15 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
755
1390
  (currency) => !map.get(currency.coinMinimalDenom)
756
1391
  );
757
1392
  break;
1393
+ case "evm":
1394
+ if (!("evm" in this._embedded)) {
1395
+ throw new Error(`No evm module for this chain: ${this.chainId}`);
1396
+ }
1397
+
1398
+ this.registeredEvmCurrencies = this.registeredEvmCurrencies.filter(
1399
+ (currency) => !map.get(currency.coinMinimalDenom)
1400
+ );
1401
+ break;
758
1402
  default:
759
1403
  throw new Error(`Unknown module: ${module}`);
760
1404
  }
@@ -768,7 +1412,21 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
768
1412
  result.set(currency.coinMinimalDenom, currency);
769
1413
  }
770
1414
  }
1415
+ for (const currency of this.registeredCosmosCurrencies) {
1416
+ result.set(
1417
+ DenomHelper.normalizeDenom(currency.coinMinimalDenom),
1418
+ currency
1419
+ );
1420
+ }
1421
+ return result;
1422
+ }
771
1423
 
1424
+ @computed
1425
+ protected get currencyMapNoReaction(): Map<string, AppCurrency> {
1426
+ const result: Map<string, AppCurrency> = new Map();
1427
+ for (const currency of this.registeredCurrenciesNoReaction) {
1428
+ result.set(currency.coinMinimalDenom, currency);
1429
+ }
772
1430
  return result;
773
1431
  }
774
1432
 
@@ -781,6 +1439,12 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
781
1439
  }
782
1440
  }
783
1441
 
1442
+ for (const currency of this.registeredStarkentCurrencies) {
1443
+ result.set(
1444
+ DenomHelper.normalizeDenom(currency.coinMinimalDenom),
1445
+ currency
1446
+ );
1447
+ }
784
1448
  return result;
785
1449
  }
786
1450
 
@@ -793,8 +1457,79 @@ export class ModularChainInfoImpl<M extends ModularChainInfo = ModularChainInfo>
793
1457
  }
794
1458
  }
795
1459
 
1460
+ for (const currency of this.registeredBitcoinCurrencies) {
1461
+ result.set(
1462
+ DenomHelper.normalizeDenom(currency.coinMinimalDenom),
1463
+ currency
1464
+ );
1465
+ }
1466
+ return result;
1467
+ }
1468
+
1469
+ @computed
1470
+ protected get evmCurrencyMap(): Map<string, AppCurrency> {
1471
+ const result: Map<string, AppCurrency> = new Map();
1472
+ if ("evm" in this._embedded) {
1473
+ for (const currency of this._embedded.evm.currencies) {
1474
+ result.set(
1475
+ DenomHelper.normalizeDenom(currency.coinMinimalDenom),
1476
+ currency
1477
+ );
1478
+ }
1479
+ }
1480
+
1481
+ for (const currency of this.registeredEvmCurrencies) {
1482
+ result.set(
1483
+ DenomHelper.normalizeDenom(currency.coinMinimalDenom),
1484
+ currency
1485
+ );
1486
+ }
1487
+
796
1488
  return result;
797
1489
  }
1490
+
1491
+ hasFeature(feature: string): boolean {
1492
+ if ("evm" in this._embedded) {
1493
+ return !!(
1494
+ "features" in this._embedded.evm &&
1495
+ this._embedded.evm.features?.includes(feature)
1496
+ );
1497
+ }
1498
+ if ("cosmos" in this._embedded) {
1499
+ return !!(
1500
+ "features" in this._embedded.cosmos &&
1501
+ this._embedded.cosmos.features?.includes(feature)
1502
+ );
1503
+ }
1504
+ return false;
1505
+ }
1506
+
1507
+ isCurrencyRegistrationInProgress(coinMinimalDenom: string): boolean {
1508
+ return (
1509
+ this.registrationInProgressCurrencyMap.get(coinMinimalDenom) || false
1510
+ );
1511
+ }
1512
+
1513
+ /**
1514
+ * @description Check if the chain matches the given modules. Either `or` or `and` condition must be provided.
1515
+ * @param or - If any of the modules in the array is available, return true.
1516
+ * @param and - If all of the modules in the array are available, return true.
1517
+ */
1518
+ matchModules({
1519
+ and,
1520
+ or,
1521
+ }:
1522
+ | { or: ChainInfoModule[]; and?: undefined }
1523
+ | { and: ChainInfoModule[]; or?: undefined }): boolean {
1524
+ return (
1525
+ (or?.some((module) => this.matchModule(module)) ?? true) &&
1526
+ (and?.every((module) => this.matchModule(module)) ?? true)
1527
+ );
1528
+ }
1529
+
1530
+ matchModule(module: ChainInfoModule): boolean {
1531
+ return this.availableModules.includes(module);
1532
+ }
798
1533
  }
799
1534
 
800
1535
  export class ChainStore<C extends ChainInfo = ChainInfo>
@@ -820,6 +1555,9 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
820
1555
  keepAlive(this, "modularChainInfoMap");
821
1556
  }
822
1557
 
1558
+ /**
1559
+ * @deprecated Use `modularChainInfos` instead
1560
+ */
823
1561
  get chainInfos(): IChainInfoImpl<C>[] {
824
1562
  return this._chainInfos;
825
1563
  }
@@ -841,6 +1579,9 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
841
1579
  return result;
842
1580
  }
843
1581
 
1582
+ /**
1583
+ * @deprecated Use `getmodularChain` or `getModularChainInfoImpl` instead
1584
+ */
844
1585
  getChain(chainId: string): IChainInfoImpl<C> {
845
1586
  const chainIdentifier = ChainIdHelper.parse(chainId);
846
1587
 
@@ -853,6 +1594,9 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
853
1594
  return chainInfo;
854
1595
  }
855
1596
 
1597
+ /**
1598
+ * @deprecated Use `hasModularChain` instead
1599
+ */
856
1600
  hasChain(chainId: string): boolean {
857
1601
  const chainIdentifier = ChainIdHelper.parse(chainId);
858
1602
 
@@ -941,27 +1685,56 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
941
1685
 
942
1686
  return new ChainInfoImpl(chainInfo, this);
943
1687
  });
1688
+
944
1689
  this._modularChainInfos = chainInfos.map((chainInfo) => {
1690
+ if (
1691
+ "evm" in chainInfo &&
1692
+ chainInfo.evm &&
1693
+ "currencies" in chainInfo && // clarify if it's ChainInfo type
1694
+ this.isEvmOnlyChainForInit(chainInfo.chainId)
1695
+ ) {
1696
+ return {
1697
+ chainId: chainInfo.chainId,
1698
+ chainName: chainInfo.chainName,
1699
+ chainSymbolImageUrl: chainInfo.chainSymbolImageUrl,
1700
+ isTestnet: chainInfo.isTestnet,
1701
+ isNative: true,
1702
+ evm: {
1703
+ ...chainInfo.evm,
1704
+ currencies: chainInfo.currencies,
1705
+ feeCurrencies: chainInfo.feeCurrencies,
1706
+ bip44: chainInfo.bip44,
1707
+ features: chainInfo.features,
1708
+ },
1709
+ };
1710
+ }
945
1711
  if ("currencies" in chainInfo) {
946
1712
  return {
947
1713
  chainId: chainInfo.chainId,
948
1714
  chainName: chainInfo.chainName,
949
1715
  chainSymbolImageUrl: chainInfo.chainSymbolImageUrl,
1716
+ isNative: true,
950
1717
  cosmos: chainInfo as C,
1718
+ ...(chainInfo.evm && {
1719
+ evm: {
1720
+ ...chainInfo.evm,
1721
+ currencies: chainInfo.currencies,
1722
+ bip44: chainInfo.bip44,
1723
+ },
1724
+ }),
951
1725
  };
952
1726
  }
953
- return chainInfo;
1727
+ return { ...chainInfo, isNative: true };
954
1728
  });
1729
+
955
1730
  this._modularChainInfoImpls = chainInfos.map((chainInfo) => {
956
- const modularChainInfo =
957
- "currencies" in chainInfo
958
- ? {
959
- chainId: chainInfo.chainId,
960
- chainName: chainInfo.chainName,
961
- chainSymbolImageUrl: chainInfo.chainSymbolImageUrl,
962
- cosmos: chainInfo as C,
963
- }
964
- : chainInfo;
1731
+ const modularChainInfo = this._modularChainInfos.find(
1732
+ (c) => c.chainId === chainInfo.chainId
1733
+ );
1734
+
1735
+ if (!modularChainInfo) {
1736
+ throw new Error(`Unknown modular chain info: ${chainInfo.chainId}`);
1737
+ }
965
1738
 
966
1739
  const prev = this.modularChainInfoImplMap.get(
967
1740
  ChainIdHelper.parse(chainInfo.chainId).identifier
@@ -1004,30 +1777,28 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
1004
1777
  chainId: cosmos.chainId,
1005
1778
  chainName: cosmos.chainName,
1006
1779
  chainSymbolImageUrl: cosmos.chainSymbolImageUrl,
1780
+ isNative: chainInfo.isNative || !cosmos.beta,
1007
1781
  cosmos,
1782
+ ...(cosmos.evm && {
1783
+ evm: {
1784
+ ...cosmos.evm,
1785
+ currencies: cosmos.currencies,
1786
+ bip44: cosmos.bip44,
1787
+ },
1788
+ }),
1008
1789
  };
1009
1790
  }
1791
+
1010
1792
  return chainInfo;
1011
1793
  });
1012
1794
  this._modularChainInfoImpls = infos.modulrChainInfos.map((chainInfo) => {
1013
- const modularChainInfo = (() => {
1014
- if ("currencies" in chainInfo) {
1015
- const cosmos = infos.chainInfos.find(
1016
- (c) => c.chainId === chainInfo.chainId
1017
- );
1018
- if (!cosmos) {
1019
- throw new Error("Can't find cosmos chain info");
1020
- }
1795
+ const modularChainInfo = this._modularChainInfos.find(
1796
+ (c) => c.chainId === chainInfo.chainId
1797
+ );
1021
1798
 
1022
- return {
1023
- chainId: cosmos.chainId,
1024
- chainName: cosmos.chainName,
1025
- chainSymbolImageUrl: cosmos.chainSymbolImageUrl,
1026
- cosmos,
1027
- };
1028
- }
1029
- return chainInfo;
1030
- })();
1799
+ if (!modularChainInfo) {
1800
+ throw new Error(`Unknown modular chain info: ${chainInfo.chainId}`);
1801
+ }
1031
1802
 
1032
1803
  const prev = this.modularChainInfoImplMap.get(
1033
1804
  ChainIdHelper.parse(chainInfo.chainId).identifier
@@ -1066,8 +1837,8 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
1066
1837
  }
1067
1838
 
1068
1839
  isEvmChain(chainId: string): boolean {
1069
- const chainInfo = this.getChain(chainId);
1070
- return chainInfo.evm != null;
1840
+ const chainInfo = this.getModularChain(chainId);
1841
+ return "evm" in chainInfo && chainInfo.evm != null;
1071
1842
  }
1072
1843
 
1073
1844
  isEvmOnlyChain(chainId: string): boolean {
@@ -1079,17 +1850,28 @@ export class ChainStore<C extends ChainInfo = ChainInfo>
1079
1850
  );
1080
1851
  }
1081
1852
 
1853
+ // 초기화 시 isEvmOnlyChain 확인하면 _modularChainInfos 할당 전이라 오류가 발생할 수 밖에 없어서 별도 추가
1854
+ isEvmOnlyChainForInit(chainId: string): boolean {
1855
+ const chainIdLikeCAIP2 = chainId.split(":");
1856
+ return chainIdLikeCAIP2.length === 2 && chainIdLikeCAIP2[0] === "eip155";
1857
+ }
1858
+
1082
1859
  isEvmOrEthermintLikeChain(chainId: string): boolean {
1083
1860
  const b = this.isEvmChain(chainId);
1084
1861
  if (b) {
1085
1862
  return true;
1086
1863
  }
1087
- const chainInfo = this.getChain(chainId);
1864
+ const chainInfo = this.getModularChain(chainId);
1088
1865
 
1089
1866
  const isEthermintLike =
1090
- chainInfo.bip44.coinType === 60 ||
1091
- !!chainInfo.features?.includes("eth-address-gen") ||
1092
- !!chainInfo.features?.includes("eth-key-sign");
1867
+ ("cosmos" in chainInfo &&
1868
+ (chainInfo.cosmos.bip44.coinType === 60 ||
1869
+ !!chainInfo.cosmos.features?.includes("eth-address-gen") ||
1870
+ !!chainInfo.cosmos.features?.includes("eth-key-sign"))) ||
1871
+ ("evm" in chainInfo &&
1872
+ (chainInfo.evm.bip44.coinType === 60 ||
1873
+ !!chainInfo.evm.features?.includes("eth-address-gen") ||
1874
+ !!chainInfo.evm.features?.includes("eth-key-sign")));
1093
1875
 
1094
1876
  return isEthermintLike;
1095
1877
  }