mirror of
https://github.com/placeholder-soft/web.git
synced 2026-01-12 08:44:22 +08:00
add depositTo and withdrawTo functionality for smart contract wallets (#9)
* add depositTo and withdrawTo functionality for smart contract wallets * tx list updates * compliance check * fix prettier * fix prettier & address nits
This commit is contained in:
1
apps/bridge/public/icons/question-mark-circled.svg
Normal file
1
apps/bridge/public/icons/question-mark-circled.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M0.877075 7.49972C0.877075 3.84204 3.84222 0.876892 7.49991 0.876892C11.1576 0.876892 14.1227 3.84204 14.1227 7.49972C14.1227 11.1574 11.1576 14.1226 7.49991 14.1226C3.84222 14.1226 0.877075 11.1574 0.877075 7.49972ZM7.49991 1.82689C4.36689 1.82689 1.82708 4.36671 1.82708 7.49972C1.82708 10.6327 4.36689 13.1726 7.49991 13.1726C10.6329 13.1726 13.1727 10.6327 13.1727 7.49972C13.1727 4.36671 10.6329 1.82689 7.49991 1.82689ZM8.24993 10.5C8.24993 10.9142 7.91414 11.25 7.49993 11.25C7.08571 11.25 6.74993 10.9142 6.74993 10.5C6.74993 10.0858 7.08571 9.75 7.49993 9.75C7.91414 9.75 8.24993 10.0858 8.24993 10.5ZM6.05003 6.25C6.05003 5.57211 6.63511 4.925 7.50003 4.925C8.36496 4.925 8.95003 5.57211 8.95003 6.25C8.95003 6.74118 8.68002 6.99212 8.21447 7.27494C8.16251 7.30651 8.10258 7.34131 8.03847 7.37854L8.03841 7.37858C7.85521 7.48497 7.63788 7.61119 7.47449 7.73849C7.23214 7.92732 6.95003 8.23198 6.95003 8.7C6.95004 9.00376 7.19628 9.25 7.50004 9.25C7.8024 9.25 8.04778 9.00601 8.05002 8.70417L8.05056 8.7033C8.05924 8.6896 8.08493 8.65735 8.15058 8.6062C8.25207 8.52712 8.36508 8.46163 8.51567 8.37436L8.51571 8.37433C8.59422 8.32883 8.68296 8.27741 8.78559 8.21506C9.32004 7.89038 10.05 7.35382 10.05 6.25C10.05 4.92789 8.93511 3.825 7.50003 3.825C6.06496 3.825 4.95003 4.92789 4.95003 6.25C4.95003 6.55376 5.19628 6.8 5.50003 6.8C5.80379 6.8 6.05003 6.55376 6.05003 6.25Z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
35
apps/bridge/src/components/BridgeToInput/BridgeToInput.tsx
Normal file
35
apps/bridge/src/components/BridgeToInput/BridgeToInput.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
type BridgeToInputProps = {
|
||||
bridgeTo: string;
|
||||
setBridgeTo: Dispatch<SetStateAction<string>>;
|
||||
action: 'deposit' | 'withdraw';
|
||||
};
|
||||
|
||||
export function BridgeToInput({ bridgeTo, setBridgeTo, action }: BridgeToInputProps) {
|
||||
function handleChangeBridgeTo(e: { target: { value: SetStateAction<string> } }) {
|
||||
setBridgeTo(e.target.value);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col p-6">
|
||||
<div className="flex flex-row items-center space-x-2 mb-1">
|
||||
<span className="font-sans text-sm font-medium text-white">{action === 'deposit' ? 'Deposit to' : 'Withdraw to'}</span>
|
||||
<div className="has-tooltip">
|
||||
<span className="tooltip -mt-10 ml-6 rounded-lg bg-cds-background-gray-90 p-2 text-black shadow-lg">
|
||||
Only send funds on networks supported by your wallet provider if it is a smart contract
|
||||
wallet or there may be permanent loss of funds.
|
||||
</span>
|
||||
<Image alt="tooltip" src="/icons/question-mark-circled.svg" width={16} height={16} />
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
className="bg-transparent font-sans text-md text-white outline-none max-[640px]:grow sm:text-xl border border-cds-background-gray-60 p-4 rounded min-[640px]:w-96"
|
||||
placeholder="Wallet address"
|
||||
value={bridgeTo}
|
||||
onChange={handleChangeBridgeTo}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { BridgeButton } from 'apps/bridge/src/components/BridgeButton/BridgeButton';
|
||||
import { BridgeInput } from 'apps/bridge/src/components/BridgeInput/BridgeInput';
|
||||
import { BridgeToInput } from 'apps/bridge/src/components/BridgeToInput/BridgeToInput';
|
||||
import { ConnectWalletButton } from 'apps/bridge/src/components/ConnectWalletButton/ConnectWalletButton';
|
||||
import { DepositModal } from 'apps/bridge/src/components/DepositModal/DepositModal';
|
||||
import { FaqSidebar } from 'apps/bridge/src/components/Faq/FaqSidebar';
|
||||
@@ -11,14 +12,18 @@ import { getAssetListForChainEnv } from 'apps/bridge/src/utils/assets/getAssetLi
|
||||
import { useApproveContract } from 'apps/bridge/src/utils/hooks/useApproveContract';
|
||||
import { useChainEnv } from 'apps/bridge/src/utils/hooks/useChainEnv';
|
||||
import { useDisclosure } from 'apps/bridge/src/utils/hooks/useDisclosure';
|
||||
import { useGetCode } from 'apps/bridge/src/utils/hooks/useGetCode';
|
||||
import { useIsContractApproved } from 'apps/bridge/src/utils/hooks/useIsContractApproved';
|
||||
import { useIsPermittedToBridge } from 'apps/bridge/src/utils/hooks/useIsPermittedToBridge';
|
||||
import { useIsWalletConnected } from 'apps/bridge/src/utils/hooks/useIsWalletConnected';
|
||||
import { usePrepareERC20Deposit } from 'apps/bridge/src/utils/hooks/usePrepareERC20Deposit';
|
||||
import { usePrepareERC20DepositTo } from 'apps/bridge/src/utils/hooks/usePrepareERC20DepositTo';
|
||||
import { usePrepareETHDeposit } from 'apps/bridge/src/utils/hooks/usePrepareETHDeposit';
|
||||
import { utils } from 'ethers';
|
||||
import { parseUnits } from 'ethers/lib/utils.js';
|
||||
import getConfig from 'next/config';
|
||||
import { useAccount, useBalance, useContractWrite } from 'wagmi';
|
||||
import { useIsPermittedToBridgeTo } from 'apps/bridge/src/utils/hooks/useIsPermittedToBridgeTo';
|
||||
|
||||
const assetList = getAssetListForChainEnv();
|
||||
|
||||
@@ -29,6 +34,7 @@ export function DepositContainer() {
|
||||
const [depositAmount, setDepositAmount] = useState('0');
|
||||
const [L1ApproveTxHash, setL1ApproveTxHash] = useState<`0x${string}` | undefined>(undefined);
|
||||
const [L1DepositTxHash, setL1DepositTxHash] = useState<`0x${string}` | undefined>(undefined);
|
||||
const [depositTo, setDepositTo] = useState('');
|
||||
const [isApprovalTx, setIsApprovalTx] = useState(false);
|
||||
const isWalletConnected = useIsWalletConnected();
|
||||
const [selectedAsset, setSelectedAsset] = useState<Asset>(assetList[0]);
|
||||
@@ -36,6 +42,8 @@ export function DepositContainer() {
|
||||
publicRuntimeConfig.assets.split(',').includes(asset.L1symbol.toLowerCase()),
|
||||
);
|
||||
const { address } = useAccount();
|
||||
const codeAtAddress = useGetCode(address);
|
||||
const isSmartContractWallet = !!codeAtAddress && codeAtAddress !== '0x';
|
||||
|
||||
const { data: L1Balance } = useBalance({
|
||||
address,
|
||||
@@ -79,11 +87,15 @@ export function DepositContainer() {
|
||||
const chainEnv = useChainEnv();
|
||||
const isMainnet = chainEnv === 'mainnet';
|
||||
const includeTosVersionByte = isMainnet;
|
||||
const isPermittedToBridge = useIsPermittedToBridge();
|
||||
const isUserPermittedToBridge = useIsPermittedToBridge();
|
||||
const isPermittedToBridgeTo = useIsPermittedToBridgeTo(depositTo as `0x${string}`);
|
||||
const isPermittedToBridge = isSmartContractWallet
|
||||
? isUserPermittedToBridge && isPermittedToBridgeTo
|
||||
: isUserPermittedToBridge;
|
||||
|
||||
// deposit eth
|
||||
const depositETHConfig = usePrepareETHDeposit({
|
||||
userAddress: address,
|
||||
userAddress: isSmartContractWallet ? (depositTo as `0x${string}`) : address,
|
||||
depositAmount,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
@@ -99,8 +111,17 @@ export function DepositContainer() {
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
});
|
||||
const depositERC20ToConfig = usePrepareERC20DepositTo({
|
||||
asset: selectedAsset,
|
||||
to: depositTo as `0x${string}`,
|
||||
depositAmount,
|
||||
readApprovalResult,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
});
|
||||
|
||||
const { writeAsync: depositERC20Write } = useContractWrite(depositERC20Config);
|
||||
const { writeAsync: depositERC20ToWrite } = useContractWrite(depositERC20ToConfig);
|
||||
|
||||
const initiateApproval = useCallback(() => {
|
||||
void (async () => {
|
||||
@@ -118,7 +139,9 @@ export function DepositContainer() {
|
||||
|
||||
// next, call the transfer function
|
||||
setIsApprovalTx(false);
|
||||
const depositResult = await depositERC20Write?.();
|
||||
const depositResult = await (isSmartContractWallet
|
||||
? depositERC20ToWrite?.()
|
||||
: depositERC20Write?.());
|
||||
if (depositResult?.hash) {
|
||||
const depositTxHash = depositResult.hash;
|
||||
setL1DepositTxHash(depositTxHash);
|
||||
@@ -128,7 +151,14 @@ export function DepositContainer() {
|
||||
onCloseDepositModal();
|
||||
}
|
||||
})();
|
||||
}, [approveWrite, depositERC20Write, onCloseDepositModal, onOpenDepositModal, setIsApprovalTx]);
|
||||
}, [
|
||||
approveWrite,
|
||||
depositERC20ToWrite,
|
||||
depositERC20Write,
|
||||
isSmartContractWallet,
|
||||
onCloseDepositModal,
|
||||
onOpenDepositModal,
|
||||
]);
|
||||
|
||||
const initiateDeposit = useCallback(() => {
|
||||
void (async () => {
|
||||
@@ -136,9 +166,13 @@ export function DepositContainer() {
|
||||
try {
|
||||
// Only bridge on mainnet if user has accepted ToS. Always allow bridging on testnet.
|
||||
if (isPermittedToBridge) {
|
||||
const depositResult = await (selectedAsset.L1contract
|
||||
? depositERC20Write?.()
|
||||
: depositETHWrite?.());
|
||||
let depositMethod;
|
||||
if (selectedAsset.L1contract) {
|
||||
depositMethod = isSmartContractWallet ? depositERC20ToWrite : depositERC20Write;
|
||||
} else {
|
||||
depositMethod = depositETHWrite;
|
||||
}
|
||||
const depositResult = await depositMethod?.();
|
||||
if (depositResult?.hash) {
|
||||
const depositTxHash = depositResult.hash;
|
||||
setL1DepositTxHash(depositTxHash);
|
||||
@@ -155,25 +189,31 @@ export function DepositContainer() {
|
||||
onOpenDepositModal,
|
||||
isPermittedToBridge,
|
||||
selectedAsset.L1contract,
|
||||
isSmartContractWallet,
|
||||
depositERC20ToWrite,
|
||||
depositERC20Write,
|
||||
depositETHWrite,
|
||||
onCloseDepositModal,
|
||||
]);
|
||||
|
||||
let button;
|
||||
let depositDisabled;
|
||||
if (!isWalletConnected) {
|
||||
button = (
|
||||
<ConnectWalletButton className="text-md flex w-full items-center justify-center rounded-md p-4 font-sans font-bold uppercase sm:w-auto" />
|
||||
);
|
||||
} else if (readApprovalResult || selectedAsset.L1symbol === 'ETH') {
|
||||
depositDisabled =
|
||||
parseFloat(depositAmount) <= 0 ||
|
||||
parseFloat(depositAmount) >= parseFloat(L1Balance?.formatted ?? '0') ||
|
||||
depositAmount === '' ||
|
||||
(isSmartContractWallet && !utils.isAddress(depositTo ?? '')) ||
|
||||
!isPermittedToBridge;
|
||||
|
||||
button = (
|
||||
<BaseButton
|
||||
onClick={initiateDeposit}
|
||||
disabled={
|
||||
parseFloat(depositAmount) <= 0 ||
|
||||
parseFloat(depositAmount) >= parseFloat(L1Balance?.formatted ?? '0') ||
|
||||
depositAmount === ''
|
||||
}
|
||||
disabled={depositDisabled}
|
||||
toChainId={chainId}
|
||||
className="text-md flex w-full items-center justify-center rounded-md p-4 font-sans font-bold uppercase sm:w-auto"
|
||||
>
|
||||
@@ -181,10 +221,13 @@ export function DepositContainer() {
|
||||
</BaseButton>
|
||||
);
|
||||
} else {
|
||||
depositDisabled =
|
||||
(isSmartContractWallet && !utils.isAddress(depositTo ?? '')) || !isPermittedToBridge;
|
||||
|
||||
button = (
|
||||
<BridgeButton
|
||||
onClick={initiateApproval}
|
||||
disabled={false}
|
||||
disabled={depositDisabled}
|
||||
className="text-md flex w-full items-center justify-center rounded-md p-4 font-sans font-bold uppercase sm:w-auto"
|
||||
>
|
||||
Approval
|
||||
@@ -216,6 +259,10 @@ export function DepositContainer() {
|
||||
{button}
|
||||
</BridgeInput>
|
||||
|
||||
{isSmartContractWallet && (
|
||||
<BridgeToInput bridgeTo={depositTo} setBridgeTo={setDepositTo} action="deposit" />
|
||||
)}
|
||||
|
||||
<div className="border-t border-sidebar-border">
|
||||
<TransactionSummary
|
||||
header="TRANSACTION SUMMARY"
|
||||
@@ -225,7 +272,7 @@ export function DepositContainer() {
|
||||
chainId={publicRuntimeConfig.l1ChainID}
|
||||
isDeposit
|
||||
/>
|
||||
<div className="w-full py-12 px-6 sm:hidden">{button}</div>
|
||||
<div className="w-full px-6 py-12 sm:hidden">{button}</div>
|
||||
</div>
|
||||
</div>
|
||||
<FaqSidebar />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { BridgeInput } from 'apps/bridge/src/components/BridgeInput/BridgeInput';
|
||||
import { BridgeToInput } from 'apps/bridge/src/components/BridgeToInput/BridgeToInput';
|
||||
import { ConnectWalletButton } from 'apps/bridge/src/components/ConnectWalletButton/ConnectWalletButton';
|
||||
import { FaqSidebar } from 'apps/bridge/src/components/Faq/FaqSidebar';
|
||||
import { BaseButton } from 'apps/bridge/src/components/SwitchNetworkButton/SwitchNetworkButton';
|
||||
@@ -9,12 +10,16 @@ import { Asset } from 'apps/bridge/src/types/Asset';
|
||||
import { getAssetListForChainEnv } from 'apps/bridge/src/utils/assets/getAssetListForChainEnv';
|
||||
import { useChainEnv } from 'apps/bridge/src/utils/hooks/useChainEnv';
|
||||
import { useDisclosure } from 'apps/bridge/src/utils/hooks/useDisclosure';
|
||||
import { useGetCode } from 'apps/bridge/src/utils/hooks/useGetCode';
|
||||
import { useIsPermittedToBridge } from 'apps/bridge/src/utils/hooks/useIsPermittedToBridge';
|
||||
import { useIsWalletConnected } from 'apps/bridge/src/utils/hooks/useIsWalletConnected';
|
||||
import { usePrepareERC20Withdrawal } from 'apps/bridge/src/utils/hooks/usePrepareERC20Withdrawal';
|
||||
import { usePrepareERC20WithdrawalTo } from 'apps/bridge/src/utils/hooks/usePrepareERC20WithdrawalTo';
|
||||
import { usePrepareETHWithdrawal } from 'apps/bridge/src/utils/hooks/usePrepareETHWithdrawal';
|
||||
import { utils } from 'ethers';
|
||||
import getConfig from 'next/config';
|
||||
import { useAccount, useBalance, useContractWrite } from 'wagmi';
|
||||
import { useIsPermittedToBridgeTo } from 'apps/bridge/src/utils/hooks/useIsPermittedToBridgeTo';
|
||||
|
||||
const assetList = getAssetListForChainEnv();
|
||||
|
||||
@@ -24,6 +29,7 @@ const chainId = parseInt(publicRuntimeConfig.l2ChainID);
|
||||
export function WithdrawContainer() {
|
||||
const [withdrawAmount, setWithdrawAmount] = useState('');
|
||||
const [L2TxHash, setL2TxHash] = useState('');
|
||||
const [withdrawTo, setWithdrawTo] = useState('');
|
||||
const isWalletConnected = useIsWalletConnected();
|
||||
const activeAssets = assetList.filter((asset) =>
|
||||
publicRuntimeConfig.assets.split(',').includes(asset.L1symbol.toLowerCase()),
|
||||
@@ -31,6 +37,9 @@ export function WithdrawContainer() {
|
||||
const [selectedAsset, setSelectedAsset] = useState<Asset>(assetList[0]);
|
||||
|
||||
const { address } = useAccount();
|
||||
const codeAtAddress = useGetCode(address);
|
||||
const isSmartContractWallet = !!codeAtAddress && codeAtAddress !== '0x';
|
||||
|
||||
const { data: L2Balance } = useBalance({
|
||||
address,
|
||||
token: selectedAsset.L2contract,
|
||||
@@ -40,7 +49,11 @@ export function WithdrawContainer() {
|
||||
const chainEnv = useChainEnv();
|
||||
const isMainnet = chainEnv === 'mainnet';
|
||||
const includeTosVersionByte = isMainnet;
|
||||
const isPermittedToBridge = useIsPermittedToBridge();
|
||||
const isUserPermittedToBridge = useIsPermittedToBridge();
|
||||
const isPermittedToBridgeTo = useIsPermittedToBridgeTo(withdrawTo as `0x${string}`);
|
||||
const isPermittedToBridge = isSmartContractWallet
|
||||
? isUserPermittedToBridge && isPermittedToBridgeTo
|
||||
: isUserPermittedToBridge;
|
||||
|
||||
const erc20WithdrawalConfig = usePrepareERC20Withdrawal({
|
||||
asset: selectedAsset,
|
||||
@@ -48,10 +61,19 @@ export function WithdrawContainer() {
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
});
|
||||
const erc20WithdrawalToConfig = usePrepareERC20WithdrawalTo({
|
||||
asset: selectedAsset,
|
||||
to: withdrawTo as `0x${string}`,
|
||||
withdrawAmount,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
});
|
||||
|
||||
const { writeAsync: withdrawERC20 } = useContractWrite(erc20WithdrawalConfig);
|
||||
const { writeAsync: withdrawERC20To } = useContractWrite(erc20WithdrawalToConfig);
|
||||
|
||||
const withdrawConfig = usePrepareETHWithdrawal({
|
||||
userAddress: address,
|
||||
userAddress: isSmartContractWallet ? (withdrawTo as `0x${string}`) : address,
|
||||
withdrawAmount,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
@@ -75,9 +97,13 @@ export function WithdrawContainer() {
|
||||
try {
|
||||
// Only bridge on mainnet if user has accepted ToS. Always allow bridging on testnet.
|
||||
if (isPermittedToBridge) {
|
||||
const withdrawalResult = await (selectedAsset.L1contract
|
||||
? withdrawERC20?.()
|
||||
: withdraw?.());
|
||||
let withdrawMethod;
|
||||
if (selectedAsset.L1contract) {
|
||||
withdrawMethod = isSmartContractWallet ? withdrawERC20To : withdrawERC20;
|
||||
} else {
|
||||
withdrawMethod = withdraw;
|
||||
}
|
||||
const withdrawalResult = await withdrawMethod?.();
|
||||
if (withdrawalResult?.hash) {
|
||||
const withdrawalTxHsh = withdrawalResult.hash;
|
||||
setL2TxHash(withdrawalTxHsh);
|
||||
@@ -94,25 +120,32 @@ export function WithdrawContainer() {
|
||||
onOpenWithdrawModal,
|
||||
isPermittedToBridge,
|
||||
selectedAsset.L1contract,
|
||||
isSmartContractWallet,
|
||||
withdrawERC20To,
|
||||
withdrawERC20,
|
||||
withdraw,
|
||||
onCloseWithdrawModal,
|
||||
]);
|
||||
|
||||
let button;
|
||||
let withdrawDisabled;
|
||||
|
||||
if (!isWalletConnected) {
|
||||
button = (
|
||||
<ConnectWalletButton className="text-md flex w-full items-center justify-center rounded-md p-4 font-sans font-bold uppercase sm:w-auto" />
|
||||
);
|
||||
} else {
|
||||
withdrawDisabled =
|
||||
parseFloat(withdrawAmount) <= 0 ||
|
||||
parseFloat(withdrawAmount) >= parseFloat(L2Balance?.formatted ?? '0') ||
|
||||
withdrawAmount === '' ||
|
||||
(isSmartContractWallet && !utils.isAddress(withdrawTo ?? '')) ||
|
||||
!isPermittedToBridge;
|
||||
|
||||
button = (
|
||||
<BaseButton
|
||||
onClick={initiateWithdrawal}
|
||||
disabled={
|
||||
parseFloat(withdrawAmount) <= 0 ||
|
||||
parseFloat(withdrawAmount) >= parseFloat(L2Balance?.formatted ?? '0') ||
|
||||
withdrawAmount === ''
|
||||
}
|
||||
disabled={withdrawDisabled}
|
||||
toChainId={chainId}
|
||||
className="text-md flex w-full items-center justify-center rounded-md p-4 font-sans font-bold uppercase sm:w-auto"
|
||||
>
|
||||
@@ -142,6 +175,11 @@ export function WithdrawContainer() {
|
||||
>
|
||||
{button}
|
||||
</BridgeInput>
|
||||
|
||||
{isSmartContractWallet && (
|
||||
<BridgeToInput bridgeTo={withdrawTo} setBridgeTo={setWithdrawTo} action="withdraw" />
|
||||
)}
|
||||
|
||||
<div className="border-t border-sidebar-border">
|
||||
<TransactionSummary
|
||||
selectedAsset={selectedAsset}
|
||||
@@ -151,7 +189,7 @@ export function WithdrawContainer() {
|
||||
chainId={publicRuntimeConfig.l2ChainID}
|
||||
isDeposit={false}
|
||||
/>
|
||||
<div className="w-full py-12 px-6 sm:hidden">{button}</div>
|
||||
<div className="w-full px-6 py-12 sm:hidden">{button}</div>
|
||||
</div>
|
||||
</div>
|
||||
<FaqSidebar />
|
||||
|
||||
@@ -19,7 +19,7 @@ export const OFACContext = createContext<OFACContextType>({
|
||||
isOFACAllowedLoading: false,
|
||||
});
|
||||
|
||||
async function fetchIsAllowed(address?: `0x${string}`): Promise<{ result: boolean }> {
|
||||
export async function fetchIsAllowed(address?: `0x${string}`): Promise<{ result: boolean }> {
|
||||
if (!address) {
|
||||
return { result: false };
|
||||
}
|
||||
|
||||
@@ -4,29 +4,19 @@ export function mergeAndSortTransactionsLists(
|
||||
txList1: BridgeTransaction[],
|
||||
txList2: BridgeTransaction[],
|
||||
) {
|
||||
const merged = [];
|
||||
let index1 = 0;
|
||||
let index2 = 0;
|
||||
let current = 0;
|
||||
|
||||
while (current < txList1.length + txList2.length) {
|
||||
const isTxList1Depleted = index1 >= txList1.length;
|
||||
const isTxList2Depleted = index2 >= txList2.length;
|
||||
|
||||
if (
|
||||
!isTxList1Depleted &&
|
||||
(isTxList2Depleted ||
|
||||
parseInt(txList1[index1].blockTimestamp) < parseInt(txList2[index2].blockTimestamp))
|
||||
) {
|
||||
merged[current] = txList1[index1];
|
||||
index1 += 1;
|
||||
} else {
|
||||
merged[current] = txList2[index2];
|
||||
index2 += 1;
|
||||
return [...txList1, ...txList2].sort((a, b) => {
|
||||
if (a.blockTimestamp && b.blockTimestamp) {
|
||||
return parseInt(b.blockTimestamp) - parseInt(a.blockTimestamp);
|
||||
}
|
||||
|
||||
current += 1;
|
||||
}
|
||||
if (a.blockTimestamp) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return merged.reverse();
|
||||
if (b.blockTimestamp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
19
apps/bridge/src/utils/hooks/useGetCode.ts
Normal file
19
apps/bridge/src/utils/hooks/useGetCode.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { providers } from 'ethers';
|
||||
import { useProvider } from 'wagmi';
|
||||
|
||||
export function useGetCode(address?: `0x${string}`) {
|
||||
const provider = useProvider<providers.JsonRpcProvider>();
|
||||
const [code, setCode] = useState<string | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (address) {
|
||||
void (async () => {
|
||||
const codeAtAddress = await provider.getCode(address);
|
||||
setCode(codeAtAddress);
|
||||
})();
|
||||
}
|
||||
}, [address, provider]);
|
||||
|
||||
return code;
|
||||
}
|
||||
14
apps/bridge/src/utils/hooks/useIsPermittedToBridgeTo.ts
Normal file
14
apps/bridge/src/utils/hooks/useIsPermittedToBridgeTo.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { fetchIsAllowed } from 'apps/bridge/src/contexts/OFACContext';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
export function useIsPermittedToBridgeTo(address?: `0x${string}`) {
|
||||
const { data: isBridgeToAllowed, isLoading: isBridgeToAllowedLoading } = useQuery(
|
||||
['isBridgeToAllowed', address],
|
||||
async () => fetchIsAllowed(address),
|
||||
{
|
||||
select: (r) => r.result,
|
||||
},
|
||||
);
|
||||
|
||||
return !!isBridgeToAllowed && !isBridgeToAllowedLoading;
|
||||
}
|
||||
49
apps/bridge/src/utils/hooks/usePrepareERC20DepositTo.ts
Normal file
49
apps/bridge/src/utils/hooks/usePrepareERC20DepositTo.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import L1StandartBridge from 'apps/bridge/src/contract-abis/L1StandardBridge';
|
||||
import { Asset } from 'apps/bridge/src/types/Asset';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { parseUnits } from 'ethers/lib/utils.js';
|
||||
import getConfig from 'next/config';
|
||||
import { Address, usePrepareContractWrite } from 'wagmi';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
type UsePrepareERC20DepositToProps = {
|
||||
asset: Asset;
|
||||
to: `0x${string}`;
|
||||
depositAmount: string;
|
||||
readApprovalResult?: boolean;
|
||||
isPermittedToBridge: boolean;
|
||||
includeTosVersionByte: boolean;
|
||||
};
|
||||
|
||||
export function usePrepareERC20DepositTo({
|
||||
asset,
|
||||
to,
|
||||
depositAmount,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
}: UsePrepareERC20DepositToProps) {
|
||||
const { config: depositConfig } = usePrepareContractWrite({
|
||||
address:
|
||||
isPermittedToBridge && depositAmount !== ''
|
||||
? publicRuntimeConfig.l1BridgeProxyAddress
|
||||
: undefined,
|
||||
abi: L1StandartBridge,
|
||||
functionName: 'depositERC20To',
|
||||
chainId: parseInt(publicRuntimeConfig.l1ChainID),
|
||||
args: [
|
||||
asset.L1contract as Address,
|
||||
asset.L2contract as Address,
|
||||
to,
|
||||
depositAmount !== ''
|
||||
? parseUnits(depositAmount, asset.decimals)
|
||||
: parseUnits('0', asset.decimals),
|
||||
100000,
|
||||
includeTosVersionByte ? publicRuntimeConfig.tosVersion : '0x',
|
||||
],
|
||||
cacheTime: 0,
|
||||
staleTime: 1,
|
||||
overrides: { gasLimit: BigNumber.from(300000) },
|
||||
});
|
||||
return depositConfig;
|
||||
}
|
||||
38
apps/bridge/src/utils/hooks/usePrepareERC20WithdrawalTo.ts
Normal file
38
apps/bridge/src/utils/hooks/usePrepareERC20WithdrawalTo.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import L2StandardBridge from '@eth-optimism/contracts-bedrock/artifacts/contracts/L2/L2StandardBridge.sol/L2StandardBridge.json';
|
||||
import { Asset } from 'apps/bridge/src/types/Asset';
|
||||
import { parseUnits } from 'ethers/lib/utils.js';
|
||||
import getConfig from 'next/config';
|
||||
import { Address, usePrepareContractWrite } from 'wagmi';
|
||||
|
||||
const { publicRuntimeConfig } = getConfig();
|
||||
|
||||
type UsePrepareERC20WithdrawalTo = {
|
||||
asset: Asset;
|
||||
to: `0x${string}`;
|
||||
withdrawAmount: string;
|
||||
isPermittedToBridge: boolean;
|
||||
includeTosVersionByte: boolean;
|
||||
};
|
||||
|
||||
export function usePrepareERC20WithdrawalTo({
|
||||
asset,
|
||||
to,
|
||||
withdrawAmount,
|
||||
isPermittedToBridge,
|
||||
includeTosVersionByte,
|
||||
}: UsePrepareERC20WithdrawalTo) {
|
||||
const { config } = usePrepareContractWrite({
|
||||
address: isPermittedToBridge ? publicRuntimeConfig.L2StandardBridge : undefined,
|
||||
abi: L2StandardBridge.abi,
|
||||
functionName: 'withdrawTo',
|
||||
chainId: parseInt(publicRuntimeConfig.l2ChainID),
|
||||
args: [
|
||||
asset.L2contract as Address,
|
||||
to,
|
||||
parseUnits(withdrawAmount === '' ? '0' : withdrawAmount, asset.decimals),
|
||||
100000,
|
||||
includeTosVersionByte ? publicRuntimeConfig.tosVersion : '0x',
|
||||
],
|
||||
});
|
||||
return config;
|
||||
}
|
||||
@@ -39,24 +39,27 @@ export function explorerTxToBridgeDeposit(tx: BlockExplorerTransaction): BridgeT
|
||||
};
|
||||
}
|
||||
|
||||
const decodedWithdrawData = l1StandardBridgeInterface.decodeFunctionData(
|
||||
const functionName = l1StandardBridgeInterface.getFunction(tx.input.slice(0, 10)).name;
|
||||
const decodedDepositData = l1StandardBridgeInterface.decodeFunctionData(
|
||||
tx.input.slice(0, 10),
|
||||
tx.input,
|
||||
);
|
||||
const token = assetList.find(
|
||||
(asset) =>
|
||||
asset.L1chainId === parseInt(publicRuntimeConfig.l1ChainID) &&
|
||||
asset.L1contract?.toLowerCase() === (decodedWithdrawData[0] as string).toLowerCase(),
|
||||
asset.L1contract?.toLowerCase() === (decodedDepositData[0] as string).toLowerCase(),
|
||||
) as Asset;
|
||||
return {
|
||||
type: 'Deposit',
|
||||
from: tx.from,
|
||||
to: tx.to,
|
||||
assetSymbol: token.L1symbol ?? '',
|
||||
amount: (decodedWithdrawData[2] as BigNumber).toString(),
|
||||
amount: (
|
||||
(functionName === 'depositERC20' ? decodedDepositData[2] : decodedDepositData[3]) as BigNumber
|
||||
).toString(),
|
||||
blockTimestamp: tx.timeStamp,
|
||||
hash: tx.hash as `0x${string}`,
|
||||
status: 'Complete',
|
||||
priceApiId: token.apiId,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ export function explorerTxToBridgeWithdrawal(tx: BlockExplorerTransaction): Brid
|
||||
};
|
||||
}
|
||||
|
||||
const functionName = l2StandardBridgeInterface.getFunction(tx.input.slice(0, 10)).name;
|
||||
const decodedWithdrawData = l2StandardBridgeInterface.decodeFunctionData(
|
||||
tx.input.slice(0, 10),
|
||||
tx.input,
|
||||
@@ -52,9 +53,11 @@ export function explorerTxToBridgeWithdrawal(tx: BlockExplorerTransaction): Brid
|
||||
from: tx.from,
|
||||
to: tx.to,
|
||||
assetSymbol: token.L2symbol ?? '',
|
||||
amount: (decodedWithdrawData[1] as BigNumber).toString(),
|
||||
amount: (
|
||||
(functionName === 'withdraw' ? decodedWithdrawData[1] : decodedWithdrawData[2]) as BigNumber
|
||||
).toString(),
|
||||
blockTimestamp: tx.timeStamp,
|
||||
hash: tx.hash as `0x${string}`,
|
||||
priceApiId: token.apiId,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export function isETHOrERC20Deposit(tx: BlockExplorerTransaction) {
|
||||
// ERC-20 desposit
|
||||
if (tx.to === ERC20_DEPOSIT_ADDRESS) {
|
||||
const functionName = l1StandardBridgeInterface.getFunction(tx.input.slice(0, 10)).name;
|
||||
if (functionName === 'depositERC20') {
|
||||
if (functionName === 'depositERC20' || functionName === 'depositERC20To') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export function isETHOrERC20Withdrawal(tx: BlockExplorerTransaction) {
|
||||
// ERC-20 Withdrawal
|
||||
if (tx.to === ERC20_WITHDRAWAL_ADDRESS) {
|
||||
const functionName = l2StandardBridgeInterface.getFunction(tx.input.slice(0, 10)).name;
|
||||
if (functionName === 'withdraw') {
|
||||
if (functionName === 'withdraw' || functionName === 'withdrawTo') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"overrides": [
|
||||
module.exports = {
|
||||
arrowParens: 'always',
|
||||
bracketSameLine: false,
|
||||
jsxSingleQuote: false,
|
||||
printWidth: 100,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'all',
|
||||
useTabs: false,
|
||||
overrides: [
|
||||
{
|
||||
"files": "*.json",
|
||||
"options": {
|
||||
"parser": "json-stringify"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
files: '*.json',
|
||||
options: {
|
||||
parser: 'json-stringify',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user