Skip to content

Commit

Permalink
Merge pull request #19 from dynamic-labs/fix/error-handling
Browse files Browse the repository at this point in the history
fix: add error handling
  • Loading branch information
matthew1809 authored Jul 10, 2024
2 parents ca93926 + 3571107 commit 209215f
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 7 deletions.
81 changes: 76 additions & 5 deletions packages/nextjs/app/safe/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { useAccount, useBalance, useReadContract } from "wagmi";
import { CheckCircleIcon } from "@heroicons/react/20/solid";
import { ClipboardIcon } from "@heroicons/react/24/outline";
import { ERC20_ABI } from "~~/lib/ABI";
import { TransactionDetails, getTransactionOnBaseSepoliaByHash } from "~~/lib/blockscout";
import {
TransactionDetails,
getTokenTransfersOnBaseSepolia,
getTransactionOnBaseSepoliaByHash,
getTransactionsOnBaseSepolia,
} from "~~/lib/blockscout";
import {
BASE_SEPOLIA_BLOCKSCOUT_TX_BASE_URL,
CROSSCHAIN_TRANSFER_CONTRACT_BASE_SEPOLIA,
Expand All @@ -33,7 +38,8 @@ const SafePage = () => {
const [safeAddress, setSafeAddress] = useState<string | null>("");

const [transactions, setTransactions] = useState<string[]>([]);
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails[]>([]); // TransactionDetails[
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails[]>([]);
const [transferDetails, setTransferDetails] = useState<TransactionDetails[]>([]);
const [refreshingTransactions, setRefreshingTransactions] = useState(false);
const [loading, setLoading] = useState(false);
const [transferAmount, setTransferAmount] = useState<number>(0);
Expand All @@ -49,6 +55,29 @@ const SafePage = () => {
chainId: chain?.id,
});

function extractAndDecodeHexString(input: string) {
// Regular expression to match a hexadecimal string
const hexPattern = /0x[0-9A-Fa-f]+/;

// Match the input string against the pattern
const match = input.match(hexPattern);

// Return the decoded hex string or null if no match is found
if (match) {
const hexString = match[0];
// Remove the '0x' prefix
const cleanedHexString = hexString.slice(2);
// Decode the hex string
let decodedString = "";
for (let i = 0; i < cleanedHexString.length; i += 2) {
decodedString += String.fromCharCode(parseInt(cleanedHexString.substr(i, 2), 16));
}
return decodedString;
} else {
return null;
}
}

const { data: safeUSDCBalance, refetch: refetchSafeUSDCBalance } = useReadContract({
abi: ERC20_ABI,
address: chain ? USDC_ADDRESS[chain?.id] : ("" as `0x${string}`),
Expand Down Expand Up @@ -112,6 +141,9 @@ const SafePage = () => {
BigInt(transferAmount * 10 ** 6),
recipientAddress,
);

notification.success("Crosschain transfer initiated successfully: " + txHash);
console.log("txHash", txHash);
setTransactions([...transactions, txHash]);
const transactionDetail = await getTransactionOnBaseSepoliaByHash(txHash);
setTransactionDetails([...transactionDetails, transactionDetail]);
Expand All @@ -127,11 +159,26 @@ const SafePage = () => {
setRefreshingTransactions(true);
setTransactionDetails([]);
const txDetails = [];

setTransferDetails([]);
const transferDetails = [];

if (!primaryWallet) return;
const transactions = await getTransactionsOnBaseSepolia(primaryWallet.address);
const transfers = await getTokenTransfersOnBaseSepolia(primaryWallet.address);

for (const txHash of transactions) {
const transactionDetail = await getTransactionOnBaseSepoliaByHash(txHash);
txDetails.push(transactionDetail);
setTransactionDetails(txDetails);
}

for (const transfer of transfers) {
const transactionDetail = await getTransactionOnBaseSepoliaByHash(transfer.tx_hash);
transferDetails.push(transactionDetail);
setTransferDetails(transferDetails);
}

setRefreshingTransactions(false);
};

Expand All @@ -157,14 +204,22 @@ const SafePage = () => {
BigInt(crossChainTransferAmount * 10 ** 6),
crossChainRecipientAddress,
);

notification.success("Crosschain transfer initiated successfully: " + txHash);
console.log("txHash", txHash);
setTransactions([...transactions, txHash]);
const transactionDetail = await getTransactionOnBaseSepoliaByHash(txHash);
setTransactionDetails([...transactionDetails, transactionDetail]);
setTransferDetails([...transactionDetails, transactionDetail]);
} catch (err) {
if (err instanceof Error) {
notification.error(err.message);
console.error(err.message);
const hasHexError = extractAndDecodeHexString((err as any).details);
if (hasHexError !== null) {
notification.error(hasHexError);
console.error(hasHexError);
} else {
notification.error((error as any).details);
console.error((error as any).details);
}
} else {
setError("Failed to transfer tokens.");
console.error(err);
Expand Down Expand Up @@ -214,6 +269,9 @@ const SafePage = () => {
<div className="flex flex-row items-center gap-2">
<CheckCircleIcon className="w-6 h-6" />
<div className="font-bold text-lg">Safe Smart Wallet deployed!</div>
<a className="btn btn-secondary" href="https://faucet.circle.com/" rel="noopener" target="_blank">
Fund it from Faucet
</a>
</div>
<div className="flex flex-row items-center gap-4">
<p>Address: {safeAddress}</p>
Expand Down Expand Up @@ -384,6 +442,19 @@ const SafePage = () => {
</div>
</div>
))}
{transferDetails.map(tx => (
<div className="flex flex-col gap-1" key={tx.hash}>
<div className="flex flex-row gap-8">
<a target="_blank" href={`${BASE_SEPOLIA_BLOCKSCOUT_TX_BASE_URL}/${tx.hash}`}>
<div className="flex flex-row gap-2 items-center">
{`${tx.hash.substring(0, 8)}...${tx.hash.substring(tx.hash.length - 8)}`}
<ExternalLinkIcon />
</div>
</a>
<div>{toMinsAgo(tx.timestamp)}</div>
</div>
</div>
))}
</div>
)}
</div>
Expand Down
12 changes: 12 additions & 0 deletions packages/nextjs/lib/blockscout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ export const getTransactionOnBaseSepoliaByHash = async (txHash: string): Promise
const response = await fetch(`https://base-sepolia.blockscout.com/api/v2/transactions/${txHash}`);
return response.json();
};

export const getTokenTransfersOnBaseSepolia = async (address: string) => {
const response = await fetch(`https://base-sepolia.blockscout.com/api/v2/addresses/${address}/token-transfers?type=`);
const json = await response.json();
return json.items;
};

export const getTransactionsOnBaseSepolia = async (address: string) => {
const response = await fetch(`https://base-sepolia.blockscout.com/api/v2/addresses/${address}/transactions`);
const json = await response.json();
return json.items;
};
2 changes: 1 addition & 1 deletion packages/nextjs/lib/dynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const sendTransaction = async (
amount: string,
wallet: Wallet,
networkConfigurations: NetworkConfigurationMap,
): Promise<string> => {
): Promise<string | undefined> => {
try {
const walletClient = wallet.connector.getWalletClient<WalletClient<Transport, Chain, Account>>();

Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/utils/scaffold-eth/notification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const ENUM_STATUSES = {
warning: <ExclamationTriangleIcon className="w-7 text-warning" />,
};

const DEFAULT_DURATION = 3000;
const DEFAULT_DURATION = 6000;
const DEFAULT_POSITION: ToastPosition = "top-center";

/**
Expand Down

0 comments on commit 209215f

Please sign in to comment.