Extrinsic.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright 2017-2020 @polkadot/app-explorer authors & contributors
  2. // This software may be modified and distributed under the terms
  3. // of the Apache-2.0 license. See the LICENSE file for details.
  4. import { BlockNumber, EventRecord, Extrinsic } from '@polkadot/types/interfaces';
  5. import React from 'react';
  6. import styled from 'styled-components';
  7. import { registry } from '@polkadot/react-api';
  8. import { AddressMini, Call, Expander, LinkExternal } from '@polkadot/react-components';
  9. import { formatNumber } from '@polkadot/util';
  10. import { useTranslation } from '../translate';
  11. import Event from '../Event';
  12. interface Props {
  13. blockNumber?: BlockNumber;
  14. className?: string;
  15. events?: EventRecord[];
  16. index: number;
  17. value: Extrinsic;
  18. }
  19. function getEra ({ era }: Extrinsic, blockNumber?: BlockNumber): [number, number] | null {
  20. if (blockNumber && era.isMortalEra) {
  21. const mortalEra = era.asMortalEra;
  22. return [mortalEra.birth(blockNumber.toNumber()), mortalEra.death(blockNumber.toNumber())];
  23. }
  24. return null;
  25. }
  26. function filterEvents (index: number, events: EventRecord[] = []): EventRecord[] {
  27. return events.filter(({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index));
  28. }
  29. function ExtrinsicDisplay ({ blockNumber, className, events, index, value }: Props): React.ReactElement<Props> {
  30. const { t } = useTranslation();
  31. const { meta, method, section } = registry.findMetaCall(value.callIndex);
  32. const era = getEra(value, blockNumber);
  33. const thisEvents = filterEvents(index, events);
  34. return (
  35. <tr
  36. className={className}
  37. key={`extrinsic:${index}`}
  38. >
  39. <td
  40. className='top'
  41. colSpan={2}
  42. >
  43. <Expander
  44. summary={`${section}.${method}`}
  45. summaryMeta={meta}
  46. >
  47. <Call
  48. className='details'
  49. mortality={
  50. era
  51. ? t('mortal, valid from #{{startAt}} to #{{endsAt}}', {
  52. replace: {
  53. endsAt: formatNumber(era[1]),
  54. startAt: formatNumber(era[0])
  55. }
  56. })
  57. : t('immortal')
  58. }
  59. tip={value.tip?.toBn()}
  60. value={value}
  61. withHash
  62. />
  63. </Expander>
  64. </td>
  65. <td
  66. className='top'
  67. colSpan={2}
  68. >
  69. {thisEvents.map((event, index) =>
  70. <Event
  71. className='explorer--BlockByHash-event'
  72. key={`event:${index}`}
  73. value={event}
  74. />
  75. )}
  76. </td>
  77. <td className='top'>
  78. {value.isSigned
  79. ? (
  80. <>
  81. <AddressMini value={value.signer} />
  82. <div className='explorer--BlockByHash-nonce'>
  83. {t('index')} {formatNumber(value.nonce)}
  84. </div>
  85. <LinkExternal
  86. data={value.hash.toHex()}
  87. type='extrinsic'
  88. />
  89. </>
  90. )
  91. : <div className='explorer--BlockByHash-unsigned'>{t('not signed')}</div>
  92. }
  93. </td>
  94. </tr>
  95. );
  96. }
  97. export default React.memo(styled(ExtrinsicDisplay)`
  98. .explorer--BlockByHash-event+.explorer--BlockByHash-event {
  99. margin-top: 0.75rem;
  100. }
  101. .explorer--BlockByHash-nonce {
  102. font-size: 0.75rem;
  103. margin-left: 2.25rem;
  104. margin-top: -0.5rem;
  105. opacity: 0.6;
  106. text-align: left;
  107. }
  108. .explorer--BlockByHash-unsigned {
  109. opacity: 0.6;
  110. font-weight: 100;
  111. }
  112. `);