1- import React , { useEffect , useState } from "react" ;
1+ import React , { useCallback , useEffect , useMemo , useState } from "react" ;
22import { Dec , DecUtils } from "@keplr-wallet/unit" ;
3- import { OsmosisChainInfo } from "../constants" ;
3+ import { OsmosisChainInfo , OsmosisTestnetChainInfo } from "../constants" ;
44import { Balances } from "../types/balance" ;
55import { api } from "../util/api" ;
66import { sendMsgs } from "../util/sendMsgs" ;
@@ -12,39 +12,71 @@ export const OsmosisTab: React.FC = () => {
1212 const [ balance , setBalance ] = useState < string > ( "" ) ;
1313 const [ recipient , setRecipient ] = useState < string > ( "" ) ;
1414 const [ amount , setAmount ] = useState < string > ( "" ) ;
15+ const [ skipSimulation , setSkipSimulation ] = useState < boolean > ( false ) ;
16+ const [ targetChainId , setTargetChainId ] = useState < string > (
17+ OsmosisTestnetChainInfo . chainId ,
18+ ) ;
19+
20+ const isExperimental = useMemo ( ( ) => {
21+ const searchParams = new URLSearchParams ( window . location . search ) ;
22+ return searchParams . get ( "experimental" ) === "true" ;
23+ } , [ ] ) ;
24+
25+ const chains = useMemo (
26+ ( ) => [
27+ {
28+ id : OsmosisTestnetChainInfo . chainId ,
29+ label : "Osmosis Testnet" ,
30+ info : OsmosisTestnetChainInfo ,
31+ } ,
32+ {
33+ id : OsmosisChainInfo . chainId ,
34+ label : "Osmosis" ,
35+ info : OsmosisChainInfo ,
36+ } ,
37+ ] ,
38+ [ ] ,
39+ ) ;
1540
16- const init = async ( ) => {
41+ const selectedChain = useMemo (
42+ ( ) => chains . find ( ( c ) => c . id === targetChainId ) ?? chains [ 0 ] ,
43+ [ chains , targetChainId ] ,
44+ ) ;
45+
46+ const init = useCallback ( async ( ) => {
1747 const keplr = window . keplr ;
1848 if ( keplr ) {
1949 try {
20- await keplr . experimentalSuggestChain ( OsmosisChainInfo ) ;
50+ // Ensure the selected chain is available in Keplr and enabled
51+ await keplr . experimentalSuggestChain ( selectedChain . info as any ) ;
52+ await keplr . enable ( selectedChain . id ) ;
2153 } catch ( e ) {
2254 if ( e instanceof Error ) {
2355 console . log ( e . message ) ;
2456 }
2557 }
2658 }
27- } ;
59+ } , [ selectedChain ] ) ;
2860
2961 const getKeyFromKeplr = async ( ) => {
30- const key = await window . keplr ?. getKey ( OsmosisChainInfo . chainId ) ;
62+ const key = await window . keplr ?. getKey ( selectedChain . id ) ;
3163 if ( key ) {
3264 setAddress ( key . bech32Address ) ;
3365 }
3466 } ;
3567
3668 const getBalance = async ( ) => {
37- const key = await window . keplr ?. getKey ( OsmosisChainInfo . chainId ) ;
69+ const key = await window . keplr ?. getKey ( selectedChain . id ) ;
3870
3971 if ( key ) {
40- const uri = `${ OsmosisChainInfo . rest } /cosmos/bank/v1beta1/balances/${ key . bech32Address } ?pagination.limit=1000` ;
72+ const uri = `${ selectedChain . info . rest } /cosmos/bank/v1beta1/balances/${ key . bech32Address } ?pagination.limit=1000` ;
4173
4274 const data = await api < Balances > ( uri ) ;
4375 const balance = data . balances . find (
44- ( balance ) => balance . denom === "uosmo"
76+ ( balance ) => balance . denom === "uosmo" ,
4577 ) ;
46- const osmoDecimal = OsmosisChainInfo . currencies . find (
47- ( currency ) => currency . coinMinimalDenom === "uosmo"
78+ const osmoDecimal = selectedChain . info . currencies . find (
79+ ( currency ) => currency . coinMinimalDenom === "uosmo" ,
4880 ) ?. coinDecimals ;
4981
5082 if ( balance ) {
@@ -58,7 +90,7 @@ export const OsmosisTab: React.FC = () => {
5890
5991 const sendBalance = async ( ) => {
6092 if ( window . keplr ) {
61- const key = await window . keplr ?. getKey ( OsmosisChainInfo . chainId ) ;
93+ const key = await window . keplr ?. getKey ( selectedChain . id ) ;
6294 const protoMsgs = {
6395 typeUrl : "/cosmos.bank.v1beta1.MsgSend" ,
6496 value : MsgSend . encode ( {
@@ -77,24 +109,39 @@ export const OsmosisTab: React.FC = () => {
77109 } ;
78110
79111 try {
80- const gasUsed = await simulateMsgs (
81- OsmosisChainInfo ,
82- key . bech32Address ,
83- [ protoMsgs ] ,
84- [ { denom : "uosmo" , amount : "236" } ]
85- ) ;
86-
87- if ( gasUsed ) {
112+ if ( isExperimental && skipSimulation ) {
113+ // Use a safe default gas when skipping simulation
114+ const defaultGas = "200000" ;
88115 await sendMsgs (
89116 window . keplr ,
90- OsmosisChainInfo ,
117+ selectedChain . info as any ,
91118 key . bech32Address ,
92119 [ protoMsgs ] ,
93120 {
94121 amount : [ { denom : "uosmo" , amount : "236" } ] ,
95- gas : Math . floor ( gasUsed * 1.5 ) . toString ( ) ,
96- }
122+ gas : defaultGas ,
123+ } ,
97124 ) ;
125+ } else {
126+ const gasUsed = await simulateMsgs (
127+ selectedChain . info as any ,
128+ key . bech32Address ,
129+ [ protoMsgs ] ,
130+ [ { denom : "uosmo" , amount : "236" } ] ,
131+ ) ;
132+
133+ if ( gasUsed ) {
134+ await sendMsgs (
135+ window . keplr ,
136+ selectedChain . info as any ,
137+ key . bech32Address ,
138+ [ protoMsgs ] ,
139+ {
140+ amount : [ { denom : "uosmo" , amount : "236" } ] ,
141+ gas : Math . floor ( gasUsed * 1.5 ) . toString ( ) ,
142+ } ,
143+ ) ;
144+ }
98145 }
99146 } catch ( e ) {
100147 if ( e instanceof Error ) {
@@ -105,16 +152,43 @@ export const OsmosisTab: React.FC = () => {
105152 } ;
106153
107154 useEffect ( ( ) => {
108- init ( ) ;
109- } , [ ] ) ;
155+ void init ( ) ;
156+ // Reset displayed data on chain change
157+ setAddress ( "" ) ;
158+ setBalance ( "" ) ;
159+ } , [ init ] ) ;
110160
111161 return (
112162 < >
113163 < h2 style = { { marginTop : "30px" } } >
114- Request to Osmosis Testnet via Keplr Provider
164+ Request to { selectedChain . label } via Keplr Provider
115165 </ h2 >
116166
117167 < div className = "item-container" >
168+ { isExperimental && (
169+ < div className = "item" >
170+ < div className = "item-title" > Switch Cosmos Chain</ div >
171+ < div className = "item-content" >
172+ < div style = { { display : "flex" , flexDirection : "column" } } >
173+ Chain:
174+ < select
175+ value = { targetChainId }
176+ onChange = { ( e ) => setTargetChainId ( e . target . value ) }
177+ >
178+ { chains . map ( ( c ) => (
179+ < option key = { c . id } value = { c . id } >
180+ { c . label }
181+ </ option >
182+ ) ) }
183+ </ select >
184+ </ div >
185+ < div style = { { marginTop : "10px" } } >
186+ Current Chain ID: { selectedChain . id }
187+ </ div >
188+ </ div >
189+ </ div >
190+ ) }
191+
118192 < div className = "item" >
119193 < div className = "item-title" > Get OSMO Address</ div >
120194
@@ -172,6 +246,55 @@ export const OsmosisTab: React.FC = () => {
172246 />
173247 </ div >
174248
249+ { isExperimental && (
250+ < div style = { { display : "flex" , flexDirection : "column" , gap : 6 } } >
251+ < div style = { { fontSize : 14 , opacity : 0.9 } } >
252+ Skip gas simulation
253+ </ div >
254+ < div
255+ role = "switch"
256+ aria-checked = { skipSimulation }
257+ onClick = { ( ) => setSkipSimulation ( ( v ) => ! v ) }
258+ style = { {
259+ display : "inline-flex" ,
260+ alignItems : "center" ,
261+ gap : 8 ,
262+ cursor : "pointer" ,
263+ userSelect : "none" ,
264+ } }
265+ >
266+ < div
267+ style = { {
268+ position : "relative" ,
269+ width : 44 ,
270+ height : 24 ,
271+ borderRadius : 9999 ,
272+ background : skipSimulation ? "#4ade80" : "#cbd5e1" ,
273+ transition : "background-color 150ms ease" ,
274+ boxShadow : "inset 0 0 0 1px rgba(0,0,0,0.05)" ,
275+ } }
276+ >
277+ < span
278+ style = { {
279+ position : "absolute" ,
280+ top : 2 ,
281+ left : skipSimulation ? 22 : 2 ,
282+ width : 20 ,
283+ height : 20 ,
284+ borderRadius : 9999 ,
285+ background : "#fff" ,
286+ boxShadow : "0 1px 3px rgba(0,0,0,0.2)" ,
287+ transition : "left 150ms ease" ,
288+ } }
289+ />
290+ </ div >
291+ < span style = { { fontSize : 12 , color : "#334155" } } >
292+ { skipSimulation ? "On" : "Off" }
293+ </ span >
294+ </ div >
295+ </ div >
296+ ) }
297+
175298 < button className = "keplr-button" onClick = { sendBalance } >
176299 Send
177300 </ button >
0 commit comments