diff --git a/apollo/subgraph.ts b/apollo/subgraph.ts index 2a53b8fc..40973d73 100644 --- a/apollo/subgraph.ts +++ b/apollo/subgraph.ts @@ -298,10 +298,14 @@ export type Broadcaster = { deposit: Scalars['BigDecimal']; /** The date this broadcaster first funded a deposit or reserve, beginning at 12:00am UTC */ firstActiveDay: Scalars['Int']; + /** The date this broadcaster first funded a deposit or reserve, beginning at 12:00am UTC */ + firstFundedDay: Scalars['Int']; /** ETH address of a broadcaster */ id: Scalars['ID']; /** The date this broadcaster last paid fees, beginning at 12:00am UTC */ lastActiveDay: Scalars['Int']; + /** The date this broadcaster last paid fees, beginning at 12:00am UTC */ + lastFundedDay: Scalars['Int']; /** Fees paid out by this broadcaster in ETH during the last 90 days */ ninetyDayVolumeETH: Scalars['BigDecimal']; /** Amount of funds in reserve */ @@ -405,8 +409,10 @@ export enum BroadcasterDay_OrderBy { Broadcaster = 'broadcaster', BroadcasterDeposit = 'broadcaster__deposit', BroadcasterFirstActiveDay = 'broadcaster__firstActiveDay', + BroadcasterFirstFundedDay = 'broadcaster__firstFundedDay', BroadcasterId = 'broadcaster__id', BroadcasterLastActiveDay = 'broadcaster__lastActiveDay', + BroadcasterLastFundedDay = 'broadcaster__lastFundedDay', BroadcasterNinetyDayVolumeEth = 'broadcaster__ninetyDayVolumeETH', BroadcasterReserve = 'broadcaster__reserve', BroadcasterSixtyDayVolumeEth = 'broadcaster__sixtyDayVolumeETH', @@ -446,6 +452,14 @@ export type Broadcaster_Filter = { firstActiveDay_lte?: InputMaybe; firstActiveDay_not?: InputMaybe; firstActiveDay_not_in?: InputMaybe>; + firstFundedDay?: InputMaybe; + firstFundedDay_gt?: InputMaybe; + firstFundedDay_gte?: InputMaybe; + firstFundedDay_in?: InputMaybe>; + firstFundedDay_lt?: InputMaybe; + firstFundedDay_lte?: InputMaybe; + firstFundedDay_not?: InputMaybe; + firstFundedDay_not_in?: InputMaybe>; id?: InputMaybe; id_gt?: InputMaybe; id_gte?: InputMaybe; @@ -462,6 +476,14 @@ export type Broadcaster_Filter = { lastActiveDay_lte?: InputMaybe; lastActiveDay_not?: InputMaybe; lastActiveDay_not_in?: InputMaybe>; + lastFundedDay?: InputMaybe; + lastFundedDay_gt?: InputMaybe; + lastFundedDay_gte?: InputMaybe; + lastFundedDay_in?: InputMaybe>; + lastFundedDay_lt?: InputMaybe; + lastFundedDay_lte?: InputMaybe; + lastFundedDay_not?: InputMaybe; + lastFundedDay_not_in?: InputMaybe>; ninetyDayVolumeETH?: InputMaybe; ninetyDayVolumeETH_gt?: InputMaybe; ninetyDayVolumeETH_gte?: InputMaybe; @@ -517,8 +539,10 @@ export enum Broadcaster_OrderBy { BroadcasterDays = 'broadcasterDays', Deposit = 'deposit', FirstActiveDay = 'firstActiveDay', + FirstFundedDay = 'firstFundedDay', Id = 'id', LastActiveDay = 'lastActiveDay', + LastFundedDay = 'lastFundedDay', NinetyDayVolumeEth = 'ninetyDayVolumeETH', Reserve = 'reserve', SixtyDayVolumeEth = 'sixtyDayVolumeETH', @@ -1121,8 +1145,10 @@ export enum DepositFundedEvent_OrderBy { Sender = 'sender', SenderDeposit = 'sender__deposit', SenderFirstActiveDay = 'sender__firstActiveDay', + SenderFirstFundedDay = 'sender__firstFundedDay', SenderId = 'sender__id', SenderLastActiveDay = 'sender__lastActiveDay', + SenderLastFundedDay = 'sender__lastFundedDay', SenderNinetyDayVolumeEth = 'sender__ninetyDayVolumeETH', SenderReserve = 'sender__reserve', SenderSixtyDayVolumeEth = 'sender__sixtyDayVolumeETH', @@ -2881,7 +2907,7 @@ export enum Pool_OrderBy { /** Livepeer protocol global parameters */ export type Protocol = { __typename: 'Protocol'; - /** Broadcasters active within the current 90 day fee window */ + /** Broadcasters that paid tickets within the current 90 day fee window */ activeBroadcasters: Array; /** Total active transcoders (up to the limit) */ activeTranscoderCount: Scalars['BigInt']; @@ -4668,8 +4694,10 @@ export enum ReserveClaimedEvent_OrderBy { ReserveHolder = 'reserveHolder', ReserveHolderDeposit = 'reserveHolder__deposit', ReserveHolderFirstActiveDay = 'reserveHolder__firstActiveDay', + ReserveHolderFirstFundedDay = 'reserveHolder__firstFundedDay', ReserveHolderId = 'reserveHolder__id', ReserveHolderLastActiveDay = 'reserveHolder__lastActiveDay', + ReserveHolderLastFundedDay = 'reserveHolder__lastFundedDay', ReserveHolderNinetyDayVolumeEth = 'reserveHolder__ninetyDayVolumeETH', ReserveHolderReserve = 'reserveHolder__reserve', ReserveHolderSixtyDayVolumeEth = 'reserveHolder__sixtyDayVolumeETH', @@ -4823,8 +4851,10 @@ export enum ReserveFundedEvent_OrderBy { ReserveHolder = 'reserveHolder', ReserveHolderDeposit = 'reserveHolder__deposit', ReserveHolderFirstActiveDay = 'reserveHolder__firstActiveDay', + ReserveHolderFirstFundedDay = 'reserveHolder__firstFundedDay', ReserveHolderId = 'reserveHolder__id', ReserveHolderLastActiveDay = 'reserveHolder__lastActiveDay', + ReserveHolderLastFundedDay = 'reserveHolder__lastFundedDay', ReserveHolderNinetyDayVolumeEth = 'reserveHolder__ninetyDayVolumeETH', ReserveHolderReserve = 'reserveHolder__reserve', ReserveHolderSixtyDayVolumeEth = 'reserveHolder__sixtyDayVolumeETH', @@ -9003,8 +9033,10 @@ export enum WinningTicketRedeemedEvent_OrderBy { SenderNonce = 'senderNonce', SenderDeposit = 'sender__deposit', SenderFirstActiveDay = 'sender__firstActiveDay', + SenderFirstFundedDay = 'sender__firstFundedDay', SenderId = 'sender__id', SenderLastActiveDay = 'sender__lastActiveDay', + SenderLastFundedDay = 'sender__lastFundedDay', SenderNinetyDayVolumeEth = 'sender__ninetyDayVolumeETH', SenderReserve = 'sender__reserve', SenderSixtyDayVolumeEth = 'sender__sixtyDayVolumeETH', @@ -9510,8 +9542,10 @@ export enum WithdrawalEvent_OrderBy { Sender = 'sender', SenderDeposit = 'sender__deposit', SenderFirstActiveDay = 'sender__firstActiveDay', + SenderFirstFundedDay = 'sender__firstFundedDay', SenderId = 'sender__id', SenderLastActiveDay = 'sender__lastActiveDay', + SenderLastFundedDay = 'sender__lastFundedDay', SenderNinetyDayVolumeEth = 'sender__ninetyDayVolumeETH', SenderReserve = 'sender__reserve', SenderSixtyDayVolumeEth = 'sender__sixtyDayVolumeETH', @@ -9569,7 +9603,7 @@ export type AccountQueryVariables = Exact<{ }>; -export type AccountQuery = { __typename: 'Query', delegator?: { __typename: 'Delegator', id: string, bondedAmount: string, principal: string, unbonded: string, withdrawnFees: string, startRound: string, lastClaimRound?: { __typename: 'Round', id: string } | null, unbondingLocks?: Array<{ __typename: 'UnbondingLock', id: string, amount: string, unbondingLockId: number, withdrawRound: string, delegate: { __typename: 'Transcoder', id: string } }> | null, delegate?: { __typename: 'Transcoder', id: string, active: boolean, status: TranscoderStatus, totalStake: string } | null } | null, transcoder?: { __typename: 'Transcoder', id: string, active: boolean, feeShare: string, rewardCut: string, status: TranscoderStatus, totalStake: string, totalVolumeETH: string, activationTimestamp: number, activationRound: string, deactivationRound: string, thirtyDayVolumeETH: string, ninetyDayVolumeETH: string, lastRewardRound?: { __typename: 'Round', id: string } | null, pools?: Array<{ __typename: 'Pool', rewardTokens?: string | null }> | null, delegators?: Array<{ __typename: 'Delegator', id: string }> | null } | null, protocol?: { __typename: 'Protocol', id: string, totalSupply: string, totalActiveStake: string, participationRate: string, inflation: string, inflationChange: string, lptPriceEth: string, roundLength: string, currentRound: { __typename: 'Round', id: string } } | null }; +export type AccountQuery = { __typename: 'Query', delegator?: { __typename: 'Delegator', id: string, bondedAmount: string, principal: string, unbonded: string, withdrawnFees: string, startRound: string, lastClaimRound?: { __typename: 'Round', id: string } | null, unbondingLocks?: Array<{ __typename: 'UnbondingLock', id: string, amount: string, unbondingLockId: number, withdrawRound: string, delegate: { __typename: 'Transcoder', id: string } }> | null, delegate?: { __typename: 'Transcoder', id: string, active: boolean, status: TranscoderStatus, totalStake: string } | null } | null, transcoder?: { __typename: 'Transcoder', id: string, active: boolean, feeShare: string, rewardCut: string, status: TranscoderStatus, totalStake: string, totalVolumeETH: string, activationTimestamp: number, activationRound: string, deactivationRound: string, thirtyDayVolumeETH: string, ninetyDayVolumeETH: string, lastRewardRound?: { __typename: 'Round', id: string } | null, pools?: Array<{ __typename: 'Pool', rewardTokens?: string | null }> | null, delegators?: Array<{ __typename: 'Delegator', id: string }> | null } | null, gateway?: { __typename: 'Broadcaster', id: string, deposit: string, reserve: string, totalVolumeETH: string, ninetyDayVolumeETH: string, firstActiveDay: number, lastActiveDay: number } | null, protocol?: { __typename: 'Protocol', id: string, totalSupply: string, totalActiveStake: string, participationRate: string, inflation: string, inflationChange: string, lptPriceEth: string, roundLength: string, currentRound: { __typename: 'Round', id: string } } | null }; export type AccountInactiveQueryVariables = Exact<{ id: Scalars['ID']; @@ -9599,6 +9633,22 @@ export type EventsQueryVariables = Exact<{ export type EventsQuery = { __typename: 'Query', transactions: Array<{ __typename: 'Transaction', events?: Array<{ __typename: 'BondEvent', additionalAmount: string, delegator: { __typename: 'Delegator', id: string }, newDelegate: { __typename: 'Transcoder', id: string }, oldDelegate?: { __typename: 'Transcoder', id: string } | null, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'BurnEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'DepositFundedEvent', amount: string, sender: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'EarningsClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'MigrateDelegatorFinalizedEvent', l1Addr: string, l2Addr: string, stake: string, delegatedStake: string, fees: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'MintEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'NewRoundEvent', transaction: { __typename: 'Transaction', from: string, id: string, timestamp: number }, round: { __typename: 'Round', id: string } } | { __typename: 'ParameterUpdateEvent', param: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'PauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'PollCreatedEvent', endBlock: string, poll: { __typename: 'Poll', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'RebondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, delegator: { __typename: 'Delegator', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'ReserveClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'ReserveFundedEvent', amount: string, reserveHolder: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'RewardEvent', rewardTokens: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'ServiceURIUpdateEvent', addr: string, serviceURI: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'SetCurrentRewardTokensEvent', currentInflation: string, currentMintableTokens: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'StakeClaimedEvent', stake: string, fees: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderActivatedEvent', activationRound: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderDeactivatedEvent', deactivationRound: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderEvictedEvent', delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderResignedEvent', delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderSlashedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TranscoderUpdateEvent', rewardCut: string, feeShare: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TransferBondEvent', amount: string, newDelegator: { __typename: 'Delegator', id: string }, oldDelegator: { __typename: 'Delegator', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'TreasuryVoteEvent', support: TreasuryVoteSupport, proposal: { __typename: 'TreasuryProposal', id: string, description: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'UnbondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, delegator: { __typename: 'Delegator', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'UnpauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'VoteEvent', voter: string, choiceID: string, poll: { __typename: 'Poll', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'WinningTicketRedeemedEvent', faceValue: string, recipient: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'WithdrawFeesEvent', amount: string, delegator: { __typename: 'Delegator', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'WithdrawStakeEvent', amount: string, delegator: { __typename: 'Delegator', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } } | { __typename: 'WithdrawalEvent', deposit: string, reserve: string, sender: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number, from: string } }> | null }>, transcoders: Array<{ __typename: 'Transcoder', id: string }> }; +export type GatewaySelfRedeemQueryVariables = Exact<{ + account: Scalars['String']; +}>; + + +export type GatewaySelfRedeemQuery = { __typename: 'Query', winningTicketRedeemedEvents: Array<{ __typename: 'WinningTicketRedeemedEvent', transaction: { __typename: 'Transaction', timestamp: number } }> }; + +export type GatewaysQueryVariables = Exact<{ + first: Scalars['Int']; + skip: Scalars['Int']; + minActiveDay: Scalars['Int']; +}>; + + +export type GatewaysQuery = { __typename: 'Query', gateways: Array<{ __typename: 'Broadcaster', id: string, deposit: string, reserve: string, totalVolumeETH: string, ninetyDayVolumeETH: string, firstActiveDay: number, lastActiveDay: number }> }; + export type MetaQueryVariables = Exact<{ [key: string]: never; }>; @@ -9653,7 +9703,7 @@ export type TransactionsQueryVariables = Exact<{ }>; -export type TransactionsQuery = { __typename: 'Query', transactions: Array<{ __typename: 'Transaction', events?: Array<{ __typename: 'BondEvent', additionalAmount: string, delegator: { __typename: 'Delegator', id: string }, newDelegate: { __typename: 'Transcoder', id: string }, oldDelegate?: { __typename: 'Transcoder', id: string } | null, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'BurnEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'DepositFundedEvent', amount: string, sender: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'EarningsClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'MigrateDelegatorFinalizedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'MintEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'NewRoundEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ParameterUpdateEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'PauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'PollCreatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'RebondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ReserveClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ReserveFundedEvent', amount: string, reserveHolder: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'RewardEvent', rewardTokens: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ServiceURIUpdateEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'SetCurrentRewardTokensEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'StakeClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderActivatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderDeactivatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderEvictedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderResignedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderSlashedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderUpdateEvent', rewardCut: string, feeShare: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TransferBondEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TreasuryVoteEvent', id: string, reason?: string | null, support: TreasuryVoteSupport, timestamp: number, weight: string, proposal: { __typename: 'TreasuryProposal', id: string, targets: Array, description: string }, treasuryVoter: { __typename: 'LivepeerAccount', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'UnbondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'UnpauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'VoteEvent', voter: string, choiceID: string, id: string, timestamp: number, poll: { __typename: 'Poll', id: string, proposal: string, endBlock: string, quorum: string, quota: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number }, round: { __typename: 'Round', id: string } } | { __typename: 'WinningTicketRedeemedEvent', faceValue: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawFeesEvent', amount: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawStakeEvent', amount: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawalEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } }> | null }>, winningTicketRedeemedEvents: Array<{ __typename: 'WinningTicketRedeemedEvent', id: string, faceValue: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } }> }; +export type TransactionsQuery = { __typename: 'Query', transactions: Array<{ __typename: 'Transaction', events?: Array<{ __typename: 'BondEvent', additionalAmount: string, delegator: { __typename: 'Delegator', id: string }, newDelegate: { __typename: 'Transcoder', id: string }, oldDelegate?: { __typename: 'Transcoder', id: string } | null, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'BurnEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'DepositFundedEvent', amount: string, sender: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'EarningsClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'MigrateDelegatorFinalizedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'MintEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'NewRoundEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ParameterUpdateEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'PauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'PollCreatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'RebondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ReserveClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ReserveFundedEvent', amount: string, reserveHolder: { __typename: 'Broadcaster', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'RewardEvent', rewardTokens: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'ServiceURIUpdateEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'SetCurrentRewardTokensEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'StakeClaimedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderActivatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderDeactivatedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderEvictedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderResignedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderSlashedEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TranscoderUpdateEvent', rewardCut: string, feeShare: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TransferBondEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'TreasuryVoteEvent', id: string, reason?: string | null, support: TreasuryVoteSupport, timestamp: number, weight: string, proposal: { __typename: 'TreasuryProposal', id: string, targets: Array, description: string }, treasuryVoter: { __typename: 'LivepeerAccount', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'UnbondEvent', amount: string, delegate: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'UnpauseEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'VoteEvent', voter: string, choiceID: string, id: string, timestamp: number, poll: { __typename: 'Poll', id: string, proposal: string, endBlock: string, quorum: string, quota: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number }, round: { __typename: 'Round', id: string } } | { __typename: 'WinningTicketRedeemedEvent', faceValue: string, sender: { __typename: 'Broadcaster', id: string }, recipient: { __typename: 'Transcoder', id: string }, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawFeesEvent', amount: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawStakeEvent', amount: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } } | { __typename: 'WithdrawalEvent', round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number } }> | null }>, winningTicketRedeemedEvents: Array<{ __typename: 'WinningTicketRedeemedEvent', id: string, faceValue: string, round: { __typename: 'Round', id: string }, transaction: { __typename: 'Transaction', id: string, timestamp: number }, sender: { __typename: 'Broadcaster', id: string }, recipient: { __typename: 'Transcoder', id: string } }> }; export type TranscoderActivatedEventsQueryVariables = Exact<{ where?: InputMaybe; @@ -9756,6 +9806,15 @@ export const AccountDocument = gql` id } } + gateway: broadcaster(id: $account) { + id + deposit + reserve + totalVolumeETH + ninetyDayVolumeETH + firstActiveDay + lastActiveDay + } protocol(id: "0") { id totalSupply @@ -10137,6 +10196,97 @@ export function useEventsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions; export type EventsLazyQueryHookResult = ReturnType; export type EventsQueryResult = Apollo.QueryResult; +export const GatewaySelfRedeemDocument = gql` + query gatewaySelfRedeem($account: String!) { + winningTicketRedeemedEvents( + first: 1 + orderBy: timestamp + orderDirection: desc + where: {sender: $account, recipient: $account} + ) { + transaction { + timestamp + } + } +} + `; + +/** + * __useGatewaySelfRedeemQuery__ + * + * To run a query within a React component, call `useGatewaySelfRedeemQuery` and pass it any options that fit your needs. + * When your component renders, `useGatewaySelfRedeemQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGatewaySelfRedeemQuery({ + * variables: { + * account: // value for 'account' + * }, + * }); + */ +export function useGatewaySelfRedeemQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GatewaySelfRedeemDocument, options); + } +export function useGatewaySelfRedeemLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GatewaySelfRedeemDocument, options); + } +export type GatewaySelfRedeemQueryHookResult = ReturnType; +export type GatewaySelfRedeemLazyQueryHookResult = ReturnType; +export type GatewaySelfRedeemQueryResult = Apollo.QueryResult; +export const GatewaysDocument = gql` + query gateways($first: Int!, $skip: Int!, $minActiveDay: Int!) { + gateways: broadcasters( + first: $first + skip: $skip + orderBy: ninetyDayVolumeETH + orderDirection: desc + where: {or: [{ninetyDayVolumeETH_gt: "0"}, {firstActiveDay_gte: $minActiveDay}]} + ) { + id + deposit + reserve + totalVolumeETH + ninetyDayVolumeETH + firstActiveDay + lastActiveDay + } +} + `; + +/** + * __useGatewaysQuery__ + * + * To run a query within a React component, call `useGatewaysQuery` and pass it any options that fit your needs. + * When your component renders, `useGatewaysQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGatewaysQuery({ + * variables: { + * first: // value for 'first' + * skip: // value for 'skip' + * minActiveDay: // value for 'minActiveDay' + * }, + * }); + */ +export function useGatewaysQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GatewaysDocument, options); + } +export function useGatewaysLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GatewaysDocument, options); + } +export type GatewaysQueryHookResult = ReturnType; +export type GatewaysLazyQueryHookResult = ReturnType; +export type GatewaysQueryResult = Apollo.QueryResult; export const MetaDocument = gql` query meta { _meta { @@ -10530,6 +10680,12 @@ export const TransactionsDocument = gql` } ... on WinningTicketRedeemedEvent { faceValue + sender { + id + } + recipient { + id + } } ... on DepositFundedEvent { sender { @@ -10590,7 +10746,7 @@ export const TransactionsDocument = gql` winningTicketRedeemedEvents( orderBy: timestamp orderDirection: desc - where: {recipient: $account} + where: {or: [{recipient: $account}, {sender: $account}]} first: $first skip: $skip ) { @@ -10604,6 +10760,12 @@ export const TransactionsDocument = gql` timestamp } faceValue + sender { + id + } + recipient { + id + } } } `; diff --git a/components/BroadcastingView/index.tsx b/components/BroadcastingView/index.tsx new file mode 100644 index 00000000..f2387f35 --- /dev/null +++ b/components/BroadcastingView/index.tsx @@ -0,0 +1,120 @@ +import { ExplorerTooltip } from "@components/ExplorerTooltip"; +import Stat from "@components/Stat"; +import dayjs from "@lib/dayjs"; +import { formatEth } from "@lib/utils"; +import { Box, Grid } from "@livepeer/design-system"; +import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; +import { GatewaysQuery } from "apollo"; +import { useMemo } from "react"; + +const SelfRedeemIndicator = () => ( + Self-redeemed winning tickets detected in the last 90 days. + } + > + + + + +); + +const BroadcastingView = ({ + gateway, + isSelfRedeeming = false, +}: { + gateway?: GatewaysQuery["gateways"] extends Array ? G | null : null; + isSelfRedeeming?: boolean; +}) => { + const activeSince = useMemo( + () => + gateway?.firstActiveDay + ? dayjs.unix(gateway.firstActiveDay).format("MMM D, YYYY") + : "N/A", + [gateway] + ); + const statusText = useMemo(() => { + const active = Number(gateway?.ninetyDayVolumeETH ?? 0) > 0; + return active ? "Active" : "Inactive"; + }, [gateway]); + const statItems = useMemo( + () => [ + { + id: "total-fees-distributed", + label: "Total fees distributed", + value: ( + + {formatEth(gateway?.totalVolumeETH)} + {isSelfRedeeming && } + + ), + tooltip: "Lifetime fees this gateway has distributed on-chain.", + }, + { + id: "status", + label: "Status", + value: statusText, + tooltip: "Active if this gateway distributed fees in the last 90 days.", + }, + { + id: "ninety-day-fees", + label: "90d fees distributed", + value: formatEth(gateway?.ninetyDayVolumeETH), + tooltip: + "Total fees distributed to orchestrators over the last 90 days.", + }, + { + id: "activation-date", + label: "Activation date", + value: activeSince, + tooltip: "First day this gateway funded deposit/reserve on-chain.", + }, + { + id: "deposit-balance", + label: "Deposit balance", + value: formatEth(gateway?.deposit), + tooltip: "Current deposit balance funded for gateway job payouts.", + }, + { + id: "reserve-balance", + label: "Reserve balance", + value: formatEth(gateway?.reserve), + tooltip: "Reserve funds available for winning ticket payouts.", + }, + ], + [gateway, activeSince, statusText, isSelfRedeeming] + ); + + return ( + + + {statItems.map((stat) => ( + + ))} + + + ); +}; + +export default BroadcastingView; diff --git a/components/ConnectButton/index.tsx b/components/ConnectButton/index.tsx index a7d5f361..cd1f62fd 100644 --- a/components/ConnectButton/index.tsx +++ b/components/ConnectButton/index.tsx @@ -1,8 +1,16 @@ import { ConnectButton as ConnectButtonRainbowKit } from "@rainbow-me/rainbowkit"; import { ConnectButtonProps } from "@rainbow-me/rainbowkit/dist/components/ConnectButton/ConnectButton"; +import { useWindowSize } from "react-use"; const ConnectButton = (props: ConnectButtonProps) => { - return ; + const { width } = useWindowSize(); + return ( + + ); }; export default ConnectButton; diff --git a/components/GatewayList/index.tsx b/components/GatewayList/index.tsx new file mode 100644 index 00000000..6a07df8c --- /dev/null +++ b/components/GatewayList/index.tsx @@ -0,0 +1,331 @@ +import { ExplorerTooltip } from "@components/ExplorerTooltip"; +import IdentityAvatar from "@components/IdentityAvatar"; +import PopoverLink from "@components/PopoverLink"; +import Table from "@components/Table"; +import { formatEth, textTruncate } from "@lib/utils"; +import { + Badge, + Box, + Flex, + IconButton, + Link as A, + Popover, + PopoverContent, + PopoverTrigger, + Text, +} from "@livepeer/design-system"; +import { DotsHorizontalIcon } from "@radix-ui/react-icons"; +import { GatewaysQuery } from "apollo"; +import { useEnsData } from "hooks"; +import Link from "next/link"; +import { useMemo } from "react"; +import { Column } from "react-table"; + +type GatewayRow = NonNullable[number] & { + depositNumber: number; + reserveNumber: number; + ninetyDayVolumeNumber: number; + totalVolumeNumber: number; +}; + +const GatewayList = ({ + data, + pageSize = 10, +}: { + pageSize?: number; + data: GatewaysQuery["gateways"] | undefined; +}) => { + const rows: GatewayRow[] = useMemo( + () => + (data ?? []).map((gateway) => ({ + ...gateway, + depositNumber: Number(gateway?.deposit ?? 0), + reserveNumber: Number(gateway?.reserve ?? 0), + ninetyDayVolumeNumber: Number(gateway?.ninetyDayVolumeETH ?? 0), + totalVolumeNumber: Number(gateway?.totalVolumeETH ?? 0), + })), + [data] + ); + + const columns: Column[] = useMemo( + () => [ + { + Header: ( + + The account routing jobs to orchestrators and paying fees. + + } + > + Gateway + + ), + id: "gateway", + accessor: "id", + Cell: ({ row, value }) => { + const address = value as string; + const identity = useEnsData(address); + const ensName = identity?.name; + const shortAddress = address.replace(address.slice(6, 38), "…"); + + return ( + + + + {row.index + 1} + + + + {ensName ? ( + + + {textTruncate(ensName, 20, "…")} + + + {address.substring(0, 6)} + + + ) : ( + {shortAddress} + )} + + + + ); + }, + }, + { + Header: ( + Current deposit balance funded for payouts.} + > + Deposit + + ), + id: "depositNumber", + accessor: "depositNumber", + Cell: ({ value }) => ( + + {formatEth(Number(value ?? 0))} + + ), + }, + { + Header: ( + Reserve funds available for winning tickets.} + > + Reserve + + ), + id: "reserveNumber", + accessor: "reserveNumber", + Cell: ({ value }) => ( + + {formatEth(Number(value ?? 0))} + + ), + }, + { + Header: ( + Fees distributed over the last 90 days.} + > + 90d Fees + + ), + id: "ninetyDayVolumeNumber", + accessor: "ninetyDayVolumeNumber", + Cell: ({ value }) => ( + + {formatEth(Number(value ?? 0))} + + ), + sortType: "number", + }, + { + Header: ( + Lifetime fees distributed on-chain.} + > + Total Fees + + ), + id: "totalVolumeNumber", + accessor: "totalVolumeNumber", + Cell: ({ value }) => ( + + {formatEth(Number(value ?? 0))} + + ), + sortType: "number", + }, + { + Header: <>, + id: "actions", + Cell: ({ row }) => ( + + { + e.stopPropagation(); + }} + asChild + > + + + + + + + { + e.stopPropagation(); + }} + onPointerEnterCapture={undefined} + onPointerLeaveCapture={undefined} + placeholder={undefined} + > + + + Account Details + + + + Profile + + + History + + + + + ), + }, + ], + [] + ); + + if (!rows?.length) { + return ( + + No gateways found. + + ); + } + + return ( + + ); +}; + +export default GatewayList; diff --git a/components/HistoryView/index.tsx b/components/HistoryView/index.tsx index cbc0ce70..18619682 100644 --- a/components/HistoryView/index.tsx +++ b/components/HistoryView/index.tsx @@ -143,6 +143,25 @@ const Index = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [events]); + // Tag winning tickets (in/out/self) within the current window + const ticketEvents = useMemo(() => { + const tickets = + data?.winningTicketRedeemedEvents?.filter( + (e) => (e?.transaction?.timestamp ?? 0) > lastEventTimestamp + ) ?? []; + const accountLower = account.toLowerCase(); + return tickets.map((e) => ({ + ...e, + direction: + e?.sender?.id?.toLowerCase() === accountLower && + e?.recipient?.id?.toLowerCase() === accountLower + ? ("self" as const) + : e?.sender?.id?.toLowerCase() === accountLower + ? ("out" as const) + : ("in" as const), + })); + }, [data?.winningTicketRedeemedEvents, account, lastEventTimestamp]); + // performs filtering of winning ticket redeemed events and merges with separate "winning tickets" // this is so Os winning tickets show properly: https://github.com/livepeer/explorer/issues/108 const mergedEvents = useMemo( @@ -154,23 +173,16 @@ const Index = () => { e?.__typename !== "TreasuryVoteEvent" && e?.__typename !== "VoteEvent" ), - ...(data?.winningTicketRedeemedEvents?.filter( - (e) => (e?.transaction?.timestamp ?? 0) > lastEventTimestamp - ) ?? []), - ...extendedTreasuryVoteEventsData.filter( - (e) => (e?.transaction?.timestamp ?? 0) > lastEventTimestamp - ), - ...extendedVoteEventsData.filter( - (e) => (e?.transaction?.timestamp ?? 0) > lastEventTimestamp - ), + ...ticketEvents, + ...extendedTreasuryVoteEventsData, + ...extendedVoteEventsData, ].sort( (a, b) => (b?.transaction?.timestamp ?? 0) - (a?.transaction?.timestamp ?? 0) ), [ events, - data, - lastEventTimestamp, + ticketEvents, extendedTreasuryVoteEventsData, extendedVoteEventsData, ] @@ -679,7 +691,16 @@ function renderSwitch(event, i: number) { ); - case "WinningTicketRedeemedEvent": + case "WinningTicketRedeemedEvent": { + const direction = event.direction; + const title = + direction === "out" + ? "Paid winning ticket" + : direction === "self" + ? "Self-redeemed winning ticket" + : "Redeemed winning ticket"; + const amountPrefix = + direction === "out" ? "-" : direction === "self" ? "±" : "+"; return ( - Redeemed winning ticket + {title} @@ -718,7 +739,7 @@ function renderSwitch(event, i: number) { {" "} - + + {amountPrefix} {numbro(event.faceValue).format({ mantissa: 3, average: true, @@ -729,6 +750,7 @@ function renderSwitch(event, i: number) { ); + } case "DepositFundedEvent": return ( { return ( - { + e.preventDefault(); + e.stopPropagation(); + window.open( + id ? `https://arbiscan.io/tx/${id}` : "https://arbiscan.io", + "_blank", + "noopener,noreferrer" + ); + }} css={{ - display: "inline-flex", - textDecoration: "none !important", - "&:hover > *": { + cursor: "pointer", + backgroundColor: "$neutral3", + color: "$neutral11", + border: "1px solid $neutral4", + transition: + "background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease", + "&:hover": { border: "1.5px solid $grass7 !important", backgroundColor: "$grass3 !important", color: "$grass11 !important", }, }} + size="1" > - - {formatTransactionHash(id)} - - - + as={ArrowTopRightIcon} + /> + ); }; diff --git a/layouts/account.tsx b/layouts/account.tsx index 67dcc43a..f7ac149b 100644 --- a/layouts/account.tsx +++ b/layouts/account.tsx @@ -1,4 +1,5 @@ import BottomDrawer from "@components/BottomDrawer"; +import BroadcastingView from "@components/BroadcastingView"; import HistoryView from "@components/HistoryView"; import HorizontalScrollContainer from "@components/HorizontalScrollContainer"; import OrchestratingView from "@components/OrchestratingView"; @@ -46,16 +47,23 @@ export interface TabType { isActive?: boolean; } -type TabTypeEnum = "delegating" | "orchestrating" | "history"; +type TabTypeEnum = "delegating" | "orchestrating" | "history" | "broadcasting"; -const ACCOUNT_VIEWS: TabTypeEnum[] = ["delegating", "orchestrating", "history"]; +const ACCOUNT_VIEWS: TabTypeEnum[] = [ + "delegating", + "orchestrating", + "broadcasting", + "history", +]; const AccountLayout = ({ account, sortedOrchestrators, + isSelfRedeeming, }: { account?: AccountQueryResult["data"] | null; sortedOrchestrators: OrchestratorsSortedQueryResult["data"]; + isSelfRedeeming?: boolean; }) => { const accountAddress = useAccountAddress(); const { width } = useWindowSize(); @@ -130,7 +138,15 @@ const AccountLayout = ({ ); const isOrchestrator = useMemo( () => Boolean(viewedAccount?.transcoder), - [viewedAccount] + [viewedAccount?.transcoder] + ); + const isGateway = useMemo( + () => Boolean(viewedAccount?.gateway), + [viewedAccount?.gateway] + ); + const isDelegator = useMemo( + () => Boolean(viewedAccount?.delegator), + [viewedAccount?.delegator] ); const isMyDelegate = useMemo( () => accountId === dataMyAccount?.delegator?.delegate?.id.toLowerCase(), @@ -150,9 +166,20 @@ const AccountLayout = ({ isOrchestrator, accountId ?? "", view ?? "delegating", - isMyDelegate + isMyDelegate, + isGateway, + isMyAccount, + isDelegator ), - [isOrchestrator, accountId, view, isMyDelegate] + [ + isOrchestrator, + accountId, + view, + isMyDelegate, + isGateway, + isMyAccount, + isDelegator, + ] ); useEffect(() => { @@ -288,6 +315,12 @@ const AccountLayout = ({ /> )} {view === "history" && } + {view === "broadcasting" && ( + + )} {(isOrchestrator || isMyDelegate || isDelegatingAndIsMyAccountView) && (width >= 1200 ? ( @@ -348,27 +381,38 @@ function getTabs( isOrchestrator: boolean, account: string, view: TabTypeEnum, - isMyDelegate: boolean + isMyDelegate: boolean, + isGateway: boolean, + isMyAccount: boolean, + hasDelegator: boolean ): Array { - const tabs: Array = [ - { - name: "Delegating", - href: `/accounts/${account}/delegating`, - isActive: view === "delegating", - }, - { - name: "History", - href: `/accounts/${account}/history`, - isActive: view === "history", - }, - ]; + const tabs: Array = []; if (isOrchestrator || isMyDelegate) { - tabs.unshift({ + tabs.push({ name: "Orchestrating", href: `/accounts/${account}/orchestrating`, isActive: view === "orchestrating", }); } + if (isGateway) { + tabs.push({ + name: "Broadcasting", + href: `/accounts/${account}/broadcasting`, + isActive: view === "broadcasting", + }); + } + if (isMyAccount || hasDelegator) { + tabs.push({ + name: "Delegating", + href: `/accounts/${account}/delegating`, + isActive: view === "delegating", + }); + } + tabs.push({ + name: "History", + href: `/accounts/${account}/history`, + isActive: view === "history", + }); return tabs; } diff --git a/layouts/main.tsx b/layouts/main.tsx index a02fee05..89a43cf7 100644 --- a/layouts/main.tsx +++ b/layouts/main.tsx @@ -57,6 +57,7 @@ import React, { } from "react"; import { isMobile } from "react-device-detect"; import ReactGA from "react-ga"; +import { LuRadioTower } from "react-icons/lu"; import { useWindowSize } from "react-use"; import { Chain } from "viem"; @@ -140,6 +141,18 @@ const Layout = ({ children, title = "Livepeer Explorer" }) => { const pendingFeesAndStake = usePendingFeesAndStakeData(accountAddress); const isBannerDisabledByQuery = query.disableUrlVerificationBanner === "true"; + const isMyAccountPage = useMemo(() => { + if (!accountAddress) return false; + return asPath.toLowerCase().includes(accountAddress.toLowerCase()); + }, [accountAddress, asPath]); + const isOrchestratorsNavActive = + (!accountAddress || !isMyAccountPage) && + (asPath.includes("/orchestrators") || + (asPath.includes("/accounts") && !asPath.includes("/broadcasting"))); + const isGatewaysNavActive = + (!accountAddress || !isMyAccountPage) && + (asPath.includes("/gateways") || asPath.includes("/broadcasting")); + const totalActivePolls = useMemo( () => pollData?.polls.filter( @@ -244,6 +257,13 @@ const Layout = ({ children, title = "Livepeer Explorer" }) => { icon: DNS, className: "orchestrators", }, + { + name: "Gateways", + href: "/gateways", + as: "/gateways", + icon: LuRadioTower, + className: "gateways", + }, { name: ( @@ -329,11 +349,6 @@ const Layout = ({ children, title = "Livepeer Explorer" }) => { } }, []); - const isMyAccountPage = useMemo(() => { - if (!accountAddress) return false; - return asPath.toLowerCase().includes(accountAddress.toLowerCase()); - }, [accountAddress, asPath]); - return ( { > - + + + + + + + + {!gateways?.gateways ? ( + + + + ) : ( + + + + )} + { const errorProps: PageProps = { hadError: true, orchestrators: null, + gateways: null, events: null, protocol: null, fallback: {}, @@ -570,8 +663,14 @@ export const getStaticProps = async () => { const { orchestrators } = await getOrchestrators(client); const { events } = await getEvents(client); const protocol = await getProtocol(client); + const { gateways } = await getGateways(client); - if (!orchestrators.data || !events.data || !protocol.data) { + if ( + !orchestrators.data || + !events.data || + !protocol.data || + !gateways.data + ) { return { props: errorProps, revalidate: 60, @@ -581,6 +680,7 @@ export const getStaticProps = async () => { const props: PageProps = { hadError: false, orchestrators: orchestrators.data, + gateways: gateways.data, events: events.data, protocol: protocol.data, fallback: {}, diff --git a/queries/account.graphql b/queries/account.graphql index 39104fd2..010b70fb 100644 --- a/queries/account.graphql +++ b/queries/account.graphql @@ -49,6 +49,15 @@ query account($account: ID!) { id } } + gateway: broadcaster(id: $account) { + id + deposit + reserve + totalVolumeETH + ninetyDayVolumeETH + firstActiveDay + lastActiveDay + } protocol(id: "0") { id totalSupply diff --git a/queries/gatewaySelfRedeem.graphql b/queries/gatewaySelfRedeem.graphql new file mode 100644 index 00000000..3add14c6 --- /dev/null +++ b/queries/gatewaySelfRedeem.graphql @@ -0,0 +1,12 @@ +query gatewaySelfRedeem($account: String!) { + winningTicketRedeemedEvents( + first: 1 + orderBy: timestamp + orderDirection: desc + where: { sender: $account, recipient: $account } + ) { + transaction { + timestamp + } + } +} diff --git a/queries/gateways.graphql b/queries/gateways.graphql new file mode 100644 index 00000000..e0fa09ea --- /dev/null +++ b/queries/gateways.graphql @@ -0,0 +1,22 @@ +query gateways($first: Int!, $skip: Int!, $minActiveDay: Int!) { + gateways: broadcasters( + first: $first + skip: $skip + orderBy: ninetyDayVolumeETH + orderDirection: desc + where: { + or: [ + { ninetyDayVolumeETH_gt: "0" } + { firstActiveDay_gte: $minActiveDay } + ] + } + ) { + id + deposit + reserve + totalVolumeETH + ninetyDayVolumeETH + firstActiveDay + lastActiveDay + } +} diff --git a/queries/transactions.graphql b/queries/transactions.graphql index 13deb72b..535f58a6 100644 --- a/queries/transactions.graphql +++ b/queries/transactions.graphql @@ -54,6 +54,12 @@ query transactions($account: String!, $first: Int!, $skip: Int!) { } ... on WinningTicketRedeemedEvent { faceValue + sender { + id + } + recipient { + id + } } ... on DepositFundedEvent { sender { @@ -114,7 +120,7 @@ query transactions($account: String!, $first: Int!, $skip: Int!) { winningTicketRedeemedEvents( orderBy: timestamp orderDirection: desc - where: { recipient: $account } + where: { or: [{ recipient: $account }, { sender: $account }] } first: $first skip: $skip ) { @@ -128,5 +134,11 @@ query transactions($account: String!, $first: Int!, $skip: Int!) { timestamp } faceValue + sender { + id + } + recipient { + id + } } }