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
c9397c2b
Unverified
Commit
c9397c2b
authored
Dec 16, 2022
by
Igor Stuev
Committed by
GitHub
Dec 16, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #444 from blockscout/address-internal-txn
Address internal txn
parents
7e78cf39
3379c833
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
461 additions
and
2 deletions
+461
-2
internal-transactions.ts
pages/api/addresses/[id]/internal-transactions.ts
+13
-0
address.ts
types/api/address.ts
+10
-0
pagination.ts
types/api/pagination.ts
+6
-1
queries.ts
types/client/queries.ts
+1
-0
AddressInternalTxs.pw.tsx
ui/address/AddressInternalTxs.pw.tsx
+35
-0
AddressInternalTxs.tsx
ui/address/AddressInternalTxs.tsx
+104
-0
AddressTokenTransfers.tsx
ui/address/AddressTokenTransfers.tsx
+2
-0
AddressTxs.tsx
ui/address/AddressTxs.tsx
+2
-0
AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png
.../AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png
+0
-0
AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png
..._/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png
+0
-0
AddressIntTxsList.tsx
ui/address/internals/AddressIntTxsList.tsx
+21
-0
AddressIntTxsListItem.tsx
ui/address/internals/AddressIntTxsListItem.tsx
+78
-0
AddressIntTxsSkeletonDesktop.tsx
ui/address/internals/AddressIntTxsSkeletonDesktop.tsx
+11
-0
AddressIntTxsSkeletonMobile.tsx
ui/address/internals/AddressIntTxsSkeletonMobile.tsx
+50
-0
AddressIntTxsTable.tsx
ui/address/internals/AddressIntTxsTable.tsx
+41
-0
AddressIntTxsTableItem.tsx
ui/address/internals/AddressIntTxsTableItem.tsx
+85
-0
Address.tsx
ui/pages/Address.tsx
+2
-1
No files found.
pages/api/addresses/[id]/internal-transactions.ts
0 → 100644
View file @
c9397c2b
import
type
{
NextApiRequest
}
from
'
next
'
;
import
getSearchParams
from
'
lib/api/getSearchParams
'
;
import
handler
from
'
lib/api/handler
'
;
const
getUrl
=
(
req
:
NextApiRequest
)
=>
{
const
searchParamsStr
=
getSearchParams
(
req
);
return
`/v2/addresses/
${
req
.
query
.
id
}
/internal-transactions
${
searchParamsStr
?
'
?
'
+
searchParamsStr
:
''
}
`
;
};
const
requestHandler
=
handler
(
getUrl
,
[
'
GET
'
]);
export
default
requestHandler
;
types/api/address.ts
View file @
c9397c2b
...
...
@@ -2,6 +2,7 @@ import type { Transaction } from 'types/api/transaction';
import
type
{
AddressTag
,
WatchlistName
}
from
'
./addressParams
'
;
import
type
{
Block
}
from
'
./block
'
;
import
type
{
InternalTransaction
}
from
'
./internalTransaction
'
;
import
type
{
TokenInfo
,
TokenType
}
from
'
./tokenInfo
'
;
import
type
{
TokenTransfer
,
TokenTransferPagination
}
from
'
./tokenTransfer
'
;
...
...
@@ -86,3 +87,12 @@ export interface AddressBlocksValidatedResponse {
items_count
:
number
;
};
}
export
interface
AddressInternalTxsResponse
{
items
:
Array
<
InternalTransaction
>
;
next_page_params
:
{
block_number
:
number
;
index
:
number
;
items_count
:
number
;
transaction_index
:
number
;
}
|
null
;
}
types/api/pagination.ts
View file @
c9397c2b
...
...
@@ -5,6 +5,7 @@ import type {
AddressTokenTransferFilters
,
AddressCoinBalanceHistoryResponse
,
AddressBlocksValidatedResponse
,
AddressInternalTxsResponse
,
}
from
'
types/api/address
'
;
import
type
{
BlocksResponse
,
BlockTransactionsResponse
,
BlockFilters
}
from
'
types/api/block
'
;
import
type
{
InternalTransactionsResponse
}
from
'
types/api/internalTransaction
'
;
...
...
@@ -18,6 +19,7 @@ import type { KeysOfObjectOrNull } from 'types/utils/KeysOfObjectOrNull';
export
type
PaginatedQueryKeys
=
QueryKeys
.
addressTxs
|
QueryKeys
.
addressTokenTransfers
|
QueryKeys
.
addressInternalTxs
|
QueryKeys
.
blocks
|
QueryKeys
.
blocksReorgs
|
QueryKeys
.
blocksUncles
|
...
...
@@ -31,6 +33,7 @@ export type PaginatedQueryKeys =
QueryKeys
.
addressBlocksValidated
;
export
type
PaginatedResponse
<
Q
extends
PaginatedQueryKeys
>
=
Q
extends
QueryKeys
.
addressInternalTxs
?
AddressInternalTxsResponse
:
Q
extends
QueryKeys
.
addressTxs
?
AddressTransactionsResponse
:
Q
extends
QueryKeys
.
addressTokenTransfers
?
AddressTokenTransferResponse
:
Q
extends
(
QueryKeys
.
blocks
|
QueryKeys
.
blocksReorgs
|
QueryKeys
.
blocksUncles
)
?
BlocksResponse
:
...
...
@@ -45,7 +48,7 @@ export type PaginatedResponse<Q extends PaginatedQueryKeys> =
never
export
type
PaginationFilters
<
Q
extends
PaginatedQueryKeys
>
=
Q
extends
QueryKeys
.
addressTxs
?
AddressTxsFilters
:
Q
extends
(
QueryKeys
.
addressTxs
|
QueryKeys
.
addressInternalTxs
)
?
AddressTxsFilters
:
Q
extends
QueryKeys
.
addressTokenTransfers
?
AddressTokenTransferFilters
:
Q
extends
QueryKeys
.
blocks
?
BlockFilters
:
Q
extends
QueryKeys
.
txsValidate
?
TTxsFilters
:
...
...
@@ -61,6 +64,7 @@ type PaginationFields = {
export
const
PAGINATION_FIELDS
:
PaginationFields
=
{
[
QueryKeys
.
addressTxs
]:
[
'
block_number
'
,
'
items_count
'
,
'
index
'
],
[
QueryKeys
.
addressInternalTxs
]:
[
'
block_number
'
,
'
items_count
'
,
'
index
'
,
'
transaction_index
'
],
[
QueryKeys
.
addressTokenTransfers
]:
[
'
block_number
'
,
'
items_count
'
,
'
index
'
,
'
transaction_hash
'
],
[
QueryKeys
.
blocks
]:
[
'
block_number
'
,
'
items_count
'
],
[
QueryKeys
.
blocksReorgs
]:
[
'
block_number
'
,
'
items_count
'
],
...
...
@@ -81,6 +85,7 @@ type PaginationFiltersFields = {
export
const
PAGINATION_FILTERS_FIELDS
:
PaginationFiltersFields
=
{
[
QueryKeys
.
addressTxs
]:
[
'
filter
'
],
[
QueryKeys
.
addressInternalTxs
]:
[
'
filter
'
],
[
QueryKeys
.
addressTokenTransfers
]:
[
'
filter
'
,
'
type
'
],
[
QueryKeys
.
addressCoinBalanceHistory
]:
[],
[
QueryKeys
.
addressBlocksValidated
]:
[],
...
...
types/client/queries.ts
View file @
c9397c2b
...
...
@@ -29,4 +29,5 @@ export enum QueryKeys {
addressTxs
=
'
addressTxs
'
,
addressTokenTransfers
=
'
addressTokenTransfers
'
,
addressBlocksValidated
=
'
address-blocks-validated
'
,
addressInternalTxs
=
'
address-internal-txs
'
,
}
ui/address/AddressInternalTxs.pw.tsx
0 → 100644
View file @
c9397c2b
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
{
test
,
expect
}
from
'
@playwright/experimental-ct-react
'
;
import
React
from
'
react
'
;
import
*
as
internalTxsMock
from
'
mocks/txs/internalTxs
'
;
import
TestApp
from
'
playwright/TestApp
'
;
import
AddressInternalTxs
from
'
./AddressInternalTxs
'
;
const
ADDRESS_HASH
=
internalTxsMock
.
base
.
from
.
hash
;
const
API_URL_TX_INTERNALS
=
`/node-api/addresses/
${
ADDRESS_HASH
}
/internal-transactions`
;
const
hooksConfig
=
{
router
:
{
query
:
{
id
:
ADDRESS_HASH
},
},
};
test
(
'
base view +@mobile
'
,
async
({
mount
,
page
})
=>
{
await
page
.
route
(
API_URL_TX_INTERNALS
,
(
route
)
=>
route
.
fulfill
({
status
:
200
,
body
:
JSON
.
stringify
(
internalTxsMock
.
baseResponse
),
}));
const
component
=
await
mount
(
<
TestApp
>
<
Box
h=
{
{
base
:
'
134px
'
,
lg
:
6
}
}
/>
<
AddressInternalTxs
/>
</
TestApp
>,
{
hooksConfig
},
);
await
page
.
waitForResponse
(
API_URL_TX_INTERNALS
),
await
expect
(
component
).
toHaveScreenshot
();
});
ui/address/AddressInternalTxs.tsx
0 → 100644
View file @
c9397c2b
import
{
Text
,
Show
,
Hide
}
from
'
@chakra-ui/react
'
;
import
castArray
from
'
lodash/castArray
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
{
Element
}
from
'
react-scroll
'
;
import
type
{
AddressFromToFilter
}
from
'
types/api/address
'
;
import
{
AddressFromToFilterValues
}
from
'
types/api/address
'
;
import
{
QueryKeys
}
from
'
types/client/queries
'
;
import
getFilterValueFromQuery
from
'
lib/getFilterValueFromQuery
'
;
import
useQueryWithPages
from
'
lib/hooks/useQueryWithPages
'
;
import
{
apos
}
from
'
lib/html-entities
'
;
import
AddressIntTxsSkeletonDesktop
from
'
ui/address/internals/AddressIntTxsSkeletonDesktop
'
;
import
AddressIntTxsSkeletonMobile
from
'
ui/address/internals/AddressIntTxsSkeletonMobile
'
;
import
AddressIntTxsTable
from
'
ui/address/internals/AddressIntTxsTable
'
;
import
EmptySearchResult
from
'
ui/apps/EmptySearchResult
'
;
import
ActionBar
from
'
ui/shared/ActionBar
'
;
import
DataFetchAlert
from
'
ui/shared/DataFetchAlert
'
;
import
Pagination
from
'
ui/shared/Pagination
'
;
import
AddressTxsFilter
from
'
./AddressTxsFilter
'
;
import
AddressIntTxsList
from
'
./internals/AddressIntTxsList
'
;
const
SCROLL_ELEM
=
'
address-internas-txs
'
;
const
SCROLL_OFFSET
=
-
100
;
const
getFilterValue
=
(
getFilterValueFromQuery
<
AddressFromToFilter
>
).
bind
(
null
,
AddressFromToFilterValues
);
const
AddressInternalTxs
=
()
=>
{
const
router
=
useRouter
();
const
[
filterValue
,
setFilterValue
]
=
React
.
useState
<
AddressFromToFilter
>
(
getFilterValue
(
router
.
query
.
filter
));
const
queryId
=
router
.
query
.
id
;
const
queryIdArray
=
castArray
(
queryId
);
const
queryIdStr
=
queryIdArray
[
0
];
const
{
data
,
isLoading
,
isError
,
pagination
,
onFilterChange
}
=
useQueryWithPages
({
apiPath
:
`/node-api/addresses/
${
queryId
}
/internal-transactions`
,
queryName
:
QueryKeys
.
addressInternalTxs
,
queryIds
:
queryIdArray
,
filters
:
{
filter
:
filterValue
},
scroll
:
{
elem
:
SCROLL_ELEM
,
offset
:
SCROLL_OFFSET
},
});
const
isPaginatorHidden
=
!
isLoading
&&
!
isError
&&
pagination
.
page
===
1
&&
!
pagination
.
hasNextPage
;
const
handleFilterChange
=
React
.
useCallback
((
val
:
string
|
Array
<
string
>
)
=>
{
const
newVal
=
getFilterValue
(
val
);
setFilterValue
(
newVal
);
onFilterChange
({
filter
:
newVal
});
},
[
onFilterChange
]);
if
(
isLoading
)
{
return
(
<>
<
Show
below=
"lg"
><
AddressIntTxsSkeletonMobile
/></
Show
>
<
Hide
below=
"lg"
><
AddressIntTxsSkeletonDesktop
/></
Hide
>
</>
);
}
if
(
isError
)
{
return
<
DataFetchAlert
/>;
}
if
(
data
.
items
.
length
===
0
&&
!
filterValue
)
{
return
<
Text
as=
"span"
>
There are no internal transactions for this address.
</
Text
>;
}
let
content
;
if
(
data
.
items
.
length
===
0
)
{
content
=
<
EmptySearchResult
text=
{
`Couldn${ apos }t find any transaction that matches your query.`
}
/>;
}
else
{
content
=
(
<>
<
Show
below=
"lg"
ssr=
{
false
}
>
<
AddressIntTxsList
data=
{
data
.
items
}
currentAddress=
{
queryIdStr
}
/>
</
Show
>
<
Hide
below=
"lg"
ssr=
{
false
}
>
<
AddressIntTxsTable
data=
{
data
.
items
}
currentAddress=
{
queryIdStr
}
/>
</
Hide
>
</>
);
}
return
(
<
Element
name=
{
SCROLL_ELEM
}
>
<
ActionBar
mt=
{
-
6
}
>
<
AddressTxsFilter
defaultFilter=
{
filterValue
}
onFilterChange=
{
handleFilterChange
}
isActive=
{
Boolean
(
filterValue
)
}
/>
{
!
isPaginatorHidden
&&
<
Pagination
ml=
"auto"
{
...
pagination
}
/>
}
</
ActionBar
>
{
content
}
</
Element
>
);
};
export
default
AddressInternalTxs
;
ui/address/AddressTokenTransfers.tsx
View file @
c9397c2b
import
castArray
from
'
lodash/castArray
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
...
...
@@ -13,6 +14,7 @@ const AddressTokenTransfers = () => {
<
TokenTransfer
path=
{
`/node-api/addresses/${ hash }/token-transfers`
}
queryName=
{
QueryKeys
.
addressTokenTransfers
}
queryIds=
{
castArray
(
router
.
query
.
id
)
}
baseAddress=
{
typeof
hash
===
'
string
'
?
hash
:
undefined
}
/>
);
...
...
ui/address/AddressTxs.tsx
View file @
c9397c2b
import
castArray
from
'
lodash/castArray
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
{
Element
}
from
'
react-scroll
'
;
...
...
@@ -30,6 +31,7 @@ const AddressTxs = () => {
const
addressTxsQuery
=
useQueryWithPages
({
apiPath
:
`/node-api/addresses/
${
router
.
query
.
id
}
/transactions`
,
queryName
:
QueryKeys
.
addressTxs
,
queryIds
:
castArray
(
router
.
query
.
id
),
filters
:
{
filter
:
filterValue
},
scroll
:
{
elem
:
SCROLL_ELEM
,
offset
:
SCROLL_OFFSET
},
});
...
...
ui/address/__screenshots__/AddressInternalTxs.pw.tsx_default_base-view-mobile-1.png
0 → 100644
View file @
c9397c2b
46 KB
ui/address/__screenshots__/AddressInternalTxs.pw.tsx_mobile_base-view-mobile-1.png
0 → 100644
View file @
c9397c2b
44.3 KB
ui/address/internals/AddressIntTxsList.tsx
0 → 100644
View file @
c9397c2b
import
{
Box
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
InternalTransaction
}
from
'
types/api/internalTransaction
'
;
import
AddressIntTxsListItem
from
'
ui/address/internals/AddressIntTxsListItem
'
;
type
Props
=
{
data
:
Array
<
InternalTransaction
>
;
currentAddress
:
string
;
}
const
AddressIntTxsList
=
({
data
,
currentAddress
}:
Props
)
=>
{
return
(
<
Box
>
{
data
.
map
((
item
)
=>
<
AddressIntTxsListItem
key=
{
item
.
transaction_hash
}
{
...
item
}
currentAddress=
{
currentAddress
}
/>)
}
</
Box
>
);
};
export
default
AddressIntTxsList
;
ui/address/internals/AddressIntTxsListItem.tsx
0 → 100644
View file @
c9397c2b
import
{
Flex
,
Tag
,
Icon
,
Box
,
HStack
,
Text
,
Link
}
from
'
@chakra-ui/react
'
;
import
BigNumber
from
'
bignumber.js
'
;
import
React
from
'
react
'
;
import
type
{
InternalTransaction
}
from
'
types/api/internalTransaction
'
;
import
appConfig
from
'
configs/app/config
'
;
import
eastArrowIcon
from
'
icons/arrows/east.svg
'
;
import
dayjs
from
'
lib/date/dayjs
'
;
import
link
from
'
lib/link/link
'
;
import
AccountListItemMobile
from
'
ui/shared/AccountListItemMobile
'
;
import
Address
from
'
ui/shared/address/Address
'
;
import
AddressIcon
from
'
ui/shared/address/AddressIcon
'
;
import
AddressLink
from
'
ui/shared/address/AddressLink
'
;
import
InOutTag
from
'
ui/shared/InOutTag
'
;
import
TxStatus
from
'
ui/shared/TxStatus
'
;
import
{
TX_INTERNALS_ITEMS
}
from
'
ui/tx/internals/utils
'
;
type
Props
=
InternalTransaction
&
{
currentAddress
:
string
};
const
TxInternalsListItem
=
({
type
,
from
,
to
,
value
,
success
,
error
,
created_contract
:
createdContract
,
transaction_hash
:
txnHash
,
block
,
timestamp
,
currentAddress
,
}:
Props
)
=>
{
const
typeTitle
=
TX_INTERNALS_ITEMS
.
find
(({
id
})
=>
id
===
type
)?.
title
;
const
toData
=
to
?
to
:
createdContract
;
const
isOut
=
Boolean
(
currentAddress
&&
currentAddress
===
from
.
hash
);
const
isIn
=
Boolean
(
currentAddress
&&
currentAddress
===
to
?.
hash
);
return
(
<
AccountListItemMobile
rowGap=
{
3
}
>
<
Flex
>
{
typeTitle
&&
<
Tag
colorScheme=
"cyan"
mr=
{
2
}
>
{
typeTitle
}
</
Tag
>
}
<
TxStatus
status=
{
success
?
'
ok
'
:
'
error
'
}
errorText=
{
error
}
/>
</
Flex
>
<
Flex
justifyContent=
"space-between"
width=
"100%"
>
<
AddressLink
fontWeight=
"700"
hash=
{
txnHash
}
truncation=
"constant"
type=
"transaction"
/>
<
Text
variant=
"secondary"
fontWeight=
"400"
fontSize=
"sm"
>
{
dayjs
(
timestamp
).
fromNow
()
}
</
Text
>
</
Flex
>
<
HStack
spacing=
{
1
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Block
</
Text
>
<
Link
href=
{
link
(
'
block
'
,
{
id
:
block
.
toString
()
})
}
>
{
block
}
</
Link
>
</
HStack
>
<
Box
w=
"100%"
display=
"flex"
columnGap=
{
3
}
>
<
Address
width=
"calc((100% - 48px) / 2)"
>
<
AddressIcon
hash=
{
from
.
hash
}
/>
<
AddressLink
ml=
{
2
}
fontWeight=
"500"
hash=
{
from
.
hash
}
/>
</
Address
>
{
(
isIn
||
isOut
)
?
<
InOutTag
isIn=
{
isIn
}
isOut=
{
isOut
}
/>
:
<
Icon
as=
{
eastArrowIcon
}
boxSize=
{
6
}
color=
"gray.500"
/>
}
<
Address
width=
"calc((100% - 48px) / 2)"
>
<
AddressIcon
hash=
{
toData
.
hash
}
/>
<
AddressLink
ml=
{
2
}
fontWeight=
"500"
hash=
{
toData
.
hash
}
/>
</
Address
>
</
Box
>
<
HStack
spacing=
{
3
}
>
<
Text
fontSize=
"sm"
fontWeight=
{
500
}
>
Value
{
appConfig
.
network
.
currency
.
symbol
}
</
Text
>
<
Text
fontSize=
"sm"
variant=
"secondary"
>
{
BigNumber
(
value
).
div
(
BigNumber
(
10
**
appConfig
.
network
.
currency
.
decimals
)).
toFormat
()
}
</
Text
>
</
HStack
>
</
AccountListItemMobile
>
);
};
export
default
TxInternalsListItem
;
ui/address/internals/AddressIntTxsSkeletonDesktop.tsx
0 → 100644
View file @
c9397c2b
import
React
from
'
react
'
;
import
SkeletonTable
from
'
ui/shared/SkeletonTable
'
;
const
TxInternalsSkeletonDesktop
=
()
=>
{
return
(
<
SkeletonTable
columns=
{
[
'
15%
'
,
'
15%
'
,
'
10%
'
,
'
20%
'
,
'
20%
'
,
'
20%
'
]
}
/>
);
};
export
default
TxInternalsSkeletonDesktop
;
ui/address/internals/AddressIntTxsSkeletonMobile.tsx
0 → 100644
View file @
c9397c2b
import
{
Skeleton
,
SkeletonCircle
,
Flex
,
Box
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
const
TxInternalsSkeletonMobile
=
()
=>
{
const
borderColor
=
useColorModeValue
(
'
blackAlpha.200
'
,
'
whiteAlpha.200
'
);
return
(
<
Box
>
{
Array
.
from
(
Array
(
2
)).
map
((
item
,
index
)
=>
(
<
Flex
key=
{
index
}
rowGap=
{
3
}
flexDirection=
"column"
paddingY=
{
6
}
borderTopWidth=
"1px"
borderColor=
{
borderColor
}
_last=
{
{
borderBottomWidth
:
'
1px
'
,
}
}
>
<
Flex
h=
{
6
}
>
<
Skeleton
w=
"100px"
mr=
{
2
}
/>
<
Skeleton
w=
"90px"
/>
</
Flex
>
<
Flex
h=
{
6
}
>
<
Skeleton
w=
"100%"
mr=
{
2
}
/>
<
Skeleton
w=
"60px"
/>
</
Flex
>
<
Flex
h=
{
6
}
>
<
Skeleton
w=
"70px"
mr=
{
2
}
/>
<
Skeleton
w=
"30px"
/>
</
Flex
>
<
Flex
h=
{
6
}
>
<
SkeletonCircle
size=
"6"
mr=
{
2
}
flexShrink=
{
0
}
/>
<
Skeleton
flexGrow=
{
1
}
mr=
{
3
}
/>
<
Skeleton
w=
{
6
}
mr=
{
3
}
/>
<
SkeletonCircle
size=
"6"
mr=
{
2
}
flexShrink=
{
0
}
/>
<
Skeleton
flexGrow=
{
1
}
mr=
{
3
}
/>
</
Flex
>
<
Flex
h=
{
6
}
>
<
Skeleton
w=
"70px"
mr=
{
2
}
/>
<
Skeleton
w=
"30px"
/>
</
Flex
>
</
Flex
>
))
}
</
Box
>
);
};
export
default
TxInternalsSkeletonMobile
;
ui/address/internals/AddressIntTxsTable.tsx
0 → 100644
View file @
c9397c2b
import
{
Table
,
Tbody
,
Tr
,
Th
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
InternalTransaction
}
from
'
types/api/internalTransaction
'
;
import
appConfig
from
'
configs/app/config
'
;
import
{
default
as
Thead
}
from
'
ui/shared/TheadSticky
'
;
import
AddressIntTxsTableItem
from
'
./AddressIntTxsTableItem
'
;
interface
Props
{
data
:
Array
<
InternalTransaction
>
;
currentAddress
:
string
;
}
const
AddressIntTxsTable
=
({
data
,
currentAddress
}:
Props
)
=>
{
return
(
<
Table
variant=
"simple"
size=
"sm"
>
<
Thead
top=
{
80
}
>
<
Tr
>
<
Th
width=
"15%"
>
Parent txn hash
</
Th
>
<
Th
width=
"15%"
>
Type
</
Th
>
<
Th
width=
"10%"
>
Block
</
Th
>
<
Th
width=
"20%"
>
From
</
Th
>
<
Th
width=
"48px"
px=
{
0
}
/>
<
Th
width=
"20%"
>
To
</
Th
>
<
Th
width=
"20%"
isNumeric
>
Value
{
appConfig
.
network
.
currency
.
symbol
}
</
Th
>
</
Tr
>
</
Thead
>
<
Tbody
>
{
data
.
map
((
item
)
=>
(
<
AddressIntTxsTableItem
key=
{
item
.
transaction_hash
}
{
...
item
}
currentAddress=
{
currentAddress
}
/>
))
}
</
Tbody
>
</
Table
>
);
};
export
default
AddressIntTxsTable
;
ui/address/internals/AddressIntTxsTableItem.tsx
0 → 100644
View file @
c9397c2b
import
{
Tr
,
Td
,
Tag
,
Icon
,
Box
,
Flex
,
Text
,
Link
}
from
'
@chakra-ui/react
'
;
import
BigNumber
from
'
bignumber.js
'
;
import
React
from
'
react
'
;
import
type
{
InternalTransaction
}
from
'
types/api/internalTransaction
'
;
import
appConfig
from
'
configs/app/config
'
;
import
rightArrowIcon
from
'
icons/arrows/east.svg
'
;
import
dayjs
from
'
lib/date/dayjs
'
;
import
link
from
'
lib/link/link
'
;
import
Address
from
'
ui/shared/address/Address
'
;
import
AddressIcon
from
'
ui/shared/address/AddressIcon
'
;
import
AddressLink
from
'
ui/shared/address/AddressLink
'
;
import
InOutTag
from
'
ui/shared/InOutTag
'
;
import
TxStatus
from
'
ui/shared/TxStatus
'
;
import
{
TX_INTERNALS_ITEMS
}
from
'
ui/tx/internals/utils
'
;
type
Props
=
InternalTransaction
&
{
currentAddress
:
string
}
const
AddressIntTxsTableItem
=
({
type
,
from
,
to
,
value
,
success
,
error
,
created_contract
:
createdContract
,
transaction_hash
:
txnHash
,
block
,
timestamp
,
currentAddress
,
}:
Props
)
=>
{
const
typeTitle
=
TX_INTERNALS_ITEMS
.
find
(({
id
})
=>
id
===
type
)?.
title
;
const
toData
=
to
?
to
:
createdContract
;
const
isOut
=
Boolean
(
currentAddress
&&
currentAddress
===
from
.
hash
);
const
isIn
=
Boolean
(
currentAddress
&&
currentAddress
===
to
?.
hash
);
return
(
<
Tr
alignItems=
"top"
>
<
Td
verticalAlign=
"middle"
>
<
Flex
rowGap=
{
3
}
flexWrap=
"wrap"
>
<
AddressLink
fontWeight=
"700"
hash=
{
txnHash
}
type=
"transaction"
/>
<
Text
variant=
"secondary"
fontWeight=
"400"
fontSize=
"sm"
>
{
dayjs
(
timestamp
).
fromNow
()
}
</
Text
>
</
Flex
>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
Flex
rowGap=
{
2
}
flexWrap=
"wrap"
>
{
typeTitle
&&
(
<
Box
w=
"126px"
display=
"inline-block"
>
<
Tag
colorScheme=
"cyan"
mr=
{
5
}
>
{
typeTitle
}
</
Tag
>
</
Box
>
)
}
<
TxStatus
status=
{
success
?
'
ok
'
:
'
error
'
}
errorText=
{
error
}
/>
</
Flex
>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
Link
href=
{
link
(
'
block
'
,
{
id
:
block
.
toString
()
})
}
>
{
block
}
</
Link
>
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
Address
display=
"inline-flex"
maxW=
"100%"
>
<
AddressIcon
hash=
{
from
.
hash
}
/>
<
AddressLink
ml=
{
2
}
fontWeight=
"500"
hash=
{
from
.
hash
}
alias=
{
from
.
name
}
flexGrow=
{
1
}
/>
</
Address
>
</
Td
>
<
Td
px=
{
0
}
verticalAlign=
"middle"
>
{
(
isIn
||
isOut
)
?
<
InOutTag
isIn=
{
isIn
}
isOut=
{
isOut
}
/>
:
<
Icon
as=
{
rightArrowIcon
}
boxSize=
{
6
}
color=
"gray.500"
/>
}
</
Td
>
<
Td
verticalAlign=
"middle"
>
<
Address
display=
"inline-flex"
maxW=
"100%"
>
<
AddressIcon
hash=
{
toData
.
hash
}
/>
<
AddressLink
hash=
{
toData
.
hash
}
alias=
{
toData
.
name
}
fontWeight=
"500"
ml=
{
2
}
/>
</
Address
>
</
Td
>
<
Td
isNumeric
verticalAlign=
"middle"
>
{
BigNumber
(
value
).
div
(
BigNumber
(
10
**
appConfig
.
network
.
currency
.
decimals
)).
toFormat
()
}
</
Td
>
</
Tr
>
);
};
export
default
React
.
memo
(
AddressIntTxsTableItem
);
ui/pages/Address.tsx
View file @
c9397c2b
...
...
@@ -11,6 +11,7 @@ import useFetch from 'lib/hooks/useFetch';
import
AddressBlocksValidated
from
'
ui/address/AddressBlocksValidated
'
;
import
AddressCoinBalance
from
'
ui/address/AddressCoinBalance
'
;
import
AddressDetails
from
'
ui/address/AddressDetails
'
;
import
AddressInternalTxs
from
'
ui/address/AddressInternalTxs
'
;
import
AddressTokenTransfers
from
'
ui/address/AddressTokenTransfers
'
;
import
AddressTxs
from
'
ui/address/AddressTxs
'
;
import
Page
from
'
ui/shared/Page/Page
'
;
...
...
@@ -39,7 +40,7 @@ const AddressPageContent = () => {
{
id
:
'
txs
'
,
title
:
'
Transactions
'
,
component
:
<
AddressTxs
/>
},
{
id
:
'
token_transfers
'
,
title
:
'
Token transfers
'
,
component
:
<
AddressTokenTransfers
/>
},
{
id
:
'
tokens
'
,
title
:
'
Tokens
'
,
component
:
null
},
{
id
:
'
internal_txn
'
,
title
:
'
Internal txn
'
,
component
:
null
},
{
id
:
'
internal_txn
'
,
title
:
'
Internal txn
'
,
component
:
<
AddressInternalTxs
/>
},
{
id
:
'
coin_balance_history
'
,
title
:
'
Coin balance history
'
,
component
:
<
AddressCoinBalance
addressQuery=
{
addressQuery
}
/>
},
// temporary show this tab in all address
// later api will return info about available tabs
...
...
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