diff --git a/package.json b/package.json
index 7b5cc21..43a563d 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
},
"dependencies": {
"@babel/core": "^7.0.0",
+ "@cosmjs/cosmwasm-stargate": "^0.29.4",
"@cosmjs/stargate": "0.26.5",
"@emotion/react": "11.6.0",
"@emotion/styled": "11.6.0",
diff --git a/public/seasy.png b/public/seasy.png
new file mode 100644
index 0000000..a65ce24
Binary files /dev/null and b/public/seasy.png differ
diff --git a/src/App.tsx b/src/App.tsx
index 65cf1d7..8b2daa4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,7 +4,7 @@ import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import { Breakpoint, BreakpointProvider } from "react-socks";
import { SecretNetworkClient } from "secretjs";
-import { chains, tokens, snips } from "./config";
+import { chains, tokens, snips, cw20s } from "./config";
import { faucetURL } from "./commons";
import "./index.css";
import { KeplrPanel } from "./KeplrStuff";
@@ -110,7 +110,7 @@ export default function App() {
const denoms = Array.from(
new Set(
- tokens.map((t) => t.withdrawals.map((w) => w.from_denom)).flat()
+ (tokens.concat(cw20s)).map((t) => t.withdrawals.map((w) => w.from_denom)).flat(),
)
);
@@ -298,6 +298,29 @@ export default function App() {
/>
))}
+
+
+ CW20s via IBC from JUNO
+
+ {cw20s.map((t) => (
+
+
+
+ ))}
("");
const [availableBalance, setAvailableBalance] = useState("");
const [loadingTx, setLoading] = useState(false);
- const [sourceCosmJs, setSourceCosmJs] =
- useState(null);
+ const sourceCosmJsRef = useRef<
+ SigningCosmWasmClient | SigningStargateClient | null>(null);
const [selectedChainIndex, setSelectedChainIndex] = useState(0);
const [fetchBalanceInterval, setFetchBalanceInterval] = useState(null);
const inputRef = useRef();
@@ -67,18 +68,32 @@ export default function Deposit({
chains[token.deposits[selectedChainIndex].source_chain_name].lcd
}/cosmos/bank/v1beta1/balances/${sourceAddress}`;
try {
- const {
- balances,
- }: {
- balances: Array<{ denom: string; amount: string }>;
- } = await (await fetch(url)).json();
-
- const balance =
- balances.find(
- (c) => c.denom === token.deposits[selectedChainIndex].from_denom
- )?.amount || "0";
-
- setAvailableBalance(balance);
+ if ((token.deposits[selectedChainIndex].from_denom).startsWith('juno1')) {
+ if (!sourceCosmJsRef.current) {
+ return;
+ }
+
+ const result = await (sourceCosmJsRef.current as SigningCosmWasmClient).queryContractSmart(
+ token.deposits[selectedChainIndex].from_denom,
+ {
+ balance: { address: sourceAddress },
+ }
+ );
+ setAvailableBalance(result.balance);
+ } else {
+ const {
+ balances,
+ }: {
+ balances: Array<{ denom: string; amount: string }>;
+ } = await (await fetch(url)).json();
+
+ const balance =
+ balances.find(
+ (c) => c.denom === token.deposits[selectedChainIndex].from_denom
+ )?.amount || "0";
+
+ setAvailableBalance(balance);
+ }
} catch (e) {
console.error(`Error while trying to query ${url}:`, e);
setAvailableBalance("Error");
@@ -99,7 +114,7 @@ export default function Deposit({
fetchSourceBalance(sourceAddress);
const interval = setInterval(
() => fetchSourceBalance(sourceAddress),
- 10_000
+ 2_000
);
setFetchBalanceInterval(interval);
@@ -135,12 +150,22 @@ export default function Deposit({
const sourceOfflineSigner = window.getOfflineSignerOnlyAmino(chain_id);
const depositFromAccounts = await sourceOfflineSigner.getAccounts();
setSourceAddress(depositFromAccounts[0].address);
- const cosmjs = await SigningStargateClient.connectWithSigner(
- rpc,
- sourceOfflineSigner,
- { prefix: bech32_prefix, broadcastPollIntervalMs: 10_000 }
- );
- setSourceCosmJs(cosmjs);
+
+ if ((token.deposits[selectedChainIndex].from_denom).startsWith('juno1')) {
+ const cosmjs = await SigningCosmWasmClient.connectWithSigner(
+ rpc,
+ sourceOfflineSigner,
+ { prefix: bech32_prefix, broadcastPollIntervalMs: 10_000 }
+ );
+ sourceCosmJsRef.current = cosmjs;
+ } else {
+ const cosmjs = await SigningStargateClient.connectWithSigner(
+ rpc,
+ sourceOfflineSigner,
+ { prefix: bech32_prefix, broadcastPollIntervalMs: 10_000 }
+ );
+ sourceCosmJsRef.current = cosmjs;
+ }
})();
}, [selectedChainIndex]);
@@ -343,7 +368,7 @@ export default function Deposit({
}}
loading={loadingTx}
onClick={async () => {
- if (!sourceCosmJs) {
+ if (!sourceCosmJsRef.current) {
console.error("No cosmjs");
return;
}
@@ -398,20 +423,45 @@ export default function Deposit({
)
) {
// Regular cosmos chain (not ethermint signing)
- const txResponse = await sourceCosmJs.sendIbcTokens(
- sourceAddress,
- secretAddress,
- {
- amount,
- denom: token.deposits[selectedChainIndex].from_denom,
- },
- "transfer",
- deposit_channel_id,
- undefined,
- Math.floor(Date.now() / 1000) + 10 * 60, // 10 minute timeout (sec)
- gasToFee(deposit_gas)
- );
- transactionHash = txResponse.transactionHash;
+ if ((token.deposits[selectedChainIndex].from_denom).startsWith('juno1')) {
+ if (!sourceCosmJsRef.current) {
+ return;
+ }
+
+ const txResponse = await (sourceCosmJsRef.current as SigningCosmWasmClient).execute(
+ sourceAddress,
+ token.deposits[selectedChainIndex].from_denom,
+ {
+ send: {
+ contract: "juno105pfttgc76dcrw44jl5067we23q7he85k5893aaaek09rdz0srlqydutp0", // ics20 on Juno (Juno<>Scrt)
+ amount: amount,
+ msg: Buffer.from(JSON.stringify({
+ channel: deposit_channel_id,
+ remote_address: secretAddress,
+ // 15 min
+ timeout: 900,
+ })).toString("base64"),
+ },
+ },
+ gasToFee(deposit_gas)
+ );
+ transactionHash = txResponse.transactionHash;
+ } else {
+ const txResponse = await (sourceCosmJsRef.current as SigningStargateClient).sendIbcTokens(
+ sourceAddress,
+ secretAddress,
+ {
+ amount,
+ denom: token.deposits[selectedChainIndex].from_denom,
+ },
+ "transfer",
+ deposit_channel_id,
+ undefined,
+ Math.floor(Date.now() / 1000) + 10 * 60, // 10 minute timeout (sec)
+ gasToFee(deposit_gas)
+ );
+ transactionHash = txResponse.transactionHash;
+ }
} else {
// Handle IBC transfers from Ethermint chains like Evmos & Injective
@@ -513,7 +563,7 @@ export default function Deposit({
// cosmjs can broadcast to Ethermint but cannot handle the response
// Broadcast the tx to Evmos
- sourceCosmJs.broadcastTx(txBytes);
+ sourceCosmJsRef.current.broadcastTx(txBytes);
transactionHash = toHex(sha256(txBytes));
}
diff --git a/src/config.ts b/src/config.ts
index fe5b6cf..0a6e3da 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -3,6 +3,8 @@ export type Token = {
name: string;
/** a snip20 token that's originated from Secret Network */
is_snip20?: boolean;
+ /** a cw20 token that's originated from Juno Network */
+ is_cw20?: boolean;
/** secret contract address of the token */
address: string;
/** secret contract code hash of the token */
@@ -798,6 +800,38 @@ export const snips: Token[] = [
}
];
+// These are cw20 tokens that are IBC compatible originating from Juno
+export const cw20s: Token[] = [
+ {
+ name: "SEASY",
+ is_cw20: true,
+ address: "secret17gg8xcx04ldqkvkrd7r9w60rdae4ck8aslt9cf", // TODO: update this
+ code_hash:
+ "5a085bd8ed89de92b35134ddd12505a602c7759ea25fb5c089ba03c8535b3042", // TODO: update this
+ image: "/seasy.png",
+ decimals: 6,
+ coingecko_id: "seasy",
+ deposits: [
+ {
+ source_chain_name: "Juno",
+ from_denom:
+ "juno19rqljkh95gh40s7qdx40ksx3zq5tm4qsmsrdz9smw668x9zdr3lqtg33mf", // SEASY cw20 address on Juno
+ channel_id: "channel-161",
+ gas: 350_000,
+ }
+ ],
+ withdrawals: [
+ {
+ target_chain_name: "Juno",
+ from_denom:
+ "ibc/70FF6A895E39F2EF8C679684215830B138B97222C615DADB8D8B0EDF9A27966F", // SEASY IBC asset denom on Secret
+ channel_id: "channel-40",
+ gas: 130_000,
+ },
+ ],
+ },
+];
+
export type Chain = {
/** display name of the chain */
chain_name: string;