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

UI Improvements #37

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f9477b2
big split of transfer form
alistair-singh Oct 15, 2024
650db4d
fix build
alistair-singh Oct 20, 2024
d6661f6
refactored out transfer form
alistair-singh Oct 20, 2024
8fc1d48
fixes
alistair-singh Oct 20, 2024
847ee7b
wireframe new transfer flow
alistair-singh Oct 21, 2024
23b1531
save
alistair-singh Oct 25, 2024
f4650ea
fixed ofac validations
alistair-singh Nov 1, 2024
bcdc9fb
wizard flow fixed
alistair-singh Nov 1, 2024
7f57152
refactored onSubmit util to sendToken hook
alistair-singh Nov 1, 2024
fbcddc5
multi step
alistair-singh Nov 3, 2024
6b9e7d0
remove redundant type
alistair-singh Nov 3, 2024
77a1680
fix failures
alistair-singh Nov 3, 2024
b081fb6
error and busy forms
alistair-singh Nov 3, 2024
16dc060
add transfer summary
alistair-singh Nov 3, 2024
695a832
deposit and approve erc20 spend
alistair-singh Nov 4, 2024
67f4763
remove unused file
alistair-singh Nov 4, 2024
cf9912c
complete transfer component layout
alistair-singh Nov 5, 2024
1ef92a1
refactor out links
alistair-singh Nov 5, 2024
114c62c
add success/failed tx links
alistair-singh Nov 5, 2024
c2b1c5a
finish erc20 approve and weth deposit
alistair-singh Nov 5, 2024
3ed6e2b
layout fixes
alistair-singh Nov 5, 2024
835bcca
refresh only
alistair-singh Nov 5, 2024
31b4a51
fix fee displays
alistair-singh Nov 6, 2024
7b93a5a
non technical error messages
alistair-singh Nov 6, 2024
c19fc36
add native token fee display
alistair-singh Nov 6, 2024
ea63106
fix put in for existential deposit
alistair-singh Nov 7, 2024
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
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ On the `.env`-file, configure of your project by assigning the values that fit y
**NEXT_PUBLIC_SNOWBRIDGE_ENV** accepts:

- `local_e2e` for local environment.
- `rococo_sepolia` for Rococo <=> Sepolia bridge.
- `paseo_sepolia` for Paseo <=> Sepolia bridge.
- `westend_sepolia` for Westend <=> Sepolia bridge.
- `polkadot_mainnet` for Polkadot <=> Ethereum bridge.

```env
NEXT_PUBLIC_SNOWBRIDGE_ENV=rococo_sepolia
NEXT_PUBLIC_SNOWBRIDGE_ENV=paseo_sepolia
```

If you are not using local chains, create an `.env.local` to set the required A.P.I. keys.
Expand Down
116 changes: 47 additions & 69 deletions app/history/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,43 +54,16 @@ import {
} from "lucide-react";
import { useRouter } from "next/navigation";
import { Suspense, useEffect, useMemo, useState } from "react";
import {
etherscanAddressLink,
etherscanERC20TokenLink,
etherscanTxHashLink,
subscanAccountLink,
subscanEventLink,
subscanExtrinsicLink,
} from "@/lib/explorerLinks";

const ITEMS_PER_PAGE = 5;
const EXPLORERS: { [env: string]: { [explorer: string]: string } } = {
local_e2e: {
etherscan: "https://no-expolorers-for-local-e2e/",
subscan_ah: "https://no-expolorers-for-local-e2e/",
subscan_bh: "https://no-expolorers-for-local-e2e/",
polkadot_js_kilt: "https://no-expolorers-for-westend/",
},
rococo_sepolia: {
etherscan: "https://sepolia.etherscan.io/",
subscan_ah: "https://assethub-rococo.subscan.io/",
subscan_bh: "https://bridgehub-rococo.subscan.io/",
polkadot_js_kilt:
"https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frilt.kilt.io/",
},
paseo_sepolia: {
etherscan: "https://sepolia.etherscan.io/",
subscan_ah: "https://assethub-paseo.subscan.io/",
subscan_bh: "https://bridgehub-paseo.subscan.io/",
polkadot_js_kilt:
"https://polkadot.js.org/apps/?rpc=wss://peregrine.kilt.io/parachain-public-ws/",
},
polkadot_mainnet: {
etherscan: "https://etherscan.io/",
subscan_ah: "https://assethub-polkadot.subscan.io/",
subscan_bh: "https://bridgehub-polkadot.subscan.io/",
subscan_kilt: "https://spiritnet.subscan.io/",
},
westend_sepolia: {
etherscan: "https://sepolia.etherscan.io/",
subscan_ah: "https://assethub-westend.subscan.io/",
subscan_bh: "https://bridgehub-westend.subscan.io/",
polkadot_js_kilt: "https://no-expolorers-for-westend/",
},
};

const isWalletTransaction = (
polkadotAccounts: WalletAccount[] | null,
ethereumAccounts: string[] | null,
Expand Down Expand Up @@ -141,36 +114,29 @@ const getDestinationTokenByAddress = (
);
};

const etherscanEventLink = (baseUrl: string, txHash: string): string => {
const slash = baseUrl.endsWith("/") ? "" : "/";
return `${baseUrl}${slash}tx/${txHash}`;
};

const subscanEventLink = (baseUrl: string, eventIndex: string): string => {
const block = eventIndex.split("-")[0];
const slash = baseUrl.endsWith("/") ? "" : "/";
return `${baseUrl}${slash}block/${block}?event=${eventIndex}&tab=event`;
};

const getExplorerLinks = (
env: environment.SnowbridgeEnvironment,
transfer: Transfer,
destination?: environment.TransferLocation,
) => {
const urls = EXPLORERS[env.name];
const links: { text: string; url: string }[] = [];
if (destination?.type == "ethereum") {
const ethTransfer = transfer as history.ToEthereumTransferResult;
links.push({
text: "Submitted to Asset Hub",
url: `${urls["subscan_ah"]}extrinsic/${ethTransfer.submitted.extrinsic_index}`,
url: subscanExtrinsicLink(
env.name,
"ah",
ethTransfer.submitted.extrinsic_index,
),
});

if (ethTransfer.bridgeHubXcmDelivered) {
links.push({
text: "Bridge Hub received XCM from Asset Hub",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
ethTransfer.bridgeHubXcmDelivered.event_index,
),
});
Expand All @@ -179,7 +145,8 @@ const getExplorerLinks = (
links.push({
text: "Message delivered to Snowbridge Message Queue",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
ethTransfer.bridgeHubChannelDelivered.event_index,
),
});
Expand All @@ -188,7 +155,8 @@ const getExplorerLinks = (
links.push({
text: "Message queued on Asset Hub Channel",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
ethTransfer.bridgeHubMessageQueued.event_index,
),
});
Expand All @@ -197,25 +165,26 @@ const getExplorerLinks = (
links.push({
text: "Message accepted by Asset Hub Channel",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
ethTransfer.bridgeHubMessageAccepted.event_index,
),
});
}
if (ethTransfer.ethereumBeefyIncluded) {
links.push({
text: "Message included by beefy client",
url: etherscanEventLink(
urls["etherscan"],
url: etherscanTxHashLink(
env.name,
ethTransfer.ethereumBeefyIncluded.transactionHash,
),
});
}
if (ethTransfer.ethereumMessageDispatched) {
links.push({
text: "Message dispatched on Ethereum",
url: etherscanEventLink(
urls["etherscan"],
url: etherscanTxHashLink(
env.name,
ethTransfer.ethereumMessageDispatched.transactionHash,
),
});
Expand All @@ -225,17 +194,15 @@ const getExplorerLinks = (
const dotTransfer = transfer as history.ToPolkadotTransferResult;
links.push({
text: "Submitted to Snowbridge Gateway",
url: etherscanEventLink(
urls["etherscan"],
dotTransfer.submitted.transactionHash,
),
url: etherscanTxHashLink(env.name, dotTransfer.submitted.transactionHash),
});

if (dotTransfer.beaconClientIncluded) {
links.push({
text: "Included by light client on Bridge Hub",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
dotTransfer.beaconClientIncluded.event_index,
),
});
Expand All @@ -244,7 +211,8 @@ const getExplorerLinks = (
links.push({
text: "Inbound message received on Asset Hub channel",
url: subscanEventLink(
urls["subscan_bh"],
env.name,
"bh",
dotTransfer.inboundMessageReceived.event_index,
),
});
Expand All @@ -253,7 +221,8 @@ const getExplorerLinks = (
links.push({
text: "Message dispatched on Asset Hub",
url: subscanEventLink(
urls["subscan_ah"],
env.name,
"ah",
dotTransfer.assetHubMessageProcessed.event_index,
),
});
Expand Down Expand Up @@ -342,7 +311,6 @@ const transferDetail = (
assetErc20Metadata: { [token: string]: assets.ERC20Metadata },
): JSX.Element => {
const destination = getEnvDetail(transfer, env);
const urls = EXPLORERS[env.name];
const links: { text: string; url: string }[] = getExplorerLinks(
env,
transfer,
Expand All @@ -357,15 +325,25 @@ const transferDetail = (
if (beneficiary.length === 66) {
beneficiary = encodeAddress(beneficiary, ss58Format);
}
const tokenUrl = `${urls["etherscan"]}token/${transfer.info.tokenAddress}`;
const tokenUrl = etherscanERC20TokenLink(
env.name,
transfer.info.tokenAddress,
);
let sourceAccountUrl;
let beneficiaryAccountUrl;
if (destination?.paraInfo) {
sourceAccountUrl = `${urls["etherscan"]}address/${transfer.info.sourceAddress}`;
beneficiaryAccountUrl = `${urls["subscan_ah"]}/account/${beneficiary}`;
sourceAccountUrl = etherscanAddressLink(
env.name,
transfer.info.sourceAddress,
);
beneficiaryAccountUrl = subscanAccountLink(env.name, "ah", beneficiary);
} else {
sourceAccountUrl = `${urls["subscan_ah"]}/account/${transfer.info.sourceAddress}`;
beneficiaryAccountUrl = `${urls["etherscan"]}address/${beneficiary}`;
sourceAccountUrl = subscanAccountLink(
env.name,
"ah",
transfer.info.sourceAddress,
);
beneficiaryAccountUrl = etherscanAddressLink(env.name, beneficiary);
}
const { tokenName, amount } = formatTokenData(
transfer,
Expand Down
11 changes: 7 additions & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { TransferComponent } from "@/components/Transfer";
import { TransferComponent } from "@/components/transfer/TransferComponent";
import { ContextComponent } from "@/components/Context";
import { MaintenanceBanner } from "@/components/MainenanceBanner";

export default function Home() {
return (
<ContextComponent>
<TransferComponent />
</ContextComponent>
<MaintenanceBanner>
<ContextComponent>
<TransferComponent />
</ContextComponent>
</MaintenanceBanner>
);
}
9 changes: 6 additions & 3 deletions app/switch/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ContextComponent } from "@/components/Context";
import { MaintenanceBanner } from "@/components/MainenanceBanner";
import { SwitchComponent } from "@/components/Switch";

export default function Switch() {
return (
<ContextComponent>
<SwitchComponent />
</ContextComponent>
<MaintenanceBanner>
<ContextComponent>
<SwitchComponent />
</ContextComponent>
</MaintenanceBanner>
);
}
88 changes: 88 additions & 0 deletions components/BalanceDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { FC, useEffect, useRef, useState } from "react";
import { assets, environment } from "@snowbridge/api";
import { getTokenBalance } from "@/utils/balances";
import { formatBalance } from "@/utils/formatting";
import { useAtomValue } from "jotai";
import {
relayChainNativeAssetAtom,
snowbridgeContextAtom,
snowbridgeEnvironmentAtom,
} from "@/store/snowbridge";
import { polkadotAccountAtom } from "@/store/polkadot";
import { ethereumAccountAtom } from "@/store/ethereum";

interface BalanceDisplayProps {
source: environment.TransferLocation;
token: string;
displayDecimals: number;
tokenMetadata: assets.ERC20Metadata | null;
}

export const BalanceDisplay: FC<BalanceDisplayProps> = ({
source,
token,
tokenMetadata,
}) => {
const polkadotAccount = useAtomValue(polkadotAccountAtom);
const ethereumAccount = useAtomValue(ethereumAccountAtom);

const sourceAccount =
source.type == "ethereum"
? (ethereumAccount ?? undefined)
: polkadotAccount?.address;
const [balanceDisplay, setBalanceDisplay] = useState<string | null>(
"Fetching...",
);
const environment = useAtomValue(snowbridgeEnvironmentAtom);
const context = useAtomValue(snowbridgeContextAtom);
const request = useRef(0);

useEffect(() => {
if (!sourceAccount || !context || !tokenMetadata) return;
setBalanceDisplay("Fetching...");
request.current = request.current + 1;
const id = request.current;
getTokenBalance({
context,
token,
ethereumChainId: BigInt(environment.ethChainId),
source,
sourceAccount,
})
.then((result) => {
if (request.current !== id) return;
let allowance = "";
if (result.gatewayAllowance !== undefined) {
allowance = ` (Allowance: ${formatBalance({
number: result.gatewayAllowance ?? 0n,
decimals: Number(tokenMetadata.decimals),
})} ${tokenMetadata.symbol})`;
}
const nativeBalance = `${formatBalance({
number: result.nativeBalance,
decimals: result.nativeTokenDecimals,
})} ${result.nativeSymbol}`;
setBalanceDisplay(
`${nativeBalance} ; ${formatBalance({
number: result.balance,
decimals: Number(tokenMetadata.decimals),
})} ${tokenMetadata.symbol} ${allowance}`,
);
})
.catch((err) => {
if (request.current !== id) return;
console.error(err);
setBalanceDisplay(null);
});
}, [source, sourceAccount, token, context, environment, tokenMetadata]);
return (
<div
className={
"text-sm text-right text-muted-foreground px-1 " +
(sourceAccount !== null ? " visible" : " hidden")
}
>
Balances: {balanceDisplay ?? "Error"}
</div>
);
};
2 changes: 1 addition & 1 deletion components/ConnectEthereumWalletButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function ConnectEthereumWalletButton() {
<ErrorDialog
open={windowEthereumError !== null}
dismiss={() => {
console.log(windowEthereumError);
console.error(windowEthereumError);
setWindowEthereumError(null);
}}
title="Ethereum Wallet Error"
Expand Down
Loading