Skip to content

Add support for Ton Application Chains #2722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/deploy-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ on:
- shibarium
- scroll_sepolia
- stability
- tac_turin
- zkevm
- zilliqa_prototestnet
- zksync
Expand Down
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@
"scroll_sepolia",
"shibarium",
"stability_testnet",
"tac_turin",
"zkevm",
"zilliqa_prototestnet",
"zksync",
Expand Down
12 changes: 12 additions & 0 deletions configs/app/apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ const statsApi = (() => {
});
})();

const tacApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST');
if (!apiHost) {
return;
}

return Object.freeze({
endpoint: apiHost,
});
})();

const visualizeApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_VISUALIZE_API_HOST');
if (!apiHost) {
Expand All @@ -136,6 +147,7 @@ const apis: Apis = Object.freeze({
metadata: metadataApi,
rewards: rewardsApi,
stats: statsApi,
tac: tacApi,
visualize: visualizeApi,
});

Expand Down
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export { default as saveOnGas } from './saveOnGas';
export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats';
export { default as suave } from './suave';
export { default as tac } from './tac';
export { default as txInterpretation } from './txInterpretation';
export { default as userOps } from './userOps';
export { default as addressProfileAPI } from './addressProfileAPI';
Expand Down
25 changes: 25 additions & 0 deletions configs/app/features/tac.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Feature } from './types';

import apis from '../apis';
import { getEnvValue } from '../utils';

const title = 'Ton Application Chain (TAC)';

const tonExplorerUrl = getEnvValue('NEXT_PUBLIC_TAC_TON_EXPLORER_URL');

const config: Feature<{ explorerUrl: string }> = (() => {
if (apis.tac && tonExplorerUrl) {
return Object.freeze({
title,
isEnabled: true,
explorerUrl: tonExplorerUrl,
});
}

return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
1 change: 1 addition & 0 deletions configs/envs/.env.pw
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=http://localhost:3006
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=http://localhost:3007
NEXT_PUBLIC_NAME_SERVICE_API_HOST=http://localhost:3008
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=http://localhost:3009
NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST=http://localhost:3100
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx
NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32']
Expand Down
46 changes: 46 additions & 0 deletions configs/envs/.env.tac_turin
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Set of ENVs for TAC Turin network explorer
# https://tac-turin.blockscout.com
# This is an auto-generated file. To update all values, run "yarn dev:preset:sync --name=tac_turin"

NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST=https://tac-operation-lifecycle.k8s-dev.blockscout.com
NEXT_PUBLIC_TAC_TON_EXPLORER_URL=https://testnet.tonscan.org

# Local ENVs
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws

# Instance ENVs
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=tac-turin.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/tac.json
NEXT_PUBLIC_GAME_BADGE_CLAIM_LINK=https://badges.blockscout.com/mint/sherblockHolmesBadge
NEXT_PUBLIC_GAS_TRACKER_UNITS=[ 'gwei' ]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x9df2e91d1eed5637f0ffb9423b1fe34ff477942c2a3e64cfa46a95be81892214
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['no-repeat center/100% auto url(https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-skins/tac.jpg)'],'text_color':['rgba(242,235,255,1)'],'button':{'_default':{'background':['rgba(30,23,44,1)']},'_hover':{'background':['rgba(66,14,70,1)']}}}
NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=TAC
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=TAC
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/tac-light.svg
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/tac-dark.svg
NEXT_PUBLIC_NETWORK_ID=2390
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/tac-light.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/tac-dark.svg
NEXT_PUBLIC_NETWORK_NAME=TAC Turin
NEXT_PUBLIC_NETWORK_RPC_URL=https://turin.rpc.tac.build
NEXT_PUBLIC_NETWORK_SHORT_NAME=TAC Turin
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/tac-turin.png
NEXT_PUBLIC_STATS_API_BASE_PATH=/stats-service
NEXT_PUBLIC_STATS_API_HOST=https://tac-turin.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
20 changes: 19 additions & 1 deletion deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,23 @@ const beaconChainSchema = yup
}),
});

const tacSchema = yup
.object()
.shape({
NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST: yup.string().test(urlTest),
NEXT_PUBLIC_TAC_TON_EXPLORER_URL: yup
.string()
.when('NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST', {
is: (value: string) => Boolean(value),
then: (schema) => schema.test(urlTest),
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_TAC_TON_EXPLORER_URL can only be used with NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST',
value => value === undefined,
),
}),
});

const parentChainCurrencySchema = yup
.object()
.shape({
Expand Down Expand Up @@ -1078,6 +1095,7 @@ const schema = yup
.concat(celoSchema)
.concat(beaconChainSchema)
.concat(bridgedTokensSchema)
.concat(sentrySchema);
.concat(sentrySchema)
.concat(tacSchema);

export default schema;
2 changes: 2 additions & 0 deletions deploy/tools/envs-validator/test/.env.tac
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST=https://tac-operation-lifecycle.blockscout.com
NEXT_PUBLIC_TAC_TON_EXPLORER_URL=https://tonscan.org
13 changes: 13 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ All json-like values should be single-quoted. If it contains a hash (`#`) or a d
- [Address profile API](ENVS.md#address-profile-api)
- [Address XStar XHS score](ENVS.md#address-xstar-xhs-score)
- [SUAVE chain](ENVS.md#suave-chain)
- [Celo chain](ENVS.md#celo-chain)
- [Ton Application Chain (TAC)](ENVS.md#ton-application-chain-tac)
- [MetaSuites extension](ENVS.md#metasuites-extension)
- [Validators list](ENVS.md#validators-list)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
Expand Down Expand Up @@ -768,6 +770,17 @@ For blockchains that use the Celo platform. _Note_, that once the Celo mainnet b

&nbsp;

### Ton Application Chain (TAC)

For Ton Application Chains, this feature enables an additional view, such as a list of cross-chain operations and a detailed page for a specific cross-chain operation, as well as extra fields on the transaction page.

| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_TAC_OPERATION_LIFECYCLE_API_HOST | `string` | URL for the TAC Operation Lifecycle service. | Required | - | `https://tac-operation-lifecycle.blockscout.com` | v2.1.0+ |
| NEXT_PUBLIC_TAC_TON_EXPLORER_URL | `string` | URL of the Ton chain explorer. This is used to build links to transactions and addresses on the Ton chain. | Required | - | `https://tonscan.org` | v2.1.0+ |

&nbsp;

### MetaSuites extension

Enables [MetaSuites browser extension](https://github.com/blocksecteam/metasuites) to integrate with the app views.
Expand Down
3 changes: 3 additions & 0 deletions icons/arrows/revert.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions icons/brands/tac.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions icons/brands/ton.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions icons/operation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions icons/operation_slim.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions icons/verification-steps/error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import type { RewardsApiResourceName, RewardsApiResourcePayload } from './servic
import { REWARDS_API_RESOURCES } from './services/rewards';
import type { StatsApiResourceName, StatsApiResourcePayload } from './services/stats';
import { STATS_API_RESOURCES } from './services/stats';
import { TAC_OPERATION_LIFECYCLE_API_RESOURCES } from './services/tac-operation-lifecycle';
import type {
TacOperationLifecycleApiPaginationFilters,
TacOperationLifecycleApiResourceName,
TacOperationLifecycleApiResourcePayload,
} from './services/tac-operation-lifecycle';
import type { IsPaginated } from './services/utils';
import { VISUALIZE_API_RESOURCES } from './services/visualize';
import type { VisualizeApiResourceName, VisualizeApiResourcePayload } from './services/visualize';
Expand All @@ -26,6 +32,7 @@ export const RESOURCES = {
metadata: METADATA_API_RESOURCES,
rewards: REWARDS_API_RESOURCES,
stats: STATS_API_RESOURCES,
tac: TAC_OPERATION_LIFECYCLE_API_RESOURCES,
visualize: VISUALIZE_API_RESOURCES,
} satisfies Record<ApiName, Record<string, ApiResource>>;

Expand All @@ -46,6 +53,7 @@ R extends GeneralApiResourceName ? GeneralApiResourcePayload<R> :
R extends MetadataApiResourceName ? MetadataApiResourcePayload<R> :
R extends RewardsApiResourceName ? RewardsApiResourcePayload<R> :
R extends StatsApiResourceName ? StatsApiResourcePayload<R> :
R extends TacOperationLifecycleApiResourceName ? TacOperationLifecycleApiResourcePayload<R> :
R extends VisualizeApiResourceName ? VisualizeApiResourcePayload<R> :
never;
/* eslint-enable @stylistic/indent */
Expand Down Expand Up @@ -77,6 +85,7 @@ export type PaginationFilters<R extends ResourceName> =
R extends BensApiResourceName ? BensApiPaginationFilters<R> :
R extends GeneralApiResourceName ? GeneralApiPaginationFilters<R> :
R extends ContractInfoApiResourceName ? ContractInfoApiPaginationFilters<R> :
R extends TacOperationLifecycleApiResourceName ? TacOperationLifecycleApiPaginationFilters<R> :
never;
/* eslint-enable @stylistic/indent */

Expand Down
38 changes: 38 additions & 0 deletions lib/api/services/tac-operation-lifecycle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { ApiResource } from '../types';
import type * as tac from '@blockscout/tac-operation-lifecycle-types';

export const TAC_OPERATION_LIFECYCLE_API_RESOURCES = {
operations: {
path: '/api/v1/tac/operations',
paginated: true,
filterFields: [ 'q' ],
},
operation: {
path: '/api/v1/tac/operations/:id',
pathParams: [ 'id' ],
},
operation_by_tx_hash: {
path: '/api/v1/tac/operations\\:byTx/:tx_hash',
pathParams: [ 'tx_hash' ],
},
stat_operations: {
path: '/api/v1/stat/operations',
},
} satisfies Record<string, ApiResource>;

export type TacOperationLifecycleApiResourceName = `tac:${ keyof typeof TAC_OPERATION_LIFECYCLE_API_RESOURCES }`;

/* eslint-disable @stylistic/indent */
export type TacOperationLifecycleApiResourcePayload<R extends TacOperationLifecycleApiResourceName> =
R extends 'tac:operations' ? tac.OperationsResponse :
R extends 'tac:operation' ? tac.OperationDetails :
R extends 'tac:operation_by_tx_hash' ? tac.OperationsFullResponse :
R extends 'tac:stat_operations' ? tac.GetOperationStatisticsResponse :
never;
/* eslint-enable @stylistic/indent */

/* eslint-disable @stylistic/indent */
export type TacOperationLifecycleApiPaginationFilters<R extends TacOperationLifecycleApiResourceName> =
R extends 'tac:operations' ? { q: string } :
never;
/* eslint-enable @stylistic/indent */
2 changes: 1 addition & 1 deletion lib/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type ApiName = 'general' | 'admin' | 'bens' | 'contractInfo' | 'metadata' | 'rewards' | 'stats' | 'visualize';
export type ApiName = 'general' | 'admin' | 'bens' | 'contractInfo' | 'metadata' | 'rewards' | 'stats' | 'visualize' | 'tac';

export interface ApiResource {
path: string;
Expand Down
7 changes: 7 additions & 0 deletions lib/hooks/useNavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export default function useNavItems(): ReturnType {
icon: 'transactions',
isActive: pathname === '/txs' || pathname === '/tx/[hash]',
};
const operations: NavItem | null = config.features.tac.isEnabled ? {
text: 'Operations',
nextRoute: { pathname: '/operations' as const },
icon: 'operation',
isActive: pathname === '/operations' || pathname === '/operation/[id]',
} : null;
const internalTxs: NavItem | null = {
text: 'Internal transactions',
nextRoute: { pathname: '/internal-txs' as const },
Expand Down Expand Up @@ -186,6 +192,7 @@ export default function useNavItems(): ReturnType {
} else {
blockchainNavItems = [
txs,
operations,
internalTxs,
userOps,
blocks,
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/pools': 'Root page',
'/pools/[hash]': 'Regular page',
'/interop-messages': 'Root page',
'/operations': 'Root page',
'/operation/[id]': 'Regular page',

// service routes, added only to make typescript happy
'/login': 'Regular page',
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/pools': DEFAULT_TEMPLATE,
'/pools/[hash]': DEFAULT_TEMPLATE,
'/interop-messages': DEFAULT_TEMPLATE,
'/operations': DEFAULT_TEMPLATE,
'/operation/[id]': DEFAULT_TEMPLATE,

// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/pools': '%network_name% DEX pools',
'/pools/[hash]': '%network_name% pool details',
'/interop-messages': '%network_name% interop messages',
'/operations': '%network_name% operations',
'/operation/[id]': '%network_name% operation %id%',

// service routes, added only to make typescript happy
'/login': '%network_name% login',
Expand Down
2 changes: 2 additions & 0 deletions lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/pools': 'DEX pools',
'/pools/[hash]': 'Pool details',
'/interop-messages': 'Interop messages',
'/operations': 'Operations',
'/operation/[id]': 'Operation details',

// service routes, added only to make typescript happy
'/login': 'Login',
Expand Down
31 changes: 31 additions & 0 deletions lib/operations/tac.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as tac from '@blockscout/tac-operation-lifecycle-types';

import { rightLineArrow } from 'toolkit/utils/htmlEntities';
import { STATUS_LABELS } from 'ui/operation/tac/utils';

export function getTacOperationStatus(type: tac.OperationType) {
switch (type) {
case tac.OperationType.TON_TAC_TON:
return `TON ${ rightLineArrow } TAC ${ rightLineArrow } TON`;
case tac.OperationType.TAC_TON:
return `TAC ${ rightLineArrow } TON`;
case tac.OperationType.TON_TAC:
return `TON ${ rightLineArrow } TAC`;
case tac.OperationType.ERROR:
return 'Error';
case tac.OperationType.ROLLBACK:
return 'Rollback';
case tac.OperationType.PENDING:
return 'Pending';
default:
return null;
}
}

export function getTacOperationStage(data: tac.OperationDetails, txHash: string) {
const currentStep = data.status_history.find((step) => step.transactions.some((tx) => tx.hash.toLowerCase() === txHash.toLowerCase()));
if (!currentStep) {
return null;
}
return STATUS_LABELS[currentStep.type];
}
Loading
Loading