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: Add safe wallet support #77

Merged
merged 24 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from 18 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
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ NEXT_PUBLIC_ARBITRUM_GOERLI_RPC_URL=
NEXT_PUBLIC_ARBITRUM_NOVA_RPC_URL=
NEXT_PUBLIC_ARBITRUM_SEPOLIA_RPC_URL=

NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=

NEXT_PUBLIC_LOCAL_ETHEREUM_RPC_URL=http://localhost:8545
NEXT_PUBLIC_LOCAL_ARBITRUM_RPC_URL=http://localhost:8547
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16.18.1
18
13 changes: 12 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
module.exports = {};
module.exports = {
webpack: (config, context) => {
if (config.plugins) {
config.plugins.push(
new context.webpack.IgnorePlugin({
resourceRegExp: /^(lokijs|pino-pretty|encoding)$/,
}),
);
}
return config;
Comment on lines +5 to +12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix error in building for nextjs: WalletConnect/walletconnect-monorepo#1908 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best if we add a link to this in the file

},
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"@arbitrum/sdk": "^3.1.12",
"@ethersproject/bignumber": "^5.1.1",
"@rainbow-me/rainbowkit": "^0.12.18",
"@types/node": "^16.7.13",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
Expand All @@ -16,7 +17,7 @@
"react-feather": "^2.0.10",
"react-tooltip": "^5.5.1",
"typescript": "^4.4.2",
"wagmi": "^0.8.10"
"wagmi": "^0.12.19"
},
"engines": {
"node": ">=16"
Expand Down
9 changes: 9 additions & 0 deletions src/app/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ if (process.env.NODE_ENV !== 'production') {
safeAddDefaultLocalNetwork();
}

// Clear cache for everything related to WalletConnect v2.
//
// TODO: Remove this once the fix for the infinite loop / memory leak is identified.
Object.keys(localStorage).forEach((key) => {
if (key === 'wagmi.requestedChains' || key.startsWith('wc@2')) {
localStorage.removeItem(key);
}
});

export default function App({ Component, pageProps }: AppProps) {
return (
<>
Expand Down
12 changes: 9 additions & 3 deletions src/app/global.css
fionnachan marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ main {
max-width: 740px;
gap: 10px;
margin-bottom: var(--space-xl);
margin-left: auto;
margin-right: auto;
}

.form-inner {
Expand Down Expand Up @@ -119,7 +121,7 @@ main {
background-color: rgb(255, 255, 255);
}

button,
.button,
input[type='submit'] {
background-color: var(--blue);
color: #fff;
Expand All @@ -135,12 +137,12 @@ input[type='submit'] {
cursor: pointer;
}

button:hover,
.button:hover,
input[type='submit']:hover {
background-color: var(--blue-dark);
}

button:disabled {
.button:disabled {
background-color: var(--blue-lighter);
cursor: inherit;
}
Expand Down Expand Up @@ -274,3 +276,7 @@ ul {
.redeem-button-container {
text-align: center;
}

div[data-rk] {
width: 100%;
}
6 changes: 3 additions & 3 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NextPage } from 'next';
import { WagmiProvider } from '@/components/WagmiProvider';
import { Providers } from '@/components/Providers';
import { Logo } from '@/components/Logo';
import { Form } from '@/components/Form';

Expand All @@ -12,9 +12,9 @@ const PageIndex: NextPage = () => {
</header>

<main>
<WagmiProvider>
<Providers>
<Form />
</WagmiProvider>
</Providers>
</main>
</>
);
Expand Down
52 changes: 5 additions & 47 deletions src/app/recover-funds/[address]/RecoverFunds.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
'use client';
import { Address } from '@arbitrum/sdk';
import { BigNumber, utils, constants } from 'ethers';
import { useNetwork } from 'wagmi';
import { getProviderFromChainId, getTargetChainId } from '@/utils';
import { mapChainIdToName } from '@/utils/network';
import { BigNumber, utils } from 'ethers';
import '../style.css';

export interface OperationInfo {
balanceToRecover: BigNumber;
aliasedAddress: string;
chainId: string;
}

export const hasBalanceOverThreshold = (balanceToRecover: BigNumber) => {
Expand All @@ -16,59 +14,19 @@ export const hasBalanceOverThreshold = (balanceToRecover: BigNumber) => {
return balanceToRecover.gte(BigNumber.from(5_000_000_000_000_000));
};

export async function getData(
chainID: number,
address: string,
): Promise<OperationInfo | null> {
// First, obtain the aliased address of the signer
const destinationAddress = new Address(address);
const { value: aliasedAddress } = destinationAddress.applyAlias();

// And get its balance to find out the amount we are transferring
try {
const l2Provider = getProviderFromChainId(chainID);
const aliasedSignerBalance = await l2Provider.getBalance(aliasedAddress);

return {
balanceToRecover: hasBalanceOverThreshold(aliasedSignerBalance)
? aliasedSignerBalance
: constants.Zero,
aliasedAddress,
};
} catch (e) {
return {
balanceToRecover: constants.Zero,
aliasedAddress,
};
}
}

type Props = {
operationInfo: OperationInfo;
address: string;
};
const RecoverFunds = ({ operationInfo, address }: Props) => {
const { chain } = useNetwork();
const targetChainID = getTargetChainId(chain?.id);

// No funds to recover
if (!hasBalanceOverThreshold(operationInfo.balanceToRecover)) {
return (
<div className="funds-message">
There are no funds stuck on {operationInfo.aliasedAddress}
<br />
(Alias of {address}) on this network
{targetChainID ? ` (${targetChainID})` : ''}.
</div>
);
}
const l2ChainId = operationInfo.chainId;

return (
<div className="funds-message">
There are {utils.formatEther(operationInfo.balanceToRecover)} ETH on{' '}
{operationInfo.aliasedAddress}
<br />
(Alias of {address}).
(Alias of {address}) on this network {mapChainIdToName[l2ChainId] ?? ''}.
</div>
);
};
Expand Down
59 changes: 47 additions & 12 deletions src/app/recover-funds/[address]/RecoverFundsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@ import {
} from '@arbitrum/sdk';
import { Inbox__factory } from '@arbitrum/sdk/dist/lib/abi/factories/Inbox__factory';
import { getBaseFee } from '@arbitrum/sdk/dist/lib/utils/lib';
import { useNetwork, useSigner } from 'wagmi';
import { goerli, mainnet, sepolia, useNetwork, useSigner } from 'wagmi';
import { getProviderFromChainId, getTargetChainId } from '@/utils';
import { BigNumber } from 'ethers';
import { ChainId } from '@/utils/network';
import { useAccountType } from '@/utils/useAccountType';

function getL1ChainIdFromL2ChainId(l2ChainId: number | undefined) {
if (!l2ChainId) {
return ChainId.Mainnet;
}

return {
[ChainId.ArbitrumOne]: ChainId.Mainnet,
[ChainId.ArbitrumGoerli]: ChainId.Goerli,
[ChainId.ArbitrumSepolia]: ChainId.Sepolia,
}[l2ChainId];
}

function RecoverFundsButton({
balanceToRecover,
Expand All @@ -25,8 +39,11 @@ function RecoverFundsButton({
}) {
const [message, setMessage] = useState('');
const [loading, setLoading] = useState(false);
const { isSmartContractWallet } = useAccountType();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if we need to use isLoading from this to show the UI because users who are on this page probably have a history of not loading isSmartContractWallet quickly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only use this flag to show the popup "To continue, please approve tx on your smart contract wallet.", by this time, user had already made a transaction, so most likely the loading has been completed, but I added the loading flag just in case

const { chain } = useNetwork();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the connected chain or the first chain from the config?? i'm slightly confused about this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useNetwork return the currently connected chain https://1.x.wagmi.sh/react/hooks/useNetwork

const { data: signer } = useSigner({ chainId: chain?.id });
const { data: signer } = useSigner({
chainId: getL1ChainIdFromL2ChainId(chain?.id),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why wouldn't we use the chainID prop directly here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chainID is the L2 network, and we need to do the transaction on L1

});

const handleRecover = useCallback(async () => {
if (!signer) {
Expand All @@ -45,10 +62,7 @@ function RecoverFundsButton({
setLoading(true);
setMessage('');

// We instantiate the Inbox factory object to make use of its methods
const targetChainID = getTargetChainId(chainID);

const baseL2Provider = getProviderFromChainId(targetChainID);
const baseL2Provider = getProviderFromChainId(chainID);
const l2Network = await getL2Network(baseL2Provider);
const inbox = Inbox__factory.connect(
l2Network.ethBridge.inbox,
Expand Down Expand Up @@ -143,26 +157,47 @@ function RecoverFundsButton({

if (!signer) return null;

if (chain?.id !== 1 && chain?.id !== 5) {
if (
chain?.id !== mainnet.id &&
chain?.id !== goerli.id &&
chain?.id !== sepolia.id
) {
return (
<div>Unknown L1 chain id. This chain is not supported by this tool</div>
);
}

if (chain?.id !== chainID) {
if (getTargetChainId(chain?.id) !== chainID) {
return (
<div>
To recover funds, connect to chain ${chain?.id} (${chain?.name})
To recover funds, connect to chain {chain?.id} ({chain?.name})
</div>
);
}

return (
<>
<div className="recover-funds-form">
<button id="recover-button" disabled={loading} onClick={handleRecover}>
Recover
</button>
<div>
<button
className="button"
id="recover-button"
disabled={loading}
onClick={handleRecover}
>
Recover
</button>
</div>
{loading && isSmartContractWallet && (
<div className="flex flex-col">
<span>
<b>
To continue, please approve tx on your smart contract wallet.
</b>{' '}
If you have k of n signers, then k of n will need to sign.
</span>
</div>
Comment on lines +193 to +200

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: text appear aligned to the left, not centered like the rest of the texts

)}
</div>
<div>
{message && (
Expand Down
Loading