diff --git a/packages/apps/staking/.env.example b/packages/apps/staking/.env.example index 6108cf5bc5..e9daab498c 100644 --- a/packages/apps/staking/.env.example +++ b/packages/apps/staking/.env.example @@ -2,6 +2,7 @@ VITE_APP_DASHBOARD_API_URL=http://0.0.0.0:5006 VITE_APP_ENVIRONMENT=testnet VITE_APP_SUPPORTED_CHAINS=80002 +VITE_APP_WALLETCONNECT_PROJECT_ID=replace-me # Links to header VITE_HEADER_LINK_DASHBOARD=http://localhost:3004 diff --git a/packages/apps/staking/src/hooks/useKVStore.ts b/packages/apps/staking/src/hooks/useKVStore.ts index d2c4a0d993..24cf987032 100644 --- a/packages/apps/staking/src/hooks/useKVStore.ts +++ b/packages/apps/staking/src/hooks/useKVStore.ts @@ -1,5 +1,5 @@ import { ChainId, IKVStore, KVStoreClient } from '@human-protocol/sdk'; -import { ethers } from 'ethers'; +import { Eip1193Provider, ethers } from 'ethers'; import { useEffect, useState } from 'react'; import { useAccount, useWalletClient } from 'wagmi'; import { SUPPORTED_CHAIN_IDS } from '../constants/chains'; @@ -8,7 +8,7 @@ import { parseErrorMessage } from '../utils/string'; import { getKVStoreData } from '../services/dashboard'; export const useKVStore = () => { - const { address, chainId } = useAccount(); + const { address, chainId, connector } = useAccount(); const { data: walletClient } = useWalletClient(); const { showError, openSnackbar } = useSnackbar(); @@ -36,9 +36,12 @@ export const useKVStore = () => { useEffect(() => { const initStakingClient = async () => { try { - if (walletClient && address) { + if (walletClient && address && connector) { checkSupportedChain(); - const provider = new ethers.BrowserProvider(window.ethereum); + const eeip193Provider = await connector?.getProvider(); + const provider = new ethers.BrowserProvider( + eeip193Provider as Eip1193Provider + ); const signer = await provider.getSigner(); const client = await KVStoreClient.build(signer); @@ -53,7 +56,7 @@ export const useKVStore = () => { initStakingClient(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [walletClient, address, chainId]); + }, [walletClient, address, chainId, connector]); const checkSupportedChain = () => { const isSupportedChain = SUPPORTED_CHAIN_IDS.includes(chainId as ChainId); diff --git a/packages/apps/staking/src/hooks/useStake.ts b/packages/apps/staking/src/hooks/useStake.ts index a2a8b74905..c9bfbc8ca7 100644 --- a/packages/apps/staking/src/hooks/useStake.ts +++ b/packages/apps/staking/src/hooks/useStake.ts @@ -5,7 +5,7 @@ import { StakerInfo, StakingClient, } from '@human-protocol/sdk'; -import { ethers } from 'ethers'; +import { Eip1193Provider, ethers } from 'ethers'; import { useEffect, useState } from 'react'; import { useAccount, useWalletClient } from 'wagmi'; import { useSnackbar } from '../providers/SnackProvider'; @@ -14,7 +14,7 @@ import { formatAmount } from '../utils/units'; import { SUPPORTED_CHAIN_IDS } from '../constants/chains'; export const useStake = () => { - const { address, chainId } = useAccount(); + const { address, chainId, connector } = useAccount(); const { data: walletClient } = useWalletClient(); const { showError, openSnackbar } = useSnackbar(); @@ -23,13 +23,19 @@ export const useStake = () => { ); const [stakingData, setStakingData] = useState(null); const [tokenBalance, setTokenBalance] = useState(0); + const [browserProvider, setBrowserProvider] = + useState(null); useEffect(() => { const initStakingClient = async () => { try { - if (walletClient && address) { + if (walletClient && address && connector) { checkSupportedChain(); - const provider = new ethers.BrowserProvider(window.ethereum); + const eeip193Provider = await connector?.getProvider(); + const provider = new ethers.BrowserProvider( + eeip193Provider as Eip1193Provider + ); + setBrowserProvider(provider); const signer = await provider.getSigner(); const client = await StakingClient.build(signer); @@ -45,7 +51,7 @@ export const useStake = () => { initStakingClient(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [walletClient, address, chainId]); + }, [walletClient, address, chainId, connector]); const checkSupportedChain = () => { const isSupportedChain = SUPPORTED_CHAIN_IDS.includes(chainId as ChainId); @@ -99,18 +105,16 @@ export const useStake = () => { }; const handleStake = async (amount: string) => { + if (!browserProvider) return; + try { checkSupportedChain(); - if (stakingClient && amount) { + if (stakingClient && amount && address) { const weiAmount = ethers.parseUnits(amount, 'ether'); await stakingClient.approveStake(weiAmount); await stakingClient.stake(weiAmount); await fetchStakingData(stakingClient); - await fetchTokenBalance( - new ethers.BrowserProvider(window.ethereum), - address!, - chainId - ); + await fetchTokenBalance(browserProvider, address, chainId); openSnackbar('Stake successful', 'success'); } } catch (error) { @@ -120,17 +124,15 @@ export const useStake = () => { }; const handleUnstake = async (amount: string) => { + if (!browserProvider) return; + try { checkSupportedChain(); - if (stakingClient && amount) { + if (stakingClient && amount && address) { const weiAmount = ethers.parseUnits(amount, 'ether'); await stakingClient.unstake(weiAmount); await fetchStakingData(stakingClient); - await fetchTokenBalance( - new ethers.BrowserProvider(window.ethereum), - address!, - chainId - ); + await fetchTokenBalance(browserProvider, address, chainId); openSnackbar('Unstake successful', 'success'); } } catch (error) { @@ -140,16 +142,14 @@ export const useStake = () => { }; const handleWithdraw = async () => { + if (!browserProvider) return; + try { checkSupportedChain(); - if (stakingClient) { + if (stakingClient && address) { await stakingClient.withdraw(); await fetchStakingData(stakingClient); - await fetchTokenBalance( - new ethers.BrowserProvider(window.ethereum), - address!, - chainId - ); + await fetchTokenBalance(browserProvider, address, chainId); openSnackbar('Withdraw successful', 'success'); } } catch (error) { diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py index aafc3132bc..35e6d1ed8f 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/escrow/escrow_utils.py @@ -190,6 +190,13 @@ def get_escrows( escrows = [] + statuses = None + if filter.status: + if isinstance(filter.status, list): + statuses = [s.name for s in filter.status] + else: + statuses = [filter.status.name] + escrows_data = get_data_from_subgraph( network, query=get_escrows_query(filter), @@ -207,7 +214,7 @@ def get_escrows( filter.exchange_oracle.lower() if filter.exchange_oracle else None ), "jobRequesterId": filter.job_requester_id, - "status": filter.status.name if filter.status else None, + "status": statuses, "from": ( int(filter.date_from.timestamp()) if filter.date_from else None ), diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/filter.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/filter.py index fb5cc4c483..c4f2d2bee6 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/filter.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/filter.py @@ -30,7 +30,7 @@ def __init__( recording_oracle: Optional[str] = None, exchange_oracle: Optional[str] = None, job_requester_id: Optional[str] = None, - status: Optional[Status] = None, + status: Optional[Status | List[Status]] = None, date_from: Optional[datetime] = None, date_to: Optional[datetime] = None, first: int = 10, diff --git a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py index b20e2ef397..137cfee5a9 100644 --- a/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py +++ b/packages/sdk/python/human-protocol-sdk/human_protocol_sdk/gql/escrow.py @@ -34,7 +34,7 @@ def get_escrows_query(filter: EscrowFilter): $recordingOracle: String $exchangeOracle: String $jobRequesterId: String - $status: String + $status: [String!] $from: Int $to: Int $orderDirection: String @@ -76,7 +76,7 @@ def get_escrows_query(filter: EscrowFilter): job_requester_clause=( "jobRequesterId: $jobRequesterId" if filter.job_requester_id else "" ), - status_clause="status: $status" if filter.status else "", + status_clause="status_in: $status" if filter.status else "", from_clause="createdAt_gte: $from" if filter.date_from else "", to_clause="createdAt_lte: $to" if filter.date_to else "", ) diff --git a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_utils.py b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_utils.py index a13681e2a0..028728c773 100644 --- a/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_utils.py +++ b/packages/sdk/python/human-protocol-sdk/test/human_protocol_sdk/escrow/test_escrow_utils.py @@ -69,7 +69,7 @@ def side_effect(subgraph_url, query, params): "recordingOracle": None, "exchangeOracle": None, "jobRequesterId": "1", - "status": "Pending", + "status": ["Pending"], "from": 1683811973, "to": 1683812007, "first": 10, @@ -104,6 +104,83 @@ def side_effect(subgraph_url, query, params): self.assertEqual(len(filtered), 1) self.assertEqual(filtered[0].chain_id, ChainId.POLYGON_AMOY) + def test_get_escrows_with_status_array(self): + """Test get_escrows with an array of statuses, similar to the TypeScript test.""" + with patch( + "human_protocol_sdk.escrow.escrow_utils.get_data_from_subgraph" + ) as mock_function: + mock_escrow_1 = { + "id": "0x1234567890123456789012345678901234567891", + "address": "0x1234567890123456789012345678901234567891", + "amountPaid": "1000000000000000000", + "balance": "1000000000000000000", + "count": "1", + "factoryAddress": "0x1234567890123456789012345678901234567890", + "finalResultsUrl": "https://example.com", + "intermediateResultsUrl": "https://example.com", + "launcher": "0x1234567890123456789012345678901234567891", + "manifestHash": "0x1234567890123456789012345678901234567891", + "manifestUrl": "https://example.com", + "recordingOracle": "0x1234567890123456789012345678901234567891", + "reputationOracle": "0x1234567890123456789012345678901234567891", + "exchangeOracle": "0x1234567890123456789012345678901234567891", + "status": "Pending", + "token": "0x1234567890123456789012345678901234567891", + "totalFundedAmount": "1000000000000000000", + } + mock_escrow_2 = { + "id": "0x1234567890123456789012345678901234567891", + "address": "0x1234567890123456789012345678901234567891", + "amountPaid": "1000000000000000000", + "balance": "1000000000000000000", + "count": "1", + "factoryAddress": "0x1234567890123456789012345678901234567890", + "finalResultsUrl": "https://example.com", + "intermediateResultsUrl": "https://example.com", + "launcher": "0x1234567890123456789012345678901234567891", + "manifestHash": "0x1234567890123456789012345678901234567891", + "manifestUrl": "https://example.com", + "recordingOracle": "0x1234567890123456789012345678901234567891", + "reputationOracle": "0x1234567890123456789012345678901234567891", + "exchangeOracle": "0x1234567890123456789012345678901234567891", + "status": "Complete", + "token": "0x1234567890123456789012345678901234567891", + "totalFundedAmount": "1000000000000000000", + } + + def side_effect(subgraph_url, query, params): + if subgraph_url == NETWORKS[ChainId.POLYGON_AMOY]: + return {"data": {"escrows": [mock_escrow_1, mock_escrow_2]}} + + mock_function.side_effect = side_effect + + filter = EscrowFilter( + chain_id=ChainId.POLYGON_AMOY, + status=[Status.Pending, Status.Complete], + ) + filtered = EscrowUtils.get_escrows(filter) + + mock_function.assert_called_with( + NETWORKS[ChainId.POLYGON_AMOY], + query=get_escrows_query(filter), + params={ + "launcher": None, + "reputationOracle": None, + "recordingOracle": None, + "exchangeOracle": None, + "jobRequesterId": None, + "status": ["Pending", "Complete"], + "from": None, + "to": None, + "first": 10, + "skip": 0, + "orderDirection": "desc", + }, + ) + self.assertEqual(len(filtered), 2) + self.assertEqual(filtered[0].address, mock_escrow_1["address"]) + self.assertEqual(filtered[1].address, mock_escrow_2["address"]) + def test_get_escrow(self): with patch( "human_protocol_sdk.escrow.escrow_utils.get_data_from_subgraph" diff --git a/packages/sdk/typescript/human-protocol-sdk/example/escrow.ts b/packages/sdk/typescript/human-protocol-sdk/example/escrow.ts index 9b691e5c0a..db5840364e 100644 --- a/packages/sdk/typescript/human-protocol-sdk/example/escrow.ts +++ b/packages/sdk/typescript/human-protocol-sdk/example/escrow.ts @@ -10,13 +10,12 @@ export const getEscrows = async () => { } const escrows = await EscrowUtils.getEscrows({ - status: EscrowStatus.Pending, - from: new Date(2023, 4, 8), - to: new Date(2023, 5, 8), + status: [EscrowStatus.Pending, EscrowStatus.Complete], chainId: ChainId.POLYGON_AMOY, + first: 1000, }); - console.log('Pending escrows:', escrows); + console.log('Pending escrows:', escrows.length); }; (async () => { diff --git a/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts b/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts index c583fbb8bd..911337964b 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/escrow.ts @@ -1688,6 +1688,11 @@ export class EscrowUtils { throw ErrorUnsupportedChainID; } + let statuses; + if (filter.status !== undefined) { + statuses = Array.isArray(filter.status) ? filter.status : [filter.status]; + statuses = statuses.map((status) => EscrowStatus[status]); + } const { escrows } = await gqlFetch<{ escrows: EscrowData[] }>( getSubgraphUrl(networkData), GET_ESCROWS_QUERY(filter), @@ -1697,12 +1702,7 @@ export class EscrowUtils { reputationOracle: filter.reputationOracle?.toLowerCase(), recordingOracle: filter.recordingOracle?.toLowerCase(), exchangeOracle: filter.exchangeOracle?.toLowerCase(), - status: - filter.status !== undefined - ? Object.entries(EscrowStatus).find( - ([, value]) => value === filter.status - )?.[0] - : undefined, + status: statuses, from: filter.from ? getUnixTimestamp(filter.from) : undefined, to: filter.to ? getUnixTimestamp(filter.to) : undefined, orderDirection: orderDirection, diff --git a/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/escrow.ts b/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/escrow.ts index 92637f37fa..844c05bb14 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/escrow.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/graphql/queries/escrow.ts @@ -53,7 +53,7 @@ export const GET_ESCROWS_QUERY = (filter: IEscrowsFilter) => { ${reputationOracle ? `reputationOracle: $reputationOracle,` : ''} ${recordingOracle ? `recordingOracle: $recordingOracle,` : ''} ${exchangeOracle ? `exchangeOracle: $exchangeOracle,` : ''} - ${status !== undefined ? `status: $status,` : ''} + ${status !== undefined ? `status_in: $status,` : ''} ${from ? `createdAt_gte: $from,` : ''} ${to ? `createdAt_lte: $to,` : ''} } @@ -66,7 +66,7 @@ export const GET_ESCROWS_QUERY = (filter: IEscrowsFilter) => { $reputationOracle: String $recordingOracle: String $exchangeOracle: String - $status: String + $status: [String!] $from: Int $to: Int $orderDirection: String diff --git a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts index dbf4662971..eb20a5b7b5 100644 --- a/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts +++ b/packages/sdk/typescript/human-protocol-sdk/src/interfaces.ts @@ -92,7 +92,7 @@ export interface IEscrowsFilter extends IPagination { recordingOracle?: string; exchangeOracle?: string; jobRequesterId?: string; - status?: EscrowStatus; + status?: EscrowStatus | EscrowStatus[]; from?: Date; to?: Date; chainId: ChainId; diff --git a/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts b/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts index b9d69c0a01..a0ae44fad1 100644 --- a/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts +++ b/packages/sdk/typescript/human-protocol-sdk/test/escrow.test.ts @@ -2681,6 +2681,48 @@ describe('EscrowUtils', () => { ); }); + test('should successfully getEscrows for the filter with status array', async () => { + const escrows = [ + { + id: '1', + address: '0x0', + amountPaid: '3', + balance: '0', + count: '1', + jobRequesterId: '1', + factoryAddress: '0x0', + launcher: '0x0', + status: 'Pending', + token: '0x0', + totalFundedAmount: '3', + }, + { + id: '2', + address: '0x0', + amountPaid: '3', + balance: '0', + count: '1', + jobRequesterId: '1', + factoryAddress: '0x0', + launcher: '0x0', + status: 'Complete', + token: '0x0', + totalFundedAmount: '3', + }, + ]; + const gqlFetchSpy = vi + .spyOn(gqlFetch, 'default') + .mockResolvedValue({ escrows }); + + const result = await EscrowUtils.getEscrows({ + chainId: ChainId.POLYGON_AMOY, + status: [EscrowStatus.Pending, EscrowStatus.Complete], + }); + + expect(result).toEqual(escrows); + expect(gqlFetchSpy).toHaveBeenCalled(); + }); + test('should successfully getEscrows for the filter', async () => { const escrows = [ {