-
-
-
-
-
Please take special note
-
+
+
+
+
+
Please take special note
+
-
- Clicking on the `Withdraw All` button means you are agreeing to close all positions and get all tokens
- transferred to your wallet.
-
-
-
+
+ Clicking on the `Withdraw All` button means you are agreeing to close all positions and get all tokens
+ transferred to your wallet.
+
-
+
);
};
-export default WithdrawAll;
+export default WithdrawAll;
\ No newline at end of file
diff --git a/frontend/src/pages/spotnet/dashboard/withdraw-all/withdraw_all.css b/frontend/src/pages/spotnet/dashboard/withdraw-all/withdraw_all.css
index c5ba5f78c..3420b480b 100644
--- a/frontend/src/pages/spotnet/dashboard/withdraw-all/withdraw_all.css
+++ b/frontend/src/pages/spotnet/dashboard/withdraw-all/withdraw_all.css
@@ -1,51 +1,3 @@
-.withdraw {
- min-height: 100vh;
- display: flex;
- width: 100%;
- margin-left: 372px;
-}
-
-@media (max-width: 1024px) {
- .withdraw {
- width: 100%;
- margin-left: 0;
- justify-content: center;
- }
-}
-
-.withdrawall-wrapper {
- position: relative;
- display: flex;
- justify-content: center;
- align-items: center;
- width: calc(100vw - 735px);
-}
-
-.withdrawall-container {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- gap: 32px;
- padding: 0 26px;
- padding-top: 20px;
- width: fit-content;
- margin: 100px 0 50px 0;
-}
-
-.withdrawall-content {
- width: 642px;
- gap: 24px;
- border-radius: 8px;
- padding: 1rem 0;
- color: var(--second-primary);
- text-align: center;
- display: flex;
- justify-content: center;
- flex-direction: column;
- align-items: center;
-}
-
.withdrawall-info-card {
background: var(--warning-colour-alt);
border: 1px solid var(--warning-colour);
@@ -95,20 +47,7 @@
}
@media (max-width: 768px) {
- .withdrawall-wrapper {
- padding: 1rem 0;
- }
-
- .withdrawall-container {
- margin: 80px 0 50px 0;
- }
-
- .withdrawall-content {
- padding: 0.5rem 24px;
- gap: 16px;
- width: 480px;
- }
-
+
.withdrawall-info-card {
width: 480px;
margin-top: 35px;
@@ -128,20 +67,13 @@
}
@media (max-width: 550px) {
- .withdrawall-wrapper {
+ /* .withdrawall-wrapper {
background: url('../../../../../public/mobile-background.png') no-repeat;
background-size: cover;
background-position: 50% 40%;
height: 100%;
- }
- .withdrawall-content {
- gap: 40px;
- display: flex;
- justify-content: center;
- flex-direction: column;
- align-items: center;
- width: 100%;
- }
+ } */
+
.withdrawall-info-container {
display: grid;
gap: 30px;
diff --git a/frontend/src/services/wallet.js b/frontend/src/services/wallet.js
index 731e88892..bd94b3b7a 100644
--- a/frontend/src/services/wallet.js
+++ b/frontend/src/services/wallet.js
@@ -1,12 +1,12 @@
import React from 'react';
import { connect, disconnect } from 'starknetkit';
+import { InjectedConnector } from 'starknetkit/injected';
import { ETH_ADDRESS, STRK_ADDRESS, USDC_ADDRESS } from '../utils/constants';
import { ReactComponent as ETH } from 'assets/icons/ethereum.svg';
import { ReactComponent as USDC } from 'assets/icons/borrow_usdc.svg';
import { ReactComponent as STRK } from 'assets/icons/strk.svg';
const CRM_TOKEN_ADDRESS = "0x051c4b1fe3bf6774b87ad0b15ef5d1472759076e42944fff9b9f641ff13e5bbe";
-let globalWallet = null;
// Check if the connected wallet holds the CRM token
export const checkForCRMToken = async (walletAddress) => {
@@ -16,9 +16,7 @@ export const checkForCRMToken = async (walletAddress) => {
}
try {
- const { wallet } = await connect({
- modalMode: "neverAsk",
- });
+ const wallet = await getWallet();
console.log('Checking CRM token balance for wallet:', wallet);
const response = await wallet.provider.callContract({
@@ -40,9 +38,22 @@ export const checkForCRMToken = async (walletAddress) => {
}
};
+export const getConnectors = () => !localStorage.getItem("starknetLastConnectedWallet") ? [
+ new InjectedConnector({ options: { id: "argentX" }}),
+ new InjectedConnector({ options: { id: "braavos" }}),
+] : [
+ new InjectedConnector({ options: { id: localStorage.getItem("starknetLastConnectedWallet") }}),
+];
+
export const getWallet = async () => {
- if (globalWallet && globalWallet.isConnected) {
- return globalWallet;
+ const { wallet } = await connect({
+ connectors: getConnectors(),
+ modalMode: "neverAsk",
+ });
+
+ if (wallet && wallet.isConnected) {
+ await wallet.enable();
+ return wallet;
}
console.log('No wallet found. Attempting to connect...');
@@ -54,8 +65,9 @@ export const connectWallet = async () => {
console.log('Attempting to connect to wallet...');
const { wallet } = await connect({
+ connectors: getConnectors(),
modalMode: "alwaysAsk",
- modalTheme: "light",
+ modalTheme: "dark"
});
if (!wallet) {
@@ -66,10 +78,8 @@ export const connectWallet = async () => {
await wallet.enable();
if (wallet.isConnected) {
- const address = wallet.selectedAddress;
- globalWallet = wallet;
- console.log('Wallet successfully connected. Address:', address);
- return address;
+ console.log('Wallet successfully connected. Address:', wallet.selectedAddress);
+ return wallet;
} else {
throw new Error('Wallet connection failed');
}
@@ -86,9 +96,7 @@ export function logout() {
export async function getTokenBalances(walletAddress) {
try {
- const { wallet } = await connect({
- modalMode: "neverAsk",
- });
+ const wallet = await getWallet();
console.log("Wallet info", wallet);
const tokenBalances = {
diff --git a/frontend/test/services/wallet.test.js b/frontend/test/services/wallet.test.js
index 5949382e7..c98ae749b 100644
--- a/frontend/test/services/wallet.test.js
+++ b/frontend/test/services/wallet.test.js
@@ -1,5 +1,6 @@
import { connect } from 'starknetkit';
-import { checkForCRMToken, connectWallet, getTokenBalances, getBalances, logout } from '../../src/services/wallet';
+import { InjectedConnector } from 'starknetkit/injected';
+import { checkForCRMToken, connectWallet, getTokenBalances, getBalances, logout, getWallet, getConnectors } from '../../src/services/wallet';
import { ETH_ADDRESS, STRK_ADDRESS, USDC_ADDRESS } from '../../src/utils/constants';
jest.mock('starknetkit', () => ({
@@ -10,7 +11,7 @@ jest.mock('starknetkit', () => ({
jest.mock(
'starknetkit/injected',
() => ({
- InjectedConnector: jest.fn(),
+ InjectedConnector: jest.fn().mockImplementation((options) => options),
}),
{ virtual: true }
);
@@ -71,6 +72,140 @@ describe('Wallet Services', () => {
});
});
+ describe('getConnectors', () => {
+ it('should return connectors array with injected connectors', () => {
+ const mockGetItem = jest.fn();
+ Object.defineProperty(window, 'localStorage', {
+ value: {
+ getItem: mockGetItem.mockReturnValue(null),
+ },
+ writable: true,
+ });
+
+ const connectors = getConnectors();
+
+ expect(connectors).toEqual([
+ new InjectedConnector({ options: { id: 'argentX' } }),
+ new InjectedConnector({ options: { id: 'braavos' } }),
+ ]);
+ });
+
+ ['argentX', 'braavos'].forEach((connector) => {
+ it(`should return connectors array with injected connectors from local storage (${connector})`, () => {
+ const mockGetItem = jest.fn();
+ Object.defineProperty(window, 'localStorage', {
+ value: {
+ getItem: mockGetItem.mockReturnValue(connector),
+ },
+ writable: true,
+ });
+
+ const connectors = getConnectors();
+
+ expect(connectors).toEqual([
+ new InjectedConnector({ options: { id: connector } }),
+ ]);
+ })
+ });
+ });
+
+ describe('getWallet', () => {
+ const testCases = [
+ { connectorId: 'argentX', expectedAddress: '0x123' },
+ { connectorId: 'braavos', expectedAddress: '0x456' },
+ ];
+
+ testCases.forEach(({ connectorId, expectedAddress }) => {
+ it(`should return wallet object if wallet is connected with ${connectorId}`, async () => {
+ const mockStarknet = {
+ wallet: {
+ isConnected: true,
+ enable: jest.fn(),
+ selectedAddress: expectedAddress,
+ },
+ };
+
+ connect.mockResolvedValue(mockStarknet);
+
+ const mockGetItem = jest.fn().mockReturnValue(connectorId);
+ const mockSetItem = jest.fn();
+
+ Object.defineProperty(window, 'localStorage', {
+ value: {
+ getItem: mockGetItem,
+ },
+ writable: true,
+ });
+
+ const wallet = await getWallet();
+
+ expect(mockGetItem).toHaveBeenCalledWith('starknetLastConnectedWallet');
+
+ expect(connect).toHaveBeenCalledWith(
+ expect.objectContaining({
+ connectors: expect.arrayContaining([
+ expect.objectContaining({
+ options: expect.objectContaining({
+ id: connectorId,
+ }),
+ }),
+ ]),
+ modalMode: 'neverAsk',
+ })
+ );
+ expect(wallet.isConnected).toBe(true);
+ expect(wallet.enable).toHaveBeenCalled();
+ expect(wallet.selectedAddress).toBe(expectedAddress);
+ });
+ })
+
+ it('should return wallet object if wallet is not choosen before', async () => {
+ const mockStarknet = {
+ wallet: {
+ isConnected: true,
+ enable: jest.fn(),
+ selectedAddress: '0x123',
+ },
+ };
+
+ connect.mockResolvedValue(mockStarknet);
+
+ const mockGetItem = jest.fn().mockReturnValue(null);
+
+ Object.defineProperty(window, 'localStorage', {
+ value: {
+ getItem: mockGetItem,
+ },
+ writable: true,
+ });
+
+ const wallet = await getWallet();
+
+ expect(mockGetItem).toHaveBeenCalledWith('starknetLastConnectedWallet');
+
+ expect(connect).toHaveBeenCalledWith(
+ expect.objectContaining({
+ connectors: expect.arrayContaining([
+ expect.objectContaining({
+ options: expect.objectContaining({
+ id: 'argentX'
+ }),
+ }),
+ expect.objectContaining({
+ options: expect.objectContaining({
+ id: 'braavos'
+ }),
+ }),
+ ]),
+ modalMode: 'neverAsk'
+ })
+ );
+ expect(wallet.isConnected).toBe(true);
+ expect(wallet.enable).toHaveBeenCalled();
+ expect(wallet.selectedAddress).toBe('0x123');
+ });
+ });
+
describe('connectWallet', () => {
it('should successfully connect wallet and return address', async () => {
const mockStarknet = {
@@ -83,16 +218,18 @@ describe('Wallet Services', () => {
connect.mockResolvedValue(mockStarknet);
- const address = await connectWallet();
+ const wallet = await connectWallet();
expect(connect).toHaveBeenCalledWith(
expect.objectContaining({
+ connectors: expect.any(Array),
modalMode: 'alwaysAsk',
- modalTheme: 'light'
+ modalTheme: 'dark'
})
);
expect(mockStarknet.wallet.enable).toHaveBeenCalled();
- expect(address).toBe('0x123');
+ expect(wallet.selectedAddress).toBe('0x123');
+ expect(wallet.isConnected).toBe(true);
});
it('should throw error when StarkNet object is not found', async () => {
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 19368239f..3f9c8f1d6 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -3266,11 +3266,6 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1:
resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
-acorn@^7.0.0:
- version "7.4.1"
- resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
- integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-
acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
@@ -4326,7 +4321,7 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
-color-name@^1.1.4, color-name@~1.1.4:
+color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
@@ -4872,6 +4867,21 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4:
dependencies:
ms "^2.1.3"
+debug@2.6.9:
+ version "2.6.9"
+ resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+
+ dependencies:
+ ms "2.0.0"
+
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4:
+ version "4.4.0"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
+ integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+ dependencies:
+ ms "^2.1.3"
+
debug@2.6.9:
version "2.6.9"
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
@@ -8104,6 +8114,11 @@ lilconfig@^3.1.3:
resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz"
integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==
+lilconfig@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz"
+ integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==
+
limes@1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/limes/-/limes-1.1.0.tgz"
@@ -8398,6 +8413,7 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8:
braces "^3.0.3"
picomatch "^2.3.1"
+
middleware-flow@0.8.0:
version "0.8.0"
resolved "https://registry.npmjs.org/middleware-flow/-/middleware-flow-0.8.0.tgz"
@@ -8423,11 +8439,12 @@ mime@^3.0.0:
resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz"
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
+
mime@1.4.1:
version "1.4.1"
resolved "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz"
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
-
+
mime@1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
@@ -8546,6 +8563,16 @@ ms@2.0.0:
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+ms@^2.1.1, ms@^2.1.3, ms@2.1.3:
+ version "2.1.3"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
+ integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
+
multicast-dns@^7.2.5:
version "7.2.5"
resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz"
@@ -11000,6 +11027,11 @@ stethoskop@1.0.0:
dependencies:
node-statsd "0.1.1"
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
+
stop-iteration-iterator@^1.0.0:
version "1.1.0"
resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz"
@@ -11152,7 +11184,7 @@ string.prototype.trimstart@^1.0.8:
define-properties "^1.2.1"
es-object-atoms "^1.0.0"
-stringify-object@^3.3.0, stringify-object@3.3.0:
+stringify-object@^3.3.0:
version "3.3.0"
resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz"
integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
diff --git a/web_app/db/seed_data.py b/web_app/db/seed_data.py
index 2f506d36b..8ea6de1cd 100644
--- a/web_app/db/seed_data.py
+++ b/web_app/db/seed_data.py
@@ -18,6 +18,7 @@
TransactionStatus,
User,
Vault,
+ ExtraDeposit
)
logging.basicConfig(level=logging.INFO)
@@ -87,6 +88,34 @@ def create_positions(session: SessionLocal, users: list[User]) -> list[Position]
logger.info("No positions created.")
return positions
+def create_extra_deposits(session: SessionLocal, positions: list[Position]) -> None:
+ """
+ Create and save fake extra deposit records associated with given positions.
+ Args:
+ session (Session): SQLAlchemy session object.
+ positions (list): List of Position objects to associate with extra deposits.
+ """
+ extra_deposits = []
+ for position in positions:
+ num_deposits = fake.random_int(min=0, max=3)
+ for _ in range(num_deposits):
+ extra_deposit = ExtraDeposit(
+ position_id=position.id,
+ token_symbol=position.token_symbol,
+ amount=str(
+ fake.random_number(digits=5)
+ ),
+ added_at=fake.date_time_this_decade(),
+ )
+ extra_deposits.append(extra_deposit)
+
+ if extra_deposits:
+ session.bulk_save_objects(extra_deposits)
+ session.commit()
+ logger.info(f"Created {len(extra_deposits)} extra deposits for {len(positions)} positions.")
+ else:
+ logger.info("No extra deposits created.")
+
def create_airdrops(session: SessionLocal, users: list[User]) -> None:
"""
@@ -193,6 +222,7 @@ def create_transaction(session: SessionLocal, positions: list[Position]) -> None
# Populate the database
users = create_users(session)
positions = create_positions(session, users)
+ create_extra_deposits(session, positions)
# create_airdrops(session, users)
# create_telegram_users(session, users)
create_vaults(session, users)