Skip to content

Commit

Permalink
Merge branch 'feat/enable-multiple-accounts' into chore/set-new-accou…
Browse files Browse the repository at this point in the history
…nt-to-current
  • Loading branch information
stanleyyconsensys committed Jan 15, 2025
2 parents 5dee95d + 1ec0dcd commit 418654d
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 46 deletions.
3 changes: 1 addition & 2 deletions packages/starknet-snap/src/rpcs/add-account.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { logger } from 'ethers';
import { type Infer } from 'superstruct';

import { BaseRequestStruct, AccountStruct } from '../utils';
import { BaseRequestStruct, AccountStruct, logger } from '../utils';
import { createAccountService } from '../utils/factory';
import { ChainRpcController } from './abstract/chain-rpc-controller';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('AccountStateManager', () => {
expect(result).toBeNull();
});

it('returns null if the account chainId is not match', async () => {
it('returns null if the account chainId does not match', async () => {
const accounts = await generateTestnetAccounts();
await mockStateWithMainnetAccounts(accounts);

Expand All @@ -67,7 +67,7 @@ describe('AccountStateManager', () => {
});

describe('findAccounts', () => {
it('returns the list of account', async () => {
it('returns the list of accounts', async () => {
const accountsInTestnet = await generateTestnetAccounts();
const accountsInMainnet = await generateMainnetAccounts();

Expand Down Expand Up @@ -98,7 +98,7 @@ describe('AccountStateManager', () => {
});

describe('upsertAccount', () => {
it('adds an account if the account not exist', async () => {
it('adds an account if the account does not exist', async () => {
const [account] = await generateTestnetAccounts(1);
const state = await mockStateWithMainnetAccounts();
const originalAccountsFromState = [...state.accContracts];
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('AccountStateManager', () => {
]);
});

it('throws an `Account does not exist` error if the removed account is not exist', async () => {
it('throws an `Account does not exist` error if the removed account does not exist', async () => {
const [removeAccount, ...accounts] = await generateTestnetAccounts();
await mockStateWithMainnetAccounts(accounts);

Expand Down
12 changes: 8 additions & 4 deletions packages/starknet-snap/src/wallet/account/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ export class AccountContractDiscovery {
/**
* Get the contract for the given public key.
* The contract is determined based on the following rules:
* 1. If a contract is deployed, then use the deployed contract.
* 2. If no contract is deployed, but has balance, then use the contract with balance.
* 3. If neither contract is deployed or has balance, then use the default contract.
* 4. If multiple contracts are deployed, then use the default contract.
*
* 1. If a Cairo 1 contract has been deployed, it will always be used regardless of whether the other contract has a balance in ETH or has been deployed.
* 2. If a Cairo 0 contract has been deployed and the other contract has not, the Cairo 0 contract will always be used regardless of whether the other contract has a balance or not, and the contract will be forced to upgrade.
* 3. If neither contract has been deployed, but a Cairo 0 contract has a balance in ETH, it will always be used regardless of whether the other contract has a balance or not, and the contract will be forced to deploy.
* 3. If neither contract has been deployed and neither has a balance in ETH, the default contract (Cairo 1) will be used."
*
* Note: The rules accommodate for most use cases, except 1 edge case:
* - Due to rule #1, if a user wont able to operated a Cairo 0 contract if a Cairo 1 contract has been deployed.
*
* @param publicKey - The public key to get the contract for.
* @returns The contract for the given public key.
Expand Down
12 changes: 5 additions & 7 deletions packages/wallet-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ function App() {
} = useAppSelector((state) => state.modals);
const { loader } = useAppSelector((state) => state.UI);
const networks = useAppSelector((state) => state.networks);
const { accounts } = useAppSelector((state) => state.wallet);
const { currentAccount } = useAppSelector((state) => state.wallet);
const { hasMetamask } = useHasMetamask();

const address =
accounts?.length > 0 ? (accounts[0] as unknown as string) : DUMMY_ADDRESS;
const chainId = networks.items?.[networks.activeNetwork]?.chainId;
const address = currentAccount ?? DUMMY_ADDRESS;

useEffect(() => {
if (!provider) {
Expand All @@ -57,12 +56,11 @@ function App() {
}, [connected, forceReconnect, hasMetamask, provider]);

useEffect(() => {
if (provider && networks.items.length > 0) {
const chainId = networks.items[networks.activeNetwork].chainId;
if (provider && networks.items.length > 0 && chainId) {
getWalletData(chainId);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [networks.activeNetwork, provider]);
}, [networks.activeNetwork, provider, chainId]);

const loading = loader.isLoading;
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,23 @@ export const TransactionsListView = ({ transactions }: Props) => {
const networks = useAppSelector((state) => state.networks);
const wallet = useAppSelector((state) => state.wallet);
const timeoutHandle = useRef(setTimeout(() => {}));
const chainId = networks.items[networks.activeNetwork]?.chainId;
const {
currentAccount,
erc20TokenBalanceSelected,
transactions: walletTransactions,
} = wallet;

useEffect(() => {
const chain = networks.items[networks.activeNetwork]?.chainId;
const address = wallet.accounts?.[0] as unknown as string;
if (chain && address) {
if (chainId && currentAccount) {
clearTimeout(timeoutHandle.current); // cancel the timeout that was in-flight
timeoutHandle.current = setTimeout(
() =>
getTransactions(
address,
wallet.erc20TokenBalanceSelected.address,
currentAccount,
erc20TokenBalanceSelected.address,
10,
chain,
chainId,
false,
true,
),
Expand All @@ -37,30 +41,29 @@ export const TransactionsListView = ({ transactions }: Props) => {
return () => clearTimeout(timeoutHandle.current);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wallet.transactions]);
}, [walletTransactions]);

useEffect(
() => {
const chain = networks.items[networks.activeNetwork]?.chainId;
const address = wallet.accounts?.[0] as unknown as string;
if (chain && address) {
if (chainId && currentAccount) {
clearTimeout(timeoutHandle.current); // cancel the timeout that was in-flight
getTransactions(
address,
wallet.erc20TokenBalanceSelected.address,
currentAccount,
erc20TokenBalanceSelected.address,
10,
chain,
chainId,
);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
// eslint-disable-next-line react-hooks/exhaustive-deps
wallet.erc20TokenBalanceSelected.address,
erc20TokenBalanceSelected.address,
// eslint-disable-next-line react-hooks/exhaustive-deps
wallet.erc20TokenBalanceSelected.chainId,
erc20TokenBalanceSelected.chainId,
// eslint-disable-next-line react-hooks/exhaustive-deps
wallet.accounts?.[0],
currentAccount,
chainId,
],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const AddTokenModalView = ({ closeModal }: Props) => {
const { setErc20TokenBalance, addErc20Token } = useStarkNetSnap();
const [enabled, setEnabled] = useState(false);
const networks = useAppSelector((state) => state.networks);
const { accounts } = useAppSelector((state) => state.wallet);
const chain = networks && networks.items[networks.activeNetwork].chainId;
const { currentAccount } = useAppSelector((state) => state.wallet);
const chainId = networks && networks.items[networks.activeNetwork].chainId;
const [isValidAddress, setIsValidAddress] = useState(false);
const [fields, setFields] = useState({
address: '',
Expand Down Expand Up @@ -105,8 +105,8 @@ export const AddTokenModalView = ({ closeModal }: Props) => {
fields.name,
fields.symbol,
parseFloat(fields.decimal),
chain,
accounts[0] as unknown as string,
chainId,
currentAccount,
);
if (newToken) {
setErc20TokenBalance(newToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const SendSummaryModalView = ({
selectedFeeToken,
}: Props) => {
const wallet = useAppSelector((state) => state.wallet);
const currentAccount = wallet.currentAccount;
const [estimatingGas, setEstimatingGas] = useState(true);
const [gasFees, setGasFees] = useState({
suggestedMaxFee: '0',
Expand Down Expand Up @@ -80,7 +81,7 @@ export const SendSummaryModalView = ({

useEffect(() => {
const fetchGasFee = () => {
if (wallet.accounts) {
if (currentAccount) {
setGasFeesError(false);
setEstimatingGas(true);
const amountBN = ethers.utils.parseUnits(
Expand All @@ -92,7 +93,7 @@ export const SendSummaryModalView = ({
wallet.erc20TokenBalanceSelected.address,
ContractFuncName.Transfer,
callData,
wallet.accounts[0] as unknown as string,
currentAccount,
chainId,
selectedFeeToken === FeeToken.STRK
? constants.TRANSACTION_VERSION.V3
Expand All @@ -113,7 +114,7 @@ export const SendSummaryModalView = ({
}
};
fetchGasFee();
}, []);
}, [currentAccount]);

useEffect(() => {
if (gasFees?.suggestedMaxFee) {
Expand Down Expand Up @@ -175,7 +176,7 @@ export const SendSummaryModalView = ({
}, [amount, wallet.erc20TokenBalanceSelected]);

const handleConfirmClick = () => {
if (wallet.accounts) {
if (currentAccount) {
const amountBN = ethers.utils.parseUnits(
amount,
wallet.erc20TokenBalanceSelected.decimals,
Expand All @@ -185,7 +186,7 @@ export const SendSummaryModalView = ({
wallet.erc20TokenBalanceSelected.address,
ContractFuncName.Transfer,
callData,
wallet.accounts[0] as unknown as string,
currentAccount,
gasFees.suggestedMaxFee,
chainId,
selectedFeeToken,
Expand All @@ -194,7 +195,7 @@ export const SendSummaryModalView = ({
if (result) {
toastr.success('Transaction sent successfully');
getTransactions(
wallet.accounts[0] as unknown as string,
currentAccount,
wallet.erc20TokenBalanceSelected.address,
10,
chainId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export const InfoIcon = styled(RoundedIcon)`
margin-right: ${(props) => props.theme.spacing.tiny2};
`;

export const AddIcon = styled(RoundedIcon)`
cursor: pointer;
margin-left: ${(props) => props.theme.spacing.tiny2};
`;

export const AddTokenButton = styled(Button).attrs((props) => ({
textStyle: {
fontWeight: props.theme.typography.bold.fontWeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
AccountDetailsContent,
AccountImageStyled,
AccountLabel,
AddIcon,
AddTokenButton,
DivList,
InfoIcon,
Expand All @@ -37,7 +38,7 @@ export const SideBarView = ({ address }: Props) => {
const [accountDetailsOpen, setAccountDetailsOpen] = useState(false);
const wallet = useAppSelector((state) => state.wallet);
const [addTokenOpen, setAddTokenOpen] = useState(false);
const { getStarkName } = useStarkNetSnap();
const { getStarkName, addNewAccount } = useStarkNetSnap();
const [starkName, setStarkName] = useState<string | undefined>(undefined);

const ref = useRef<HTMLDivElement>();
Expand Down Expand Up @@ -114,6 +115,7 @@ export const SideBarView = ({ address }: Props) => {
<RowDiv>
<InfoIcon onClick={() => setInfoModalOpen(true)}>i</InfoIcon>
<AccountAddress address={address} starkName={starkName} />
<AddIcon onClick={async () => await addNewAccount(chainId)}>+</AddIcon>
</RowDiv>
<DivList ref={ref as any}>
<AssetsList />
Expand Down
Loading

0 comments on commit 418654d

Please sign in to comment.