diff --git a/.gitignore b/.gitignore index b8e5e2b8..1725614d 100755 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ yarn-error.log .env generated/ result* -schema.graphql diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b4c64b4..ebc3c008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,23 @@ Changelog ========= +## 1.2.0 + +### Compatible with: + +- [`cardano-graphql`: `2.2.0`](https://github.com/input-output-hk/cardano-graphql/releases/tag/2.2.0) + +### Features +- [show deposits and reclaims when present in transaction](https://github.com/input-output-hk/cardano-explorer-app/pull/361) +- [add withdrawals to TransactionInfo](https://github.com/input-output-hk/cardano-explorer-app/pull/363) +- [resolve stake address searches based on withdrawals](https://github.com/input-output-hk/cardano-explorer-app/pull/363) +- [use stake pool hash over slot leader reference](https://github.com/input-output-hk/cardano-explorer-app/pull/364) +### Chores +- [use new combined cardano-graphql schema from client package, removing local build step](https://github.com/input-output-hk/cardano-explorer-app/pull/362) + ## 1.1.0 ### Features -- [Split slots & blocks into separate columns](https://github.com/input-output-hk/cardano-explorer-app/pull/338) +- [Split slots & blocks into separate columns](https://github.com/input-output-hk/cardano-explorer-app/pull/347) - [Add package.json version to footer](https://github.com/input-output-hk/cardano-explorer-app/pull/348) ### Chores - [Dynamically calc Byron epochs length](https://github.com/input-output-hk/cardano-explorer-app/pull/334) diff --git a/codegen.yml b/codegen.yml index ee1c1d5a..cbb645d7 100644 --- a/codegen.yml +++ b/codegen.yml @@ -1,5 +1,5 @@ overwrite: true -schema: schema.graphql +schema: node_modules/@cardano-graphql/client-ts/api/schema.graphql documents: "source/**/*.graphql" generates: generated/typings/graphql-schema.d.ts: diff --git a/package.json b/package.json index 118edd15..d77f5c69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cardano-explorer-app", - "version": "1.1.0", + "version": "1.2.0", "description": "Cardano Explorer App", "author": "Daedalus Team @ Input Output HK", "license": "Apache-2.0", @@ -13,8 +13,7 @@ "test": "jest", "test:e2e": "cypress run", "test:e2e:dev": "cypress open", - "generate:graphql-schema": "node source/utils/generateGraphQLSchema.js", - "generate:graphql-typings": "yarn generate:graphql-schema && graphql-codegen", + "generate:graphql-typings": "graphql-codegen", "export": "next export source --outdir build/static", "static:build": "NODE_ENV=production yarn build && yarn export", "static:serve": "serve ./build/static", @@ -72,7 +71,7 @@ "@babel/plugin-transform-react-display-name": "7.8.3", "@babel/plugin-transform-react-jsx-self": "7.9.0", "@babel/plugin-transform-react-jsx-source": "7.9.0", - "@cardano-graphql/client-ts": "2.1.0", + "@cardano-graphql/client-ts": "2.2.0", "@cypress/webpack-preprocessor": "4.1.1", "@graphql-codegen/cli": "1.2.0", "@graphql-codegen/typescript": "1.2.0", diff --git a/packages-cache/@cardano-graphql-client-ts-2.1.0.tgz b/packages-cache/@cardano-graphql-client-ts-2.1.0.tgz deleted file mode 100644 index 6a83cb66..00000000 Binary files a/packages-cache/@cardano-graphql-client-ts-2.1.0.tgz and /dev/null differ diff --git a/packages-cache/@cardano-graphql-client-ts-2.2.0.tgz b/packages-cache/@cardano-graphql-client-ts-2.2.0.tgz new file mode 100644 index 00000000..6ce23253 Binary files /dev/null and b/packages-cache/@cardano-graphql-client-ts-2.2.0.tgz differ diff --git a/source/features/address/api/transformers.ts b/source/features/address/api/transformers.ts index 538f3872..e1e6afbf 100644 --- a/source/features/address/api/transformers.ts +++ b/source/features/address/api/transformers.ts @@ -1,15 +1,36 @@ import { Currency } from 'cardano-js'; -import { SearchForAddressQuery } from '../../../../generated/typings/graphql-schema'; -import { IAddressSummary } from '../types'; +import { + SearchForPaymentAddressQuery, + SearchForStakeAddressQuery, +} from '../../../../generated/typings/graphql-schema'; +import { IPaymentAddressSummary, IStakeAddressSummary } from '../types'; -export const addressDetailTransformer = ( +export const paymentAddressDetailTransformer = ( address: string, - s: SearchForAddressQuery -): IAddressSummary => ({ - address, - finalBalance: Currency.Util.lovelacesToAda( - s.utxos_aggregate?.aggregate?.sum?.value || '0' - ), - transactionsCount: - s.transactions_aggregate?.aggregate?.count.toString() || '0', -}); + s: SearchForPaymentAddressQuery +): IPaymentAddressSummary => { + return { + address, + finalBalance: Currency.Util.lovelacesToAda( + s.utxos_aggregate?.aggregate?.sum?.value || '0' + ), + transactionsCount: + s.transactions_aggregate?.aggregate?.count.toString() || '0' + } +}; + +export const stakeAddressDetailTransformer = ( + address: string, + s: SearchForStakeAddressQuery +): IStakeAddressSummary => { + return { + address, + totalWithdrawals: + s.withdrawals_aggregate?.aggregate?.count.toString() || '0', + totalWithdrawn: Currency.Util.lovelacesToAda( + s.withdrawals_aggregate?.aggregate?.sum?.amount || '0' + ), + transactionsCount: + s.transactions_aggregate?.aggregate?.count.toString() || '0' + } +}; diff --git a/source/features/address/types.ts b/source/features/address/types.ts index 2cf26c3a..5789f357 100644 --- a/source/features/address/types.ts +++ b/source/features/address/types.ts @@ -1,5 +1,13 @@ export interface IAddressSummary { address: string; - finalBalance: string; transactionsCount: string; } + +export interface IPaymentAddressSummary extends IAddressSummary { + finalBalance: string; +} + +export interface IStakeAddressSummary extends IAddressSummary { + totalWithdrawals: string; + totalWithdrawn: string; +} diff --git a/source/features/address/ui/AddressSummary.tsx b/source/features/address/ui/AddressSummary.tsx index b8936e0b..6b7953a6 100644 --- a/source/features/address/ui/AddressSummary.tsx +++ b/source/features/address/ui/AddressSummary.tsx @@ -6,12 +6,30 @@ import styles from './AddressSummary.module.scss'; export interface IAddressSummaryProps { address: string; - finalBalance: string; title: string; transactionsCount: string; } -const AddressSummary = (props: IAddressSummaryProps) => { +export interface IPaymentAddressSummaryProps extends IAddressSummaryProps { + finalBalance: string; +} + +export interface IStakeAddressSummaryProps extends IAddressSummaryProps { + totalWithdrawn: string; + totalWithdrawals: string; +} + +type AddressSummaryProps = IPaymentAddressSummaryProps | IStakeAddressSummaryProps + +function isPaymentAddress (props: AddressSummaryProps): props is IPaymentAddressSummaryProps { + return (props as IPaymentAddressSummaryProps).finalBalance !== undefined; +} + +function isStakeAddress (props: AddressSummaryProps): props is IStakeAddressSummaryProps { + return (props as IStakeAddressSummaryProps).totalWithdrawn !== undefined; +} + +const AddressSummary = (props: AddressSummaryProps) => { const { translate } = useI18nFeature().store; return (
@@ -32,12 +50,30 @@ const AddressSummary = (props: IAddressSummaryProps) => {
{props.transactionsCount}
-
-
- {translate('address.summaryBalanceLabel')} + {isPaymentAddress(props) && ( +
+
+ {translate('address.summaryBalanceLabel')} +
+
{props.finalBalance} ADA
-
{props.finalBalance} ADA
-
+ )} + {isStakeAddress(props) && ( + <> +
+
+ {translate('withdrawals')} +
+
{props.totalWithdrawals}
+
+
+
+ {translate('address.totalWithdrawn')} +
+
{props.totalWithdrawn} ADA
+
+ + )}
; + + public searchForStakeAddressQuery: GraphQLRequest< + SearchForStakeAddressQuery, + SearchForStakeAddressQueryVariables >; public searchByIdQuery: GraphQLRequest< @@ -37,9 +45,13 @@ export class SearchApi { >; constructor(client: GraphQLClient) { - this.searchForAddressQuery = new GraphQLRequest( + this.searchForPaymentAddressQuery = new GraphQLRequest( + client, + searchForPaymentAddressQuery + ); + this.searchForStakeAddressQuery = new GraphQLRequest( client, - searchForAddressQuery + searchForStakeAddressQuery ); this.searchByIdQuery = new GraphQLRequest(client, searchByIdQuery); this.searchForBlockByNumberQuery = new GraphQLRequest( diff --git a/source/features/search/api/searchForAddress.graphql b/source/features/search/api/searchForPaymentAddress.graphql similarity index 95% rename from source/features/search/api/searchForAddress.graphql rename to source/features/search/api/searchForPaymentAddress.graphql index bcb06fc8..0b03e242 100644 --- a/source/features/search/api/searchForAddress.graphql +++ b/source/features/search/api/searchForPaymentAddress.graphql @@ -1,4 +1,4 @@ -query searchForAddress( +query searchForPaymentAddress( $address: String! ) { utxos_aggregate ( diff --git a/source/features/search/api/searchForStakeAddress.graphql b/source/features/search/api/searchForStakeAddress.graphql new file mode 100644 index 00000000..e479bb4a --- /dev/null +++ b/source/features/search/api/searchForStakeAddress.graphql @@ -0,0 +1,38 @@ +#import "../../transactions/api/TransactionDetails.graphql" + +query searchForStakeAddress( + $address: String! +) { + transactions_aggregate ( + where: { + withdrawals: { + address: { + _eq: $address + } + } + } + ) { + aggregate { + count + } + } + withdrawals { + transaction { + ...TransactionDetails + } + } + withdrawals_aggregate ( + where: { + address: { + _eq: $address + } + } + ) { + aggregate { + count + sum { + amount + } + } + } +} diff --git a/source/features/search/index.ts b/source/features/search/index.ts index 410338f0..623e8027 100644 --- a/source/features/search/index.ts +++ b/source/features/search/index.ts @@ -10,16 +10,18 @@ import { SearchStore } from './store'; * Defines the actions that are supported by this feature */ export class SearchActions { + public addressNotFound: Action<{ address: string }> = new Action(); public addressSearchRequested: Action<{ address: string }> = new Action(); public blockNumberSearchRequested: Action<{ number: number }> = new Action(); public epochNumberSearchRequested: Action<{ number: number }> = new Action(); public idSearchRequested: Action<{ id: string }> = new Action(); - public unknownSearchRequested: Action<{ query: string }> = new Action(); public searchById: Action<{ id: string }> = new Action(); - public searchForAddress: Action<{ address: string }> = new Action(); + public searchForPaymentAddress: Action<{ address: string }> = new Action(); + public searchForStakeAddress: Action<{ address: string }> = new Action(); public searchForBlockByNumber: Action<{ number: number }> = new Action(); public searchForEpochByNumber: Action<{ number: number }> = new Action(); public subscribeToEpoch: Action<{ number: number }> = new Action(); + public unknownSearchRequested: Action<{ query: string }> = new Action(); public unsubscribeFromEpoch: Action = new Action(); } diff --git a/source/features/search/specs/helpers/exampleAddressData.ts b/source/features/search/specs/helpers/exampleAddressData.ts index 77521578..330afe6c 100644 --- a/source/features/search/specs/helpers/exampleAddressData.ts +++ b/source/features/search/specs/helpers/exampleAddressData.ts @@ -1,4 +1,4 @@ -export const exampleAddressData = { +export const examplePaymentAddressData = { address: 'Ae2tdPwUPEZBZTsRj7nGvvWQDTkqD9KLpCPpuZvjA1roL7KLDDVgkPU5S8j', finalBalance: '0', transactions: [ @@ -25,3 +25,9 @@ export const exampleAddressData = { ], transactionsCount: '2', }; + +export const exampleStakeAddressData = { + address: 'stake1u8gu2vzsk2nc6hsphudhl3p6aum7nd59ajqjg9zp00uvxcqwsdray', + totalWithdrawn: '1563.774139', + transactionsCount: '1', +}; diff --git a/source/features/search/specs/searchForAddress.spec.ts b/source/features/search/specs/searchForAddress.spec.ts deleted file mode 100644 index 940f2ac2..00000000 --- a/source/features/search/specs/searchForAddress.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import waitForExpect from 'wait-for-expect'; -import { ISearchFeature } from '../index'; -import { exampleAddressData } from './helpers/exampleAddressData'; -import { setupSearchFeature } from './helpers/setup'; - -describe('Searching for an address summary', () => { - let search: ISearchFeature; - beforeEach(() => { - search = setupSearchFeature(); - search.start(); - }); - afterEach(() => { - search.stop(); - }); - it('searches for a summary related to the provided address', async () => { - // 1. Trigger action - search.actions.searchForAddress.trigger({ - address: exampleAddressData.address, - }); - - // 2. Check the API query status - expect(search.api.searchForAddressQuery.isExecuting).toBe(true); - - // 3. Access the observable search result provided by the store - await waitForExpect(() => { - expect(search.store?.addressSearchResult?.finalBalance).toBe( - exampleAddressData.finalBalance - ); - expect(search.store?.addressSearchResult?.transactionsCount).toBe( - exampleAddressData.transactionsCount - ); - }); - }); -}); diff --git a/source/features/search/specs/searchForPaymentAddress.spec.ts b/source/features/search/specs/searchForPaymentAddress.spec.ts new file mode 100644 index 00000000..3529f2b1 --- /dev/null +++ b/source/features/search/specs/searchForPaymentAddress.spec.ts @@ -0,0 +1,34 @@ +import waitForExpect from 'wait-for-expect'; +import { ISearchFeature } from '../index'; +import { examplePaymentAddressData } from './helpers/exampleAddressData'; +import { setupSearchFeature } from './helpers/setup'; + +describe('Searching for a payment address summary', () => { + let search: ISearchFeature; + beforeEach(() => { + search = setupSearchFeature(); + search.start(); + }); + afterEach(() => { + search.stop(); + }); + it('searches for a summary related to the provided payment address', async () => { + // 1. Trigger action + search.actions.searchForPaymentAddress.trigger({ + address: examplePaymentAddressData.address, + }); + + // 2. Check the API query status + expect(search.api.searchForPaymentAddressQuery.isExecuting).toBe(true); + + // 3. Access the observable search result provided by the store + await waitForExpect(() => { + expect(search.store?.paymentAddressSearchResult?.finalBalance).toBe( + examplePaymentAddressData.finalBalance + ); + expect(search.store?.paymentAddressSearchResult?.transactionsCount).toBe( + examplePaymentAddressData.transactionsCount + ); + }); + }); +}); diff --git a/source/features/search/specs/searchForStakeAddress.spec.ts b/source/features/search/specs/searchForStakeAddress.spec.ts new file mode 100644 index 00000000..123de9f7 --- /dev/null +++ b/source/features/search/specs/searchForStakeAddress.spec.ts @@ -0,0 +1,34 @@ +import waitForExpect from 'wait-for-expect'; +import { ISearchFeature } from '../index'; +import { exampleStakeAddressData } from './helpers/exampleAddressData'; +import { setupSearchFeature } from './helpers/setup'; + +describe('Searching for a stake address summary', () => { + let search: ISearchFeature; + beforeEach(() => { + search = setupSearchFeature(); + search.start(); + }); + afterEach(() => { + search.stop(); + }); + it('searches for a summary related to the provided stake address', async () => { + // 1. Trigger action + search.actions.searchForStakeAddress.trigger({ + address: exampleStakeAddressData.address, + }); + + // 2. Check the API query status + expect(search.api.searchForStakeAddressQuery.isExecuting).toBe(true); + + // 3. Access the observable search result provided by the store + await waitForExpect(() => { + expect(search.store?.stakeAddressSearchResult?.totalWithdrawn).toBe( + exampleStakeAddressData.totalWithdrawn + ); + expect(search.store?.stakeAddressSearchResult?.transactionsCount).toBe( + exampleStakeAddressData.transactionsCount + ); + }); + }); +}); diff --git a/source/features/search/store.ts b/source/features/search/store.ts index 20774556..19e0c0a2 100644 --- a/source/features/search/store.ts +++ b/source/features/search/store.ts @@ -3,9 +3,12 @@ import { ActionProps, createActionBindings } from '../../lib/ActionBinding'; import Reaction, { createReactions } from '../../lib/mobx/Reaction'; import { Store } from '../../lib/Store'; import { isDefined } from '../../lib/types'; -import { addressDetailTransformer } from '../address/api/transformers'; +import { + paymentAddressDetailTransformer, + stakeAddressDetailTransformer +} from '../address/api/transformers'; import { ADDRESS_SEARCH_RESULT_PATH } from '../address/config'; -import { IAddressSummary } from '../address/types'; +import { IAddressSummary, IPaymentAddressSummary, IStakeAddressSummary } from '../address/types'; import { blockDetailsTransformer } from '../blocks/api/transformers'; import { BLOCK_SEARCH_RESULT_PATH } from '../blocks/config'; import { IBlockDetailed } from '../blocks/types'; @@ -31,9 +34,10 @@ export enum SearchType { } export class SearchStore extends Store { - @observable public addressSearchResult: IAddressSummary | null = null; @observable public blockSearchResult: IBlockDetailed | null = null; @observable public epochSearchResult: IEpochOverview | null = null; + @observable public paymentAddressSearchResult: IPaymentAddressSummary | null = null; + @observable public stakeAddressSearchResult: IStakeAddressSummary | null = null; @observable public transactionSearchResult: ITransactionDetails | null = null; private readonly searchApi: SearchApi; @@ -71,7 +75,8 @@ export class SearchStore extends Store { this.onSearchByEpochNumberRequested, ], [this.searchActions.searchById, this.searchById], - [this.searchActions.searchForAddress, this.searchForAddress], + [this.searchActions.searchForPaymentAddress, this.searchForPaymentAddress], + [this.searchActions.searchForStakeAddress, this.searchForStakeAddress], [ this.searchActions.searchForBlockByNumber, this.searchForBlockByNumber, @@ -98,11 +103,12 @@ export class SearchStore extends Store { @computed get isSearching() { return ( - this.searchApi.searchForAddressQuery.isExecuting || - this.searchApi.searchForBlockByNumberQuery.isExecuting || this.searchApi.searchByIdQuery.isExecuting || this.searchApi.searchForBlockByNumberQuery.isExecuting || - this.searchApi.searchForEpochByNumberQuery.isExecuting + this.searchApi.searchForBlockByNumberQuery.isExecuting || + this.searchApi.searchForEpochByNumberQuery.isExecuting || + this.searchApi.searchForPaymentAddressQuery.isExecuting || + this.searchApi.searchForStakeAddressQuery.isExecuting ); } @@ -189,20 +195,21 @@ export class SearchStore extends Store { }); }; - @action private searchForAddress = async ({ - address, + @action private searchForPaymentAddress = async ({ + address }: { address: string; }) => { // Do not execute queries more than necessary! if ( - this.searchApi.searchForAddressQuery.isExecuting || - this.addressSearchResult?.address === address + this.searchApi.searchForPaymentAddressQuery.isExecuting || + this.paymentAddressSearchResult?.address === address ) { return; } - this.addressSearchResult = null; - const result = await this.searchApi.searchForAddressQuery.execute({ + this.paymentAddressSearchResult = null; + this.stakeAddressSearchResult = null; + const result = await this.searchApi.searchForPaymentAddressQuery.execute({ address, }); if (isDefined(result)) { @@ -215,7 +222,36 @@ export class SearchStore extends Store { }); } runInAction(() => { - this.addressSearchResult = addressDetailTransformer(address, result); + this.paymentAddressSearchResult = paymentAddressDetailTransformer(address, result); + }); + } + }; + + @action private searchForStakeAddress = async ({ + address, + }: { + address: string; + }) => { + // Do not execute queries more than necessary! + if ( + this.searchApi.searchForStakeAddressQuery.isExecuting || + this.stakeAddressSearchResult?.address === address + ) { + return; + } + this.stakeAddressSearchResult = null; + this.paymentAddressSearchResult = null; + const result = await this.searchApi.searchForStakeAddressQuery.execute({ + address, + }); + if (isDefined(result)) { + if (result.withdrawals_aggregate?.aggregate?.count === '0') { + return this.searchActions.unknownSearchRequested.trigger({ + query: address, + }); + } + runInAction(() => { + this.stakeAddressSearchResult = stakeAddressDetailTransformer(address, result); }); } }; diff --git a/source/features/search/ui/AddressSearchResult.tsx b/source/features/search/ui/AddressSearchResult.tsx index 963ff88f..64fab89c 100644 --- a/source/features/search/ui/AddressSearchResult.tsx +++ b/source/features/search/ui/AddressSearchResult.tsx @@ -25,36 +25,49 @@ export const AddressSearchResult = () => { const { query } = navigation.store; const { address } = query; if (isString(address)) { - actions.searchForAddress.trigger({ address }); + if (address.substring(0, 5) === 'stake') { + actions.searchForStakeAddress.trigger({ address }); + } else { + actions.searchForPaymentAddress.trigger({ address }); + } } }); return ( {() => { - const { addressSearchResult } = store; + const { paymentAddressSearchResult, stakeAddressSearchResult } = store; + const address = paymentAddressSearchResult?.address || stakeAddressSearchResult?.address || null; + const transactionsCount = paymentAddressSearchResult?.transactionsCount || stakeAddressSearchResult?.transactionsCount; if ( - !api.searchForAddressQuery.hasBeenExecutedAtLeastOnce || + ( paymentAddressSearchResult && !api.searchForPaymentAddressQuery.hasBeenExecutedAtLeastOnce ) || + ( stakeAddressSearchResult && !api.searchForStakeAddressQuery.hasBeenExecutedAtLeastOnce ) || store.isSearching ) { return ; - } else if (addressSearchResult) { - const { - address, - finalBalance, - transactionsCount, - } = addressSearchResult; + } else if (address !== null && transactionsCount !== null) { return ( <>
- + { paymentAddressSearchResult && ( + + )} + { stakeAddressSearchResult && ( + + )}
- { }} perPage={navigation.store.query.perPage as string} currentPage={(navigation.store.query.page as string) ?? 1} - total={parseInt(transactionsCount, 10)} + total={parseInt(paymentAddressSearchResult?.transactionsCount || stakeAddressSearchResult?.transactionsCount || '0', 10)} transactions={transactions.store.browsedAddressTransactions} - /> + />}
); diff --git a/source/features/transactions/api/TransactionDetails.graphql b/source/features/transactions/api/TransactionDetails.graphql index 2be03a9d..218fae75 100644 --- a/source/features/transactions/api/TransactionDetails.graphql +++ b/source/features/transactions/api/TransactionDetails.graphql @@ -5,6 +5,7 @@ fragment TransactionDetails on Transaction { number, slotNo, } + deposit, fee, hash, includedAt, @@ -20,4 +21,8 @@ fragment TransactionDetails on Transaction { value }, totalOutput + withdrawals { + address + amount + } } diff --git a/source/features/transactions/api/getAddressTransactions.graphql b/source/features/transactions/api/getAddressTransactions.graphql index 387bff5e..5aaf7cb5 100644 --- a/source/features/transactions/api/getAddressTransactions.graphql +++ b/source/features/transactions/api/getAddressTransactions.graphql @@ -8,6 +8,12 @@ query getAddressTransactions( transactions( where: { _or: [{ + withdrawals: { + address: { + _eq: $address + } + } + }, { inputs: { address: { _eq: $address diff --git a/source/features/transactions/api/transformers.ts b/source/features/transactions/api/transformers.ts index 68c7c07d..5628976c 100644 --- a/source/features/transactions/api/transformers.ts +++ b/source/features/transactions/api/transformers.ts @@ -11,6 +11,7 @@ export const transactionDetailsTransformer = ( id: tx.block?.hash, number: tx.block?.number, }, + deposit: Currency.Util.lovelacesToAda(tx.deposit), fee: Currency.Util.lovelacesToAda(tx.fee), id: tx.hash, includedAt: new Date(tx.includedAt), @@ -24,4 +25,8 @@ export const transactionDetailsTransformer = ( value: Currency.Util.lovelacesToAda(i.value), })), totalOutput: Currency.Util.lovelacesToAda(tx.totalOutput), + withdrawals: tx.withdrawals?.filter(isDefined).map((i) => ({ + address: i.address, + value: Currency.Util.lovelacesToAda(i.amount) + })) || [] }); diff --git a/source/features/transactions/components/TransactionInfo.tsx b/source/features/transactions/components/TransactionInfo.tsx index 7f5fe465..55f2d898 100644 --- a/source/features/transactions/components/TransactionInfo.tsx +++ b/source/features/transactions/components/TransactionInfo.tsx @@ -18,6 +18,7 @@ import { ITransactionDetails, ITransactionInput, ITransactionOutput, + IWithdrawal, } from '../types'; import styles from './TransactionInfo.module.scss'; @@ -48,7 +49,7 @@ const TransactionAddressMobile = (props: { address: string }) => ); -type AddressInputOutput = ITransactionInput | ITransactionOutput; +type AddressInputOutput = ITransactionInput | IWithdrawal | ITransactionOutput; interface IAddressesRowProps { addresses?: Array; @@ -119,7 +120,7 @@ const TransactionInfo = (props: ITransactionInfoProps) => { }); }; const epoch = props.block.epoch === '-' ? 0 : props.block.epoch; - + const depositLabel = parseInt(props.deposit) >= 0 ? 'transaction.deposit' : 'transaction.depositReclaim' return (
{props.title && ( @@ -203,7 +204,7 @@ const TransactionInfo = (props: ITransactionInfoProps) => {
@@ -227,6 +228,16 @@ const TransactionInfo = (props: ITransactionInfoProps) => {
+ {/* ===== DEPOSIT ===== */} + {props.deposit !== '0' && ( +
+
+ {translate(depositLabel)} +
+
{Math.abs(parseInt(props.deposit))} ADA
+
+ )} + {/* ===== TOTAL OUTPUT ===== */}
diff --git a/source/features/transactions/types.ts b/source/features/transactions/types.ts index 63148484..2a29265c 100644 --- a/source/features/transactions/types.ts +++ b/source/features/transactions/types.ts @@ -13,6 +13,10 @@ export interface ITransactionOutput extends ICoin { index: number; } +export interface IWithdrawal extends ICoin { + address: string; +} + export interface ITransactionDetails { block: { epoch?: number | '-'; @@ -20,10 +24,12 @@ export interface ITransactionDetails { number?: number | null; slot?: number | null; }; + deposit: string; fee: string; id: string; includedAt: Date; inputs: ITransactionInput[]; outputs?: ITransactionOutput[]; totalOutput: string; + withdrawals: IWithdrawal[]; } diff --git a/source/utils/generateGraphQLSchema.js b/source/utils/generateGraphQLSchema.js deleted file mode 100644 index 94f1a144..00000000 --- a/source/utils/generateGraphQLSchema.js +++ /dev/null @@ -1,17 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const { loadFilesSync } = require('@graphql-tools/load-files'); -const { mergeTypeDefs } = require('@graphql-tools/merge'); -const { printSchemaWithDirectives } = require('@graphql-tools/utils'); -const { makeExecutableSchema } = require('@graphql-tools/schema'); - -const cardanoDbHasuraSchema = loadFilesSync('./node_modules/@cardano-graphql/client-ts/api/cardano-db-hasura/schema.graphql'); -const genesisSchema = loadFilesSync('./node_modules/@cardano-graphql/client-ts/api/genesis/schema.graphql'); -const mergedTypes = mergeTypeDefs([cardanoDbHasuraSchema, genesisSchema], { - throwOnConflict: true -}); - -const executableSchema = makeExecutableSchema({ typeDefs: [mergedTypes] }); -const schema = printSchemaWithDirectives(executableSchema); - -fs.writeFileSync(path.join(__dirname, '../../schema.graphql'), schema); diff --git a/yarn.lock b/yarn.lock index b19022f1..9d6fa36c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1443,10 +1443,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cardano-graphql/client-ts@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@cardano-graphql/client-ts/-/client-ts-2.1.0.tgz#f93ddbe4cb88c7b4f5048bc0692e575bd81d9993" - integrity sha512-OnvlWkMxSoH2NFfSLPTNRGnzqg4efZmXc34wBUefTfPvmGaNO1NxYVP1wW2nS5/lfkNxwSyFPKX3yNu+MNKh2Q== +"@cardano-graphql/client-ts@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@cardano-graphql/client-ts/-/client-ts-2.2.0.tgz#a8f725ab642a9605f9c4aee3b78d2a637f568675" + integrity sha512-1ob9UboUijZxXllSlaYtsoOWM4EXcmPlKyhm1o5+kY2qkD7lRzOs6TBpJegrU4xQX+TGhLALawP+l6lNiHIsTg== "@cnakazawa/watch@^1.0.3": version "1.0.4"