diff --git a/modules/address/components/Address.tsx b/modules/address/components/Address.tsx
index 5d79be46f..dd0b104cb 100644
--- a/modules/address/components/Address.tsx
+++ b/modules/address/components/Address.tsx
@@ -11,6 +11,8 @@ import { formatAddress } from 'lib/utils';
import { useWeb3 } from 'modules/web3/hooks/useWeb3';
import { getENS } from 'modules/web3/helpers/ens';
import React, { useEffect, useState } from 'react';
+import { getDefaultProvider } from 'modules/web3/helpers/getDefaultProvider';
+import { SupportedNetworks } from 'modules/web3/constants/networks';
export const Address = React.memo(function Address({
address,
@@ -19,14 +21,15 @@ export const Address = React.memo(function Address({
address: string;
maxLength?: number;
}): React.ReactElement {
- const { provider } = useWeb3();
const [addressFormated, setAddressFormatted] = useState(formatAddress(address || '').toLowerCase());
async function fetchENSName() {
- if (!address || !provider) {
+ if (!address) {
return;
}
+ const provider = getDefaultProvider(SupportedNetworks.MAINNET);
+
const ens = await getENS({ address, provider });
ens ? setAddressFormatted(ens) : setAddressFormatted(formatAddress(address).toLowerCase());
diff --git a/modules/app/components/DateWithHover.tsx b/modules/app/components/DateWithHover.tsx
index 6d39038cd..068d3b5d9 100644
--- a/modules/app/components/DateWithHover.tsx
+++ b/modules/app/components/DateWithHover.tsx
@@ -16,12 +16,15 @@ export function DateWithHover({
timeago,
label
}: {
- date: Date | string | number;
+ date: Date | string | number | null;
timeago?: boolean;
label?: string;
}): React.ReactElement {
+ if (!date) {
+ return N/A;
+ }
return (
-
+
{timeago ? `${formatTimeAgo(date ?? '')}` : `${formatDateWithTime(date ?? '')}`}
);
diff --git a/modules/contracts/eth-sdk.config.ts b/modules/contracts/eth-sdk.config.ts
index d744de5c7..8c980aa97 100644
--- a/modules/contracts/eth-sdk.config.ts
+++ b/modules/contracts/eth-sdk.config.ts
@@ -53,7 +53,7 @@ const config: EthSdkConfig = {
pollingOld: '0xF9be8F0945acDdeeDaA64DFCA5Fe9629D0CF8E5D',
pot: '0x197E90f9FAD81970bA7976f33CbD77088E5D7cf7',
vat: '0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B',
- voteDelegateFactory: '0xD897F108670903D1d6070fcf818f9db3615AF272',
+ voteDelegateFactory: '0x4E76FbE44fa5Dae076a7f4f676250e7941421fbA',
voteProxyFactory: '0x6FCD258af181B3221073A96dD90D1f7AE7eEc408',
voteProxyFactoryOld: '0xa63E145309cadaa6A903a19993868Ef7E85058BE',
vow: '0xA950524441892A31ebddF91d3cEEFa04Bf454466'
diff --git a/modules/delegates/api/fetchDelegatedTo.ts b/modules/delegates/api/fetchDelegatedTo.ts
index 88931a372..bd6682324 100644
--- a/modules/delegates/api/fetchDelegatedTo.ts
+++ b/modules/delegates/api/fetchDelegatedTo.ts
@@ -9,34 +9,45 @@ SPDX-License-Identifier: AGPL-3.0-or-later
import { add } from 'date-fns';
import { utils } from 'ethers';
import logger from 'lib/logger';
-import { Query } from 'modules/gql/generated/graphql';
import { gqlRequest } from 'modules/gql/gqlRequest';
import { allDelegates } from 'modules/gql/queries/allDelegates';
-import { mkrDelegatedToV2 } from 'modules/gql/queries/mkrDelegatedTo';
+import { delegatorHistory } from 'modules/gql/queries/subgraph/delegatorHistory';
import { SupportedNetworks } from 'modules/web3/constants/networks';
import { networkNameToChainId } from 'modules/web3/helpers/chain';
import { isAboutToExpireCheck, isExpiredCheck } from 'modules/migration/helpers/expirationChecks';
-import { DelegationHistoryWithExpirationDate, MKRDelegatedToDAIResponse } from '../types';
+import { DelegationHistoryWithExpirationDate, MKRDelegatedToResponse } from '../types';
import { getNewOwnerFromPrevious } from 'modules/migration/delegateAddressLinks';
+import { Query, AllDelegatesRecord } from 'modules/gql/generated/graphql';
export async function fetchDelegatedTo(
address: string,
network: SupportedNetworks
): Promise {
try {
- // Returns the records with the aggregated delegated data
- const data = await gqlRequest({
- chainId: networkNameToChainId(network),
- query: mkrDelegatedToV2,
- variables: { argAddress: address.toLowerCase() }
- });
// We fetch the delegates information from the DB to extract the expiry date of each delegate
// TODO: This information could be aggregated in the "mkrDelegatedTo" query in gov-polling-db, and returned there, as an improvement.
const chainId = networkNameToChainId(network);
const delegatesData = await gqlRequest({ chainId, query: allDelegates });
const delegates = delegatesData.allDelegates.nodes;
- const res: MKRDelegatedToDAIResponse[] = data.mkrDelegatedToV2.nodes;
+ // Returns the records with the aggregated delegated data
+ const data = await gqlRequest({
+ chainId: networkNameToChainId(network),
+ useSubgraph: true,
+ query: delegatorHistory,
+ variables: { address: address.toLowerCase() }
+ });
+ const res: MKRDelegatedToResponse[] = data.delegationHistories.map(x => {
+ return {
+ delegateContractAddress: x.delegate.id,
+ lockAmount: x.amount,
+ blockTimestamp: x.timestamp,
+ hash: x.txnHash,
+ blockNumber: x.blockNumber,
+ immediateCaller: address
+ };
+ });
+
const delegatedTo = res.reduce((acc, { delegateContractAddress, lockAmount, blockTimestamp, hash }) => {
const existing = acc.find(({ address }) => address === delegateContractAddress) as
| DelegationHistoryWithExpirationDate
@@ -44,22 +55,25 @@ export async function fetchDelegatedTo(
// We sum the total of lockAmounts in different events to calculate the current delegated amount
if (existing) {
- existing.lockAmount = utils.formatEther(
- utils.parseEther(existing.lockAmount).add(utils.parseEther(lockAmount))
- );
- existing.events.push({ lockAmount, blockTimestamp, hash });
+ existing.lockAmount = utils.formatEther(utils.parseEther(existing.lockAmount).add(lockAmount));
+ existing.events.push({ lockAmount: utils.formatEther(lockAmount), blockTimestamp, hash });
} else {
const delegatingTo = delegates.find(
i => i?.voteDelegate?.toLowerCase() === delegateContractAddress.toLowerCase()
- );
+ ) as (AllDelegatesRecord & { version: string }) | undefined;
+
+ if (!delegatingTo) {
+ return acc;
+ }
const delegatingToWalletAddress = delegatingTo?.delegate?.toLowerCase();
// Get the expiration date of the delegate
const expirationDate = add(new Date(delegatingTo?.blockTimestamp), { years: 1 });
- const isAboutToExpire = isAboutToExpireCheck(expirationDate);
- const isExpired = isExpiredCheck(expirationDate);
+ //only v1 delegate contracts expire
+ const isAboutToExpire = delegatingTo.version !== '2' && isAboutToExpireCheck(expirationDate);
+ const isExpired = delegatingTo.version !== '2' && isExpiredCheck(expirationDate);
// If it has a new owner address, check if it has renewed the contract
const newOwnerAddress = getNewOwnerFromPrevious(delegatingToWalletAddress as string, network);
@@ -73,9 +87,9 @@ export async function fetchDelegatedTo(
expirationDate,
isExpired,
isAboutToExpire: !isExpired && isAboutToExpire,
- lockAmount: utils.formatEther(utils.parseEther(lockAmount)),
+ lockAmount: utils.formatEther(lockAmount),
isRenewed: !!newRenewedContract,
- events: [{ lockAmount, blockTimestamp, hash }]
+ events: [{ lockAmount: utils.formatEther(lockAmount), blockTimestamp, hash }]
} as DelegationHistoryWithExpirationDate);
}
diff --git a/modules/delegates/api/fetchDelegates.ts b/modules/delegates/api/fetchDelegates.ts
index 2ebf5887d..0ccbef709 100644
--- a/modules/delegates/api/fetchDelegates.ts
+++ b/modules/delegates/api/fetchDelegates.ts
@@ -268,7 +268,7 @@ export async function fetchDelegates(
const aSupport = a.mkrDelegated ? a.mkrDelegated : 0;
return new BigNumberJS(aSupport).gt(new BigNumberJS(bSupport)) ? -1 : 1;
} else if (sortBy === 'date') {
- return a.expirationDate > b.expirationDate ? -1 : 1;
+ return a.expirationDate && b.expirationDate ? (a.expirationDate > b.expirationDate ? -1 : 1) : 0;
} else if (sortBy === 'delegators') {
const delegationHistoryA = formatDelegationHistory(a.mkrLockedDelegate);
const delegationHistoryB = formatDelegationHistory(b.mkrLockedDelegate);
@@ -444,7 +444,7 @@ export async function fetchDelegatesPaginated({
orderDirection,
seed,
delegateType,
- searchTerm,
+ searchTerm
}: DelegatesValidatedQueryParams): Promise {
const chainId = networkNameToChainId(network);
@@ -473,8 +473,7 @@ export async function fetchDelegatesPaginated({
}
]
};
- (searchTerm) &&
- delegatesQueryFilter.and.push({ voteDelegate: { in: filteredDelegateAddresses } });
+ searchTerm && delegatesQueryFilter.and.push({ voteDelegate: { in: filteredDelegateAddresses } });
}
const delegatesQueryVariables = {
@@ -492,8 +491,8 @@ export async function fetchDelegatesPaginated({
delegatesQueryVariables['seed'] = seed;
}
- const [githubExecutives, delegatesExecSupport, delegatesQueryRes, delegationMetricsRes] =
- await Promise.all([
+ const [githubExecutives, delegatesExecSupport, delegatesQueryRes, delegationMetricsRes] = await Promise.all(
+ [
getGithubExecutives(network),
fetchDelegatesExecSupport(network),
gqlRequest({
@@ -505,7 +504,8 @@ export async function fetchDelegatesPaginated({
chainId,
query: delegationMetricsQuery
})
- ]);
+ ]
+ );
const delegatesData = {
paginationInfo: {
@@ -562,7 +562,7 @@ export async function fetchDelegatesPaginated({
previous: allDelegatesEntry?.previous,
next: allDelegatesEntry?.next
};
- }) as DelegatePaginated[],
+ }) as DelegatePaginated[]
};
return delegatesData;
diff --git a/modules/delegates/types/delegate.d.ts b/modules/delegates/types/delegate.d.ts
index 02bfd4bb9..8007bc334 100644
--- a/modules/delegates/types/delegate.d.ts
+++ b/modules/delegates/types/delegate.d.ts
@@ -29,6 +29,8 @@ export type DelegateContractInformation = {
mkrDelegated: string;
proposalsSupported: number;
mkrLockedDelegate: MKRLockedDelegateAPIResponse[];
+ version: string;
+ lastVoteDate: number | null;
};
export type Delegate = {
@@ -42,7 +44,7 @@ export type Delegate = {
lastVoteDate: number | null;
expired: boolean;
isAboutToExpire: boolean;
- expirationDate: Date;
+ expirationDate: Date | null;
externalUrl?: string;
combinedParticipation?: string;
pollParticipation?: string;
@@ -116,7 +118,7 @@ export type MKRLockedDelegateAPIResponse = {
hash: string;
};
-export type MKRDelegatedToDAIResponse = MKRLockedDelegateAPIResponse & {
+export type MKRDelegatedToResponse = MKRLockedDelegateAPIResponse & {
hash: string;
immediateCaller: string;
};
diff --git a/modules/gql/gql.constants.ts b/modules/gql/gql.constants.ts
index 99de022c1..768a69b37 100644
--- a/modules/gql/gql.constants.ts
+++ b/modules/gql/gql.constants.ts
@@ -12,6 +12,15 @@ export const STAGING_MAINNET_SPOCK_URL = 'https://pollingdb2-mainnet-staging.mak
export const MAINNET_SPOCK_URL = 'https://pollingdb2-mainnet-prod.makerdao.com/api/v1';
export const TENDERLY_SPOCK_URL = 'https://pollingdb2-tenderly-staging.makerdao.com/api/v1';
+/* Subgraph URLs */
+
+// const usePrivateSubgraph = process.env.USE_PRIVATE_SUBGRAPH === 'true';
+// const permission = usePrivateSubgraph ? 'private' : 'public';
+export const TENDERLY_SUBGRAPH_URL =
+ 'https://query-subgraph-staging.sky.money/private/subgraphs/name/jetstreamgg/subgraph-testnet';
+export const MAINNET_SUBGRAPH_URL =
+ 'https://query-subgraph-staging.sky.money/private/subgraphs/name/jetstreamgg/subgraph-mainnet';
+
export enum QueryFilterNames {
Active = 'active',
PollId = 'pollId',
diff --git a/modules/gql/gqlRequest.ts b/modules/gql/gqlRequest.ts
index 647e287bb..534777b71 100644
--- a/modules/gql/gqlRequest.ts
+++ b/modules/gql/gqlRequest.ts
@@ -15,6 +15,7 @@ import { CHAIN_INFO } from 'modules/web3/constants/networks';
type GqlRequestProps = {
chainId?: SupportedChainId;
+ useSubgraph?: boolean;
query: RequestDocument;
variables?: Variables | null;
};
@@ -22,15 +23,24 @@ type GqlRequestProps = {
// TODO we'll be able to remove the "any" if we update all the instances of gqlRequest to pass
export const gqlRequest = async ({
chainId,
+ useSubgraph = false,
query,
variables
}: GqlRequestProps): Promise => {
try {
const id = chainId ?? SupportedChainId.MAINNET;
- const url = CHAIN_INFO[id].spockUrl;
+ let url;
+ if (useSubgraph) {
+ url = CHAIN_INFO[id].subgraphUrl;
+ } else {
+ url = CHAIN_INFO[id].spockUrl;
+ }
if (!url) {
- return Promise.reject(new ApiError(`Missing spock url in configuration for chainId: ${id}`));
+ return Promise.reject(
+ new ApiError(`Missing ${useSubgraph ? 'subgraph' : 'spock'} url in configuration for chainId: ${id}`)
+ );
}
+
const resp = await backoffRetry(
3,
() => request(url, query, variables),
@@ -42,7 +52,7 @@ export const gqlRequest = async ({
return resp;
} catch (e) {
const status = e.response ? e.response.status : 500;
- const errorMessage = status === 403 ? 'Rate limited on gov polling' : e.message;
+ const errorMessage = e.message;
const message = `Error on GraphQL query, Chain ID: ${chainId}, query: ${query}, message: ${errorMessage}`;
throw new ApiError(message, status, 'Error fetching gov polling data');
}
diff --git a/modules/gql/queries/subgraph/allDelegations.ts b/modules/gql/queries/subgraph/allDelegations.ts
new file mode 100644
index 000000000..843618986
--- /dev/null
+++ b/modules/gql/queries/subgraph/allDelegations.ts
@@ -0,0 +1,18 @@
+/*
+SPDX-FileCopyrightText: © 2023 Dai Foundation
+SPDX-License-Identifier: AGPL-3.0-or-later
+*/
+
+import { gql } from 'graphql-request';
+
+export const allDelegations = gql`
+ {
+ delegations(first: 1000) {
+ delegator
+ delegate {
+ id
+ }
+ amount
+ }
+ }
+`;
diff --git a/modules/gql/queries/subgraph/delegateHistoryArray.ts b/modules/gql/queries/subgraph/delegateHistoryArray.ts
new file mode 100644
index 000000000..215ce14ed
--- /dev/null
+++ b/modules/gql/queries/subgraph/delegateHistoryArray.ts
@@ -0,0 +1,24 @@
+/*
+SPDX-FileCopyrightText: © 2023 Dai Foundation
+SPDX-License-Identifier: AGPL-3.0-or-later
+*/
+
+import { gql } from 'graphql-request';
+
+export const delegateHistoryArray = gql`
+ query delegateHistoryArray($delegates: [String!]!) {
+ delegates(first: 1000, where: { id_in: $delegates }) {
+ delegationHistory {
+ amount
+ accumulatedAmount
+ delegator
+ blockNumber
+ timestamp
+ txnHash
+ delegate {
+ id
+ }
+ }
+ }
+ }
+`;
diff --git a/modules/gql/queries/subgraph/delegatorHistory.ts b/modules/gql/queries/subgraph/delegatorHistory.ts
new file mode 100644
index 000000000..cea74d36c
--- /dev/null
+++ b/modules/gql/queries/subgraph/delegatorHistory.ts
@@ -0,0 +1,22 @@
+/*
+SPDX-FileCopyrightText: © 2023 Dai Foundation
+
+SPDX-License-Identifier: AGPL-3.0-or-later
+*/
+
+import { gql } from 'graphql-request';
+
+export const delegatorHistory = gql`
+ query delegatorHistory($address: String!) {
+ delegationHistories(first: 1000, where: { delegator: $address }) {
+ amount
+ accumulatedAmount
+ delegate {
+ id
+ }
+ timestamp
+ txnHash
+ blockNumber
+ }
+ }
+`;
diff --git a/modules/migration/components/MigrationBanner.tsx b/modules/migration/components/MigrationBanner.tsx
index 5a9f77bd0..b25a561f6 100644
--- a/modules/migration/components/MigrationBanner.tsx
+++ b/modules/migration/components/MigrationBanner.tsx
@@ -21,19 +21,25 @@ export function MigrationBanner(): React.ReactElement | null {
isDelegatedToExpiredContract,
isDelegateContractExpired,
isDelegateContractExpiring,
- isShadowDelegate
+ isShadowDelegate,
+ isDelegateV1Contract,
+ isDelegatedToV1Contract
} = useMigrationStatus();
const showDelegationMigrationBanner =
(isDelegateContractExpired && !isShadowDelegate) ||
(isDelegateContractExpiring && !isShadowDelegate) ||
+ (isDelegateV1Contract && !isShadowDelegate) ||
isDelegatedToExpiringContract ||
- isDelegatedToExpiredContract;
+ isDelegatedToExpiredContract ||
+ isDelegatedToV1Contract;
const { variant, href, copy } = getMigrationBannerContent({
isDelegatedToExpiredContract,
isDelegateContractExpired: isDelegateContractExpired && !isShadowDelegate,
isDelegatedToExpiringContract,
- isDelegateContractExpiring: isDelegateContractExpiring && !isShadowDelegate
+ isDelegateContractExpiring: isDelegateContractExpiring && !isShadowDelegate,
+ isDelegateV1Contract,
+ isDelegatedToV1Contract
});
return showDelegationMigrationBanner ? (
diff --git a/modules/migration/components/MigrationInfo.tsx b/modules/migration/components/MigrationInfo.tsx
index 852871749..6dee1912d 100644
--- a/modules/migration/components/MigrationInfo.tsx
+++ b/modules/migration/components/MigrationInfo.tsx
@@ -25,7 +25,7 @@ export function MigrationInfo({
- Maker delegate contracts are{' '}
+ Maker v1 delegate contracts are{' '}
{' '}
in order to protect the Maker protocol against stale MKR tokens participating in Maker governance.
+ The new v2 delegate contracts, however, do not expire.
diff --git a/modules/migration/helpers/getMigrationBannerContent.ts b/modules/migration/helpers/getMigrationBannerContent.ts
index 73b04911e..f6929b943 100644
--- a/modules/migration/helpers/getMigrationBannerContent.ts
+++ b/modules/migration/helpers/getMigrationBannerContent.ts
@@ -10,12 +10,16 @@ export const getMigrationBannerContent = ({
isDelegateContractExpired,
isDelegateContractExpiring,
isDelegatedToExpiredContract,
- isDelegatedToExpiringContract
+ isDelegatedToExpiringContract,
+ isDelegateV1Contract,
+ isDelegatedToV1Contract
}: {
isDelegateContractExpired: boolean;
isDelegateContractExpiring: boolean;
isDelegatedToExpiredContract: boolean;
isDelegatedToExpiringContract: boolean;
+ isDelegateV1Contract: boolean;
+ isDelegatedToV1Contract: boolean;
}): { variant: string; href: string; copy: string } => {
// a delegate having an expired contract is
if (isDelegateContractExpired) {
@@ -35,6 +39,14 @@ export const getMigrationBannerContent = ({
};
}
+ if (isDelegateV1Contract) {
+ return {
+ variant: 'bannerNotice',
+ href: '/migration/delegate',
+ copy: 'Your delegate contract needs to be migrated to v2. Please visit the migration page to migrate it.'
+ };
+ }
+
// next check if user has delegated to an expired contract
if (isDelegatedToExpiredContract) {
return {
@@ -53,6 +65,15 @@ export const getMigrationBannerContent = ({
};
}
+ // next check if user has delegated to a v1 contract
+ if (isDelegatedToV1Contract) {
+ return {
+ variant: 'bannerNotice',
+ href: '/migration/delegator',
+ copy: 'You have MKR delegated to a v1 delegate contract. Please visit the migration page to migrate your MKR to a v2 delegate contract.'
+ };
+ }
+
// if we're here, something went wrong
return {
variant: '',
diff --git a/modules/migration/hooks/useMigrationStatus.tsx b/modules/migration/hooks/useMigrationStatus.tsx
index 41d5fbc3f..9cfb2fd18 100644
--- a/modules/migration/hooks/useMigrationStatus.tsx
+++ b/modules/migration/hooks/useMigrationStatus.tsx
@@ -21,6 +21,8 @@ export function useMigrationStatus(): {
isDelegateContractExpired: boolean;
isDelegateContractExpiring: boolean;
isShadowDelegate: boolean;
+ isDelegateV1Contract: boolean;
+ isDelegatedToV1Contract: boolean;
} {
const { account: address, network } = useWeb3();
const { cache } = useSWRConfig();
@@ -48,6 +50,8 @@ export function useMigrationStatus(): {
: false
: false;
+ const isDelegateV1Contract = !!delegateContractExpirationDate; // TODO: update with version === '1' when available
+
const isDelegateContractExpiring = delegateContractExpirationDate
? isAboutToExpireCheck(delegateContractExpirationDate)
: false;
@@ -70,11 +74,19 @@ export function useMigrationStatus(): {
}, false)
: false;
+ const isDelegatedToV1Contract = delegatedToData
+ ? delegatedToData.delegatedTo.reduce((acc, cur) => {
+ return acc || !!cur.expirationDate;
+ }, false)
+ : false;
+
return {
isDelegatedToExpiredContract,
isDelegatedToExpiringContract,
isDelegateContractExpired,
isDelegateContractExpiring,
- isShadowDelegate
+ isShadowDelegate,
+ isDelegateV1Contract,
+ isDelegatedToV1Contract
};
}
diff --git a/modules/web3/constants/networks.ts b/modules/web3/constants/networks.ts
index 457500850..1759fad97 100644
--- a/modules/web3/constants/networks.ts
+++ b/modules/web3/constants/networks.ts
@@ -16,7 +16,9 @@ export const NetworkContextName = 'NETWORK';
import {
MAINNET_SPOCK_URL,
STAGING_MAINNET_SPOCK_URL,
- TENDERLY_SPOCK_URL
+ TENDERLY_SPOCK_URL,
+ TENDERLY_SUBGRAPH_URL,
+ MAINNET_SUBGRAPH_URL
} from 'modules/gql/gql.constants';
export enum SupportedConnectors {
@@ -47,6 +49,7 @@ type ChainInfo = {
};
const { TENDERLY_RPC_URL } = tenderlyTestnetData;
+const TENDERLY_CONTAINER_ID = 'c91028eb-78e1-4305-a289-5cd07adc7fb9';
//todo: change name to SUPPORTED_CHAIN_INFO
export const CHAIN_INFO: ChainInfo = {
@@ -60,6 +63,7 @@ export const CHAIN_INFO: ChainInfo = {
defaultRpc: NodeProviders.ALCHEMY,
spockUrl:
process.env.NEXT_PUBLIC_VERCEL_ENV === 'development' ? STAGING_MAINNET_SPOCK_URL : MAINNET_SPOCK_URL,
+ subgraphUrl: MAINNET_SUBGRAPH_URL,
rpcs: {
[NodeProviders.INFURA]: `https://mainnet.infura.io/v3/${config.INFURA_KEY}`,
[NodeProviders.ALCHEMY]: `https://eth-mainnet.g.alchemy.com/v2/${config.ALCHEMY_KEY}`
@@ -93,7 +97,7 @@ export const CHAIN_INFO: ChainInfo = {
showInProduction: false
},
[SupportedChainId.TENDERLY]: {
- blockExplorerUrl: `dashboard.tenderly.co/explorer/vnet/${config.TENDERLY_RPC_KEY}`,
+ blockExplorerUrl: `dashboard.tenderly.co/pullup-labs/endgame-0/testnet/${TENDERLY_CONTAINER_ID}`,
blockExplorerName: 'Etherscan',
chainId: SupportedChainId.TENDERLY,
label: 'Tenderly',
@@ -101,8 +105,12 @@ export const CHAIN_INFO: ChainInfo = {
network: SupportedNetworks.TENDERLY,
defaultRpc: NodeProviders.TENDERLY,
spockUrl: TENDERLY_SPOCK_URL,
+ subgraphUrl: TENDERLY_SUBGRAPH_URL,
rpcs: {
- [NodeProviders.TENDERLY]: config.USE_MOCK_WALLET && TENDERLY_RPC_URL ? TENDERLY_RPC_URL : `https://virtual.mainnet.rpc.tenderly.co/${config.TENDERLY_RPC_KEY}`
+ [NodeProviders.TENDERLY]:
+ config.USE_MOCK_WALLET && TENDERLY_RPC_URL
+ ? TENDERLY_RPC_URL
+ : `https://virtual.mainnet.rpc.tenderly.co/${config.TENDERLY_RPC_KEY}`
},
showInProduction: false
}
diff --git a/modules/web3/helpers/ens.ts b/modules/web3/helpers/ens.ts
index e4f7271cc..4454f9f09 100644
--- a/modules/web3/helpers/ens.ts
+++ b/modules/web3/helpers/ens.ts
@@ -16,7 +16,7 @@ export async function getENS({
provider
}: {
address: string;
- provider: providers.Web3Provider;
+ provider: providers.BaseProvider;
}): Promise {
try {
const name = await provider.lookupAddress(address);
diff --git a/modules/web3/helpers/getEtherscanLink.ts b/modules/web3/helpers/getEtherscanLink.ts
index 760b3dbd7..312bd260f 100644
--- a/modules/web3/helpers/getEtherscanLink.ts
+++ b/modules/web3/helpers/getEtherscanLink.ts
@@ -20,6 +20,9 @@ export function getEtherscanLink(
switch (type) {
case 'transaction':
+ if (network === SupportedNetworks.TENDERLY) {
+ return `${prefix}/tx/mainnet/${data}`;
+ }
return `${prefix}/tx/${data}`;
case 'address':
default:
diff --git a/modules/web3/types/chain.d.ts b/modules/web3/types/chain.d.ts
index 85bbecc8f..0852c4f60 100644
--- a/modules/web3/types/chain.d.ts
+++ b/modules/web3/types/chain.d.ts
@@ -19,6 +19,7 @@ export type SupportedChain = {
network: SupportedNetworks;
defaultRpc: string;
spockUrl?: string;
+ subgraphUrl?: string;
type: 'gasless' | 'normal';
showInProduction: boolean;
rpcs: {
diff --git a/next-env.d.ts b/next-env.d.ts
index 4f11a03dc..a4a7b3f5c 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
diff --git a/pages/delegates/index.tsx b/pages/delegates/index.tsx
index 31be102ad..5aadc0a8e 100644
--- a/pages/delegates/index.tsx
+++ b/pages/delegates/index.tsx
@@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
import { useMemo, useState, useRef, useEffect } from 'react';
import { Heading, Box, Flex, Card, Text, Button } from 'theme-ui';
import { GetStaticProps } from 'next';
-import { useRouter } from 'next/router';
import ErrorPage from 'modules/app/components/ErrorPage';
import { useBreakpointIndex } from '@theme-ui/match-media';
import shallow from 'zustand/shallow';
@@ -130,8 +129,6 @@ const Delegates = ({
setIsRendering(false);
}, []);
- const router = useRouter();
-
useEffect(() => {
if (shouldLoadMore) {
if (shadowDelegates.length >= 15 && !loadAllDelegates) {
diff --git a/pages/migration/delegate.tsx b/pages/migration/delegate.tsx
index 095a4726d..39bd10502 100644
--- a/pages/migration/delegate.tsx
+++ b/pages/migration/delegate.tsx
@@ -27,7 +27,8 @@ export default function DelegateMigrationPage(): React.ReactElement {
const { account, provider } = useWeb3();
const [migrationInfoAcknowledged, setMigrationInfoAcknowledged] = useState(false);
- const { isDelegateContractExpiring, isDelegateContractExpired } = useMigrationStatus();
+ const { isDelegateContractExpiring, isDelegateContractExpired, isDelegateV1Contract } =
+ useMigrationStatus();
const {
newOwnerAddress,
@@ -40,7 +41,7 @@ export default function DelegateMigrationPage(): React.ReactElement {
const connectedAddressFound = !!previousOwnerAddress || !!newOwnerAddress;
// the user should be shown the steps to take action if:
- // a - the connected account has an expired/expiring contract
+ // a - the connected account has an expired/expiring contract or needs to migrate to v2
// or
// b - the connected count is the new account of a previous delegate
// and has not created the delegate contract yet
@@ -48,6 +49,7 @@ export default function DelegateMigrationPage(): React.ReactElement {
// a
isDelegateContractExpiring ||
isDelegateContractExpired ||
+ isDelegateV1Contract ||
// b
(!!newOwnerAddress && !newOwnerHasDelegateContract);
@@ -55,7 +57,7 @@ export default function DelegateMigrationPage(): React.ReactElement {
// delegate contract is either expired or expiring and we don't have
// a request to migrate the address yet, show migration info
if (
- (isDelegateContractExpired || isDelegateContractExpiring) &&
+ (isDelegateContractExpired || isDelegateContractExpiring || isDelegateV1Contract) &&
!connectedAddressFound &&
!migrationInfoAcknowledged
) {
@@ -65,18 +67,18 @@ export default function DelegateMigrationPage(): React.ReactElement {
// same status as above, but user has acknowledged migration info,
// show new address step
if (
- (isDelegateContractExpiring || isDelegateContractExpired) &&
+ (isDelegateContractExpiring || isDelegateContractExpired || isDelegateV1Contract) &&
!connectedAddressFound &&
migrationInfoAcknowledged
) {
return STEPS.NEW_ADDRESS;
}
- // delegate contract is either expired or expiring
+ // delegate contract is either expired or expiring or needs to migrate to v2
// and we have processed the request to migrate
// but user is connected with old address
if (
- (isDelegateContractExpiring || isDelegateContractExpired) &&
+ (isDelegateContractExpiring || isDelegateContractExpired || isDelegateV1Contract) &&
connectedAddressFound &&
previousOwnerConnected
) {
@@ -93,6 +95,7 @@ export default function DelegateMigrationPage(): React.ReactElement {
}, [
isDelegateContractExpired,
isDelegateContractExpiring,
+ isDelegateV1Contract,
previousOwnerAddress,
newOwnerAddress,
newOwnerHasDelegateContract,
@@ -133,12 +136,21 @@ export default function DelegateMigrationPage(): React.ReactElement {
{isDelegateContractExpiring &&
!isDelegateContractExpired &&
'Your delegate contract is expiring soon. Please migrate as soon as possible.'}
- {((!isDelegateContractExpired && !isDelegateContractExpiring && newOwnerHasDelegateContract) ||
+ {/* TODO: Add the right message for v2 migration when decided */}
+ {isDelegateV1Contract &&
+ !isDelegateContractExpired &&
+ !isDelegateContractExpiring &&
+ 'Your delegate contract needs to be migrated to v2.'}
+ {((!isDelegateV1Contract &&
+ !isDelegateContractExpired &&
+ !isDelegateContractExpiring &&
+ newOwnerHasDelegateContract) ||
!actionNeeded) &&
'No contract migration is necessary at this time'}
{isDelegateContractExpired ||
+ isDelegateV1Contract ||
(isDelegateContractExpiring && (
i.address.toLowerCase() === delegate.previous?.address.toLowerCase()
);
- return !delegate.expired && !delegate.isAboutToExpire && isPreviousDelegate;
+ return (
+ !delegate.expirationDate || (!delegate.expired && !delegate.isAboutToExpire && isPreviousDelegate)
+ );
});
}, [addressDelegations, delegatesThatAreAboutToExpiry]);