Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
3bc942b4
Commit
3bc942b4
authored
Feb 09, 2024
by
Max Alekseenko
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' into marketplace-admin-api
parents
1603b336
c7197bb4
Changes
14
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
117 additions
and
30 deletions
+117
-30
buildUrl.test.ts
lib/api/buildUrl.test.ts
+43
-0
buildUrl.ts
lib/api/buildUrl.ts
+7
-1
client.ts
lib/web3/client.ts
+15
-7
AddressTokenTransfers.pw.tsx
ui/address/AddressTokenTransfers.pw.tsx
+2
-2
AddressTokens.pw.tsx
ui/address/AddressTokens.pw.tsx
+2
-2
useBlockQuery.tsx
ui/block/useBlockQuery.tsx
+7
-3
useBlockTxQuery.tsx
ui/block/useBlockTxQuery.tsx
+7
-3
useBlockWithdrawalsQuery.tsx
ui/block/useBlockWithdrawalsQuery.tsx
+7
-2
Address.tsx
ui/pages/Address.tsx
+6
-6
Tokens.pw.tsx
ui/pages/Tokens.pw.tsx
+1
-1
Transaction.tsx
ui/pages/Transaction.tsx
+6
-3
TxDetails.tsx
ui/tx/TxDetails.tsx
+5
-0
TxDetailsDegraded.tsx
ui/tx/TxDetailsDegraded.tsx
+4
-0
TxUserOps.tsx
ui/tx/TxUserOps.tsx
+5
-0
No files found.
lib/api/buildUrl.test.ts
0 → 100644
View file @
3bc942b4
import
buildUrl
from
'
./buildUrl
'
;
test
(
'
builds URL for resource without path params
'
,
()
=>
{
const
url
=
buildUrl
(
'
config_backend_version
'
);
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/config/backend-version
'
);
});
test
(
'
builds URL for resource with path params
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42
'
);
});
describe
(
'
falsy query parameters
'
,
()
=>
{
test
(
'
leaves "false" as query parameter
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
},
{
includeTx
:
false
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42?includeTx=false
'
);
});
test
(
'
leaves "null" as query parameter
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
},
{
includeTx
:
null
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42?includeTx=null
'
);
});
test
(
'
strips out empty string as query parameter
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
},
{
includeTx
:
null
,
sort
:
''
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42?includeTx=null
'
);
});
test
(
'
strips out "undefined" as query parameter
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
},
{
includeTx
:
null
,
sort
:
undefined
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42?includeTx=null
'
);
});
});
test
(
'
builds URL with array-like query parameters
'
,
()
=>
{
const
url
=
buildUrl
(
'
block
'
,
{
height_or_hash
:
'
42
'
},
{
includeTx
:
[
'
0x11
'
,
'
0x22
'
],
sort
:
'
asc
'
});
expect
(
url
).
toBe
(
'
https://localhost:3003/api/v2/blocks/42?includeTx%5B0%5D=0x11&includeTx%5B1%5D=0x22&sort=asc
'
);
});
test
(
'
builds URL for resource with custom API endpoint
'
,
()
=>
{
const
url
=
buildUrl
(
'
token_verified_info
'
,
{
chainId
:
'
42
'
,
hash
:
'
0x11
'
});
expect
(
url
).
toBe
(
'
https://localhost:3005/api/v1/chains/42/token-infos/0x11
'
);
});
lib/api/buildUrl.ts
View file @
3bc942b4
...
...
@@ -19,7 +19,13 @@ export default function buildUrl<R extends ResourceName>(
queryParams
&&
Object
.
entries
(
queryParams
).
forEach
(([
key
,
value
])
=>
{
// there are some pagination params that can be null or false for the next page
value
!==
undefined
&&
value
!==
''
&&
url
.
searchParams
.
append
(
key
,
String
(
value
));
if
(
value
!==
undefined
&&
value
!==
''
)
{
if
(
Array
.
isArray
(
value
))
{
value
.
forEach
((
v
,
i
)
=>
url
.
searchParams
.
append
(
`
${
key
}
[
${
i
}
]`
,
String
(
v
)));
}
else
{
url
.
searchParams
.
append
(
key
,
String
(
value
));
}
}
});
return
url
.
toString
();
...
...
lib/web3/client.ts
View file @
3bc942b4
...
...
@@ -2,10 +2,18 @@ import { createPublicClient, http } from 'viem';
import
currentChain
from
'
./currentChain
'
;
export
const
publicClient
=
createPublicClient
({
export
const
publicClient
=
(()
=>
{
if
(
currentChain
.
rpcUrls
.
public
.
http
.
filter
(
Boolean
).
length
===
0
)
{
return
;
}
try
{
return
createPublicClient
({
chain
:
currentChain
,
transport
:
http
(),
batch
:
{
multicall
:
true
,
},
});
});
}
catch
(
error
)
{}
})();
ui/address/AddressTokenTransfers.pw.tsx
View file @
3bc942b4
...
...
@@ -108,7 +108,7 @@ test.describe('socket', () => {
},
};
const
API_URL_NO_TOKEN
=
buildApiUrl
(
'
address_token_transfers
'
,
{
hash
:
CURRENT_ADDRESS
})
+
'
?type=
'
;
const
API_URL_NO_TOKEN
=
buildApiUrl
(
'
address_token_transfers
'
,
{
hash
:
CURRENT_ADDRESS
});
await
page
.
route
(
API_URL_NO_TOKEN
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
...
...
@@ -144,7 +144,7 @@ test.describe('socket', () => {
},
};
const
API_URL_NO_TOKEN
=
buildApiUrl
(
'
address_token_transfers
'
,
{
hash
:
CURRENT_ADDRESS
})
+
'
?type=
'
;
const
API_URL_NO_TOKEN
=
buildApiUrl
(
'
address_token_transfers
'
,
{
hash
:
CURRENT_ADDRESS
});
await
page
.
route
(
API_URL_NO_TOKEN
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
...
...
ui/address/AddressTokens.pw.tsx
View file @
3bc942b4
...
...
@@ -13,8 +13,8 @@ import AddressTokens from './AddressTokens';
const
ADDRESS_HASH
=
addressMock
.
withName
.
hash
;
const
API_URL_ADDRESS
=
buildApiUrl
(
'
address
'
,
{
hash
:
ADDRESS_HASH
});
const
API_URL_TOKENS
=
buildApiUrl
(
'
address_tokens
'
,
{
hash
:
ADDRESS_HASH
});
const
API_URL_NFT
=
buildApiUrl
(
'
address_nfts
'
,
{
hash
:
ADDRESS_HASH
})
+
'
?type=
'
;
const
API_URL_COLLECTIONS
=
buildApiUrl
(
'
address_collections
'
,
{
hash
:
ADDRESS_HASH
})
+
'
?type=
'
;
const
API_URL_NFT
=
buildApiUrl
(
'
address_nfts
'
,
{
hash
:
ADDRESS_HASH
});
const
API_URL_COLLECTIONS
=
buildApiUrl
(
'
address_collections
'
,
{
hash
:
ADDRESS_HASH
});
const
nextPageParams
=
{
items_count
:
50
,
...
...
ui/block/useBlockQuery.tsx
View file @
3bc942b4
...
...
@@ -50,6 +50,10 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
const
rpcQuery
=
useQuery
<
RpcResponseType
,
unknown
,
Block
|
null
>
({
queryKey
:
[
'
RPC
'
,
'
block
'
,
{
heightOrHash
}
],
queryFn
:
async
()
=>
{
if
(
!
publicClient
)
{
return
null
;
}
const
blockParams
=
heightOrHash
.
startsWith
(
'
0x
'
)
?
{
blockHash
:
heightOrHash
as
`0x
${
string
}
`
}
:
{
blockNumber
:
BigInt
(
heightOrHash
)
};
return
publicClient
.
getBlock
(
blockParams
).
catch
(()
=>
null
);
},
...
...
@@ -86,13 +90,13 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
};
},
placeholderData
:
GET_BLOCK
,
enabled
:
apiQuery
.
isError
||
apiQuery
.
errorUpdateCount
>
0
,
enabled
:
publicClient
!==
undefined
&&
(
apiQuery
.
isError
||
apiQuery
.
errorUpdateCount
>
0
)
,
retry
:
false
,
refetchOnMount
:
false
,
});
React
.
useEffect
(()
=>
{
if
(
apiQuery
.
isPlaceholderData
)
{
if
(
apiQuery
.
isPlaceholderData
||
!
publicClient
)
{
return
;
}
...
...
@@ -109,7 +113,7 @@ export default function useBlockQuery({ heightOrHash }: Params): BlockQuery {
}
},
[
rpcQuery
.
data
,
rpcQuery
.
isPlaceholderData
]);
const
isRpcQuery
=
Boolean
((
apiQuery
.
isError
||
apiQuery
.
isPlaceholderData
)
&&
apiQuery
.
errorUpdateCount
>
0
&&
rpcQuery
.
data
);
const
isRpcQuery
=
Boolean
(
publicClient
&&
(
apiQuery
.
isError
||
apiQuery
.
isPlaceholderData
)
&&
apiQuery
.
errorUpdateCount
>
0
&&
rpcQuery
.
data
);
const
query
=
isRpcQuery
?
rpcQuery
as
UseQueryResult
<
Block
,
ResourceError
<
{
status
:
number
}
>>
:
apiQuery
;
return
{
...
...
ui/block/useBlockTxQuery.tsx
View file @
3bc942b4
...
...
@@ -63,6 +63,10 @@ export default function useBlockTxQuery({ heightOrHash, blockQuery, tab }: Param
const
rpcQuery
=
useQuery
<
RpcResponseType
,
unknown
,
BlockTransactionsResponse
|
null
>
({
queryKey
:
[
'
RPC
'
,
'
block_txs
'
,
{
heightOrHash
}
],
queryFn
:
async
()
=>
{
if
(
!
publicClient
)
{
return
null
;
}
const
blockParams
=
heightOrHash
.
startsWith
(
'
0x
'
)
?
{
blockHash
:
heightOrHash
as
`0x
${
string
}
`
,
includeTransactions
:
true
}
:
{
blockNumber
:
BigInt
(
heightOrHash
),
includeTransactions
:
true
};
...
...
@@ -125,13 +129,13 @@ export default function useBlockTxQuery({ heightOrHash, blockQuery, tab }: Param
};
},
placeholderData
:
GET_BLOCK_WITH_TRANSACTIONS
,
enabled
:
tab
===
'
txs
'
&&
(
blockQuery
.
isDegradedData
||
apiQuery
.
isError
||
apiQuery
.
errorUpdateCount
>
0
),
enabled
:
publicClient
!==
undefined
&&
tab
===
'
txs
'
&&
(
blockQuery
.
isDegradedData
||
apiQuery
.
isError
||
apiQuery
.
errorUpdateCount
>
0
),
retry
:
false
,
refetchOnMount
:
false
,
});
React
.
useEffect
(()
=>
{
if
(
apiQuery
.
isPlaceholderData
)
{
if
(
apiQuery
.
isPlaceholderData
||
!
publicClient
)
{
return
;
}
...
...
@@ -151,7 +155,7 @@ export default function useBlockTxQuery({ heightOrHash, blockQuery, tab }: Param
const
isRpcQuery
=
Boolean
((
blockQuery
.
isDegradedData
||
((
apiQuery
.
isError
||
apiQuery
.
isPlaceholderData
)
&&
apiQuery
.
errorUpdateCount
>
0
)
)
&&
rpcQuery
.
data
);
)
&&
rpcQuery
.
data
&&
publicClient
);
const
rpcQueryWithPages
:
QueryWithPagesResult
<
'
block_txs
'
>
=
React
.
useMemo
(()
=>
{
return
{
...
...
ui/block/useBlockWithdrawalsQuery.tsx
View file @
3bc942b4
...
...
@@ -65,6 +65,10 @@ export default function useBlockWithdrawalsQuery({ heightOrHash, blockQuery, tab
const
rpcQuery
=
useQuery
<
RpcResponseType
,
unknown
,
BlockWithdrawalsResponse
|
null
>
({
queryKey
:
[
'
RPC
'
,
'
block
'
,
{
heightOrHash
}
],
queryFn
:
async
()
=>
{
if
(
!
publicClient
)
{
return
null
;
}
const
blockParams
=
heightOrHash
.
startsWith
(
'
0x
'
)
?
{
blockHash
:
heightOrHash
as
`0x
${
string
}
`
}
:
{
blockNumber
:
BigInt
(
heightOrHash
)
};
return
publicClient
.
getBlock
(
blockParams
).
catch
(()
=>
null
);
},
...
...
@@ -89,6 +93,7 @@ export default function useBlockWithdrawalsQuery({ heightOrHash, blockQuery, tab
},
placeholderData
:
GET_BLOCK
,
enabled
:
publicClient
!==
undefined
&&
tab
===
'
withdrawals
'
&&
config
.
features
.
beaconChain
.
isEnabled
&&
(
blockQuery
.
isDegradedData
||
apiQuery
.
isError
||
apiQuery
.
errorUpdateCount
>
0
),
...
...
@@ -97,7 +102,7 @@ export default function useBlockWithdrawalsQuery({ heightOrHash, blockQuery, tab
});
React
.
useEffect
(()
=>
{
if
(
apiQuery
.
isPlaceholderData
)
{
if
(
apiQuery
.
isPlaceholderData
||
!
publicClient
)
{
return
;
}
...
...
@@ -117,7 +122,7 @@ export default function useBlockWithdrawalsQuery({ heightOrHash, blockQuery, tab
const
isRpcQuery
=
Boolean
((
blockQuery
.
isDegradedData
||
((
apiQuery
.
isError
||
apiQuery
.
isPlaceholderData
)
&&
apiQuery
.
errorUpdateCount
>
0
)
)
&&
rpcQuery
.
data
);
)
&&
rpcQuery
.
data
&&
publicClient
);
const
rpcQueryWithPages
:
QueryWithPagesResult
<
'
block_withdrawals
'
>
=
React
.
useMemo
(()
=>
{
return
{
...
...
ui/pages/Address.tsx
View file @
3bc942b4
...
...
@@ -67,7 +67,7 @@ const AddressPageContent = () => {
const
userOpsAccountQuery
=
useApiQuery
(
'
user_ops_account
'
,
{
pathParams
:
{
hash
},
queryOptions
:
{
enabled
:
Boolean
(
hash
),
enabled
:
Boolean
(
hash
)
&&
config
.
features
.
userOps
.
isEnabled
,
placeholderData
:
USER_OPS_ACCOUNT
,
},
});
...
...
@@ -160,16 +160,18 @@ const AddressPageContent = () => {
].
filter
(
Boolean
);
},
[
addressQuery
.
data
,
contractTabs
,
addressTabsCountersQuery
.
data
,
userOpsAccountQuery
.
data
]);
const
isLoading
=
addressQuery
.
isPlaceholderData
||
(
config
.
features
.
userOps
.
isEnabled
&&
userOpsAccountQuery
.
isPlaceholderData
);
const
tags
=
(
<
EntityTags
data=
{
addressQuery
.
data
}
isLoading=
{
addressQuery
.
isPlaceholderData
}
isLoading=
{
isLoading
}
tagsBefore=
{
[
!
addressQuery
.
data
?.
is_contract
?
{
label
:
'
eoa
'
,
display_name
:
'
EOA
'
}
:
undefined
,
addressQuery
.
data
?.
implementation_address
?
{
label
:
'
proxy
'
,
display_name
:
'
Proxy
'
}
:
undefined
,
addressQuery
.
data
?.
token
?
{
label
:
'
token
'
,
display_name
:
'
Token
'
}
:
undefined
,
isSafeAddress
?
{
label
:
'
safe
'
,
display_name
:
'
Multisig: Safe
'
}
:
undefined
,
userOpsAccountQuery
.
data
?
{
label
:
'
user_ops_acc
'
,
display_name
:
'
Smart contract wallet
'
}
:
undefined
,
config
.
features
.
userOps
.
isEnabled
&&
userOpsAccountQuery
.
data
?
{
label
:
'
user_ops_acc
'
,
display_name
:
'
Smart contract wallet
'
}
:
undefined
,
]
}
/>
);
...
...
@@ -189,8 +191,6 @@ const AddressPageContent = () => {
};
},
[
appProps
.
referrer
]);
const
isLoading
=
addressQuery
.
isPlaceholderData
;
const
titleSecondRow
=
(
<
Flex
alignItems=
"center"
w=
"100%"
columnGap=
{
2
}
rowGap=
{
2
}
flexWrap=
{
{
base
:
'
wrap
'
,
lg
:
'
nowrap
'
}
}
>
{
addressQuery
.
data
?.
ens_domain_name
&&
(
...
...
@@ -241,7 +241,7 @@ const AddressPageContent = () => {
<
AddressDetails
addressQuery=
{
addressQuery
}
scrollRef=
{
tabsScrollRef
}
/>
{
/* should stay before tabs to scroll up with pagination */
}
<
Box
ref=
{
tabsScrollRef
}
></
Box
>
{
(
addressQuery
.
isPlaceholderData
||
addressTabsCountersQuery
.
isPlaceholderData
||
userOpsAccount
Query
.
isPlaceholderData
)
?
{
(
isLoading
||
addressTabsCounters
Query
.
isPlaceholderData
)
?
<
TabsSkeleton
tabs=
{
tabs
}
/>
:
content
}
...
...
ui/pages/Tokens.pw.tsx
View file @
3bc942b4
...
...
@@ -111,7 +111,7 @@ base.describe('bridged tokens', async() => {
});
test
(
'
base view
'
,
async
({
mount
,
page
})
=>
{
await
page
.
route
(
BRIDGED_TOKENS_API_URL
+
'
?chain_ids=99
'
,
(
route
)
=>
route
.
fulfill
({
await
page
.
route
(
BRIDGED_TOKENS_API_URL
+
'
?chain_ids
%5B0%5D
=99
'
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
body
:
JSON
.
stringify
(
bridgedFilteredTokens
),
}));
...
...
ui/pages/Transaction.tsx
View file @
3bc942b4
...
...
@@ -7,6 +7,7 @@ import config from 'configs/app';
import
{
useAppContext
}
from
'
lib/contexts/app
'
;
import
throwOnResourceLoadError
from
'
lib/errors/throwOnResourceLoadError
'
;
import
getQueryParamString
from
'
lib/router/getQueryParamString
'
;
import
{
publicClient
}
from
'
lib/web3/client
'
;
import
TextAd
from
'
ui/shared/ad/TextAd
'
;
import
EntityTags
from
'
ui/shared/EntityTags
'
;
import
PageTitle
from
'
ui/shared/Page/PageTitle
'
;
...
...
@@ -33,7 +34,7 @@ const TransactionPageContent = () => {
const
txQuery
=
useTxQuery
();
const
{
data
,
isPlaceholderData
,
isError
,
error
,
errorUpdateCount
}
=
txQuery
;
const
showDegradedView
=
(
isError
||
isPlaceholderData
)
&&
errorUpdateCount
>
0
;
const
showDegradedView
=
publicClient
&&
(
isError
||
isPlaceholderData
)
&&
errorUpdateCount
>
0
;
const
tabs
:
Array
<
RoutedTab
>
=
(()
=>
{
const
detailsComponent
=
showDegradedView
?
...
...
@@ -97,9 +98,11 @@ const TransactionPageContent = () => {
return
<
RoutedTabs
tabs=
{
tabs
}
/>;
})();
if
(
error
?.
status
===
422
)
{
if
(
isError
&&
!
showDegradedView
)
{
if
(
error
?.
status
===
422
||
error
?.
status
===
404
)
{
throwOnResourceLoadError
({
resource
:
'
tx
'
,
error
,
isError
:
true
});
}
}
return
(
<>
...
...
ui/tx/TxDetails.tsx
View file @
3bc942b4
import
React
from
'
react
'
;
import
TestnetWarning
from
'
ui/shared/alerts/TestnetWarning
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
TxInfo
from
'
./details/TxInfo
'
;
import
type
{
TxQuery
}
from
'
./useTxQuery
'
;
...
...
@@ -10,6 +11,10 @@ interface Props {
}
const
TxDetails
=
({
txQuery
}:
Props
)
=>
{
if
(
txQuery
.
isError
)
{
return
<
DataFetchAlert
/>;
}
return
(
<>
<
TestnetWarning
mb=
{
6
}
isLoading=
{
txQuery
.
isPlaceholderData
}
/>
...
...
ui/tx/TxDetailsDegraded.tsx
View file @
3bc942b4
...
...
@@ -37,6 +37,10 @@ const TxDetailsDegraded = ({ hash, txQuery }: Props) => {
const
query
=
useQuery
<
RpcResponseType
,
unknown
,
Transaction
|
null
>
({
queryKey
:
[
'
RPC
'
,
'
tx
'
,
{
hash
}
],
queryFn
:
async
()
=>
{
if
(
!
publicClient
)
{
throw
new
Error
(
'
No public RPC client
'
);
}
const
tx
=
await
publicClient
.
getTransaction
({
hash
:
hash
as
`0x
${
string
}
`
});
if
(
!
tx
)
{
...
...
ui/tx/TxUserOps.tsx
View file @
3bc942b4
...
...
@@ -2,6 +2,7 @@ import React from 'react';
import
{
USER_OPS_ITEM
}
from
'
stubs/userOps
'
;
import
{
generateListStub
}
from
'
stubs/utils
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
useQueryWithPages
from
'
ui/shared/pagination/useQueryWithPages
'
;
import
TxPendingAlert
from
'
ui/tx/TxPendingAlert
'
;
import
TxSocketAlert
from
'
ui/tx/TxSocketAlert
'
;
...
...
@@ -28,6 +29,10 @@ const TxUserOps = ({ txQuery }: Props) => {
return
txQuery
.
socketStatus
?
<
TxSocketAlert
status=
{
txQuery
.
socketStatus
}
/>
:
<
TxPendingAlert
/>;
}
if
(
txQuery
.
isError
)
{
return
<
DataFetchAlert
/>;
}
return
<
UserOpsContent
query=
{
userOpsQuery
}
showTx=
{
false
}
/>;
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment