Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bridge history #332

Merged
merged 8 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions packages/background/src/keyring/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,16 @@ export class KeyRingService {
const ethereumKeyFeatures =
await this.chainsService.getChainEthereumKeyFeatures(chainId);

const isEvm =
(await this.chainsService.getChainInfo(chainId)).features?.includes(
"evm"
) ?? false;

if (ethereumKeyFeatures.address || ethereumKeyFeatures.signing) {
// Check the comment on the method itself.
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
if (!isEvm) {
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
}
}

return this.keyRing.getKey(
Expand Down Expand Up @@ -308,10 +315,16 @@ export class KeyRingService {
const coinType = await this.chainsService.getChainCoinType(chainId);
const ethereumKeyFeatures =
await this.chainsService.getChainEthereumKeyFeatures(chainId);
const isEvm =
(await this.chainsService.getChainInfo(chainId)).features?.includes(
"evm"
) ?? false;

if (ethereumKeyFeatures.address || ethereumKeyFeatures.signing) {
// Check the comment on the method itself.
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
if (!isEvm) {
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
}
}

const key = await this.keyRing.getKey(
Expand Down Expand Up @@ -476,10 +489,16 @@ export class KeyRingService {
const coinType = await this.chainsService.getChainCoinType(chainId);
const ethereumKeyFeatures =
await this.chainsService.getChainEthereumKeyFeatures(chainId);
const isEvm =
(await this.chainsService.getChainInfo(chainId)).features?.includes(
"evm"
) ?? false;

if (ethereumKeyFeatures.address || ethereumKeyFeatures.signing) {
// Check the comment on the method itself.
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
if (!isEvm) {
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
}
}

const key = await this.keyRing.getKey(
Expand Down Expand Up @@ -567,10 +586,16 @@ export class KeyRingService {
const coinType = await this.chainsService.getChainCoinType(chainId);
const ethereumKeyFeatures =
await this.chainsService.getChainEthereumKeyFeatures(chainId);
const isEvm =
(await this.chainsService.getChainInfo(chainId)).features?.includes(
"evm"
) ?? false;

if (ethereumKeyFeatures.address || ethereumKeyFeatures.signing) {
// Check the comment on the method itself.
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
if (!isEvm) {
this.keyRing.throwErrorIfEthermintWithLedgerButNotSupported(chainId);
}
}

const key = await this.keyRing.getKey(
Expand Down
5 changes: 5 additions & 0 deletions packages/fetch-extension/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import { PropsalVoteStatus } from "./pages/proposals/proposal-vote-status";
import { FetchnameService } from "./pages/fetch-name-service";
import { DomainDetails } from "./pages/fetch-name-service/domain-details";
import { BridgePage } from "./pages/bridge";
import { BridgeHistoryView } from "./pages/bridge/bridge-history";

window.keplr = new Keplr(
manifest.version,
Expand Down Expand Up @@ -211,6 +212,10 @@ ReactDOM.render(
element={<IBCTransferPage />}
/>
<Route path="/bridge" element={<BridgePage />} />
<Route
path="/bridge-history"
element={<BridgeHistoryView />}
/>
<Route path="/setting" element={<SettingPage />} />
<Route
path="/keystone/import-pubkey"
Expand Down
2 changes: 1 addition & 1 deletion packages/fetch-extension/src/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@
"sign.list.message.wasm.button.close": "Close",
"sign.list.message.wasm/MsgInstantiateContract.title": "Instantiate Wasm Contract",
"sign.list.message.wasm/MsgInstantiateContract.content": "Instantiate code ID <b>{codeId}</b> contract with<only-admin-exist> <b>{admin}</b> admin account and</only-admin-exist> <b>{label}</b> label<only-funds-exist> by funding <b>{funds}</b></only-funds-exist>",
"sign.list.message.wasm/MsgExecuteContract.title": "Execute Wasm Contract",
"sign.list.message.wasm/MsgExecuteContract.title": "Execute Contract",
"sign.list.message.wasm/MsgExecuteContract.content": "Execute contract <b>{address}</b><only-sent-exist> by sending <b>{sent}</b></only-sent-exist>",
"sign.list.message.wasm/MsgExecuteContract.content.badge.secret-wasm": "Encrypted",
"sign.list.message.wasm/MsgExecuteContract.content.warning.secret-wasm.failed-decryption": "Failed to decrypt Secret message. This may be due to Fetch Wallet viewing key not matching the transaction viewing key.",
Expand Down
209 changes: 209 additions & 0 deletions packages/fetch-extension/src/pages/bridge/bridge-history.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { HeaderLayout } from "@layouts/header-layout";
import React, { FunctionComponent } from "react";
import { useNavigate } from "react-router";
import { observer } from "mobx-react-lite";
import style from "./style.module.scss";
import { useStore } from "../../stores";
import { FormattedMessage } from "react-intl";
import { CoinPretty } from "@keplr-wallet/unit";
import { BridgeHistory } from "@keplr-wallet/stores";
import { Bech32Address } from "@keplr-wallet/cosmos";
import { Button } from "reactstrap";
import restartIcon from "@assets/icon/undo.png";

export const proposalOptions = {
ProposalActive: "PROPOSAL_STATUS_VOTING_PERIOD",
ProposalPassed: "PROPOSAL_STATUS_PASSED",
ProposalRejected: "PROPOSAL_STATUS_REJECTED",
ProposalFailed: "PROPOSAL_STATUS_FAILED",
};

const FETCHSTATION_TXN_URL =
"https://fetchstation.azoyalabs.com/mainnet/explorer/transactions/";
const ETHERSCAN_TXN_URL = "https://etherscan.io/tx/";

export const BridgeHistoryView: FunctionComponent = observer(() => {
const navigate = useNavigate();

const { chainStore, accountStore, queriesStore } = useStore();
const accountInfo = accountStore.getAccount(chainStore.current.chainId);

const isEvm = chainStore.current.features?.includes("evm") ?? false;
const currentQueriesStore = queriesStore.get(chainStore.current.chainId);

const currentChainBridgeHistoryQuery = isEvm
? currentQueriesStore.evm.queryBridgeHistory
: currentQueriesStore.cosmwasm.queryBridgeHistory;
const bridgeHistory = currentChainBridgeHistoryQuery.getBridgeHistory(
accountInfo.bech32Address
);

return (
<HeaderLayout
showChainName={false}
canChangeChainInfo={false}
alternativeTitle={"Bridge History"}
onBackButton={() => {
navigate(-1);
}}
showBottomMenu={false}
rightRenderer={
<img
src={restartIcon}
className={style["refresh"]}
onClick={(e) => {
e.preventDefault();

bridgeHistory.fetch();
}}
/>
}
>
<div className={style["proposalContainer"]}>
{bridgeHistory.isFetching ? (
<div className={style["loaderScreen"]}>
<i className="fa fa-spinner fa-spin fa-fw" />
</div>
) : bridgeHistory.history.length === 0 ? (
<div className={style["loaderScreen"]}>
<p>
<FormattedMessage id="search.no-result-found" />
</p>
</div>
) : (
bridgeHistory.history
.reverse()
.map((history) => (
<BridgeStatus key={history.swapId} history={history} />
))
)}
</div>
</HeaderLayout>
);
});

const BridgeStatus: FunctionComponent<{ history: BridgeHistory }> = observer(
({ history }) => {
const { chainStore, queriesStore } = useStore();
const isEvm = chainStore.current.features?.includes("evm") ?? false;
const currentQueriesStore = queriesStore.get(chainStore.current.chainId);

const counterChainSwapStatusQuery = isEvm
? currentQueriesStore.cosmwasm.queryBridgeReverseSwapHash
: currentQueriesStore.evm.queryBridgeReverseSwapHash;
const reverseSwapHash = counterChainSwapStatusQuery.getReverseSwapHash(
history.swapId
);
const fetCurrency = chainStore.current.currencies.find(
(c) => c.coinDenom === "FET"
);

return (
<div className={style["bHistory"]}>
<div className={style["hContent"]}>
<p className={style["sId"]}>{`Swap id #${history.swapId}`}</p>
<p className={style["hTitle"]}>{`Send ${
fetCurrency
? new CoinPretty(fetCurrency, history.amount)
.maxDecimals(2)
.toString()
: "0 FET"
}`}</p>
<p className={style["hDesc"]}>
To: {Bech32Address.shortenAddress(history.to, 22, !isEvm)}
</p>
</div>

<div className={style["hStatus"]}>
{reverseSwapHash.isFetching ? (
<i
style={{ position: "absolute", top: "35%" }}
className="fa fa-spinner fa-spin fa-fw"
/>
) : (
<div>
<Button
disabled={!history.transactionHash}
color="darkgrey"
className={style["statusButton"]}
onClick={() => {
window.open(
`${isEvm ? ETHERSCAN_TXN_URL : FETCHSTATION_TXN_URL}${
history.transactionHash
}`,
"_blank",
"noreferrer"
);
}}
>
{isEvm ? (
<img
draggable={false}
src={require("@assets/img/ethereum.svg")}
className={style["ethLogo"]}
/>
) : (
<img
draggable={false}
src={require("@assets/svg/fetch_logo_black.svg")}
className={style["fetLogo"]}
/>
)}
<img
draggable={false}
className={style["status"]}
src={require("@assets/svg/" +
`${
history.transactionHash ? "gov-tick.svg" : "gov-clock.svg"
}`)}
/>
</Button>
<img
className={style["arrowIcon"]}
src={require("@assets/svg/arrow-right-outline.svg")}
alt=""
draggable={false}
/>
<Button
disabled={!reverseSwapHash.hash}
color="darkgrey"
className={style["statusButton"]}
onClick={() => {
window.open(
`${!isEvm ? ETHERSCAN_TXN_URL : FETCHSTATION_TXN_URL}${
reverseSwapHash.hash
}`,
"_blank",
"noreferrer"
);
}}
>
{!isEvm ? (
<img
draggable={false}
src={require("@assets/img/ethereum.svg")}
className={style["ethLogo"]}
/>
) : (
<img
draggable={false}
src={require("@assets/svg/fetch_logo_black.svg")}
className={style["fetLogo"]}
/>
)}
<img
draggable={false}
className={style["status"]}
src={require("@assets/svg/" +
`${
reverseSwapHash.hash ? "gov-tick.svg" : "gov-clock.svg"
}`)}
/>
</Button>
</div>
)}
</div>
</div>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ export const Configure: FunctionComponent<{
</div>
<div className={style["formInnerContainer"]}>
<AddressInput
label={intl.formatMessage({
id: "send.input.recipient",
})}
label="Recipient (Fetchhub address)"
recipientConfig={recipientConfig}
disableAddressBook
value={""}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ export const FetchhubBridge: FunctionComponent<{
</div>
<div className={style["formInnerContainer"]}>
<AddressInput
label={intl.formatMessage({
id: "send.input.recipient",
})}
label={"Recipient (Ethereum address)"}
recipientConfig={nativeBridgeConfig.recipientConfig}
// memoConfig={nativeBridgeConfig.memoConfig}
// ibcChannelConfig={nativeBridgeConfig.channelConfig}
Expand Down
15 changes: 15 additions & 0 deletions packages/fetch-extension/src/pages/bridge/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EthereumBridge } from "./ethereum-bridge";
import { FetchhubBridge } from "./fetchhub-bridge";
import { HeaderLayout } from "@layouts/header-layout";
import { Dec, IntPretty } from "@keplr-wallet/unit";
import { Button } from "reactstrap";

export const BridgePage: FunctionComponent = observer(() => {
const { chainStore, queriesStore } = useStore();
Expand Down Expand Up @@ -46,6 +47,20 @@ export const BridgePage: FunctionComponent = observer(() => {
onBackButton={() => {
navigate(-1);
}}
rightRenderer={
<Button
className={style["historyBtn"]}
color="primary"
outline
onClick={(e) => {
e.preventDefault();

navigate("/bridge-history");
}}
>
History
</Button>
}
>
{isLoading ? (
<p className={style["loaderScreen"]}>
Expand Down
Loading
Loading