fixed merge conflict and fixed number formatting

pull/594/head
mulili 6 years ago
commit 312b6309ff
  1. 2
      .credo.exs
  2. 24
      README.md
  3. 26
      apps/block_scout_web/assets/__tests__/pages/address.js
  4. 11
      apps/block_scout_web/assets/css/components/_navbar.scss
  5. 10
      apps/block_scout_web/assets/css/components/_tile.scss
  6. 6
      apps/block_scout_web/assets/js/lib/currency.js
  7. 122
      apps/block_scout_web/assets/js/pages/address.js
  8. 52
      apps/block_scout_web/assets/js/pages/block.js
  9. 130
      apps/block_scout_web/assets/js/pages/chain.js
  10. 125
      apps/block_scout_web/assets/js/pages/transaction.js
  11. 17
      apps/block_scout_web/assets/js/router.js
  12. 33
      apps/block_scout_web/lib/block_scout_web/chain.ex
  13. 5
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  14. 33
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  15. 7
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  16. 57
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  17. 34
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex
  18. 7
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  19. 7
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  20. 4
      apps/block_scout_web/lib/block_scout_web/controllers/pending_transaction_controller.ex
  21. 36
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/holder_controller.ex
  22. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/read_contract_controller.ex
  23. 2
      apps/block_scout_web/lib/block_scout_web/controllers/tokens/token_controller.ex
  24. 7
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_controller.ex
  25. 14
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  26. 8
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex
  27. 9
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex
  28. 259
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  29. 8
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  30. 18
      apps/block_scout_web/lib/block_scout_web/router.ex
  31. 6
      apps/block_scout_web/lib/block_scout_web/templates/address/_link.html.eex
  32. 17
      apps/block_scout_web/lib/block_scout_web/templates/address/_responsive_hash.html.eex
  33. 47
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  34. 35
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  35. 20
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  36. 10
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex
  37. 34
      apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex
  38. 11
      apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/_function_response.html.eex
  39. 15
      apps/block_scout_web/lib/block_scout_web/templates/address_read_contract/index.html.eex
  40. 10
      apps/block_scout_web/lib/block_scout_web/templates/address_token/_tokens.html.eex
  41. 132
      apps/block_scout_web/lib/block_scout_web/templates/address_token/index.html.eex
  42. 34
      apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex
  43. 9
      apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex
  44. 2
      apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex
  45. 2
      apps/block_scout_web/lib/block_scout_web/templates/chain/show.html.eex
  46. 10
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  47. 4
      apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex
  48. 25
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_function_response.html.eex
  49. 3
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  50. 19
      apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/_token_balances.html.eex
  51. 95
      apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex
  52. 32
      apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
  53. 17
      apps/block_scout_web/lib/block_scout_web/templates/tokens/read_contract/index.html.eex
  54. 6
      apps/block_scout_web/lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex
  55. 23
      apps/block_scout_web/lib/block_scout_web/templates/tokens/token/show.html.eex
  56. 8
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_tile.html.eex
  57. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_token_transfer.html.eex
  58. 2
      apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex
  59. 19
      apps/block_scout_web/lib/block_scout_web/templates/transaction/overview.html.eex
  60. 4
      apps/block_scout_web/lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex
  61. 6
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/index.html.eex
  62. 6
      apps/block_scout_web/lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex
  63. 5
      apps/block_scout_web/lib/block_scout_web/views/address_read_contract_view.ex
  64. 9
      apps/block_scout_web/lib/block_scout_web/views/address_token_view.ex
  65. 112
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  66. 41
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/contract_view.ex
  67. 22
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex
  68. 5
      apps/block_scout_web/lib/block_scout_web/views/internal_transaction_view.ex
  69. 3
      apps/block_scout_web/lib/block_scout_web/views/smart_contract_view.ex
  70. 50
      apps/block_scout_web/lib/block_scout_web/views/tokens/holder_view.ex
  71. 93
      apps/block_scout_web/lib/block_scout_web/views/transaction_view.ex
  72. 298
      apps/block_scout_web/priv/gettext/default.pot
  73. 298
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  74. 110
      apps/block_scout_web/test/block_scout_web/controllers/address_token_controller_test.exs
  75. 2
      apps/block_scout_web/test/block_scout_web/controllers/address_transaction_controller_test.exs
  76. 10
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  77. 172
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  78. 124
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs
  79. 10
      apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs
  80. 92
      apps/block_scout_web/test/block_scout_web/controllers/tokens/holder_controller_test.exs
  81. 2
      apps/block_scout_web/test/block_scout_web/controllers/tokens/read_contract_controller_test.exs
  82. 23
      apps/block_scout_web/test/block_scout_web/features/pages/token_page.ex
  83. 52
      apps/block_scout_web/test/block_scout_web/features/viewing_addresses_test.exs
  84. 22
      apps/block_scout_web/test/block_scout_web/features/viewing_tokens_test.exs
  85. 7
      apps/block_scout_web/test/block_scout_web/features/viewing_transactions_test.exs
  86. 25
      apps/block_scout_web/test/block_scout_web/views/address_token_view_test.exs
  87. 182
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  88. 43
      apps/block_scout_web/test/block_scout_web/views/internal_transaction_view_test.exs
  89. 40
      apps/block_scout_web/test/block_scout_web/views/tokens/holder_view_test.exs
  90. 73
      apps/block_scout_web/test/block_scout_web/views/tokens/smart_contract_view_test.exs
  91. 54
      apps/block_scout_web/test/block_scout_web/views/transaction_view_test.exs
  92. 2
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
  93. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/encoder.ex
  94. 38
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex
  95. 6
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/web_socket/web_socket_client.ex
  96. 36
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/encoder_test.exs
  97. 6
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/web_socket_test.exs
  98. 6
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs
  99. 3
      apps/ethereum_jsonrpc/test/support/ethereum_jsonrpc/case/geth/http_websocket.ex
  100. 12
      apps/explorer/config/dev.secret.exs.example
  101. Some files were not shown because too many files have changed in this diff Show More

@ -76,7 +76,7 @@
#
{Credo.Check.Design.AliasUsage,
excluded_namespaces: ~w(Socket Task),
excluded_lastnames: ~w(Address DateTime Full Name Number Repo Time Unit),
excluded_lastnames: ~w(Address DateTime Fetcher Full Name Number Repo Time Unit),
priority: :low},
# For some checks, you can also set other parameters

@ -46,6 +46,7 @@ The [development stack page](https://github.com/poanetwork/blockscout/wiki/Devel
* [Libtool](https://www.gnu.org/software/libtool/)
* For Mac OSX users: `brew install libtool`
* GitHub for code storage
* [Inotify-tools](https://github.com/rvoicilas/inotify-tools/wiki) for Linux users only
### Build and Run
@ -57,7 +58,9 @@ The [development stack page](https://github.com/poanetwork/blockscout/wiki/Devel
3. Set up default configurations.
`cp apps/explorer/config/dev.secret.exs.example apps/explorer/config/dev.secret.exs`
`cp apps/block_scout_web/config/dev.secret.exs.example apps/block_scout_web/config/dev.secret.exs`
`cp apps/block_scout_web/config/dev.secret.exs.example apps/block_scout_web/config/dev.secret.exs`
<br />Linux: Update the database username and password configuration in `apps/explorer/config/dev.secret.exs`
<br />Mac: Remove the `username` and `password` fields from `apps/explorer/config/dev.secret.exs`
<br />Optional: Set up default configuration for testing.
`cp apps/explorer/config/test.secret.exs.example apps/explorer/config/test.secret.exs`
Example usage: Changing the default Postgres port from localhost:15432 if [Boxen](https://github.com/boxen/boxen) is installed.
@ -91,6 +94,25 @@ _Additional runtime options:_
![BlockScout Example](explorer_example.gif)
### Configuring Ethereum Classic and other EVM Chains
**Note: Most of these modifications will be consolidated into a single file in the future.**
1. Update the import file in `apps/block_scout_web/assets/css/theme/_variables.scss`. There are several preset css files for our supported chains which include Ethereum Classic, Ethereum Mainnet, Ropsten Testnet, Kovan Testnet, POA Core, and POA Sokol. To deploy Ethereum Classic, change the import to `ethereum_classic_variables`.
2. Update the logo file in `apps/block_scout_web/config/config.exs`. To deploy Ethereum Classic, change this file to `classic_ethereum_logo.svg`.
3. Update the `check_origin` configuration in `apps/block_scout_web/config/prod.exs`. This allows realtime events to occur on your endpoint.
4. Update the node configuration. You will need a full tracing node with WebSockets enabled. Make the changes in the following files (dev/prod):
* `apps/explorer/config/dev/parity.exs`
* `apps/explorer/config/prod/parity.exs`
* `apps/indexer/config/dev/parity.exs`
* `apps/indexer/config/prod/parity.exs`
5. Update the dropdown menu in the main navigation `apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex`
6. Update the coin in `apps/explorer/config/config.exs`. This will pull relevant information from Coinmarketcap.com.
### Umbrella Project Organization

@ -5,9 +5,8 @@ describe('PAGE_LOAD', () => {
const state = initialState
const action = {
type: 'PAGE_LOAD',
params: {
addressHash: '1234'
}
beyondPageOne: false,
addressHash: '1234'
}
const output = reducer(state, action)
@ -19,10 +18,8 @@ describe('PAGE_LOAD', () => {
const state = initialState
const action = {
type: 'PAGE_LOAD',
params: {
addressHash: '1234',
blockNumber: '4321'
}
beyondPageOne: true,
addressHash: '1234'
}
const output = reducer(state, action)
@ -34,10 +31,9 @@ describe('PAGE_LOAD', () => {
const state = initialState
const action = {
type: 'PAGE_LOAD',
params: {
addressHash: '1234',
filter: 'to'
}
addressHash: '1234',
beyondPageOne: false,
filter: 'to'
}
const output = reducer(state, action)
@ -49,11 +45,9 @@ describe('PAGE_LOAD', () => {
const state = initialState
const action = {
type: 'PAGE_LOAD',
params: {
addressHash: '1234',
filter: 'to',
blockNumber: '4321'
}
beyondPageOne: true,
addressHash: '1234',
filter: 'to'
}
const output = reducer(state, action)

@ -34,9 +34,10 @@
background: transparent;
width: auto;
font-size: 12px;
border-left: none;
border-right: none;
color: $white;
border-color: transparentize($white, 0.30);
padding-right: 0px;
&::-webkit-input-placeholder { /* Chrome/Opera/Safari */
@ -57,16 +58,20 @@
}
@include media-breakpoint-up(xl) {
width: calc(43ch + #{$input-padding-x * 2});
width: calc(41ch + #{$input-padding-x});
}
}
.navbar .input-group-text {
background: none;
border-right: none;
border-left: none;
border-color: transparentize($white, 0.30);
}
.input-group-append {
margin-left: 0px;
}
.fa-search {
color: $white;
}

@ -1,7 +1,6 @@
.tile {
font-size: 12px;
color: $text-muted;
border-radius: 2px;
line-height: 1.4rem;
border: 1px solid $border-color;
border-left: 4px solid $primary;
@ -49,6 +48,10 @@
}
&-token {
border: 1px solid $border-color;
}
&-token-transfer {
border-left: 4px solid $orange;
padding-bottom: 10px;
@ -110,6 +113,11 @@
&-hash {
font-weight: 300;
}
&-lg {
font-size: 16px;
color: $body-color;
}
}
.tile-badge {

@ -34,8 +34,10 @@ function tryUpdateCalculatedUsdValues (el, usdExchangeRate = el.dataset.usdExcha
const formattedUsd = formatUsdValue(usd)
if (formattedUsd !== el.innerHTML) el.innerHTML = formattedUsd
}
function updateAllCalculatedUsdValues (usdExchangeRate) {
$('[data-usd-exchange-rate]').each((i, el) => tryUpdateCalculatedUsdValues(el, usdExchangeRate))
let currentUsdExchangeRate
export function updateAllCalculatedUsdValues (usdExchangeRate) {
currentUsdExchangeRate = usdExchangeRate
$('[data-usd-exchange-rate]').each((i, el) => tryUpdateCalculatedUsdValues(el, currentUsdExchangeRate))
}
updateAllCalculatedUsdValues()

@ -1,10 +1,11 @@
import $ from 'jquery'
import URI from 'urijs'
import humps from 'humps'
import numeral from 'numeral'
import socket from '../socket'
import router from '../router'
import { batchChannel, initRedux } from '../utils'
import { updateAllAges } from '../lib/from_now'
import { updateAllCalculatedUsdValues } from '../lib/currency.js'
import { loadTokenBalanceDropdown } from '../lib/token_balance_dropdown'
const BATCH_THRESHOLD = 10
@ -25,9 +26,9 @@ export function reducer (state = initialState, action) {
switch (action.type) {
case 'PAGE_LOAD': {
return Object.assign({}, state, {
addressHash: action.params.addressHash,
beyondPageOne: !!action.params.blockNumber,
filter: action.params.filter,
addressHash: action.addressHash,
beyondPageOne: action.beyondPageOne,
filter: action.filter,
transactionCount: numeral(action.transactionCount).value()
})
}
@ -97,59 +98,66 @@ export function reducer (state = initialState, action) {
}
}
router.when('/address/:addressHash').then((params) => initRedux(reducer, {
main (store) {
const { addressHash } = params
const addressChannel = socket.channel(`addresses:${addressHash}`, {})
const state = store.dispatch({
type: 'PAGE_LOAD',
params,
transactionCount: $('[data-selector="transaction-count"]').text()
})
addressChannel.join()
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
addressChannel.on('balance', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_BALANCE', msg }))
if (!state.beyondPageOne) {
addressChannel.on('transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs })
))
const $addressDetailsPage = $('[data-page="address-details"]')
if ($addressDetailsPage.length) {
initRedux(reducer, {
main (store) {
const addressHash = $addressDetailsPage[0].dataset.pageAddressHash
const addressChannel = socket.channel(`addresses:${addressHash}`, {})
const { filter, blockNumber } = humps.camelizeKeys(URI(window.location).query(true))
const state = store.dispatch({
type: 'PAGE_LOAD',
addressHash,
filter,
beyondPageOne: !!blockNumber,
transactionCount: $('[data-selector="transaction-count"]').text()
})
addressChannel.join()
addressChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
addressChannel.on('balance', (msg) => store.dispatch({ type: 'RECEIVED_UPDATED_BALANCE', msg }))
if (!state.beyondPageOne) {
addressChannel.on('transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs })
))
addressChannel.on('internal_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', msgs })
))
}
},
render (state, oldState) {
const $balance = $('[data-selector="balance-card"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $emptyInternalTransactionsList = $('[data-selector="empty-internal-transactions-list"]')
const $emptyTransactionsList = $('[data-selector="empty-transactions-list"]')
const $internalTransactionsList = $('[data-selector="internal-transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
const $transactionsList = $('[data-selector="transactions-list"]')
addressChannel.on('internal_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_INTERNAL_TRANSACTION_BATCH', msgs })
))
}
},
render (state, oldState) {
const $balance = $('[data-selector="balance-card"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $emptyInternalTransactionsList = $('[data-selector="empty-internal-transactions-list"]')
const $emptyTransactionsList = $('[data-selector="empty-transactions-list"]')
const $internalTransactionsList = $('[data-selector="internal-transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
const $transactionsList = $('[data-selector="transactions-list"]')
if ($emptyInternalTransactionsList.length && state.newInternalTransactions.length) window.location.reload()
if ($emptyTransactionsList.length && state.newTransactions.length) window.location.reload()
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.balance !== state.balance) {
$balance.empty().append(state.balance)
loadTokenBalanceDropdown()
}
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newInternalTransactions !== state.newInternalTransactions && $internalTransactionsList.length) {
$internalTransactionsList.prepend(state.newInternalTransactions.slice(oldState.newInternalTransactions.length).reverse().join(''))
}
if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) {
$transactionsList.prepend(state.newTransactions.slice(oldState.newTransactions.length).reverse().join(''))
updateAllAges()
if ($emptyInternalTransactionsList.length && state.newInternalTransactions.length) window.location.reload()
if ($emptyTransactionsList.length && state.newTransactions.length) window.location.reload()
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.balance !== state.balance) {
$balance.empty().append(state.balance)
loadTokenBalanceDropdown()
updateAllCalculatedUsdValues()
}
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newInternalTransactions !== state.newInternalTransactions && $internalTransactionsList.length) {
$internalTransactionsList.prepend(state.newInternalTransactions.slice(oldState.newInternalTransactions.length).reverse().join(''))
}
if (oldState.newTransactions !== state.newTransactions && $transactionsList.length) {
$transactionsList.prepend(state.newTransactions.slice(oldState.newTransactions.length).reverse().join(''))
updateAllAges()
}
}
}
}))
})
}

@ -1,7 +1,7 @@
import $ from 'jquery'
import URI from 'urijs'
import humps from 'humps'
import socket from '../socket'
import router from '../router'
import { updateAllAges } from '../lib/from_now'
import { initRedux } from '../utils'
@ -15,7 +15,7 @@ export function reducer (state = initialState, action) {
switch (action.type) {
case 'PAGE_LOAD': {
return Object.assign({}, state, {
beyondPageOne: !!action.blockNumber
beyondPageOne: action.beyondPageOne
})
}
case 'CHANNEL_DISCONNECTED': {
@ -35,26 +35,32 @@ export function reducer (state = initialState, action) {
}
}
router.when('/blocks', { exactPathMatch: true }).then(({ blockNumber }) => initRedux(reducer, {
main (store) {
const state = store.dispatch({ type: 'PAGE_LOAD', blockNumber })
if (!state.beyondPageOne) {
const blocksChannel = socket.channel(`blocks:new_block`, {})
blocksChannel.join()
blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
blocksChannel.on('new_block', (msg) =>
store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })
)
}
},
render (state, oldState) {
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $blocksList = $('[data-selector="blocks-list"]')
const $blockListPage = $('[data-page="block-list"]')
if ($blockListPage.length) {
initRedux(reducer, {
main (store) {
const state = store.dispatch({
type: 'PAGE_LOAD',
beyondPageOne: !!humps.camelizeKeys(URI(window.location).query(true)).blockNumber
})
if (!state.beyondPageOne) {
const blocksChannel = socket.channel(`blocks:new_block`, {})
blocksChannel.join()
blocksChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
blocksChannel.on('new_block', (msg) =>
store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) })
)
}
},
render (state, oldState) {
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $blocksList = $('[data-selector="blocks-list"]')
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.newBlock !== state.newBlock) {
$blocksList.prepend(state.newBlock)
updateAllAges()
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.newBlock !== state.newBlock) {
$blocksList.prepend(state.newBlock)
updateAllAges()
}
}
}
}))
})
}

@ -1,7 +1,6 @@
import $ from 'jquery'
import humps from 'humps'
import numeral from 'numeral'
import router from '../router'
import socket from '../socket'
import { updateAllAges } from '../lib/from_now'
import { exchangeRateChannel, formatUsdValue } from '../lib/currency'
@ -69,74 +68,77 @@ export function reducer (state = initialState, action) {
}
let chart
router.when('', { exactPathMatch: true }).then(() => initRedux(reducer, {
main (store) {
const addressesChannel = socket.channel(`addresses:new_address`)
addressesChannel.join()
addressesChannel.on('count', msg => store.dispatch({ type: 'RECEIVED_NEW_ADDRESS_COUNT', msg: humps.camelizeKeys(msg) }))
const $chainDetailsPage = $('[data-page="chain-details"]')
if ($chainDetailsPage.length) {
initRedux(reducer, {
main (store) {
const addressesChannel = socket.channel(`addresses:new_address`)
addressesChannel.join()
addressesChannel.on('count', msg => store.dispatch({ type: 'RECEIVED_NEW_ADDRESS_COUNT', msg: humps.camelizeKeys(msg) }))
const blocksChannel = socket.channel(`blocks:new_block`)
store.dispatch({
type: 'PAGE_LOAD',
transactionCount: $('[data-selector="transaction-count"]').text()
})
blocksChannel.join()
blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
const blocksChannel = socket.channel(`blocks:new_block`)
store.dispatch({
type: 'PAGE_LOAD',
transactionCount: $('[data-selector="transaction-count"]').text()
})
blocksChannel.join()
blocksChannel.on('new_block', msg => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
exchangeRateChannel.on('new_rate', (msg) => store.dispatch({ type: 'RECEIVED_NEW_EXCHANGE_RATE', msg: humps.camelizeKeys(msg) }))
exchangeRateChannel.on('new_rate', (msg) => store.dispatch({ type: 'RECEIVED_NEW_EXCHANGE_RATE', msg: humps.camelizeKeys(msg) }))
const transactionsChannel = socket.channel(`transactions:new_transaction`)
transactionsChannel.join()
transactionsChannel.on('new_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) }))
)
const transactionsChannel = socket.channel(`transactions:new_transaction`)
transactionsChannel.join()
transactionsChannel.on('new_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) }))
)
chart = createMarketHistoryChart($('[data-chart="marketHistoryChart"]')[0])
},
render (state, oldState) {
const $addressCount = $('[data-selector="address-count"]')
const $averageBlockTime = $('[data-selector="average-block-time"]')
const $blockList = $('[data-selector="chain-block-list"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $marketCap = $('[data-selector="market-cap"]')
const $transactionsList = $('[data-selector="transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
chart = createMarketHistoryChart($('[data-chart="marketHistoryChart"]')[0])
},
render (state, oldState) {
const $addressCount = $('[data-selector="address-count"]')
const $averageBlockTime = $('[data-selector="average-block-time"]')
const $blockList = $('[data-selector="chain-block-list"]')
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $marketCap = $('[data-selector="market-cap"]')
const $transactionsList = $('[data-selector="transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
if (oldState.addressCount !== state.addressCount) {
$addressCount.empty().append(state.addressCount)
}
if (oldState.averageBlockTime !== state.averageBlockTime) {
$averageBlockTime.empty().append(state.averageBlockTime)
}
if (oldState.usdMarketCap !== state.usdMarketCap) {
$marketCap.empty().append(formatUsdValue(state.usdMarketCap))
}
if (oldState.newBlock !== state.newBlock) {
$blockList.children().last().remove()
$blockList.prepend(state.newBlock)
updateAllAges()
}
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newTransactions !== state.newTransactions) {
const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length)
$transactionsList
.children()
.slice($transactionsList.children().length - newTransactionsToInsert.length, $transactionsList.children().length)
.remove()
$transactionsList.prepend(newTransactionsToInsert.reverse().join(''))
if (oldState.addressCount !== state.addressCount) {
$addressCount.empty().append(state.addressCount)
}
if (oldState.averageBlockTime !== state.averageBlockTime) {
$averageBlockTime.empty().append(state.averageBlockTime)
}
if (oldState.usdMarketCap !== state.usdMarketCap) {
$marketCap.empty().append(formatUsdValue(state.usdMarketCap))
}
if (oldState.newBlock !== state.newBlock) {
$blockList.children().last().remove()
$blockList.prepend(state.newBlock)
updateAllAges()
}
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newTransactions !== state.newTransactions) {
const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length)
$transactionsList
.children()
.slice($transactionsList.children().length - newTransactionsToInsert.length, $transactionsList.children().length)
.remove()
$transactionsList.prepend(newTransactionsToInsert.reverse().join(''))
updateAllAges()
}
updateAllAges()
}
if (oldState.availableSupply !== state.availableSupply || oldState.marketHistoryData !== state.marketHistoryData) {
chart.update(state.availableSupply, state.marketHistoryData)
if (oldState.availableSupply !== state.availableSupply || oldState.marketHistoryData !== state.marketHistoryData) {
chart.update(state.availableSupply, state.marketHistoryData)
}
}
}
}))
})
}

@ -1,8 +1,8 @@
import $ from 'jquery'
import URI from 'urijs'
import humps from 'humps'
import numeral from 'numeral'
import socket from '../socket'
import router from '../router'
import { updateAllAges } from '../lib/from_now'
import { batchChannel, initRedux } from '../utils'
@ -22,7 +22,7 @@ export function reducer (state = initialState, action) {
switch (action.type) {
case 'PAGE_LOAD': {
return Object.assign({}, state, {
beyondPageOne: !!action.index,
beyondPageOne: action.beyondPageOne,
blockNumber: parseInt(action.blockNumber, 10),
transactionCount: numeral(action.transactionCount).value()
})
@ -63,67 +63,72 @@ export function reducer (state = initialState, action) {
}
}
router.when('/tx/:transactionHash').then(() => initRedux(reducer, {
main (store) {
const blocksChannel = socket.channel(`blocks:new_block`, {})
const $transactionBlockNumber = $('[data-selector="block-number"]')
store.dispatch({
type: 'PAGE_LOAD',
blockNumber: $transactionBlockNumber.text()
})
blocksChannel.join()
blocksChannel.on('new_block', (msg) => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
},
render (state, oldState) {
const $blockConfirmations = $('[data-selector="block-confirmations"]')
const $transactionDetailsPage = $('[data-page="transaction-details"]')
if ($transactionDetailsPage.length) {
initRedux(reducer, {
main (store) {
const blocksChannel = socket.channel(`blocks:new_block`, {})
const $transactionBlockNumber = $('[data-selector="block-number"]')
store.dispatch({
type: 'PAGE_LOAD',
blockNumber: $transactionBlockNumber.text()
})
blocksChannel.join()
blocksChannel.on('new_block', (msg) => store.dispatch({ type: 'RECEIVED_NEW_BLOCK', msg: humps.camelizeKeys(msg) }))
},
render (state, oldState) {
const $blockConfirmations = $('[data-selector="block-confirmations"]')
if (oldState.confirmations !== state.confirmations) {
$blockConfirmations.empty().append(numeral(state.confirmations).format())
if (oldState.confirmations !== state.confirmations) {
$blockConfirmations.empty().append(numeral(state.confirmations).format())
}
}
}
}))
})
}
router.when('/txs', { exactPathMatch: true }).then((params) => initRedux(reducer, {
main (store) {
const { index } = params
const state = store.dispatch({
type: 'PAGE_LOAD',
transactionCount: $('[data-selector="transaction-count"]').text(),
index
})
if (!state.beyondPageOne) {
const transactionsChannel = socket.channel(`transactions:new_transaction`)
transactionsChannel.join()
transactionsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
transactionsChannel.on('new_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) }))
)
}
},
render (state, oldState) {
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $transactionsList = $('[data-selector="transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
const $transactionListPage = $('[data-page="transaction-list"]')
if ($transactionListPage.length) {
initRedux(reducer, {
main (store) {
const state = store.dispatch({
type: 'PAGE_LOAD',
transactionCount: $('[data-selector="transaction-count"]').text(),
beyondPageOne: !!humps.camelizeKeys(URI(window.location).query(true)).index
})
if (!state.beyondPageOne) {
const transactionsChannel = socket.channel(`transactions:new_transaction`)
transactionsChannel.join()
transactionsChannel.onError(() => store.dispatch({ type: 'CHANNEL_DISCONNECTED' }))
transactionsChannel.on('new_transaction', batchChannel((msgs) =>
store.dispatch({ type: 'RECEIVED_NEW_TRANSACTION_BATCH', msgs: humps.camelizeKeys(msgs) }))
)
}
},
render (state, oldState) {
const $channelBatching = $('[data-selector="channel-batching-message"]')
const $channelBatchingCount = $('[data-selector="channel-batching-count"]')
const $channelDisconnected = $('[data-selector="channel-disconnected-message"]')
const $transactionsList = $('[data-selector="transactions-list"]')
const $transactionCount = $('[data-selector="transaction-count"]')
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newTransactions !== state.newTransactions) {
const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length)
$transactionsList
.children()
.slice($transactionsList.children().length - newTransactionsToInsert.length, $transactionsList.children().length)
.remove()
$transactionsList.prepend(newTransactionsToInsert.reverse().join(''))
if (state.channelDisconnected) $channelDisconnected.show()
if (oldState.transactionCount !== state.transactionCount) $transactionCount.empty().append(numeral(state.transactionCount).format())
if (state.batchCountAccumulator) {
$channelBatching.show()
$channelBatchingCount[0].innerHTML = numeral(state.batchCountAccumulator).format()
} else {
$channelBatching.hide()
}
if (oldState.newTransactions !== state.newTransactions) {
const newTransactionsToInsert = state.newTransactions.slice(oldState.newTransactions.length)
$transactionsList
.children()
.slice($transactionsList.children().length - newTransactionsToInsert.length, $transactionsList.children().length)
.remove()
$transactionsList.prepend(newTransactionsToInsert.reverse().join(''))
updateAllAges()
updateAllAges()
}
}
}
}))
})
}

@ -1,17 +0,0 @@
import Path from 'path-parser'
import URI from 'urijs'
import humps from 'humps'
export default {
when (pattern, { exactPathMatch } = { exactPathMatch: false }) {
return new Promise((resolve) => {
const path = Path.createPath(pattern || '/')
const match = exactPathMatch ? path.test(window.location.pathname) : path.partialTest(window.location.pathname)
if (match) {
const routeParams = humps.camelizeKeys(match)
const queryParams = humps.camelizeKeys(URI(window.location).query(true))
resolve(Object.assign({}, queryParams, routeParams))
}
})
}
}

@ -12,7 +12,18 @@ defmodule BlockScoutWeb.Chain do
string_to_transaction_hash: 1
]
alias Explorer.Chain.{Address, Block, InternalTransaction, Log, TokenTransfer, Transaction}
alias Explorer.Chain.{
Address,
Address.TokenBalance,
Block,
Hash,
InternalTransaction,
Log,
Token,
TokenTransfer,
Transaction
}
alias Explorer.PagingOptions
@page_size 50
@ -118,6 +129,13 @@ defmodule BlockScoutWeb.Chain do
def paging_options(%{"inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: inserted_at}]
def paging_options(%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at}),
do: [paging_options: %{@default_paging_options | key: {name, type, inserted_at}}]
def paging_options(%{"value" => value, "address_hash" => address_hash}) do
[paging_options: %{@default_paging_options | key: {value, address_hash}}]
end
def paging_options(_params), do: [paging_options: @default_paging_options]
def param_to_block_number(formatted_number) when is_binary(formatted_number) do
@ -167,6 +185,19 @@ defmodule BlockScoutWeb.Chain do
%{"inserted_at" => inserted_at_datetime}
end
defp paging_params(%Token{name: name, type: type, inserted_at: inserted_at}) do
inserted_at_datetime =
inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.to_iso8601()
%{"token_name" => name, "token_type" => type, "token_inserted_at" => inserted_at_datetime}
end
defp paging_params(%TokenBalance{address_hash: address_hash, value: value}) do
%{"address_hash" => Hash.to_string(address_hash), "value" => Decimal.to_integer(value)}
end
defp transaction_from_param(param) do
with {:ok, hash} <- string_to_transaction_hash(param) do
hash_to_transaction(hash)

@ -17,8 +17,9 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
full_options =
[
necessity_by_association: %{
from_address: :optional,
to_address: :optional
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
]
|> Keyword.merge(paging_options(params))

@ -0,0 +1,33 @@
defmodule BlockScoutWeb.AddressTokenController do
use BlockScoutWeb, :controller
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
import BlockScoutWeb.AddressController, only: [transaction_count: 1]
import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
def index(conn, %{"address_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash) do
tokens_plus_one = Chain.tokens_with_number_of_transfers_from_address(address_hash, paging_options(params))
{tokens, next_page} = split_list_by_page(tokens_plus_one)
render(
conn,
"index.html",
address: address,
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
next_page_params: next_page_params(next_page, tokens, params),
tokens: tokens
)
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -17,9 +17,10 @@ defmodule BlockScoutWeb.AddressTransactionController do
full_options =
[
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
]
|> Keyword.merge(paging_options(params))

@ -0,0 +1,57 @@
defmodule BlockScoutWeb.API.RPC.ContractController do
use BlockScoutWeb, :controller
alias Explorer.Chain
def getabi(conn, params) do
with {:address_param, {:ok, address_param}} <- fetch_address(params),
{:format, {:ok, address_hash}} <- to_address_hash(address_param),
{:contract, {:ok, contract}} <- to_smart_contract(address_hash) do
render(conn, :getabi, %{abi: contract.abi})
else
{:address_param, :error} ->
render(conn, :error, error: "Query parameter address is required")
{:format, :error} ->
render(conn, :error, error: "Invalid address hash")
{:contract, :not_found} ->
render(conn, :error, error: "Contract source code not verified")
end
end
def getsourcecode(conn, params) do
with {:address_param, {:ok, address_param}} <- fetch_address(params),
{:format, {:ok, address_hash}} <- to_address_hash(address_param),
{:contract, {:ok, contract}} <- to_smart_contract(address_hash) do
render(conn, :getsourcecode, %{contract: contract})
else
{:address_param, :error} ->
render(conn, :error, error: "Query parameter address is required")
{:format, :error} ->
render(conn, :error, error: "Invalid address hash")
{:contract, :not_found} ->
render(conn, :getsourcecode, %{contract: nil})
end
end
defp fetch_address(params) do
{:address_param, Map.fetch(params, "address")}
end
defp to_address_hash(address_hash_string) do
{:format, Chain.string_to_address_hash(address_hash_string)}
end
defp to_smart_contract(address_hash) do
result =
case Chain.address_hash_to_smart_contract(address_hash) do
nil -> :not_found
contract -> {:ok, contract}
end
{:contract, result}
end
end

@ -0,0 +1,34 @@
defmodule BlockScoutWeb.API.RPC.TransactionController do
use BlockScoutWeb, :controller
alias Explorer.Chain
def gettxreceiptstatus(conn, params) do
with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params),
{:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param) do
status = to_transaction_status(transaction_hash)
render(conn, :gettxreceiptstatus, %{status: status})
else
{:txhash_param, :error} ->
render(conn, :error, error: "Query parameter txhash is required")
{:format, :error} ->
render(conn, :error, error: "Invalid txhash format")
end
end
defp fetch_txhash(params) do
{:txhash_param, Map.fetch(params, "txhash")}
end
defp to_transaction_hash(transaction_hash_string) do
{:format, Chain.string_to_transaction_hash(transaction_hash_string)}
end
defp to_transaction_status(transaction_hash) do
case Chain.hash_to_transaction(transaction_hash) do
{:error, :not_found} -> ""
{:ok, transaction} -> transaction.status
end
end
end

@ -14,9 +14,10 @@ defmodule BlockScoutWeb.BlockTransactionController do
Keyword.merge(
[
necessity_by_association: %{
block: :required,
from_address: :required,
to_address: :optional
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
}
],
paging_options(params)

@ -19,9 +19,10 @@ defmodule BlockScoutWeb.ChainController do
transactions =
Chain.recent_collated_transactions(
necessity_by_association: %{
block: :required,
from_address: :required,
to_address: :optional
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional
},
paging_options: %PagingOptions{page_size: 5}
)

@ -10,8 +10,8 @@ defmodule BlockScoutWeb.PendingTransactionController do
Keyword.merge(
[
necessity_by_association: %{
from_address: :optional,
to_address: :optional
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
],
paging_options(params)

@ -0,0 +1,36 @@
defmodule BlockScoutWeb.Tokens.HolderController do
use BlockScoutWeb, :controller
alias Explorer.Chain
import BlockScoutWeb.Chain,
only: [
split_list_by_page: 1,
paging_options: 1,
next_page_params: 3
]
def index(conn, %{"token_id" => address_hash_string} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, token} <- Chain.token_from_address_hash(address_hash),
token_balances <- Chain.fetch_token_holders_from_token_hash(address_hash, paging_options(params)) do
{token_balances_paginated, next_page} = split_list_by_page(token_balances)
render(
conn,
"index.html",
token: token,
token_balances: token_balances_paginated,
total_token_holders: Chain.count_token_holders_from_token_hash(address_hash),
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash),
next_page_params: next_page_params(next_page, token_balances_paginated, params)
)
else
:error ->
not_found(conn)
{:error, :not_found} ->
not_found(conn)
end
end
end

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.Tokens.ReadContractController do
"index.html",
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash),
total_address_in_token_transfers: Chain.count_addresses_in_token_transfers_from_token_hash(address_hash)
total_token_holders: Chain.count_token_holders_from_token_hash(address_hash)
)
else
:error ->

@ -17,7 +17,7 @@ defmodule BlockScoutWeb.Tokens.TokenController do
transfers: token_transfers_paginated,
token: token,
total_token_transfers: Chain.count_token_transfers_from_token_hash(address_hash),
total_address_in_token_transfers: Chain.count_addresses_in_token_transfers_from_token_hash(address_hash),
total_token_holders: Chain.count_token_holders_from_token_hash(address_hash),
next_page_params: next_page_params(next_page, token_transfers_paginated, params)
)
else

@ -10,9 +10,10 @@ defmodule BlockScoutWeb.TransactionController do
Keyword.merge(
[
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional
:block => :required,
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
],
paging_options(params)

@ -12,18 +12,20 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
Chain.hash_to_transaction(
hash,
necessity_by_association: %{
block: :optional,
from_address: :optional,
to_address: :optional,
token_transfers: :optional
:block => :optional,
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
:token_transfers => :optional
}
) do
full_options =
Keyword.merge(
[
necessity_by_association: %{
from_address: :required,
to_address: :optional
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
],
paging_options(params)

@ -12,10 +12,10 @@ defmodule BlockScoutWeb.TransactionLogController do
Chain.hash_to_transaction(
transaction_hash,
necessity_by_association: %{
block: :optional,
from_address: :required,
to_address: :optional,
token_transfers: :optional
:block => :optional,
[from_address: :names] => :required,
[to_address: :names] => :optional,
:token_transfers => :optional
}
) do
full_options =

@ -12,10 +12,11 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
Chain.hash_to_transaction(
hash,
necessity_by_association: %{
block: :optional,
from_address: :optional,
to_address: :optional,
token_transfers: :optional
:block => :optional,
[created_contract_address: :names] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
:token_transfers => :optional
}
) do
full_options =

@ -234,6 +234,76 @@ defmodule BlockScoutWeb.Etherscan do
"result" => nil
}
@contract_getabi_example_value %{
"status" => "1",
"message" => "OK",
"result" =>
~s([{"constant":false,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"burn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"is_expired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"voucher_token","type":"bytes32"}],"name":"is_burnt","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"voucher_token","type":"bytes32"},{"name":"_lifetime","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}])
}
@contract_getabi_example_value_error %{
"status" => "0",
"message" => "Contract source code not verified",
"result" => nil
}
@contract_getsourcecode_example_value %{
"status" => "1",
"message" => "OK",
"result" => %{
"SourceCode" => """
pragma solidity >0.4.24;
contract Test {
constructor() public { b = hex"12345678901234567890123456789012"; }
event Event(uint indexed a, bytes32 b);
event Event2(uint indexed a, bytes32 b);
function foo(uint a) public { emit Event(a, b); }
bytes32 b;
}
""",
"ABI" => """
[{
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event"
}, {
"type":"event",
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"bytes32","indexed":false}],
"name":"Event2"
}, {
"type":"function",
"inputs": [{"name":"a","type":"uint256"}],
"name":"foo",
"outputs": []
}]
""",
"ContractName" => "Test",
"CompilerVersion" => "v0.2.1-2016-01-30-91a6b35",
"OptimizationUsed" => "1"
}
}
@contract_getsourcecode_example_value_error %{
"status" => "0",
"message" => "Invalid address hash",
"result" => nil
}
@transaction_gettxreceiptstatus_example_value %{
"status" => "1",
"message" => "OK",
"result" => %{
"status" => "1"
}
}
@transaction_gettxreceiptstatus_example_value_error %{
"status" => "0",
"message" => "Query parameter txhash is required",
"result" => nil
}
@status_type %{
type: "status",
enum: ~s(["0", "1"]),
@ -543,6 +613,67 @@ defmodule BlockScoutWeb.Etherscan do
}
}
@contract_model %{
name: "Contract",
fields: %{
"SourceCode" => %{
type: "contract source code",
definition: "The contract's source code.",
example: """
"pragma solidity >0.4.24;
contract Test {
constructor() public { b = hex"12345678901234567890123456789012"; }
event Event(uint indexed a, bytes32 b);
event Event2(uint indexed a, bytes32 b);
function foo(uint a) public { emit Event(a, b); }
bytes32 b;
}"
"""
},
"ABI" => %{
type: "ABI",
definition: "JSON string for the contract's Application Binary Interface (ABI)",
example: """
"[{
\\"type\\":\\"event\\",
\\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\",\\"indexed\\":true},{\\"name\\":\\"b\\",\\"type\\":\\"bytes32\\",\\"indexed\\":false}],
\\"name\\":\\"Event\\"
}, {
\\"type\\":\\"event\\",
\\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\",\\"indexed\\":true},{\\"name\\":\\"b\\",\\"type\\":\\"bytes32\\",\\"indexed\\":false}],
\\"name\\":\\"Event2\\"
}, {
\\"type\\":\\"function\\",
\\"inputs\\": [{\\"name\\":\\"a\\",\\"type\\":\\"uint256\\"}],
\\"name\\":\\"foo\\",
\\"outputs\\": []
}]"
"""
},
"ContractName" => %{
type: "string",
example: ~S("Some name")
},
"OptimizationUsed" => %{
type: "optimization used",
enum: ~s(["0", "1"]),
enum_interpretation: %{"0" => "false", "1" => "true"}
}
}
}
@transaction_receipt_status_model %{
name: "TransactionReceiptStatus",
fields: %{
status: %{
type: "status",
enum: ~s(["0", "1"]),
enum_interpretation: %{"0" => "fail", "1" => "pass"}
}
}
}
@account_balance_action %{
name: "balance",
description: "Get balance for address",
@ -1125,6 +1256,117 @@ defmodule BlockScoutWeb.Etherscan do
]
}
@contract_getabi_action %{
name: "getabi",
description: "Get ABI for verified contract.",
required_params: [
%{
key: "address",
placeholder: "addressHash",
type: "string",
description: "A 160-bit code used for identifying contracts."
}
],
optional_params: [],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@contract_getabi_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "abi",
definition: "JSON string for the Application Binary Interface (ABI)"
}
}
}
},
%{
code: "200",
description: "error",
example_value: Jason.encode!(@contract_getabi_example_value_error)
}
]
}
@contract_getsourcecode_action %{
name: "getsourcecode",
description: "Get contract source code for verified contract.",
required_params: [
%{
key: "address",
placeholder: "addressHash",
type: "string",
description: "A 160-bit code used for identifying contracts."
}
],
optional_params: [],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@contract_getsourcecode_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "array",
array_type: @contract_model
}
}
}
},
%{
code: "200",
description: "error",
example_value: Jason.encode!(@contract_getsourcecode_example_value_error)
}
]
}
@transaction_gettxreceiptstatus_action %{
name: "gettxreceiptstatus",
description: "Get transaction receipt status.",
required_params: [
%{
key: "txhash",
placeholder: "transactionHash",
type: "string",
description: "Transaction hash. Hash of contents of the transaction."
}
],
optional_params: [],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@transaction_gettxreceiptstatus_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "model",
model: @transaction_receipt_status_model
}
}
}
},
%{
code: "200",
description: "error",
example_value: Jason.encode!(@transaction_gettxreceiptstatus_example_value_error)
}
]
}
@account_module %{
name: "account",
actions: [
@ -1158,12 +1400,27 @@ defmodule BlockScoutWeb.Etherscan do
actions: [@block_getblockreward_action]
}
@contract_module %{
name: "contract",
actions: [
@contract_getabi_action,
@contract_getsourcecode_action
]
}
@transaction_module %{
name: "transaction",
actions: [@transaction_gettxreceiptstatus_action]
}
@documentation [
@account_module,
@logs_module,
@token_module,
@stats_module,
@block_module
@block_module,
@contract_module,
@transaction_module
]
def get_documentation do

@ -46,10 +46,10 @@ defmodule BlockScoutWeb.Notifier do
transaction_hashes
|> Chain.hashes_to_transactions(
necessity_by_association: %{
block: :required,
from_address: :optional,
to_address: :optional,
token_transfers: :optional
:block => :required,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
:token_transfers => :optional
}
)
|> Enum.each(&broadcast_transaction/1)

@ -29,7 +29,9 @@ defmodule BlockScoutWeb.Router do
"account" => RPC.AddressController,
"logs" => RPC.LogsController,
"token" => RPC.TokenController,
"stats" => RPC.StatsController
"stats" => RPC.StatsController,
"contract" => RPC.ContractController,
"transaction" => RPC.TransactionController
})
end
@ -90,6 +92,13 @@ defmodule BlockScoutWeb.Router do
as: :read_contract
)
resources(
"/tokens",
AddressTokenController,
only: [:index],
as: :token
)
resources(
"/token_balances",
AddressTokenBalanceController,
@ -105,6 +114,13 @@ defmodule BlockScoutWeb.Router do
only: [:index],
as: :read_contract
)
resources(
"/token_holders",
Tokens.HolderController,
only: [:index],
as: :holder
)
end
resources(

@ -1,5 +1,5 @@
<%= if @address_hash do %>
<%= link to: address_path(BlockScoutWeb.Endpoint, :show, @address_hash), "data-test": "address_hash_link" do %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address_hash: @address_hash, contract: @contract, truncate: assigns[:truncate] %>
<%= if @address do %>
<%= link to: address_path(BlockScoutWeb.Endpoint, :show, @address), "data-test": "address_hash_link" do %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address: @address, contract: @contract, truncate: assigns[:truncate] %>
<% end %>
<% end %>

@ -1,8 +1,13 @@
<span class="<%= if @contract do %>contract-address<% end %>" data-address-hash="<%= @address_hash %>">
<%= if assigns[:truncate] do %>
<%= BlockScoutWeb.AddressView.trimmed_hash(@address_hash) %>
<span class="<%= if @contract do %>contract-address<% end %>" data-address-hash="<%= @address.hash %>">
<%= if name = primary_name(@address) do %>
<span class="d-none d-md-none d-lg-inline" title="<%= @address.hash %>"><%= name %></span>
<span class="d-md-inline-block d-lg-none " title="<%= @address.hash %>"><%= name %></span>
<% else %>
<span class="d-none d-md-none d-lg-inline"><%= @address_hash %></span>
<span class="d-md-inline-block d-lg-none"><%= BlockScoutWeb.AddressView.trimmed_hash(@address_hash) %></span>
<% end%>
<%= if assigns[:truncate] do %>
<%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %>
<% else %>
<span class="d-none d-md-none d-lg-inline"><%= @address.hash %></span>
<span class="d-md-inline-block d-lg-none"><%= BlockScoutWeb.AddressView.trimmed_hash(@address.hash) %></span>
<% end %>
<% end %>
</span>

@ -1,48 +1,53 @@
<section class="address-overview">
<section class="address-overview" data-page="address-details" data-page-address-hash="<%= @address %>">
<div class="row">
<div class="card-section col-md-12 col-lg-8">
<div class="card">
<div class="card-body">
<div class="icon-links float-right">
<span data-clipboard-text="<%= @address %>">
<button type="button" class="icon-link" id="button" data-toggle="tooltip" data-placement="top" title="<%= gettext("Copy Address") %>" aria-label="Copy Address">
<button type="button" class="icon-link" id="button" data-toggle="tooltip" data-placement="top" title="<%= gettext("Copy Address") %>" aria-label="<%= gettext("Copy Address") %>">
<i class="fas fa-clone"></i>
</button>
</span>
<span data-toggle="modal" data-target="#qrModal">
<button type="button" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("QR Code") %>" aria-label="Show QR Code">
<button type="button" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("QR Code") %>" aria-label="<%= gettext("Show QR Code") %>">
<i class="fas fa-qrcode"></i>
</button>
</span>
</div>
<h1 class="card-title"><%= address_title(@address) %> Details </h1>
<h1 class="card-title"><%= address_title(@address) %> <%= gettext "Details" %> </h1>
<h3 class="<%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address.hash %></h3>
<div class="d-flex flex-row flex-md-column justify-content-start text-muted">
<span class="mr-4 mb-md-2"><span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %></span> <%= gettext "Transactions" %></span>
<div class="d-flex flex-row justify-content-start text-muted mr-4 mb-md-2">
<span class="mr-4 mb-md-2">
<%= if address_name = primary_name(@address) do %>
<strong class="mr-4 mb-md-2 text-primary"><%= address_name %></strong>
<% end %>
<span data-selector="transaction-count"><%= Cldr.Number.to_string!(@transaction_count, format: "#,###") %></span> <%= gettext "Transactions" %>
</span>
<%= if @address.token do %>
<span class="mb-md-2">
<%= link(token_title(@address.token), to: token_path(@conn, :show, @address.hash), "data-test": "token_hash_link" ) %>
</span>
<% end %>
</div>
<%= if contract?(@address) do %>
<span class="mr-4" data-test="address_contract_creator">
<%= gettext "Contract created by" %>
<%= link(
trimmed_hash(@address.contracts_creation_internal_transaction.from_address_hash),
to: address_path(
BlockScoutWeb.Endpoint,
:show,
@address.contracts_creation_internal_transaction.from_address_hash
)
) %>
trimmed_hash(@address.contracts_creation_internal_transaction.from_address_hash),
to: address_path(@conn, :show, @address.contracts_creation_internal_transaction.from_address_hash)
) %>
<%= gettext "at" %>
<%= link(
trimmed_hash(@address.contracts_creation_internal_transaction.transaction_hash),
to: transaction_path(
BlockScoutWeb.Endpoint,
:show,
@address.contracts_creation_internal_transaction.transaction_hash
),
"data-test": "transaction_hash_link"
) %>
trimmed_hash(@address.contracts_creation_internal_transaction.transaction_hash),
to: transaction_path(@conn, :show, @address.contracts_creation_internal_transaction.transaction_hash),
"data-test": "transaction_hash_link"
) %>
</span>
<% end %>
</div>
@ -62,7 +67,7 @@
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="qrModalLabel"><%= gettext "QR Code" %></h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= gettext("Close") %>">
<span aria-hidden="true">&times;</span>
</button>
</div>

@ -9,7 +9,14 @@
<%= link(
gettext("Transactions"),
class: "nav-link",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
@ -17,12 +24,12 @@
gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link active") do %>
<%= gettext("Code") %>
@ -35,7 +42,7 @@
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
@ -46,7 +53,7 @@
<%= if !smart_contract_verified?(@address) do %>
<%= link(
gettext("Verify and Publish"),
to: address_verify_contract_path(@conn, :new, @conn.params["address_id"]),
to: address_verify_contract_path(@conn, :new, @address.hash),
class: "button button-primary button-sm float-right ml-3",
"data-test": "verify_and_publish"
) %>
@ -55,25 +62,25 @@
<%= if smart_contract_verified?(@address) do %>
<div class="mb-4">
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted">Contract name:</dt>
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Contract name:" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.name %></dd>
</dl>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted">Optimization enabled</dt>
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Optimization enabled" %></dt>
<dd class="col-sm-8 col-md-10"><%= format_optimization(@address.smart_contract.optimization) %></dd>
</dl>
<dl class="row">
<dt class="col-sm-4 col-md-2 text-muted">Compiler version</dt>
<dt class="col-sm-4 col-md-2 text-muted"><%= gettext "Compiler version" %></dt>
<dd class="col-sm-8 col-md-10"><%= @address.smart_contract.compiler_version %></dd>
</dl>
</div>
<hr/>
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3>Contract source code</h3>
<h3><%= gettext "Contract source code" %></h3>
<span class="icon-links" data-clipboard-text="<%= @address.smart_contract.contract_source_code %>">
<button type="button" class="button button-secondary button-sm" id="button" data-toggle="tooltip" data-placement="top">
Copy Contract Source Code
<%= gettext "Copy Contract Source Code" %>
</button>
</span>
</div>
@ -88,10 +95,10 @@
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3>Contract ABI</h3>
<h3><%= gettext "Contract ABI" %></h3>
<span class="icon-links" data-clipboard-text="<%= format_smart_contract_abi(@address.smart_contract.abi) %>">
<button type="button" class="button button-secondary button-sm" id="button">
Copy Contract ABI
<%= gettext "Copy Contract ABI" %>
</button>
</span>
</div>
@ -108,10 +115,10 @@
<% end %>
<section>
<div class="d-flex justify-content-between align-items-baseline">
<h3>Contract creation code</h3>
<h3><%= gettext "Contract creation code" %></h3>
<span class="icon-links" data-clipboard-text="<%= @address.contract_code %>">
<button type="button" class="button button-secondary button-sm" id="button">
Copy Contract Creation Code
<%= gettext "Copy Contract Creation Code" %>
</button>
</span>
</div>

@ -2,26 +2,26 @@
<div class="card">
<div class="card-body">
<h1 class="card-title">New Smart Contract</h1>
<h1 class="card-title"><%= gettext "New Smart Contract" %></h1>
<%= form_for @changeset,
address_verify_contract_path(@conn, :create, @conn.params["address_id"]),
fn f -> %>
<div class="form-group">
<%= label f, :address_hash, "Contract Address" %>
<%= label f, :address_hash, gettext("Contract Address") %>
<%= text_input f, :address_hash, class: "form-control", "aria-describedby": "contract-address-help-block", readonly: true %>
<%= error_tag f, :address_hash, id: "contract-address-help-block", class: "text-danger" %>
</div>
<div class="form-group">
<%= label f, :name, "Contract Name" %>
<%= label f, :name, gettext("Contract Name") %>
<%= text_input f, :name, class: "form-control", "aria-describedby": "contract-name-help-block", "data-test": "contract_name" %>
<%= error_tag f, :name, id: "contract-name-help-block", class: "text-danger" %>
</div>
<div class="form-group mb-4">
<%= label f, :compiler_version, "Compiler" %>
<%= label f, :compiler_version, gettext("Compiler") %>
<%= select f, :compiler_version, @compiler_versions, class: "form-control", selected: "latest", "aria-describedby": "compiler-help-block" %>
<%= error_tag f, :compiler_version, id: "compiler-help-block", class: "text-danger" %>
</div>
@ -31,19 +31,19 @@
<div class="form-check mb-2">
<%= radio_button f, :optimization, false, checked: true, class: "form-check-input", "aria-describedby": "optimization-help-block" %>
<%= label :smart_contract_optimization, :false, "No", class: "form-check-label" %>
<%= label :smart_contract_optimization, :false, gettext("No"), class: "form-check-label" %>
</div>
<div class="form-check">
<%= radio_button f, :optimization, true, class: "form-check-input", "aria-describedby": "optimization-help-block" %>
<%= label :smart_contract_optimization, :true, "Yes", class: "form-check-label" %>
<%= label :smart_contract_optimization, :true, gettext("Yes"), class: "form-check-label" %>
</div>
<%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger" %>
</div>
<div class="form-group mb-4">
<%= label f, :contract_source_code, "Enter the Solidity Contract Code below" %>
<%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code below") %>
<%= textarea f, :contract_source_code, class: "form-control monospace", rows: 3, "aria-describedby": "contract-source-code-help-block" %>
<%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger", "data-test": "contract-source-code-error" %>
</div>
@ -56,10 +56,10 @@
class="d-none px-4 position-absolute button button-primary button-sm mr-2">
<i class="fa fa-spinner fa-spin"></i> <%= gettext("loading.....") %>
</button>
<%= submit "Verify and publish", class: "button button-primary button-sm mr-2", "data-loading": "animation" %>
<%= reset "Reset", class: "button button-secondary button-sm mr-2" %>
<%= submit gettext("Verify and publish"), class: "button button-primary button-sm mr-2", "data-loading": "animation" %>
<%= reset gettext("Reset"), class: "button button-secondary button-sm mr-2" %>
<%= link(
"Cancel",
gettext("Cancel"),
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
class: "button button-sm") %>
<% end %>

@ -1,21 +1,21 @@
<div class="tile tile-type-internal-transaction fade-in" data-test="internal_transaction" data-internal-transaction-id="<%= @internal_transaction.id %>">
<div class="row">
<div class="col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center tile-label mb-1 mb-md-0 pl-md-4">
<%= gettext("Internal Transaction") %>
<%= gettext("Internal Transaction") %>
</div>
<div class="col-md-8 col-lg-8 d-flex flex-column text-nowrap pr-2 pr-sm-2 pr-md-0">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @internal_transaction.transaction_hash %>
<span class="text-nowrap">
<%= if @address.hash == @internal_transaction.from_address_hash do %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address_hash: @internal_transaction.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address: @internal_transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
<% else %>
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @internal_transaction.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: @internal_transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
<% end %>
&rarr;
<%= if @address.hash == BlockScoutWeb.InternalTransactionView.to_address_hash(@internal_transaction) do %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address_hash: BlockScoutWeb.InternalTransactionView.to_address_hash(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address: BlockScoutWeb.InternalTransactionView.to_address(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
<% else %>
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: BlockScoutWeb.InternalTransactionView.to_address_hash(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: BlockScoutWeb.InternalTransactionView.to_address(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
<% end %>
</span>
<span class="tile-title text-truncate mt-3 mt-md-0">

@ -12,7 +12,14 @@
<%= link(
gettext("Transactions"),
class: "nav-link",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
@ -20,13 +27,13 @@
gettext("Internal Transactions"),
class: "nav-link active",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<%= if contract?(@address) do %>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
@ -40,7 +47,7 @@
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
@ -49,22 +56,27 @@
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Internal Transactions</a>
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext("Internal Transactions") %></a>
<div class="dropdown-menu">
<%= link(
gettext("Transactions"),
class: "dropdown-item",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Tokens"),
class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= if contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
@ -96,7 +108,7 @@
<div class="dropdown-menu dropdown-menu-right filter" aria-labelledby="dropdownMenu2">
<%= link(
gettext("All"),
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"]),
to: address_internal_transaction_path(@conn, :index, @address.hash),
class: "address__link address__link--active dropdown-item",
"data-test": "filter_option"
) %>
@ -105,7 +117,7 @@
to: address_internal_transaction_path(
@conn,
:index,
@conn.params["address_id"],
@address.hash,
filter: "to"
),
class: "address__link address__link--active dropdown-item",
@ -116,7 +128,7 @@
to: address_internal_transaction_path(
@conn,
:index,
@conn.params["address_id"],
@address.hash,
filter: "from"
),
class: "address__link address__link--active dropdown-item",

@ -1,11 +0,0 @@
<div class="tile tile-muted tile-function-response monospace">
[ <%= @function_name %> method Response ]
<p class="text-dark">
[
<%= for item <- @outputs do %>
<span class="text-dark function-response-item"><%= if named_argument?(item) do %><%= item["name"] %><% end %>(<%= item["type"] %>) : <%= item["value"] %></span>
<% end %>
]
</p>
</div>

@ -9,7 +9,14 @@
<%= link(
gettext("Transactions"),
class: "nav-link",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
@ -17,12 +24,12 @@
gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
@ -34,7 +41,7 @@
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link active")%>
</li>
</ul>

@ -0,0 +1,10 @@
<div class="tile tile-type-token">
<div class="row justify-content">
<div class="col-md-12 d-flex flex-column tile-label">
<%= link(to: token_path(@conn, :show, @token.contract_address_hash), class: "tile-title-lg") do %>
<%= token_name(@token) %>
<% end %>
<span><%= @token.type %> - <%= number_of_transfers(@token) %></span>
</div>
</div>
</div>

@ -0,0 +1,132 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<section>
<div class="card">
<div class="card-header">
<!-- DESKTOP TAB NAV -->
<ul class="nav nav-tabs card-header-tabs d-none d-md-inline-flex">
<li class="nav-item">
<%= link(
gettext("Transactions"),
class: "nav-link",
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link active",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item"> <%= link(
gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<%= if AddressView.contract?(@address) do %>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
<%= if AddressView.smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
</li>
<% end %>
<%= if AddressView.smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
<%= if AddressView.smart_contract_with_read_only_functions?(@address) do %>
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
</ul>
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Tokens" %></a>
<div class="dropdown-menu">
<%= link(
gettext("Transactions"),
class: "dropdown-item",
to: address_transaction_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Tokens"),
class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= if AddressView.contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
<%= if AddressView.smart_contract_verified?(@address) do %>
<i class="far fa-check-circle"></i>
<% end %>
<% end %>
<% end %>
</div>
</li>
</ul>
</div>
<div class="card-body">
<h2 class="card-title"><%= gettext "Tokens" %></h2>
<%= if Enum.any?(@tokens) do %>
<%= for token <- @tokens do %>
<%= render "_tokens.html", conn: @conn, token: token %>
<% end %>
<% else %>
<div class="tile tile-muted text-center">
<span><%= gettext "There are no tokens for this address." %></span>
</div>
<% end %>
<div>
<%= if @next_page_params do %>
<%= link(
gettext("Next"),
class: "button button-secondary button-sm float-right",
to: address_token_path(
@conn,
:index,
@address,
@next_page_params
)
) %>
<% end %>
</div>
</div>
</div>
</section>
</section>

@ -12,7 +12,14 @@
<%= link(
gettext("Transactions"),
class: "nav-link active",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
<%= link(
gettext("Tokens"),
class: "nav-link",
to: address_token_path(@conn, :index, @address.hash)
) %>
</li>
<li class="nav-item">
@ -20,13 +27,13 @@
gettext("Internal Transactions"),
class: "nav-link",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
</li>
<%= if contract?(@address) do %>
<li class="nav-item">
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "nav-link") do %>
<%= gettext("Code") %>
@ -40,7 +47,7 @@
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: address_read_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_read_contract_path(@conn, :index, @address.hash),
class: "nav-link")%>
</li>
<% end %>
@ -49,22 +56,27 @@
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Transactions</a>
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Transactions" %></a>
<div class="dropdown-menu">
<%= link(
gettext("Transactions"),
class: "dropdown-item",
to: address_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_transaction_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Tokens"),
class: "dropdown-item",
to: address_token_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Internal Transactions"),
class: "dropdown-item",
"data-test": "internal_transactions_tab_link",
to: address_internal_transaction_path(@conn, :index, @conn.params["address_id"])
to: address_internal_transaction_path(@conn, :index, @address.hash)
) %>
<%= if contract?(@address) do %>
<%= link(
to: address_contract_path(@conn, :index, @conn.params["address_id"]),
to: address_contract_path(@conn, :index, @address.hash),
class: "dropdown-item") do %>
<%= gettext("Code") %>
@ -97,7 +109,7 @@
<div class="dropdown-menu dropdown-menu-right filter" aria-labelledby="dropdownMenu2">
<%= link(
gettext("All"),
to: address_transaction_path(@conn, :index, @conn.params["address_id"]),
to: address_transaction_path(@conn, :index, @address.hash),
class: "address__link address__link--active dropdown-item",
"data-test": "filter_option"
) %>
@ -106,7 +118,7 @@
to: address_transaction_path(
@conn,
:index,
@conn.params["address_id"],
@address.hash,
filter: "to"
),
class: "address__link address__link--active dropdown-item",
@ -117,7 +129,7 @@
to: address_transaction_path(
@conn,
:index,
@conn.params["address_id"],
@address.hash,
filter: "from"
),
class: "address__link address__link--active dropdown-item",

@ -1,8 +1,13 @@
<section class="container">
<section class="container" data-page="block-list">
<div class="card">
<div class="card-body">
<div data-selector="channel-disconnected-message" style="display:none;">
<div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost, click to load newer blocks" %></a>
</div>
</div>
<h1>Blocks</h1>
<h1><%= gettext "Blocks" %></h1>
<span data-selector="blocks-list">
<%= for block <- @blocks do %>

@ -20,7 +20,7 @@
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Transactions</a>
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext "Transactions" %></a>
<div class="dropdown-menu">
<%= link(
gettext("Transactions"),

@ -1,4 +1,4 @@
<div class="dashboard-banner-container d-none d-md-block">
<div class="dashboard-banner-container d-none d-md-block" data-page="chain-details">
<div class="container">
<div class="dashboard-banner">
<div class="dashboard-banner-chart">

@ -3,7 +3,7 @@
<%= link to: chain_path(@conn, :show), class: "navbar-brand", "data-test": "header_logo" do %>
<img class="navbar-logo" src="<%= Application.get_env(:block_scout_web, BlockScoutWeb.Chain)[:logo] %>" />
<% end %>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="<%= gettext("Toggle navigation") %>">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
@ -26,12 +26,12 @@
</ul>
<%= form_for @conn, chain_path(@conn, :search), [class: "form-inline my-2 my-lg-0", method: :get, enforce_utf8: false], fn f -> %>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text" id="search-icon"><i class="fas fa-search"></i></span>
<%= search_input f, :q, class: 'form-control mr-auto', placeholder: gettext("Search by address, transaction hash, or block number"), "aria-describedby": "search-icon", "aria-label": gettext("Search"), "data-test": "search_input" %>
<div class="input-group-append">
<button class="input-group-text" id="search-icon"><i class="fas fa-search"></i></button>
</div>
<%= search_input f, :q, class: 'form-control mr-sm-2 ml-auto', placeholder: gettext("Search by address, transaction hash, or block number"), "aria-describedby": "search-icon", "aria-label": "Search", "data-test": "search_input" %>
</div>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only" type="submit">Search</button>
<button class="btn btn-outline-success my-2 my-sm-0 sr-only" type="submit"><%= gettext "Search" %></button>
<% end %>
<ul class="navbar-nav">
<li class="nav-item dropdown">

@ -56,10 +56,10 @@
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: transaction.hash %>
<span class="text-nowrap">
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: transaction.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(transaction.from_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(transaction.from_address) %>
&rarr;
<%= if transaction.to_address_hash do %>
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: transaction.to_address_hash, contract: BlockScoutWeb.AddressView.contract?(transaction.to_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: transaction.to_address, contract: BlockScoutWeb.AddressView.contract?(transaction.to_address) %>
<% else %>
<%= gettext("Contract Address Pending") %>
<% end %>

@ -1,11 +1,20 @@
<div class="tile tile-muted tile-function-response monospace">
[ <%= @function_name %> method Response ]
<pre class="pre-wrap">
<code>
[ <%= @function_name %> method Response ]
<p class="text-dark">
[
<%= for item <- @outputs do %>
<span class="text-dark function-response-item"><%= if named_argument?(item) do %><%= item["name"] %><% end %>(<%= item["type"] %>) : <%= item["value"] %></span>
<% end %>
]
</p>
<p class="text-dark">
[
<%= for item <- @outputs do %>
<span class="text-dark function-response-item">
<%= if named_argument?(item) do %>
<%= item["name"] %>
<% end %>
(<%= item["type"] %>) : <%= values(item["value"]) %>
</span>
<% end %>
]
</p>
</code>
</pre>
</div>

@ -9,7 +9,7 @@
</div>
<%= if queryable?(function["inputs"]) do %>
<div class="">
<div>
<form class="form-inline" data-function-form data-url="<%= smart_contract_path(@conn, :show, @address.hash) %>">
<input type="hidden" name="function_name" value='<%= function["name"] %>' />
@ -50,7 +50,6 @@
<input class="wei-ether" type="checkbox" autocomplete="off">
<span class="d-inline-block" data-conversion-text-wei><%= gettext("WEI")%></span>
<span class="d-inline-block" data-conversion-text-eth><%= gettext("ETH")%></span>
<span>
</div>
<% else %>
<%= output["value"] %>

@ -0,0 +1,19 @@
<div class="tile tile-type-token fade-in mb-10" data-test="token_holders">
<div class="row">
<div class="col-md-7 col-lg-8 d-flex flex-column">
<span>
<%= render BlockScoutWeb.AddressView, "_link.html", address: @token_balance.address, contract: BlockScoutWeb.AddressView.contract?(@token_balance.address) %>
</span>
<span>
<span class="text-dark">
<%= format_token_balance_value(@token_balance.value, @token) %> <%= @token.symbol %>
</span>
<%= if @token.total_supply > 0 do %>
(<%= total_supply_percentage(@token_balance.value, @token.total_supply) %>)
<% end %>
</span>
</div>
</div>
</div>

@ -0,0 +1,95 @@
<section class="container">
<%= render(
OverviewView,
"_details.html",
token: @token,
total_token_transfers: @total_token_transfers,
total_token_holders: @total_token_holders,
conn: @conn
) %>
<section>
<div class="card">
<div class="card-header">
<!-- DESKTOP TAB NAV -->
<ul class="nav nav-tabs card-header-tabs d-none d-md-inline-flex">
<li class="nav-item">
<%= link(
gettext("Token Transfers"),
class: "nav-link",
to: token_path(@conn, :show, @token.contract_address_hash)
) %>
</li>
<%= if TokenView.smart_contract_with_read_only_functions?(@token) do %>
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: token_read_contract_path(@conn, :index, @token.contract_address_hash),
class: "nav-link")%>
</li>
<% end %>
<li class="nav-item">
<%= link(
gettext("Token Holders"),
class: "nav-link active",
"data-test": "token_holders_tab",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</li>
</ul>
<!-- MOBILE DROPDOWN NAV -->
<ul class="nav nav-tabs card-header-tabs d-md-none">
<li class="nav-item dropdown flex-fill text-center">
<a class="nav-link active dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"><%= gettext("Token Holders") %></a>
<div class="dropdown-menu">
<%= link(
gettext("Token Transfers"),
class: "dropdown-item",
to: token_path(@conn, :show, @token.contract_address_hash)
) %>
<%= if TokenView.smart_contract_with_read_only_functions?(@token) do %>
<%= link(
gettext("Read Contract"),
to: "#",
class: "dropdown-item")%>
<% end %>
<%= link(
gettext("Token Holders"),
class: "dropdown-item",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</div>
</li>
</ul>
</div>
<!-- Token Holders -->
<div class="card-body">
<h2 class="card-title"><%= gettext "Token Holders" %></h2>
<%= if Enum.any?(@token_balances) do %>
<%= for token_balance <- @token_balances do %>
<%= render "_token_balances.html", token: @token, token_balance: token_balance %>
<% end %>
<% else %>
<div class="tile tile-muted text-center">
<span data-selector="empty-transactions-list">
<%= gettext "There are no holders for this Token." %>
</span>
</div>
<% end %>
<%= if @next_page_params do %>
<%= link(
gettext("Next Page"),
class: "button button-secondary button-small float-right mt-4",
to: token_holder_path(@conn, :index, @token.contract_address_hash, @next_page_params)
) %>
<% end %>
</div>
</div>
</section>
</section>

@ -5,12 +5,12 @@
<div class="card-body">
<div class="icon-links float-right">
<span data-clipboard-text="<%= @token.contract_address_hash %>">
<button type="button" class="icon-link" id="button" data-toggle="tooltip" data-placement="top" title="<%= gettext("Copy Address") %>" aria-label="Copy Address">
<button type="button" class="icon-link" id="button" data-toggle="tooltip" data-placement="top" title="<%= gettext("Copy Address") %>" aria-label="<%= gettext("Copy Address") %>">
<i class="fas fa-clone"></i>
</button>
</span>
<span data-toggle="modal" data-target="#qrModal">
<button type="button" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("QR Code") %>" aria-label="Show QR Code">
<button type="button" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("QR Code") %>" aria-label="<%= gettext("QR Code") %>">
<i class="fas fa-qrcode"></i>
</button>
</span>
@ -23,17 +23,25 @@
<% end %>
</h1>
<h3><%= to_string(@token.contract_address_hash) %></h3>
<div class="d-flex flex-row justify-content-start text-muted">
<span class="mr-4"> <%= @token.type %> </span>
<span class="mr-4"><%= @total_address_in_token_transfers %> <%= gettext "addresses" %></span>
<span class="mr-4"><%= @total_token_transfers %> <%= gettext "Transfers" %></span>
<%= if decimals?(@token) do %>
<span class="mr-4"><%= @token.decimals %> <%= gettext "decimals" %></span>
<% end %>
<div class="d-flex flex-column flex-md-row justify-content-start text-muted">
<span class="mr-4 mb-3 mb-md-0">
<%= link to:
address_path(@conn, :show, @token.contract_address_hash),
"data-test": "token_contract_address"
do %>
<%= gettext "View Contract" %>
<% end %>
</span>
<div class="d-flex flex-row justify-content-start text-muted">
<span class="mr-4"> <%= @token.type %> </span>
<span class="mr-4"><%= @total_token_holders %> <%= gettext "addresses" %></span>
<span class="mr-4"><%= @total_token_transfers %> <%= gettext "Transfers" %></span>
<%= if decimals?(@token) do %>
<span class="mr-4"><%= @token.decimals %> <%= gettext "decimals" %></span>
<% end %>
</div>
</div>
</div>
</div>
@ -71,7 +79,7 @@
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="qrModalLabel"><%= gettext "QR Code" %></h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= gettext("Close") %>">
<span aria-hidden="true">&times;</span>
</button>
</div>

@ -4,7 +4,8 @@
"_details.html",
token: @token,
total_token_transfers: @total_token_transfers,
total_address_in_token_transfers: @total_address_in_token_transfers
total_token_holders: @total_token_holders,
conn: @conn
) %>
<section>
@ -26,6 +27,15 @@
to: token_read_contract_path(@conn, :index, @conn.params["token_id"]),
class: "nav-link active")%>
</li>
<li class="nav-item">
<%= link(
gettext("Token Holders"),
class: "nav-link",
"data-test": "token_holders_tab",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</li>
</ul>
<!-- MOBILE DROPDOWN NAV -->
@ -42,6 +52,11 @@
gettext("Read Contract"),
to: "#",
class: "nav-link")%>
<%= link(
gettext("Token Holders"),
class: "nav-link",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</div>
</li>
</ul>

@ -1,4 +1,4 @@
<div class="tile tile-type-token fade-in mb-10">
<div class="tile tile-type-token-transfer fade-in">
<div class="row">
<div class="pl-5 col-md-2 d-flex flex-column align-items-left justify-content-start justify-content-lg-center">
<span class="tile-label"><%= gettext "Token Transfer" %></span>
@ -11,14 +11,14 @@
<span>
<%= render BlockScoutWeb.AddressView,
"_link.html",
address_hash: to_string(@transfer.from_address_hash),
address: @transfer.from_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.from_address) %>
&rarr;
<%= render BlockScoutWeb.AddressView,
"_link.html",
address_hash: to_string(@transfer.to_address_hash),
address: @transfer.to_address,
contract: BlockScoutWeb.AddressView.contract?(@transfer.to_address) %>
</span>

@ -4,7 +4,8 @@
"_details.html",
token: @token,
total_token_transfers: @total_token_transfers,
total_address_in_token_transfers: @total_address_in_token_transfers
total_token_holders: @total_token_holders,
conn: @conn
) %>
<section>
@ -24,10 +25,19 @@
<li class="nav-item">
<%= link(
gettext("Read Contract"),
to: token_read_contract_path(@conn, :index, @conn.params["id"]),
to: token_read_contract_path(@conn, :index, @token.contract_address_hash),
class: "nav-link")%>
</li>
<% end %>
<li class="nav-item"i>
<%= link(
gettext("Token Holders"),
class: "nav-link",
"data-test": "token_holders_tab",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</li>
</ul>
<!-- MOBILE DROPDOWN NAV -->
@ -37,15 +47,20 @@
<div class="dropdown-menu">
<%= link(
gettext("Token Transfers"),
class: "nav-link active",
class: "dropdown-item",
to: token_path(@conn, :show, @token.contract_address_hash)
) %>
<%= if smart_contract_with_read_only_functions?(@token) do %>
<%= link(
gettext("Read Contract"),
to: "#",
class: "nav-link")%>
class: "dropdown-item")%>
<% end %>
<%= link(
gettext("Token Holders"),
class: "dropdown-item",
to: token_holder_path(@conn, :index, @token.contract_address_hash)
) %>
</div>
</li>
</ul>

@ -11,13 +11,9 @@
<div class="col-md-7 col-lg-8 d-flex flex-column pr-2 pr-sm-2 pr-md-0">
<%= render "_link.html", transaction_hash: @transaction.hash %>
<span class="text-nowrap">
<%= BlockScoutWeb.AddressView.display_address_hash(assigns[:current_address], @transaction.from_address) %>
<%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:from, assigns[:current_address]) |> BlockScoutWeb.AddressView.render_partial() %>
&rarr;
<%= if assigns[:current_address] && assigns[:current_address].hash == to_address_hash(@transaction) do %>
<%= render BlockScoutWeb.AddressView, "_responsive_hash.html", address_hash: to_address_hash(@transaction), contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address) %>
<% else %>
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: to_address_hash(@transaction), contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address) %>
<% end %>
<%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:to, assigns[:current_address]) |> BlockScoutWeb.AddressView.render_partial() %>
</span>
<span class="d-flex flex-md-row flex-column mt-3 mt-md-0">
<span class="tile-title">

@ -11,9 +11,9 @@
</span>
<% end %>
<% end %>
<%= BlockScoutWeb.AddressView.display_address_hash(@address, @token_transfer.from_address, true) %>
<%= @token_transfer |> BlockScoutWeb.AddressView.address_partial_selector(:from, @address, true) |> BlockScoutWeb.AddressView.render_partial() %>
&rarr;
<%= BlockScoutWeb.AddressView.display_address_hash(@address, @token_transfer.to_address, true) %>
<%= @token_transfer |> BlockScoutWeb.AddressView.address_partial_selector(:to, @address, true) |> BlockScoutWeb.AddressView.render_partial() %>
</span>
<span class="col-12 col-md-7 ml-3 ml-sm-0">
<%= token_transfer_amount(@token_transfer) %>

@ -1,4 +1,4 @@
<section class="container">
<section class="container" data-page="transaction-list">
<div class="card">
<div class="card-header">

@ -1,20 +1,23 @@
<% block = @transaction.block %>
<section>
<section data-page="transaction-details">
<div class="row">
<div class="col-md-12 col-lg-8">
<!-- Transaction Details -->
<div class="card">
<div class="card-body">
<div class="icon-links float-right">
<span data-clipboard-text="<%= @transaction %>">
<button type="button" class="icon-link" id="button" data-toggle="tooltip" data-placement="top" title="<%= gettext("Copy Txn Hash") %>" aria-label="<%= gettext("Copy Transaction Hash") %>">
<i class="fas fa-clone"></i>
</button>
</span>
</div>
<h1 class="card-title"><%= gettext "Transaction Details" %> </h1>
<h3 data-test="transaction_detail_hash"><%= @transaction %> </h3>
<span class="d-block mb-2">
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @transaction.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(@transaction.from_address) %>
<span class="d-block mb-2 text-muted">
<%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:from, nil) |> BlockScoutWeb.AddressView.render_partial() %>
<span class="text-muted"> &rarr; </span>
<%= if @transaction.to_address_hash do %>
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @transaction.to_address_hash, contract: BlockScoutWeb.AddressView.contract?(@transaction.to_address) %>
<% else %>
<%= gettext("Contract Address Pending") %>
<% end %>
<%= @transaction |> BlockScoutWeb.AddressView.address_partial_selector(:to, nil) |> BlockScoutWeb.AddressView.render_partial() %>
</span>
<div class="d-flex flex-row justify-content-start text-muted">
<span class="mr-4 text-<%= BlockScoutWeb.TransactionView.type_suffix(@transaction) %>"><%= BlockScoutWeb.TransactionView.transaction_display_type(@transaction) %></span>

@ -6,9 +6,9 @@
<div class="col-md-9 col-lg-10 d-flex flex-column text-nowrap">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @internal_transaction.transaction_hash %>
<span class="text-nowrap">
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @internal_transaction.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: @internal_transaction.from_address, contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.from_address) %>
&rarr;
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: BlockScoutWeb.InternalTransactionView.to_address_hash(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: BlockScoutWeb.InternalTransactionView.to_address(@internal_transaction), contract: BlockScoutWeb.AddressView.contract?(@internal_transaction.to_address) %>
</span>
<span class="tile-title text-truncate">

@ -67,7 +67,7 @@
<%= for log <- @logs do %>
<div data-test="transaction_log" class="tile tile-muted">
<dl class="row">
<dt class="col-md-1"> Address </dt>
<dt class="col-md-1"> <%= gettext "Address" %> </dt>
<dd class="col-md-11">
<h3 class="">
<%= link(
@ -78,7 +78,7 @@
) %>
</h3>
</dd>
<dt class="col-md-1"> Topics </dt>
<dt class="col-md-1"><%= gettext "Topics" %></dt>
<dd class="col-md-11">
<%= unless is_nil(log.first_topic) do %>
<div class="text-dark">
@ -100,7 +100,7 @@
<% end %>
</dd>
<dt class="col-md-1">
Data
<%= gettext "Data" %>
</dt>
<dd class="col-md-11">
<%= unless is_nil(log.data) do %>

@ -1,4 +1,4 @@
<div class="tile tile-type-token fade-in">
<div class="tile tile-type-token-transfer fade-in">
<div class="row justify-content-end">
<div class="col-12 col-md-4 col-lg-2 d-flex align-items-center justify-content-start justify-content-lg-center tile-label">
<%= gettext("Token Transfer") %>
@ -7,9 +7,9 @@
<div class="col-12 col-md-8 col-lg-10 d-flex flex-column text-nowrap">
<%= render BlockScoutWeb.TransactionView, "_link.html", transaction_hash: @token_transfer.transaction_hash %>
<span class="text-nowrap">
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @token_transfer.from_address_hash, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.from_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.from_address) %>
&rarr;
<%= render BlockScoutWeb.AddressView, "_link.html", address_hash: @token_transfer.to_address_hash, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address) %>
<%= render BlockScoutWeb.AddressView, "_link.html", address: @token_transfer.to_address, contract: BlockScoutWeb.AddressView.contract?(@token_transfer.to_address) %>
</span>
<span class="tile-title text-truncate">

@ -6,9 +6,4 @@ defmodule BlockScoutWeb.AddressReadContractView do
def queryable?(inputs), do: Enum.any?(inputs)
def address?(type), do: type == "address"
def named_argument?(%{"name" => ""}), do: false
def named_argument?(%{"name" => nil}), do: false
def named_argument?(%{"name" => _}), do: true
def named_argument?(_), do: false
end

@ -0,0 +1,9 @@
defmodule BlockScoutWeb.AddressTokenView do
use BlockScoutWeb, :view
alias BlockScoutWeb.AddressView
def number_of_transfers(token) do
ngettext("%{count} transfer", "%{count} transfers", token.number_of_transfers)
end
end

@ -1,10 +1,46 @@
defmodule BlockScoutWeb.AddressView do
use BlockScoutWeb, :view
alias Explorer.Chain.{Address, Hash, SmartContract}
alias Explorer.Chain.{Address, Hash, SmartContract, Token, TokenTransfer, Transaction}
@dialyzer :no_match
def address_partial_selector(struct_to_render_from, direction, current_address, truncate \\ false)
def address_partial_selector(%TokenTransfer{to_address: address}, :to, current_address, truncate) do
matching_address_check(current_address, address, contract?(address), truncate)
end
def address_partial_selector(%TokenTransfer{from_address: address}, :from, current_address, truncate) do
matching_address_check(current_address, address, contract?(address), truncate)
end
def address_partial_selector(
%Transaction{to_address_hash: nil, created_contract_address_hash: nil},
:to,
_current_address,
_truncate
) do
gettext("Contract Address Pending")
end
def address_partial_selector(
%Transaction{to_address: nil, created_contract_address: contract_address},
:to,
current_address,
truncate
) do
matching_address_check(current_address, contract_address, true, truncate)
end
def address_partial_selector(%Transaction{to_address: address}, :to, current_address, truncate) do
matching_address_check(current_address, address, contract?(address), truncate)
end
def address_partial_selector(%Transaction{from_address: address}, :from, current_address, truncate) do
matching_address_check(current_address, address, contract?(address), truncate)
end
def address_title(%Address{} = address) do
if contract?(address) do
gettext("Contract Address")
@ -45,10 +81,35 @@ defmodule BlockScoutWeb.AddressView do
|> Base.encode64()
end
def render_partial(%{partial: partial, address: address, contract: contract?, truncate: truncate}) do
render(
partial,
address: address,
contract: contract?,
truncate: truncate
)
end
def render_partial(text), do: text
def smart_contract_verified?(%Address{smart_contract: %SmartContract{}}), do: true
def smart_contract_verified?(%Address{smart_contract: nil}), do: false
def smart_contract_with_read_only_functions?(%Address{smart_contract: %SmartContract{}} = address) do
Enum.any?(address.smart_contract.abi, & &1["constant"])
end
def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false
def token_title(%Token{name: nil, contract_address_hash: contract_address_hash}) do
contract_address_hash
|> to_string
|> String.slice(0..5)
end
def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
def trimmed_hash(%Hash{} = hash) do
string_hash = to_string(hash)
"#{String.slice(string_hash, 0..5)}#{String.slice(string_hash, -6..-1)}"
@ -56,38 +117,33 @@ defmodule BlockScoutWeb.AddressView do
def trimmed_hash(_), do: ""
def smart_contract_with_read_only_functions?(%Address{smart_contract: %SmartContract{}} = address) do
Enum.any?(address.smart_contract.abi, & &1["constant"])
defp matching_address_check(%Address{hash: hash} = current_address, %Address{hash: hash}, contract?, truncate) do
%{
partial: "_responsive_hash.html",
address: current_address,
contract: contract?,
truncate: truncate
}
end
def smart_contract_with_read_only_functions?(%Address{smart_contract: nil}), do: false
def display_address_hash(current_address, target_address, truncate \\ false)
def display_address_hash(nil, target_address, truncate) do
render(
"_link.html",
address_hash: target_address.hash,
contract: contract?(target_address),
defp matching_address_check(_current_address, %Address{} = address, contract?, truncate) do
%{
partial: "_link.html",
address: address,
contract: contract?,
truncate: truncate
)
}
end
def display_address_hash(current_address, target_address, truncate) do
if current_address.hash == target_address.hash do
render(
"_responsive_hash.html",
address_hash: current_address.hash,
contract: contract?(current_address),
truncate: truncate
)
else
render(
"_link.html",
address_hash: target_address.hash,
contract: contract?(target_address),
truncate: truncate
)
@doc """
Returns the primary name of an address if available.
"""
def primary_name(%Address{names: [_ | _] = address_names}) do
case Enum.find(address_names, &(&1.primary == true)) do
nil -> nil
%Address.Name{name: name} -> name
end
end
def primary_name(%Address{names: _}), do: nil
end

@ -0,0 +1,41 @@
defmodule BlockScoutWeb.API.RPC.ContractView do
use BlockScoutWeb, :view
alias BlockScoutWeb.API.RPC.RPCView
def render("getabi.json", %{abi: abi}) do
RPCView.render("show.json", data: Jason.encode!(abi))
end
def render("getsourcecode.json", %{contract: contract}) do
RPCView.render("show.json", data: prepare_contract(contract))
end
def render("error.json", assigns) do
RPCView.render("error.json", assigns)
end
defp prepare_contract(nil) do
[
%{
"SourceCode" => "",
"ABI" => "Contract source code not verified",
"ContractName" => "",
"CompilerVersion" => "",
"OptimizationUsed" => ""
}
]
end
defp prepare_contract(contract) do
[
%{
"SourceCode" => contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
"OptimizationUsed" => if(contract.optimization, do: "1", else: "0")
}
]
end
end

@ -0,0 +1,22 @@
defmodule BlockScoutWeb.API.RPC.TransactionView do
use BlockScoutWeb, :view
alias BlockScoutWeb.API.RPC.RPCView
def render("gettxreceiptstatus.json", %{status: status}) do
prepared_status = prepare_tx_receipt_status(status)
RPCView.render("show.json", data: %{"status" => prepared_status})
end
def render("error.json", assigns) do
RPCView.render("error.json", assigns)
end
defp prepare_tx_receipt_status(""), do: ""
defp prepare_tx_receipt_status(nil), do: ""
defp prepare_tx_receipt_status(:ok), do: "1"
defp prepare_tx_receipt_status(_), do: "0"
end

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.InternalTransactionView do
use BlockScoutWeb, :view
@dialyzer :no_match
alias Explorer.Chain.InternalTransaction
alias Explorer.Chain.{Address, InternalTransaction}
def create?(%InternalTransaction{type: :create}), do: true
def create?(_), do: false
@ -11,4 +11,7 @@ defmodule BlockScoutWeb.InternalTransactionView do
def to_address_hash(%InternalTransaction{to_address_hash: nil, created_contract_address_hash: hash}), do: hash
def to_address_hash(%InternalTransaction{to_address_hash: hash}), do: hash
def to_address(%InternalTransaction{to_address: nil, created_contract_address: %Address{} = address}), do: address
def to_address(%InternalTransaction{to_address: %Address{} = address}), do: address
end

@ -9,4 +9,7 @@ defmodule BlockScoutWeb.SmartContractView do
def named_argument?(%{"name" => nil}), do: false
def named_argument?(%{"name" => _}), do: true
def named_argument?(_), do: false
def values(values) when is_list(values), do: Enum.join(values, ",")
def values(value), do: value
end

@ -0,0 +1,50 @@
defmodule BlockScoutWeb.Tokens.HolderView do
use BlockScoutWeb, :view
alias BlockScoutWeb.Tokens.{OverviewView, TokenView}
alias Explorer.Chain.{Token}
@doc """
Calculates the percentage of the value from the given total supply.
## Examples
iex> value = Decimal.new(200)
iex> total_supply = Decimal.new(1000)
iex> BlockScoutWeb.Tokens.HolderView.total_supply_percentage(value, total_supply)
"20.0000%"
"""
def total_supply_percentage(value, total_supply) do
result =
value
|> Decimal.div(total_supply)
|> Decimal.mult(100)
|> Decimal.round(4)
|> Decimal.to_string()
result <> "%"
end
@doc """
Formats the token balance value according to the Token's type.
## Examples
iex> token = build(:token, type: "ERC-20", decimals: 2)
iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(100000, token)
"1,000"
iex> token = build(:token, type: "ERC-721")
iex> BlockScoutWeb.Tokens.HolderView.format_token_balance_value(1, token)
1
"""
def format_token_balance_value(value, %Token{type: "ERC-20", decimals: decimals}) do
format_according_to_decimals(value, decimals)
end
def format_token_balance_value(value, _token) do
value
end
end

@ -10,6 +10,8 @@ defmodule BlockScoutWeb.TransactionView do
defguardp is_transaction_type(mod) when mod in [InternalTransaction, Transaction]
defdelegate formatted_timestamp(block), to: BlockView
def confirmations(%Transaction{block: block}, named_arguments) when is_list(named_arguments) do
case block do
nil -> 0
@ -17,22 +19,23 @@ defmodule BlockScoutWeb.TransactionView do
end
end
def from_or_to_address?(_token_transfer, nil), do: false
def contract_creation?(%Transaction{to_address: nil}), do: true
def from_or_to_address?(%{from_address_hash: from_hash, to_address_hash: to_hash}, %Address{hash: hash}) do
from_hash == hash || to_hash == hash
end
def contract_creation?(_), do: false
# This is the address to be shown in the to field
def to_address_hash(%Transaction{to_address_hash: nil, created_contract_address_hash: address_hash}), do: address_hash
def to_address(%Transaction{to_address: nil, created_contract_address: %Address{} = address}), do: address
def to_address_hash(%Transaction{to_address: %Address{hash: address_hash}}), do: address_hash
def to_address(%Transaction{to_address: %Address{} = address}), do: address
def fee(%Transaction{} = transaction) do
{_, value} = Chain.fee(transaction, :wei)
value
end
def format_gas_limit(gas) do
Number.to_string!(gas)
end
def formatted_fee(%Transaction{} = transaction, opts) do
transaction
|> Chain.fee(:wei)
@ -43,34 +46,6 @@ defmodule BlockScoutWeb.TransactionView do
end
end
def gas_used(%Transaction{gas_used: nil}), do: gettext("Pending")
def gas_used(%Transaction{gas_used: gas_used}) do
Number.to_string!(gas_used)
end
def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do
AddressView.contract?(from_address) || AddressView.contract?(to_address)
end
def involves_token_transfers?(%Transaction{token_transfers: []}), do: false
def involves_token_transfers?(%Transaction{token_transfers: transfers}) when is_list(transfers), do: true
def contract_creation?(%Transaction{to_address: nil}), do: true
def contract_creation?(_), do: false
def qr_code(%Transaction{hash: hash}) do
hash
|> to_string()
|> QRCode.to_png()
|> Base.encode64()
end
def format_gas_limit(gas) do
Number.to_string!(gas)
end
def formatted_status(transaction) do
transaction
|> Chain.transaction_to_status()
@ -82,7 +57,11 @@ defmodule BlockScoutWeb.TransactionView do
end
end
defdelegate formatted_timestamp(block), to: BlockView
def from_or_to_address?(_token_transfer, nil), do: false
def from_or_to_address?(%{from_address_hash: from_hash, to_address_hash: to_hash}, %Address{hash: hash}) do
from_hash == hash || to_hash == hash
end
def gas(%type{gas: gas}) when is_transaction_type(type) do
Cldr.Number.to_string!(gas)
@ -95,22 +74,39 @@ defmodule BlockScoutWeb.TransactionView do
format_wei_value(gas_price, unit)
end
def gas_used(%Transaction{gas_used: nil}), do: gettext("Pending")
def gas_used(%Transaction{gas_used: gas_used}) do
Number.to_string!(gas_used)
end
def hash(%Transaction{hash: hash}) do
to_string(hash)
end
def involves_contract?(%Transaction{from_address: from_address, to_address: to_address}) do
AddressView.contract?(from_address) || AddressView.contract?(to_address)
end
def involves_token_transfers?(%Transaction{token_transfers: []}), do: false
def involves_token_transfers?(%Transaction{token_transfers: transfers}) when is_list(transfers), do: true
def qr_code(%Transaction{hash: hash}) do
hash
|> to_string()
|> QRCode.to_png()
|> Base.encode64()
end
def status(transaction) do
Chain.transaction_to_status(transaction)
end
def type_suffix(%Transaction{} = transaction) do
cond do
involves_token_transfers?(transaction) -> "token"
contract_creation?(transaction) -> "contract-creation"
involves_contract?(transaction) -> "contract-call"
true -> "transaction"
end
end
# This is the address to be shown in the to field
def to_address_hash(%Transaction{to_address_hash: nil, created_contract_address_hash: address_hash}),
do: address_hash
def to_address_hash(%Transaction{to_address_hash: address_hash}), do: address_hash
def transaction_display_type(%Transaction{} = transaction) do
cond do
@ -121,6 +117,15 @@ defmodule BlockScoutWeb.TransactionView do
end
end
def type_suffix(%Transaction{} = transaction) do
cond do
involves_token_transfers?(transaction) -> "token-transfer"
contract_creation?(transaction) -> "contract-creation"
involves_contract?(transaction) -> "contract-call"
true -> "transaction"
end
end
@doc """
Converts a transaction's Wei value to Ether and returns a formatted display value.

@ -8,7 +8,7 @@ msgstr ""
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Blocks"
msgstr ""
@ -33,28 +33,23 @@ msgstr ""
msgid "BlockScout"
msgstr ""
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
#: lib/block_scout_web/templates/address_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
#: lib/block_scout_web/templates/address/overview.html.eex:27
#: lib/block_scout_web/templates/address_contract/index.html.eex:10
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:59
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:10
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token/index.html.eex:73
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:74
#: lib/block_scout_web/templates/address_token/index.html.eex:72
#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_transaction/index.html.eex:148
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:141
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
@ -141,12 +136,12 @@ msgid "%{count} transactions in this block"
msgstr ""
#: lib/block_scout_web/templates/transaction_log/index.html.eex:70
#: lib/block_scout_web/views/address_view.ex:79
#: lib/block_scout_web/views/address_view.ex:48
msgid "Address"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:131
#: lib/block_scout_web/templates/address_transaction/index.html.eex:135
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:127
#: lib/block_scout_web/templates/address_transaction/index.html.eex:128
#: lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
@ -157,12 +152,12 @@ msgstr ""
msgid "Overview"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:55
#: lib/block_scout_web/views/transaction_view.ex:56
msgid "Success"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:120
#: lib/block_scout_web/templates/address_transaction/index.html.eex:124
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:116
#: lib/block_scout_web/templates/address_transaction/index.html.eex:117
#: lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
@ -209,8 +204,8 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/index.html.eex:16
#: lib/block_scout_web/templates/transaction/index.html.eex:35
#: lib/block_scout_web/templates/transaction/overview.html.eex:43
#: lib/block_scout_web/views/transaction_view.ex:53
#: lib/block_scout_web/views/transaction_view.ex:79
#: lib/block_scout_web/views/transaction_view.ex:55
#: lib/block_scout_web/views/transaction_view.ex:77
msgid "Pending"
msgstr ""
@ -298,10 +293,12 @@ msgstr ""
msgid "Showing #%{number}"
msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:15
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:68
#: lib/block_scout_web/templates/transaction/_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/overview.html.eex:87
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
msgstr ""
@ -311,20 +308,16 @@ msgstr ""
msgid "Gwei"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:26
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:74
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:143
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:26
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:139
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_token/index.html.eex:83
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:27
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:84
#: lib/block_scout_web/templates/address_transaction/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:73
#: lib/block_scout_web/templates/address_token/index.html.eex:82
#: lib/block_scout_web/templates/address_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:49
@ -336,7 +329,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -370,8 +363,8 @@ msgstr ""
msgid "Wei"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:114
#: lib/block_scout_web/templates/address_transaction/index.html.eex:118
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:110
#: lib/block_scout_web/templates/address_transaction/index.html.eex:111
#: lib/block_scout_web/views/address_internal_transaction_view.ex:11
#: lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
@ -409,7 +402,6 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
#: lib/block_scout_web/views/address_view.ex:99
msgid "Market Cap"
msgstr ""
@ -450,22 +442,16 @@ msgstr ""
msgid "TXNs"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:19
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
#: lib/block_scout_web/templates/address_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:69
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:19
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:64
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:67
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token/index.html.eex:19
#: lib/block_scout_web/templates/address_token/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:78
#: lib/block_scout_web/templates/address_token/index.html.eex:109
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:70
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:79
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:112
#: lib/block_scout_web/templates/address_token/index.html.eex:68
#: lib/block_scout_web/templates/address_token/index.html.eex:77
#: lib/block_scout_web/templates/address_token/index.html.eex:104
#: lib/block_scout_web/templates/address_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:68
#: lib/block_scout_web/templates/address_transaction/index.html.eex:67
msgid "Tokens"
msgstr ""
@ -473,7 +459,7 @@ msgstr ""
msgid "Total Gas Used"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:124
#: lib/block_scout_web/views/transaction_view.ex:116
msgid "Transaction"
msgstr ""
@ -497,7 +483,7 @@ msgid "Contract"
msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:12
#: lib/block_scout_web/views/address_view.ex:77
#: lib/block_scout_web/views/address_view.ex:46
msgid "Contract Address"
msgstr ""
@ -509,8 +495,8 @@ msgstr ""
msgid "There are no Transactions"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:158
#: lib/block_scout_web/templates/address_transaction/index.html.eex:162
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:154
#: lib/block_scout_web/templates/address_transaction/index.html.eex:155
#: lib/block_scout_web/templates/block/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:78
@ -528,19 +514,14 @@ msgstr ""
msgid "Verify and Publish"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:36
#: lib/block_scout_web/templates/address_contract/index.html.eex:57
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:82
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:36
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:77
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:81
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_token/index.html.eex:38
#: lib/block_scout_web/templates/address_token/index.html.eex:91
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:39
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:92
#: lib/block_scout_web/templates/address_transaction/index.html.eex:39
#: lib/block_scout_web/templates/address_transaction/index.html.eex:82
#: lib/block_scout_web/templates/address_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:81
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:122
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
msgid "Code"
@ -566,18 +547,22 @@ msgid "Newer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:122
#: lib/block_scout_web/views/transaction_view.ex:114
msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:69
#: lib/block_scout_web/templates/layout/_topnav.html.eex:42
msgid "POA Sokol"
msgstr ""
@ -617,7 +602,7 @@ msgid "Wallet addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:108
#: lib/block_scout_web/templates/address_transaction/index.html.eex:101
#: lib/block_scout_web/templates/transaction/index.html.eex:53
msgid "Connection Lost, click to load newer transactions"
msgstr ""
@ -631,13 +616,15 @@ msgid "Copy Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:4
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:4
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:4
msgid "Internal Transaction"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:68
#: lib/block_scout_web/templates/address/overview.html.eex:69
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81
@ -645,53 +632,53 @@ msgid "QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:152
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:148
msgid "There are no internal transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:156
#: lib/block_scout_web/templates/address_transaction/index.html.eex:149
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:64
#: lib/block_scout_web/views/address_view.ex:21
#: lib/block_scout_web/views/address_view.ex:55
#: lib/block_scout_web/views/address_view.ex:24
msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:123
#: lib/block_scout_web/views/transaction_view.ex:115
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:38
msgid "Contract created by"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:43
#: lib/block_scout_web/templates/address/overview.html.eex:44
msgid "at"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:44
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:36
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
msgid "Block #%{number}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:31
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:29
#: lib/block_scout_web/templates/transaction/_tile.html.eex:43
msgid "IN"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:29
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:27
#: lib/block_scout_web/templates/transaction/_tile.html.eex:39
msgid "OUT"
msgstr ""
@ -702,25 +689,25 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:46
#: lib/block_scout_web/templates/address_contract/index.html.eex:91
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:51
msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:52
msgid "ETH"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:44
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:49
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:89
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:84
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_token/index.html.eex:50
#: lib/block_scout_web/templates/address_token/index.html.eex:58
#: lib/block_scout_web/templates/address_token/index.html.eex:99
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:51
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:59
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:100
#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
#: lib/block_scout_web/templates/address_transaction/index.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:49
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:27
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:55
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:26
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:44
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:52
#: lib/block_scout_web/templates/tokens/token/show.html.eex:27
#: lib/block_scout_web/templates/tokens/token/show.html.eex:55
@ -738,8 +725,8 @@ msgid "Block Height #%{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:69
#: lib/block_scout_web/templates/address/overview.html.eex:77
#: lib/block_scout_web/templates/address/overview.html.eex:70
#: lib/block_scout_web/templates/address/overview.html.eex:78
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:90
msgid "Close"
@ -779,7 +766,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:121
#: lib/block_scout_web/views/transaction_view.ex:113
msgid "Token Transfer"
msgstr ""
@ -796,7 +783,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:103
#: lib/block_scout_web/templates/address_transaction/index.html.eex:96
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
@ -825,6 +812,7 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:18
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:49
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:18
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:44
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:47
#: lib/block_scout_web/templates/tokens/token/show.html.eex:18
#: lib/block_scout_web/templates/tokens/token/show.html.eex:46
@ -857,7 +845,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "API"
msgstr ""
@ -867,7 +855,7 @@ msgid "There are no token transfers for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:94
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:52
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:67
msgid "loading..."
msgstr ""
@ -1061,12 +1049,12 @@ msgid "Max of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:104
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:100
msgid "Connection Lost, click to load newer internal transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:99
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:95
msgid "More internal transactions have come in"
msgstr ""
@ -1088,13 +1076,12 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:123
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:134
#: lib/block_scout_web/templates/address_token/index.html.eex:118
msgid "Next"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:116
#: lib/block_scout_web/templates/address_token/index.html.eex:111
msgid "There are no tokens for this address."
msgstr ""
@ -1121,12 +1108,12 @@ msgid "Compiler"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:121
#: lib/block_scout_web/templates/address_contract/index.html.eex:73
msgid "Compiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:144
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract ABI"
msgstr ""
@ -1136,20 +1123,35 @@ msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:162
#: lib/block_scout_web/templates/address_contract/index.html.eex:118
msgid "Contract creation code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:113
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:128
#: lib/block_scout_web/templates/address_contract/index.html.eex:80
msgid "Contract source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:101
msgid "Copy Contract ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:121
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
msgid "Copy Contract Source Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:103
msgid "Data"
@ -1171,7 +1173,7 @@ msgid "No"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:117
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
msgid "Optimization enabled"
msgstr ""
@ -1181,8 +1183,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Search"
msgstr ""
@ -1201,6 +1203,11 @@ msgstr ""
msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify and publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:39
msgid "Yes"
@ -1214,61 +1221,4 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:6
msgid "Connection Lost, click to load newer blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:54
msgid "(Awaiting internal transactions for status)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:58
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:56
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:130
#: lib/block_scout_web/templates/address_contract/index.html.eex:146
#: lib/block_scout_web/templates/address_contract/index.html.eex:164
msgid "Copy Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:103
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
msgid "Accounts"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:6
msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:128
msgid "There are no token transfers for this address."
msgstr ""

@ -20,7 +20,7 @@ msgstr "Block"
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:56
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:13
msgid "Blocks"
msgstr "Blocks"
@ -45,28 +45,23 @@ msgstr "Height"
msgid "BlockScout"
msgstr "BlockScout"
#: lib/block_scout_web/templates/address/index.html.eex:28
#: lib/block_scout_web/templates/address/overview.html.eex:28
#: lib/block_scout_web/templates/address_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
#: lib/block_scout_web/templates/address/overview.html.eex:27
#: lib/block_scout_web/templates/address_contract/index.html.eex:10
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:64
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:12
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:59
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:10
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token/index.html.eex:73
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:74
#: lib/block_scout_web/templates/address_token/index.html.eex:72
#: lib/block_scout_web/templates/address_transaction/index.html.eex:13
#: lib/block_scout_web/templates/address_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_transaction/index.html.eex:63
#: lib/block_scout_web/templates/address_transaction/index.html.eex:148
#: lib/block_scout_web/templates/address_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_transaction/index.html.eex:62
#: lib/block_scout_web/templates/address_transaction/index.html.eex:141
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
#: lib/block_scout_web/templates/block_transaction/index.html.eex:23
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:36
#: lib/block_scout_web/templates/chain/show.html.eex:73
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/layout/_topnav.html.eex:18
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:46
#: lib/block_scout_web/templates/transaction/index.html.eex:56
msgid "Transactions"
@ -153,12 +148,12 @@ msgid "%{count} transactions in this block"
msgstr "%{count} transactions in this block"
#: lib/block_scout_web/templates/transaction_log/index.html.eex:70
#: lib/block_scout_web/views/address_view.ex:79
#: lib/block_scout_web/views/address_view.ex:48
msgid "Address"
msgstr "Address"
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:131
#: lib/block_scout_web/templates/address_transaction/index.html.eex:135
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:127
#: lib/block_scout_web/templates/address_transaction/index.html.eex:128
#: lib/block_scout_web/views/address_internal_transaction_view.ex:10
#: lib/block_scout_web/views/address_transaction_view.ex:10
msgid "From"
@ -169,12 +164,12 @@ msgstr "From"
msgid "Overview"
msgstr "Overview"
#: lib/block_scout_web/views/transaction_view.ex:55
#: lib/block_scout_web/views/transaction_view.ex:56
msgid "Success"
msgstr "Success"
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:120
#: lib/block_scout_web/templates/address_transaction/index.html.eex:124
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:116
#: lib/block_scout_web/templates/address_transaction/index.html.eex:117
#: lib/block_scout_web/views/address_internal_transaction_view.ex:9
#: lib/block_scout_web/views/address_transaction_view.ex:9
msgid "To"
@ -221,8 +216,8 @@ msgstr "Showing %{count} Transactions"
#: lib/block_scout_web/templates/transaction/index.html.eex:16
#: lib/block_scout_web/templates/transaction/index.html.eex:35
#: lib/block_scout_web/templates/transaction/overview.html.eex:43
#: lib/block_scout_web/views/transaction_view.ex:53
#: lib/block_scout_web/views/transaction_view.ex:79
#: lib/block_scout_web/views/transaction_view.ex:55
#: lib/block_scout_web/views/transaction_view.ex:77
msgid "Pending"
msgstr "Pending"
@ -310,10 +305,12 @@ msgstr ""
msgid "Showing #%{number}"
msgstr ""
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:15
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:22
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:68
#: lib/block_scout_web/templates/transaction/_tile.html.eex:20
#: lib/block_scout_web/templates/transaction/overview.html.eex:87
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:16
#: lib/block_scout_web/views/wei_helpers.ex:72
msgid "Ether"
msgstr "POA"
@ -323,20 +320,16 @@ msgstr "POA"
msgid "Gwei"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:26
#: lib/block_scout_web/templates/address_contract/index.html.eex:75
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:60
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:74
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:143
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:26
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:69
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:59
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:72
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:139
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_token/index.html.eex:26
#: lib/block_scout_web/templates/address_token/index.html.eex:83
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:27
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:84
#: lib/block_scout_web/templates/address_transaction/index.html.eex:28
#: lib/block_scout_web/templates/address_transaction/index.html.eex:73
#: lib/block_scout_web/templates/address_token/index.html.eex:82
#: lib/block_scout_web/templates/address_transaction/index.html.eex:27
#: lib/block_scout_web/templates/address_transaction/index.html.eex:72
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:49
@ -348,7 +341,7 @@ msgstr ""
msgid "Internal Transactions"
msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -382,8 +375,8 @@ msgstr ""
msgid "Wei"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:114
#: lib/block_scout_web/templates/address_transaction/index.html.eex:118
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:110
#: lib/block_scout_web/templates/address_transaction/index.html.eex:111
#: lib/block_scout_web/views/address_internal_transaction_view.ex:11
#: lib/block_scout_web/views/address_transaction_view.ex:11
msgid "All"
@ -421,7 +414,6 @@ msgstr ""
#: lib/block_scout_web/templates/chain/show.html.eex:17
#: lib/block_scout_web/templates/layout/app.html.eex:24
#: lib/block_scout_web/views/address_view.ex:99
msgid "Market Cap"
msgstr ""
@ -462,22 +454,16 @@ msgstr ""
msgid "TXNs"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:19
#: lib/block_scout_web/templates/address_contract/index.html.eex:70
#: lib/block_scout_web/templates/address_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:69
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:19
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:64
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:67
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:17
#: lib/block_scout_web/templates/address_token/index.html.eex:19
#: lib/block_scout_web/templates/address_token/index.html.eex:69
#: lib/block_scout_web/templates/address_token/index.html.eex:78
#: lib/block_scout_web/templates/address_token/index.html.eex:109
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:70
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:79
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:112
#: lib/block_scout_web/templates/address_token/index.html.eex:68
#: lib/block_scout_web/templates/address_token/index.html.eex:77
#: lib/block_scout_web/templates/address_token/index.html.eex:104
#: lib/block_scout_web/templates/address_transaction/index.html.eex:20
#: lib/block_scout_web/templates/address_transaction/index.html.eex:68
#: lib/block_scout_web/templates/address_transaction/index.html.eex:67
msgid "Tokens"
msgstr ""
@ -485,7 +471,7 @@ msgstr ""
msgid "Total Gas Used"
msgstr ""
#: lib/block_scout_web/views/transaction_view.ex:124
#: lib/block_scout_web/views/transaction_view.ex:116
msgid "Transaction"
msgstr ""
@ -509,7 +495,7 @@ msgid "Contract"
msgstr ""
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:12
#: lib/block_scout_web/views/address_view.ex:77
#: lib/block_scout_web/views/address_view.ex:46
msgid "Contract Address"
msgstr ""
@ -521,8 +507,8 @@ msgstr ""
msgid "There are no Transactions"
msgstr ""
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:158
#: lib/block_scout_web/templates/address_transaction/index.html.eex:162
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:154
#: lib/block_scout_web/templates/address_transaction/index.html.eex:155
#: lib/block_scout_web/templates/block/index.html.eex:20
#: lib/block_scout_web/templates/block_transaction/index.html.eex:50
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:78
@ -540,19 +526,14 @@ msgstr "Contract Source Code"
msgid "Verify and Publish"
msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:36
#: lib/block_scout_web/templates/address_contract/index.html.eex:57
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
#: lib/block_scout_web/templates/address_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:82
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:36
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:77
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:81
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:34
#: lib/block_scout_web/templates/address_token/index.html.eex:38
#: lib/block_scout_web/templates/address_token/index.html.eex:91
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:39
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:92
#: lib/block_scout_web/templates/address_transaction/index.html.eex:39
#: lib/block_scout_web/templates/address_transaction/index.html.eex:82
#: lib/block_scout_web/templates/address_transaction/index.html.eex:38
#: lib/block_scout_web/templates/address_transaction/index.html.eex:81
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:122
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
msgid "Code"
@ -578,18 +559,22 @@ msgid "Newer"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:122
#: lib/block_scout_web/views/transaction_view.ex:114
msgid "Contract Creation"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:66
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
#: lib/block_scout_web/templates/layout/_topnav.html.eex:39
msgid "Mainnet"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:69
#: lib/block_scout_web/templates/layout/_topnav.html.eex:42
msgid "POA Sokol"
msgstr ""
@ -629,7 +614,7 @@ msgid "Wallet addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:108
#: lib/block_scout_web/templates/address_transaction/index.html.eex:101
#: lib/block_scout_web/templates/transaction/index.html.eex:53
msgid "Connection Lost, click to load newer transactions"
msgstr ""
@ -643,13 +628,15 @@ msgid "Copy Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:4
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:4
#: lib/block_scout_web/templates/transaction_internal_transaction/_internal_transaction.html.eex:4
msgid "Internal Transaction"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:68
#: lib/block_scout_web/templates/address/overview.html.eex:69
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:81
@ -657,53 +644,53 @@ msgid "QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:152
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:148
msgid "There are no internal transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:156
#: lib/block_scout_web/templates/address_transaction/index.html.eex:149
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:64
#: lib/block_scout_web/views/address_view.ex:21
#: lib/block_scout_web/views/address_view.ex:55
#: lib/block_scout_web/views/address_view.ex:24
msgid "Contract Address Pending"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:123
#: lib/block_scout_web/views/transaction_view.ex:115
msgid "Contract Call"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:38
msgid "Contract created by"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:43
#: lib/block_scout_web/templates/address/overview.html.eex:44
msgid "at"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:21
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:44
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:36
#: lib/block_scout_web/templates/transaction/_tile.html.eex:30
msgid "Block #%{number}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:31
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:29
#: lib/block_scout_web/templates/transaction/_tile.html.eex:43
msgid "IN"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/internal_transaction/_tile.html.eex:29
#:
#: lib/block_scout_web/templates/address_internal_transaction/_internal_transaction.html.eex:27
#: lib/block_scout_web/templates/transaction/_tile.html.eex:39
msgid "OUT"
msgstr ""
@ -714,25 +701,25 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:46
#: lib/block_scout_web/templates/address_contract/index.html.eex:91
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:51
msgid "WEI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:52
msgid "ETH"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:44
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:49
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:89
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:45
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:55
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:84
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:43
#: lib/block_scout_web/templates/address_token/index.html.eex:50
#: lib/block_scout_web/templates/address_token/index.html.eex:58
#: lib/block_scout_web/templates/address_token/index.html.eex:99
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:51
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:59
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:100
#: lib/block_scout_web/templates/address_transaction/index.html.eex:50
#: lib/block_scout_web/templates/address_transaction/index.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:49
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:27
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:55
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:26
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:44
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:52
#: lib/block_scout_web/templates/tokens/token/show.html.eex:27
#: lib/block_scout_web/templates/tokens/token/show.html.eex:55
@ -750,8 +737,8 @@ msgid "Block Height #%{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:69
#: lib/block_scout_web/templates/address/overview.html.eex:77
#: lib/block_scout_web/templates/address/overview.html.eex:70
#: lib/block_scout_web/templates/address/overview.html.eex:78
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:82
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:90
msgid "Close"
@ -791,7 +778,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/token/_token_transfer.html.eex:4
#: lib/block_scout_web/templates/transaction_token_transfer/_token_transfer.html.eex:4
#: lib/block_scout_web/views/transaction_view.ex:121
#: lib/block_scout_web/views/transaction_view.ex:113
msgid "Token Transfer"
msgstr ""
@ -808,7 +795,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:103
#: lib/block_scout_web/templates/address_transaction/index.html.eex:96
#: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/transaction/index.html.eex:48
msgid "More transactions have come in"
@ -837,6 +824,7 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:18
#: lib/block_scout_web/templates/tokens/holder/index.html.eex:49
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:18
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:44
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:47
#: lib/block_scout_web/templates/tokens/token/show.html.eex:18
#: lib/block_scout_web/templates/tokens/token/show.html.eex:46
@ -869,7 +857,7 @@ msgid "Total Supply"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "API"
msgstr ""
@ -879,7 +867,7 @@ msgid "There are no token transfers for this transaction."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:94
#: lib/block_scout_web/templates/address_read_contract/index.html.eex:52
#: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:67
msgid "loading..."
msgstr ""
@ -1073,12 +1061,12 @@ msgid "Max of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:104
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:100
msgid "Connection Lost, click to load newer internal transactions"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:99
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:95
msgid "More internal transactions have come in"
msgstr ""
@ -1100,13 +1088,12 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:123
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:134
#: lib/block_scout_web/templates/address_token/index.html.eex:118
msgid "Next"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token/index.html.eex:116
#: lib/block_scout_web/templates/address_token/index.html.eex:111
msgid "There are no tokens for this address."
msgstr ""
@ -1133,12 +1120,12 @@ msgid "Compiler"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:121
#: lib/block_scout_web/templates/address_contract/index.html.eex:73
msgid "Compiler version"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:144
#: lib/block_scout_web/templates/address_contract/index.html.eex:98
msgid "Contract ABI"
msgstr ""
@ -1148,20 +1135,35 @@ msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:162
#: lib/block_scout_web/templates/address_contract/index.html.eex:118
msgid "Contract creation code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:113
#: lib/block_scout_web/templates/address_contract/index.html.eex:65
msgid "Contract name:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:128
#: lib/block_scout_web/templates/address_contract/index.html.eex:80
msgid "Contract source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:101
msgid "Copy Contract ABI"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:121
msgid "Copy Contract Creation Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:83
msgid "Copy Contract Source Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/index.html.eex:103
msgid "Data"
@ -1183,7 +1185,7 @@ msgid "No"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:117
#: lib/block_scout_web/templates/address_contract/index.html.eex:69
msgid "Optimization enabled"
msgstr ""
@ -1193,8 +1195,8 @@ msgid "Reset"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:46
#: lib/block_scout_web/templates/layout/_topnav.html.eex:53
#: lib/block_scout_web/templates/layout/_topnav.html.eex:29
#: lib/block_scout_web/templates/layout/_topnav.html.eex:34
msgid "Search"
msgstr ""
@ -1213,6 +1215,11 @@ msgstr ""
msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify and publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:39
msgid "Yes"
@ -1226,61 +1233,4 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:6
msgid "Connection Lost, click to load newer blocks"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:54
msgid "(Awaiting internal transactions for status)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:58
msgid "Error: %{reason}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/views/transaction_view.ex:56
msgid "Error: (Awaiting internal transactions for reason)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:130
#: lib/block_scout_web/templates/address_contract/index.html.eex:146
#: lib/block_scout_web/templates/address_contract/index.html.eex:164
msgid "Copy Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:103
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:59
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
msgid "Accounts"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:4
msgid "Addresses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:6
msgid "Showing 250 addresses of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/index.html.eex:8
msgid "total addresses with a balance"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:128
msgid "There are no token transfers for this address."
msgstr ""

@ -0,0 +1,110 @@
defmodule BlockScoutWeb.AddressTokenControllerTest do
use BlockScoutWeb.ConnCase
import BlockScoutWeb.Router.Helpers, only: [address_token_path: 3]
alias Explorer.Chain.{Token}
describe "GET index/2" do
test "with invalid address hash", %{conn: conn} do
conn = get(conn, address_token_path(conn, :index, "invalid_address"))
assert html_response(conn, 422)
end
test "with valid address hash without address", %{conn: conn} do
conn = get(conn, address_token_path(conn, :index, "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"))
assert html_response(conn, 404)
end
test "returns tokens for the address", %{conn: conn} do
address = insert(:address)
token1 =
:token
|> insert(name: "token1")
token2 =
:token
|> insert(name: "token2")
insert(
:token_transfer,
token_contract_address: token1.contract_address,
from_address: address,
to_address: build(:address)
)
insert(
:token_transfer,
token_contract_address: token2.contract_address,
from_address: build(:address),
to_address: address
)
conn = get(conn, address_token_path(conn, :index, address))
actual_token_hashes =
conn.assigns.tokens
|> Enum.map(& &1.contract_address_hash)
assert html_response(conn, 200)
assert Enum.member?(actual_token_hashes, token1.contract_address_hash)
assert Enum.member?(actual_token_hashes, token2.contract_address_hash)
end
test "returns next page of results based on last seen token", %{conn: conn} do
address = insert(:address)
second_page_tokens =
1..50
|> Enum.reduce([], fn i, acc ->
token = insert(:token, name: "A Token#{i}", type: "ERC-20")
insert(:token_transfer, token_contract_address: token.contract_address, from_address: address)
acc ++ [token.name]
end)
|> Enum.sort()
token = insert(:token, name: "Another Token", type: "ERC-721")
insert(:token_transfer, token: token, from_address: address)
%Token{name: name, type: type} = token
conn =
get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, address.hash), %{
"name" => name,
"type" => type
})
actual_tokens =
conn.assigns.tokens
|> Enum.map(& &1.name)
|> Enum.sort()
assert second_page_tokens == actual_tokens
end
test "next_page_params exists if not on last page", %{conn: conn} do
address = insert(:address)
Enum.each(1..51, fn i ->
token = insert(:token, name: "A Token#{i}", type: "ERC-20")
insert(:token_transfer, token_contract_address: token.contract_address, from_address: address)
end)
conn = get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert conn.assigns.next_page_params
end
test "next_page_params are empty if on last page", %{conn: conn} do
address = insert(:address)
token = insert(:token)
insert(:token_transfer, token_contract_address: token.contract_address, from_address: address)
conn = get(conn, address_token_path(BlockScoutWeb.Endpoint, :index, address.hash))
refute conn.assigns.next_page_params
end
end
end

@ -128,7 +128,7 @@ defmodule BlockScoutWeb.AddressTransactionControllerTest do
:transaction
|> insert(to_address: nil, created_contract_address_hash: address.hash)
|> with_block(block)
|> Explorer.Repo.preload(:token_transfers)
|> Explorer.Repo.preload([[created_contract_address: :names], [from_address: :names], :token_transfers])
insert(
:internal_transaction_create,

@ -1680,12 +1680,12 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
address = insert(:address)
block1 = insert(:block, number: block_number1, miner: address)
_block2 = insert(:block, number: block_number2, miner: address)
_block1 = insert(:block, number: block_number1, miner: address)
block2 = insert(:block, number: block_number2, miner: address)
:transaction
|> insert(gas_price: 2)
|> with_block(block1, gas_used: 2)
|> with_block(block2, gas_used: 2)
expected_reward =
block_reward.reward
@ -1705,8 +1705,8 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
expected_result = [
%{
"blockNumber" => to_string(block1.number),
"timeStamp" => to_string(block1.timestamp),
"blockNumber" => to_string(block2.number),
"timeStamp" => to_string(block2.timestamp),
"blockReward" => to_string(expected_reward.value)
}
]

@ -0,0 +1,172 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase
describe "getabi" do
test "with missing address hash", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getabi"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "address is required"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with an invalid address hash", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getabi",
"address" => "badhash"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "Invalid address hash"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with an address that doesn't exist", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getabi",
"address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == nil
assert response["status"] == "0"
assert response["message"] == "Contract source code not verified"
end
test "with a verified contract address", %{conn: conn} do
contract = insert(:smart_contract)
params = %{
"module" => "contract",
"action" => "getabi",
"address" => to_string(contract.address_hash)
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == Jason.encode!(contract.abi)
assert response["status"] == "1"
assert response["message"] == "OK"
end
end
describe "getsourcecode" do
test "with missing address hash", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getsourcecode"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "address is required"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with an invalid address hash", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => "badhash"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "Invalid address hash"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with an address that doesn't exist", %{conn: conn} do
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
}
expected_result = [
%{
"SourceCode" => "",
"ABI" => "Contract source code not verified",
"ContractName" => "",
"CompilerVersion" => "",
"OptimizationUsed" => ""
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "with a verified contract address", %{conn: conn} do
contract = insert(:smart_contract, optimization: true)
params = %{
"module" => "contract",
"action" => "getsourcecode",
"address" => to_string(contract.address_hash)
}
expected_result = [
%{
"SourceCode" => contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version,
# The contract's optimization value is true, so the expected value
# for `OptimzationUsed` is "1". If it was false, the expected value
# would be "0".
"OptimizationUsed" => "1"
}
]
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == expected_result
assert response["status"] == "1"
assert response["message"] == "OK"
end
end
end

@ -0,0 +1,124 @@
defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
use BlockScoutWeb.ConnCase
describe "gettxreceiptstatus" do
test "with missing txhash", %{conn: conn} do
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "txhash is required"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with an invalid txhash", %{conn: conn} do
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus",
"txhash" => "badhash"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] =~ "Invalid txhash format"
assert response["status"] == "0"
assert Map.has_key?(response, "result")
refute response["result"]
end
test "with a txhash that doesn't exist", %{conn: conn} do
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus",
"txhash" => "0x40eb908387324f2b575b4879cd9d7188f69c8fc9d87c901b9e2daaea4b442170"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == %{"status" => ""}
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "with a txhash with ok status", %{conn: conn} do
block = insert(:block)
transaction =
:transaction
|> insert()
|> with_block(block, status: :ok)
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus",
"txhash" => "#{transaction.hash}"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == %{"status" => "1"}
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "with a txhash with error status", %{conn: conn} do
block = insert(:block)
transaction =
:transaction
|> insert()
|> with_block(block, status: :error)
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus",
"txhash" => "#{transaction.hash}"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == %{"status" => "0"}
assert response["status"] == "1"
assert response["message"] == "OK"
end
test "with a txhash with nil status", %{conn: conn} do
transaction = insert(:transaction, status: nil)
params = %{
"module" => "transaction",
"action" => "gettxreceiptstatus",
"txhash" => "#{transaction.hash}"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == %{"status" => ""}
assert response["status"] == "1"
assert response["message"] == "OK"
end
end
end

@ -20,8 +20,14 @@ defmodule BlockScoutWeb.BlockTransactionControllerTest do
test "returns transactions for the block", %{conn: conn} do
block = insert(:block)
:transaction |> insert() |> with_block(block)
:transaction |> insert(to_address: nil) |> with_block(block)
:transaction
|> insert()
|> with_block(block)
:transaction
|> insert(to_address: nil)
|> with_block(block)
|> with_contract_creation(insert(:contract_address))
conn = get(conn, block_transaction_path(BlockScoutWeb.Endpoint, :index, block.number))

@ -0,0 +1,92 @@
defmodule BlockScoutWeb.Tokens.HolderControllerTest do
use BlockScoutWeb.ConnCase
alias Explorer.Chain.Hash
describe "GET index/3" do
test "with invalid address hash", %{conn: conn} do
conn = get(conn, token_holder_path(BlockScoutWeb.Endpoint, :index, "invalid_address"))
assert html_response(conn, 404)
end
test "with a token that doesn't exist", %{conn: conn} do
address = build(:address)
conn = get(conn, token_holder_path(BlockScoutWeb.Endpoint, :index, address.hash))
assert html_response(conn, 404)
end
test "successfully renders the page", %{conn: conn} do
token = insert(:token)
insert_list(
2,
:token_balance,
token_contract_address_hash: token.contract_address_hash
)
conn =
get(
conn,
token_holder_path(BlockScoutWeb.Endpoint, :index, token.contract_address_hash)
)
assert html_response(conn, 200)
end
test "returns next page of results based on last seen token balance", %{conn: conn} do
contract_address = build(:contract_address, hash: "0x6937cb25eb54bc013b9c13c47ab38eb63edd1493")
token = insert(:token, contract_address: contract_address)
second_page_token_balances =
1..50
|> Enum.map(
&insert(
:token_balance,
token_contract_address_hash: token.contract_address_hash,
value: &1 + 1000
)
)
|> Enum.map(& &1.value)
token_balance =
insert(
:token_balance,
token_contract_address_hash: token.contract_address_hash,
value: 50000
)
conn =
get(conn, token_holder_path(conn, :index, token.contract_address_hash), %{
"value" => Decimal.to_integer(token_balance.value),
"address_hash" => Hash.to_string(token_balance.address_hash)
})
actual_token_balances =
conn.assigns.token_balances
|> Enum.map(& &1.value)
|> Enum.reverse()
assert second_page_token_balances == actual_token_balances
end
test "next_page_params exists if not on last page", %{conn: conn} do
contract_address = build(:contract_address, hash: "0x6937cb25eb54bc013b9c13c47ab38eb63edd1493")
token = insert(:token, contract_address: contract_address)
Enum.each(
1..51,
&insert(
:token_balance,
token_contract_address_hash: token.contract_address_hash,
value: &1 + 1000
)
)
conn = get(conn, token_holder_path(conn, :index, token.contract_address_hash))
assert conn.assigns.next_page_params
end
end
end

@ -31,7 +31,7 @@ defmodule BlockScoutWeb.Tokens.ReadContractControllerTest do
assert html_response(conn, 200)
assert token.contract_address_hash == conn.assigns.token.contract_address_hash
assert conn.assigns.total_token_transfers
assert conn.assigns.total_address_in_token_transfers
assert conn.assigns.total_token_holders
end
end
end

@ -0,0 +1,23 @@
defmodule BlockScoutWeb.TokenPage do
@moduledoc false
use Wallaby.DSL
import Wallaby.Query, only: [css: 1, css: 2]
alias Explorer.Chain.{Address}
def visit_page(session, %Address{hash: address_hash}) do
visit_page(session, address_hash)
end
def visit_page(session, contract_address_hash) do
visit(session, "tokens/#{contract_address_hash}")
end
def click_tokens_holders(session) do
click(session, css("[data-test='token_holders_tab']"))
end
def token_holders(count: count) do
css("[data-test='token_holders']", count: count)
end
end

@ -2,7 +2,6 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
use BlockScoutWeb.FeatureCase, async: true
alias Explorer.Chain.Wei
alias Explorer.Factory
alias BlockScoutWeb.{AddressPage, AddressView, Notifier}
setup do
@ -41,7 +40,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
describe "viewing contract creator" do
test "see the contract creator and transaction links", %{session: session} do
address = insert(:address)
contract = insert(:address, contract_code: Factory.data("contract_code"))
contract = insert(:contract_address)
transaction = insert(:transaction, from_address: address, created_contract_address: contract)
internal_transaction =
@ -63,9 +62,9 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
test "see the contract creator and transaction links even when the creator is another contract", %{session: session} do
lincoln = insert(:address)
contract = insert(:address, contract_code: Factory.data("contract_code"))
contract = insert(:contract_address)
transaction = insert(:transaction)
another_contract = insert(:address, contract_code: Factory.data("contract_code"))
another_contract = insert(:contract_address)
insert(
:internal_transaction,
@ -256,19 +255,22 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
session: session
} do
lincoln = addresses.lincoln
contract_address = insert(:contract_address)
from_lincoln =
:transaction
|> insert(from_address: lincoln, to_address: nil)
|> with_block(block)
|> with_contract_creation(contract_address)
internal_transaction =
insert(
:internal_transaction_create,
:internal_transaction_create
|> insert(
transaction: from_lincoln,
from_address: lincoln,
index: 0
)
|> with_contract_creation(contract_address)
session
|> AddressPage.visit_page(addresses.lincoln)
@ -285,12 +287,12 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
lincoln = addresses.lincoln
taft = addresses.taft
contract_token_address = insert(:contract_address)
insert(:token, contract_address: contract_token_address)
contract_address = insert(:contract_address)
insert(:token, contract_address: contract_address)
transaction =
:transaction
|> insert(from_address: lincoln, to_address: contract_token_address)
|> insert(from_address: lincoln, to_address: contract_address)
|> with_block(block)
insert(
@ -298,7 +300,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: taft,
transaction: transaction,
token_contract_address: contract_token_address
token_contract_address: contract_address
)
session
@ -318,12 +320,12 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
taft = addresses.taft
morty = build(:address)
contract_token_address = insert(:contract_address)
insert(:token, contract_address: contract_token_address)
contract_address = insert(:contract_address)
insert(:token, contract_address: contract_address)
transaction =
:transaction
|> insert(from_address: lincoln, to_address: contract_token_address)
|> insert(from_address: lincoln, to_address: contract_address)
|> with_block(block)
insert(
@ -331,7 +333,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: taft,
transaction: transaction,
token_contract_address: contract_token_address
token_contract_address: contract_address
)
insert(
@ -339,7 +341,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: morty,
transaction: transaction,
token_contract_address: contract_token_address
token_contract_address: contract_address
)
session
@ -358,17 +360,13 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
lincoln = addresses.lincoln
taft = addresses.taft
contract_token_address =
insert(
:address,
contract_code: Factory.data("contract_code")
)
contract_address = insert(:contract_address)
insert(:token, contract_address: contract_token_address)
insert(:token, contract_address: contract_address)
transaction =
:transaction
|> insert(from_address: lincoln, to_address: contract_token_address)
|> insert(from_address: lincoln, to_address: contract_address)
|> with_block(block)
insert_list(
@ -377,7 +375,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: taft,
transaction: transaction,
token_contract_address: contract_token_address
token_contract_address: contract_address
)
session
@ -393,12 +391,12 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
lincoln = addresses.lincoln
taft = addresses.taft
contract_token_address = insert(:contract_address)
insert(:token, contract_address: contract_token_address)
contract_address = insert(:contract_address)
insert(:token, contract_address: contract_address)
transaction =
:transaction
|> insert(from_address: lincoln, to_address: contract_token_address)
|> insert(from_address: lincoln, to_address: contract_address)
|> with_block(block)
insert_list(
@ -407,7 +405,7 @@ defmodule BlockScoutWeb.ViewingAddressesTest do
from_address: lincoln,
to_address: taft,
transaction: transaction,
token_contract_address: contract_token_address
token_contract_address: contract_address
)
session

@ -0,0 +1,22 @@
defmodule BlockScoutWeb.ViewingTokensTest do
use BlockScoutWeb.FeatureCase, async: true
alias BlockScoutWeb.TokenPage
describe "viewing token holders" do
test "list the token holders", %{session: session} do
token = insert(:token)
insert_list(
2,
:token_balance,
token_contract_address_hash: token.contract_address_hash
)
session
|> TokenPage.visit_page(token.contract_address)
|> TokenPage.click_tokens_holders()
|> assert_has(TokenPage.token_holders(count: 2))
end
end
end

@ -84,12 +84,17 @@ defmodule BlockScoutWeb.ViewingTransactionsTest do
end
test "contract creation is shown for to_address on list page", %{session: session} do
contract_address = insert(:contract_address)
transaction =
:transaction
|> insert(to_address: nil)
|> with_block()
|> with_contract_creation(contract_address)
insert(:internal_transaction_create, transaction: transaction, index: 0)
:internal_transaction_create
|> insert(transaction: transaction, index: 0)
|> with_contract_creation(contract_address)
session
|> TransactionListPage.visit_page()

@ -0,0 +1,25 @@
defmodule BlockScoutWeb.AddressTokenViewTest do
use BlockScoutWeb.ConnCase, async: true
alias BlockScoutWeb.AddressTokenView
describe "number_of_transfers/1" do
test "returns the singular form when there is only one transfer" do
token = %{number_of_transfers: 1}
assert AddressTokenView.number_of_transfers(token) == "1 transfer"
end
test "returns the plural form when there is more than one transfer" do
token = %{number_of_transfers: 2}
assert AddressTokenView.number_of_transfers(token) == "2 transfers"
end
test "returns the plural form when there are 0 transfers" do
token = %{number_of_transfers: 0}
assert AddressTokenView.number_of_transfers(token) == "0 transfers"
end
end
end

@ -1,9 +1,106 @@
defmodule BlockScoutWeb.AddressViewTest do
use BlockScoutWeb.ConnCase, async: true
alias Explorer.Chain.Data
alias Explorer.Chain.{Address, Data, Hash, Transaction}
alias BlockScoutWeb.AddressView
describe "address_partial_selector/4" do
test "for a pending contract creation to address" do
transaction = insert(:transaction, to_address: nil, created_contract_address_hash: nil)
assert AddressView.address_partial_selector(transaction, :to, nil) == "Contract Address Pending"
end
test "will truncate address" do
transaction = %Transaction{to_address: to_address} = insert(:transaction)
assert %{
partial: "_link.html",
address: ^to_address,
contract: false,
truncate: true
} = AddressView.address_partial_selector(transaction, :to, nil, true)
end
test "for a non-contract to address not on address page" do
transaction = %Transaction{to_address: to_address} = insert(:transaction)
assert %{
partial: "_link.html",
address: ^to_address,
contract: false,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, nil)
end
test "for a non-contract to address non matching address page" do
transaction = %Transaction{to_address: to_address} = insert(:transaction)
assert %{
partial: "_link.html",
address: ^to_address,
contract: false,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, nil)
end
test "for a non-contract to address matching address page" do
transaction = %Transaction{to_address: to_address} = insert(:transaction)
assert %{
partial: "_responsive_hash.html",
address: ^to_address,
contract: false,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, transaction.to_address)
end
test "for a contract to address non matching address page" do
contract_address = insert(:contract_address)
transaction = insert(:transaction, to_address: nil, created_contract_address: contract_address)
assert %{
partial: "_link.html",
address: ^contract_address,
contract: true,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, transaction.to_address)
end
test "for a contract to address matching address page" do
contract_address = insert(:contract_address)
transaction = insert(:transaction, to_address: nil, created_contract_address: contract_address)
assert %{
partial: "_responsive_hash.html",
address: ^contract_address,
contract: true,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, contract_address)
end
test "for a non-contract from address not on address page" do
transaction = %Transaction{to_address: to_address} = insert(:transaction)
assert %{
partial: "_link.html",
address: ^to_address,
contract: false,
truncate: false
} = AddressView.address_partial_selector(transaction, :to, nil)
end
test "for a non-contract from address matching address page" do
transaction = %Transaction{from_address: from_address} = insert(:transaction)
assert %{
partial: "_responsive_hash.html",
address: ^from_address,
contract: false,
truncate: false
} = AddressView.address_partial_selector(transaction, :from, transaction.from_address)
end
end
describe "contract?/1" do
test "with a smart contract" do
{:ok, code} = Data.cast("0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef")
@ -15,6 +112,10 @@ defmodule BlockScoutWeb.AddressViewTest do
address = insert(:address, contract_code: nil)
refute AddressView.contract?(address)
end
test "with nil address" do
assert AddressView.contract?(nil)
end
end
describe "qr_code/1" do
@ -24,6 +125,27 @@ defmodule BlockScoutWeb.AddressViewTest do
end
end
describe "render_partial/1" do
test "renders _link partial" do
address = build(:address)
assert {:safe, _} =
AddressView.render_partial(%{partial: "_link.html", address: address, contract: false, truncate: false})
end
test "renders _responsive_hash partial" do
address = build(:address)
assert {:safe, _} =
AddressView.render_partial(%{
partial: "_responsive_hash.html",
address: address,
contract: false,
truncate: false
})
end
end
describe "smart_contract_verified?/1" do
test "returns true when smart contract is verified" do
smart_contract = insert(:smart_contract)
@ -90,4 +212,62 @@ defmodule BlockScoutWeb.AddressViewTest do
refute AddressView.smart_contract_with_read_only_functions?(address)
end
end
describe "token_title/1" do
test "returns the 6 first chars of address hash when token has no name" do
token = insert(:token, name: nil)
expected_hash = to_string(token.contract_address_hash)
assert String.starts_with?(expected_hash, AddressView.token_title(token))
end
test "returns name(symbol) when token has name" do
token = insert(:token, name: "super token money", symbol: "ST$")
assert AddressView.token_title(token) == "super token money (ST$)"
end
end
describe "hash/1" do
test "gives a string version of an address's hash" do
address = %Address{
hash: %Hash{
byte_count: 20,
bytes: <<139, 243, 141, 71, 100, 146, 144, 100, 242, 212, 211, 165, 101, 32, 167, 106, 179, 223, 65, 91>>
}
}
assert AddressView.hash(address) == "0x8bf38d4764929064f2d4d3a56520a76ab3df415b"
end
end
describe "balance_block_number/1" do
test "gives empty string with no fetched balance block number present" do
assert AddressView.balance_block_number(%Address{}) == ""
end
test "gives block number when fetched balance block number is non-nil" do
assert AddressView.balance_block_number(%Address{fetched_coin_balance_block_number: 1_000_000}) == "1000000"
end
end
describe "primary_name/1" do
test "gives an address's primary name when present" do
address = insert(:address)
address_name = insert(:address_name, address: address, primary: true, name: "POA Foundation Wallet")
insert(:address_name, address: address, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address, :names)
assert AddressView.primary_name(preloaded_address) == address_name.name
end
test "returns nil when no primary available" do
address_name = insert(:address_name, name: "POA Wallet")
preloaded_address = Explorer.Repo.preload(address_name.address, :names)
refute AddressView.primary_name(preloaded_address)
end
end
end

@ -16,4 +16,47 @@ defmodule BlockScoutWeb.InternalTransactionViewTest do
refute InternalTransactionView.create?(internal_transaction)
end
end
describe "to_address_hash/1" do
setup do
transaction = insert(:transaction)
{:ok, transaction: transaction}
end
test "with a contract address", %{transaction: transaction} do
internal_transaction = insert(:internal_transaction_create, transaction: transaction, index: 1)
assert InternalTransactionView.to_address_hash(internal_transaction) ==
internal_transaction.created_contract_address_hash
end
test "without a contract address", %{transaction: transaction} do
internal_transaction = insert(:internal_transaction, transaction: transaction, index: 1)
assert InternalTransactionView.to_address_hash(internal_transaction) == internal_transaction.to_address_hash
end
end
describe "to_address/1" do
setup do
transaction = insert(:transaction)
{:ok, transaction: transaction}
end
test "with a contract address", %{transaction: transaction} do
internal_transaction = insert(:internal_transaction_create, transaction: transaction, index: 1)
preloaded_internal_transaction = Explorer.Repo.preload(internal_transaction, :to_address)
assert InternalTransactionView.to_address(preloaded_internal_transaction) ==
preloaded_internal_transaction.created_contract_address
end
test "without a contract address", %{transaction: transaction} do
internal_transaction = insert(:internal_transaction, transaction: transaction, index: 1)
preloaded_internal_transaction = Explorer.Repo.preload(internal_transaction, :created_contract_address)
assert InternalTransactionView.to_address(preloaded_internal_transaction) ==
preloaded_internal_transaction.to_address
end
end
end

@ -0,0 +1,40 @@
defmodule BlockScoutWeb.Tokens.HolderViewTest do
use BlockScoutWeb.ConnCase, async: true
alias BlockScoutWeb.Tokens.HolderView
alias Explorer.Chain.{Address.TokenBalance, Token}
doctest BlockScoutWeb.Tokens.HolderView, import: true
describe "total_supply_percentage/2" do
test "returns the percentage of the Token total supply" do
%Token{total_supply: total_supply} = build(:token, total_supply: 1000)
%TokenBalance{value: value} = build(:token_balance, value: 200)
assert HolderView.total_supply_percentage(value, total_supply) == "20.0000%"
end
test "considers 4 decimals" do
%Token{total_supply: total_supply} = build(:token, total_supply: 100_000_009)
%TokenBalance{value: value} = build(:token_balance, value: 500)
assert HolderView.total_supply_percentage(value, total_supply) == "0.0005%"
end
end
describe "format_token_balance_value/1" do
test "formats according to token decimals when it's a ERC-20" do
token = build(:token, type: "ERC-20", decimals: 2)
token_balance = build(:token_balance, value: 2_000_000)
assert HolderView.format_token_balance_value(token_balance.value, token) == "20,000"
end
test "returns the value when it's ERC-721" do
token = build(:token, type: "ERC-721")
token_balance = build(:token_balance, value: 1)
assert HolderView.format_token_balance_value(token_balance.value, token) == 1
end
end
end

@ -0,0 +1,73 @@
defmodule BlockScoutWeb.SmartContractViewTest do
use BlockScoutWeb.ConnCase, async: true
alias BlockScoutWeb.SmartContractView
describe "queryable?" do
test "returns true when there are inputs" do
inputs = [%{"name" => "_narcoId", "type" => "uint256"}]
assert SmartContractView.queryable?(inputs)
end
test "returns false when there are no inputs" do
inputs = []
refute SmartContractView.queryable?(inputs)
end
end
describe "address?" do
test "returns true when the type is equal to the string 'address'" do
type = "address"
assert SmartContractView.address?(type)
end
test "returns false when the type is not equal the string 'address'" do
type = "name"
refute SmartContractView.address?(type)
end
end
describe "named_argument?/1" do
test "returns false when name is blank" do
arguments = %{"name" => ""}
refute SmartContractView.named_argument?(arguments)
end
test "returns false when name is nil" do
arguments = %{"name" => nil}
refute SmartContractView.named_argument?(arguments)
end
test "returns true when there is name" do
arguments = %{"name" => "POA"}
assert SmartContractView.named_argument?(arguments)
end
test "returns false arguments don't match" do
arguments = nil
refute SmartContractView.named_argument?(arguments)
end
end
describe "values/1" do
test "joins the values when it is a list" do
values = [8, 6, 9, 2, 2, 37]
assert SmartContractView.values(values) == "8,6,9,2,2,37"
end
test "returns the value" do
value = "POA"
assert SmartContractView.values(value) == "POA"
end
end
end

@ -5,6 +5,35 @@ defmodule BlockScoutWeb.TransactionViewTest do
alias Explorer.Repo
alias BlockScoutWeb.TransactionView
describe "confirmations/2" do
test "returns 0 if pending transaction" do
transaction = build(:transaction, block: nil)
assert 0 == TransactionView.confirmations(transaction, [])
end
test "returns string of number of blocks validated since subject block" do
block = insert(:block)
transaction =
:transaction
|> insert()
|> with_block(block)
assert "1" == TransactionView.confirmations(transaction, max_block_number: block.number + 1)
end
end
describe "contract_creation?/1" do
test "returns true if contract creation transaction" do
assert TransactionView.contract_creation?(build(:transaction, to_address: nil))
end
test "returns false if not contract" do
refute TransactionView.contract_creation?(build(:transaction))
end
end
describe "formatted_fee/2" do
test "pending transaction with no Receipt" do
{:ok, gas_price} = Wei.cast(3_000_000_000)
@ -78,10 +107,35 @@ defmodule BlockScoutWeb.TransactionViewTest do
end
end
test "gas/1 returns the gas as a string" do
assert "2" == TransactionView.gas(build(:transaction, gas: 2))
end
test "hash/1 returns the hash as a string" do
assert "test" == TransactionView.hash(build(:transaction, hash: "test"))
end
describe "qr_code/1" do
test "it returns an encoded value" do
transaction = build(:transaction)
assert {:ok, _} = Base.decode64(TransactionView.qr_code(transaction))
end
end
describe "to_address_hash/1" do
test "returns contract address for created contract transaction" do
contract = insert(:contract_address)
transaction = insert(:transaction, to_address: nil, created_contract_address: contract)
assert contract.hash == TransactionView.to_address_hash(transaction)
end
test "returns hash for transaction" do
transaction =
:transaction
|> insert(to_address: build(:address), created_contract_address: nil)
|> Repo.preload([:created_contract_address, :to_address])
assert TransactionView.to_address(transaction) == transaction.to_address
end
end
end

@ -129,7 +129,7 @@ defmodule EthereumJSONRPC do
[%{contract_address: String.t(), data: String.t(), id: String.t()}],
json_rpc_named_arguments,
[{:block_number, non_neg_integer()}]
) :: {:ok, list()}
) :: {:ok, list()} | {:error, term()}
def execute_contract_functions(functions, json_rpc_named_arguments, opts \\ []) do
block_number = Keyword.get(opts, :block_number)

@ -67,8 +67,8 @@ defmodule EthereumJSONRPC.Encoder do
This functions assumes the result["id"] is the name of the function the result is for.
"""
@spec decode_abi_results({any(), [map()]}, [map()], %{String.t() => [any()]}) :: map()
def decode_abi_results({:ok, results}, abi, functions) do
@spec decode_abi_results([map()], [map()], %{String.t() => [any()]}) :: map()
def decode_abi_results(results, abi, functions) do
selectors =
abi
|> ABI.parse_specification()

@ -10,6 +10,11 @@ defmodule EthereumJSONRPC.Receipt do
alias EthereumJSONRPC
alias EthereumJSONRPC.Logs
# > 21000 gas is charged for any transaction as a "base fee". This covers the cost of an elliptic curve operation to
# > recover the sender address from the signature as well as the disk and bandwidth space of storing the transaction.
# -- https://github.com/ethereum/wiki/wiki/Design-Rationale
@base_fee_gas 21_000
@type elixir :: %{String.t() => String.t() | non_neg_integer}
@typedoc """
@ -81,6 +86,33 @@ defmodule EthereumJSONRPC.Receipt do
* If `"gas"` (supplied by caller from `EthereumJSONRPC.Transaction.elixir`) `==` `"gasUsed"`, then `:status` is
`:error`
iex> EthereumJSONRPC.Receipt.elixir_to_params(
...> %{
...> "blockHash" => "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
...> "blockNumber" => 46147,
...> "contractAddress" => nil,
...> "cumulativeGasUsed" => 21001,
...> "from" => "0xa1e4380a3b1f749673e270229993ee55f35663b4",
...> "gas" => 21001,
...> "gasUsed" => 21001,
...> "logs" => [],
...> "logsBloom" => "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
...> "root" => "0x96a8e009d2b88b1483e6941e6812e32263b05683fac202abc622a3e31aed1957",
...> "to" => "0x5df9b87991262f6ba471f09758cde1c0fc1de734",
...> "transactionHash" => "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
...> "transactionIndex" => 0
...> }
...> )
%{
cumulative_gas_used: 21001,
gas_used: 21001,
status: :error,
transaction_hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
transaction_index: 0
}
* Except, when it is the base transaction fee (21,000 gas)
iex> EthereumJSONRPC.Receipt.elixir_to_params(
...> %{
...> "blockHash" => "0x4e3a3754410177e6937ef1f84bba68ea139e8d1a2258c5f85db9f1cd715a1bdd",
@ -101,7 +133,7 @@ defmodule EthereumJSONRPC.Receipt do
%{
cumulative_gas_used: 21000,
gas_used: 21000,
status: :error,
status: :ok,
transaction_hash: "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060",
transaction_index: 0
}
@ -299,6 +331,10 @@ defmodule EthereumJSONRPC.Receipt do
end
end
# Temporary fix for https://github.com/poanetwork/blockscout/issues/673 as this is the most common gas == gas_used,
# but not failed scenario. Only internal transactions can prove if gas == gas_used means failure pre-Byzantium.
defp pre_byzantium_status(@base_fee_gas, @base_fee_gas), do: :ok
defp pre_byzantium_status(gas, gas_used) when is_integer(gas) and is_integer(gas_used) do
if gas_used < gas do
:ok

@ -39,7 +39,11 @@ defmodule EthereumJSONRPC.WebSocket.WebSocketClient do
# `:depth`, `:verify`, and `:verify_fun`, are based on `:hackney_connect.ssl_opts_1/2` as we use `:hackney` through
# `:httpoison` and this keeps the SSL rules consistent between HTTP and WebSocket
:websocket_client.start_link(fsm_name, url, __MODULE__, url,
:websocket_client.start_link(
fsm_name,
url,
__MODULE__,
url,
ssl_verify: :verify_peer,
socket_opts: [
cacerts: :certifi.cacerts(),

@ -94,25 +94,23 @@ defmodule EthereumJSONRPC.EncoderTest do
describe "decode_abi_results/3" do
test "separates the selectors and map the results" do
result =
{:ok,
[
%{
id: "get1",
jsonrpc: "2.0",
result: "0x000000000000000000000000000000000000000000000000000000000000002a"
},
%{
id: "get2",
jsonrpc: "2.0",
result: "0x000000000000000000000000000000000000000000000000000000000000002a"
},
%{
id: "get3",
jsonrpc: "2.0",
result: "0x0000000000000000000000000000000000000000000000000000000000000020"
}
]}
result = [
%{
id: "get1",
jsonrpc: "2.0",
result: "0x000000000000000000000000000000000000000000000000000000000000002a"
},
%{
id: "get2",
jsonrpc: "2.0",
result: "0x000000000000000000000000000000000000000000000000000000000000002a"
},
%{
id: "get3",
jsonrpc: "2.0",
result: "0x0000000000000000000000000000000000000000000000000000000000000020"
}
]
abi = [
%{

@ -24,6 +24,8 @@ defmodule EthereumJSONRPC.WebSocketTest do
|> WebSocket.json_rpc(transport_options)
end
# Infura timeouts on 2018-09-10
@tag :no_geth
test "can get error", %{subscribe_named_arguments: subscribe_named_arguments} do
transport_options = subscribe_named_arguments[:transport_options]
@ -79,6 +81,8 @@ defmodule EthereumJSONRPC.WebSocketTest do
assert is_binary(subscription_id)
end
# Infura timeouts on 2018-09-10
@tag :no_geth
test "delivers new heads to caller", %{
block_interval: block_interval,
subscribe_named_arguments: subscribe_named_arguments
@ -132,6 +136,8 @@ defmodule EthereumJSONRPC.WebSocketTest do
assert :ok = WebSocket.unsubscribe(subscription)
end
# Infura timeouts on 2018-09-10
@tag :no_geth
test "stops messages being sent to subscriber", %{
block_interval: block_interval,
subscribe_named_arguments: subscribe_named_arguments

@ -569,6 +569,8 @@ defmodule EthereumJSONRPCTest do
assert is_binary(subscription_id)
end
# Infura timeouts on 2018-09-12
@tag :no_geth
test "delivers new heads to caller", %{
block_interval: block_interval,
subscribe_named_arguments: subscribe_named_arguments
@ -599,6 +601,8 @@ defmodule EthereumJSONRPCTest do
end
describe "unsubscribe/2" do
# Infura timeouts on 2018-09-10
@tag :no_geth
test "can unsubscribe", %{subscribe_named_arguments: subscribe_named_arguments} do
transport = Keyword.fetch!(subscribe_named_arguments, :transport)
transport_options = subscribe_named_arguments[:transport_options]
@ -622,6 +626,8 @@ defmodule EthereumJSONRPCTest do
assert :ok = EthereumJSONRPC.unsubscribe(subscription)
end
# Infura timeouts on 2018-09-10
@tag :no_geth
test "stops messages being sent to subscriber", %{
block_interval: block_interval,
subscribe_named_arguments: subscribe_named_arguments

@ -6,7 +6,8 @@ defmodule EthereumJSONRPC.Case.Geth.HTTPWebSocket do
def setup do
EthereumJSONRPC.WebSocket.Case.Geth.setup()
|> Map.put(:json_rpc_named_arguments,
|> Map.put(
:json_rpc_named_arguments,
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,

@ -1 +1,13 @@
use Mix.Config
# Configure your database
config :explorer, Explorer.Repo,
adapter: Ecto.Adapters.Postgres,
database: "explorer_dev",
hostname: "localhost",
username: "postgres",
password: "<REPLACE WITH THE PASSWORD YOU CHOSE>",
loggers: [],
pool_size: 20,
pool_timeout: 60_000,
timeout: 80_000

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save