Skip to content

Commit

Permalink
Merge pull request #2090 from blockscout/mud
Browse files Browse the repository at this point in the history
MUD
  • Loading branch information
tom2drum authored Jul 30, 2024
2 parents 03c1bd4 + 2a0bc74 commit c4ae053
Show file tree
Hide file tree
Showing 87 changed files with 2,142 additions and 45 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- arbitrum
- base
- celo_alfajores
- garnet
- gnosis
- eth
- eth_sepolia
Expand Down
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@
"arbitrum",
"base",
"celo_alfajores",
"garnet",
"gnosis",
"eth",
"eth_goerli",
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 @@ -18,6 +18,7 @@ export { default as growthBook } from './growthBook';
export { default as marketplace } from './marketplace';
export { default as metasuites } from './metasuites';
export { default as mixpanel } from './mixpanel';
export { default as mudFramework } from './mudFramework';
export { default as multichainButton } from './multichainButton';
export { default as nameService } from './nameService';
export { default as publicTagsSubmission } from './publicTagsSubmission';
Expand Down
22 changes: 22 additions & 0 deletions configs/app/features/mudFramework.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Feature } from './types';

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

const title = 'MUD framework';

const config: Feature<{ isEnabled: true }> = (() => {
if (rollup.isEnabled && rollup.type === 'optimistic' && getEnvValue('NEXT_PUBLIC_HAS_MUD_FRAMEWORK') === 'true') {
return Object.freeze({
title,
isEnabled: true,
});
}

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

export default config;
50 changes: 50 additions & 0 deletions configs/envs/.env.garnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Set of ENVs for Garnet (dev only)
# https://https://explorer.garnetchain.com//

# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000

# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME="Garnet Testnet"
NEXT_PUBLIC_NETWORK_ID=17069
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_RPC_URL=https://partner-rpc.garnetchain.com/tireless-strand-dreamt-overcome

# api configuration
NEXT_PUBLIC_API_HOST=explorer.garnetchain.com
NEXT_PUBLIC_API_BASE_PATH=/

# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
## views
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
# app features
NEXT_PUBLIC_APP_INSTANCE=local
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3000/login
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/redstone-testnet.json
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/redstone.json
NEXT_PUBLIC_AD_BANNER_PROVIDER=none
## sidebar
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/garnet.svg
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/garnet.svg
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/garnet-dark.svg
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/garnet-dark.svg
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgb(169, 31, 47)
NEXT_PUBLIC_OG_DESCRIPTION="Redstone is the home for onchain games, worlds, and other MUD applications"
# rollup
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth-holesky.blockscout.com/
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://garnet.qry.live/withdraw
NEXT_PUBLIC_HAS_MUD_FRAMEWORK=true
10 changes: 10 additions & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,16 @@ const schema = yup
value => value === undefined,
),
}),
NEXT_PUBLIC_HAS_MUD_FRAMEWORK: yup.boolean()
.when('NEXT_PUBLIC_ROLLUP_TYPE', {
is: 'optimistic',
then: (schema) => schema,
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_HAS_MUD_FRAMEWORK can only be used with NEXT_PUBLIC_ROLLUP_TYPE=optimistic',
value => value === undefined,
),
}),

// 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
Expand Down
1 change: 1 addition & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ This feature is **enabled by default** with the `coinzilla` ads provider. To swi
| NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0+ |
| NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL | `string` | URL for L2 -> L1 withdrawals (Optimistic stack only) | Required for `optimistic` rollups | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0+ |
| NEXT_PUBLIC_FAULT_PROOF_ENABLED | `boolean` | Set to `true` for chains with fault proof system enabled (Optimistic stack only) | - | - | `true` | v1.31.0+ |
| NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | - |

&nbsp;

Expand Down
4 changes: 4 additions & 0 deletions icons/MUD.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/MUD_menu.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ import type {
AddressCollectionsResponse,
AddressNFTTokensFilter,
AddressCoinBalanceHistoryChartOld,
AddressMudTables,
AddressMudTablesFilter,
AddressMudRecords,
AddressMudRecordsFilter,
AddressMudRecordsSorting,
AddressMudRecord,
} from 'types/api/address';
import type { AddressesResponse } from 'types/api/addresses';
import type { AddressMetadataInfo, PublicTagTypesResponse } from 'types/api/addressMetadata';
Expand Down Expand Up @@ -61,6 +67,7 @@ import type {
import type { IndexingStatus } from 'types/api/indexingStatus';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { LogsResponseTx, LogsResponseAddress } from 'types/api/log';
import type { MudWorldsResponse } from 'types/api/mudWorlds';
import type { NovesAccountHistoryResponse, NovesDescribeTxsResponse, NovesResponseData } from 'types/api/noves';
import type {
OptimisticL2DepositsResponse,
Expand Down Expand Up @@ -654,6 +661,34 @@ export const RESOURCES = {
path: '/api/v2/optimism/games/count',
},

// MUD worlds on optimism
mud_worlds: {
path: '/api/v2/mud/worlds',
filterFields: [],
},

address_mud_tables: {
path: '/api/v2/mud/worlds/:hash/tables',
pathParams: [ 'hash' as const ],
filterFields: [ 'q' as const ],
},

address_mud_tables_count: {
path: '/api/v2/mud/worlds/:hash/tables/count',
pathParams: [ 'hash' as const ],
},

address_mud_records: {
path: '/api/v2/mud/worlds/:hash/tables/:table_id/records',
pathParams: [ 'hash' as const, 'table_id' as const ],
filterFields: [ 'filter_key0' as const, 'filter_key1' as const ],
},

address_mud_record: {
path: '/api/v2/mud/worlds/:hash/tables/:table_id/records/:record_id',
pathParams: [ 'hash' as const, 'table_id' as const, 'record_id' as const ],
},

// arbitrum L2
arbitrum_l2_messages: {
path: '/api/v2/arbitrum/messages/:direction',
Expand Down Expand Up @@ -899,6 +934,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'verified_contracts' |
'optimistic_l2_output_roots' | 'optimistic_l2_withdrawals' | 'optimistic_l2_txn_batches' | 'optimistic_l2_deposits' |
'optimistic_l2_dispute_games' |
'mud_worlds'| 'address_mud_tables' | 'address_mud_records' |
'shibarium_deposits' | 'shibarium_withdrawals' |
'arbitrum_l2_messages' | 'arbitrum_l2_txn_batches' | 'arbitrum_l2_txn_batch_txs' | 'arbitrum_l2_txn_batch_blocks' |
'zkevm_l2_deposits' | 'zkevm_l2_withdrawals' | 'zkevm_l2_txn_batches' | 'zkevm_l2_txn_batch_txs' |
Expand Down Expand Up @@ -1056,6 +1092,11 @@ Q extends 'user_op_interpretation'? TxInterpretationResponse :
Q extends 'noves_transaction' ? NovesResponseData :
Q extends 'noves_address_history' ? NovesAccountHistoryResponse :
Q extends 'noves_describe_txs' ? NovesDescribeTxsResponse :
Q extends 'mud_worlds' ? MudWorldsResponse :
Q extends 'address_mud_tables' ? AddressMudTables :
Q extends 'address_mud_tables_count' ? number :
Q extends 'address_mud_records' ? AddressMudRecords :
Q extends 'address_mud_record' ? AddressMudRecord :
never;
/* eslint-enable @typescript-eslint/indent */

Expand Down Expand Up @@ -1087,6 +1128,8 @@ Q extends 'addresses_lookup' ? EnsAddressLookupFilters :
Q extends 'domains_lookup' ? EnsDomainLookupFilters :
Q extends 'user_ops' ? UserOpsFilters :
Q extends 'validators' ? ValidatorsFilters :
Q extends 'address_mud_tables' ? AddressMudTablesFilter :
Q extends 'address_mud_records' ? AddressMudRecordsFilter :
never;
/* eslint-enable @typescript-eslint/indent */

Expand All @@ -1099,5 +1142,6 @@ Q extends 'address_txs' ? TransactionsSorting :
Q extends 'addresses_lookup' ? EnsLookupSorting :
Q extends 'domains_lookup' ? EnsLookupSorting :
Q extends 'validators' ? ValidatorsSorting :
Q extends 'address_mud_records' ? AddressMudRecordsSorting :
never;
/* eslint-enable @typescript-eslint/indent */
7 changes: 7 additions & 0 deletions lib/capitalizeFirstLetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function capitalizeFirstLetter(text: string) {
if (!text || !text.length) {
return '';
}

return text.charAt(0).toUpperCase() + text.slice(1);
}
1 change: 1 addition & 0 deletions lib/date/dayjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dayjs.extend(minMax);
dayjs.updateLocale('en', {
formats: {
llll: `MMM DD YYYY HH:mm:ss A (Z${ nbsp }UTC)`,
lll: 'MMM D, YYYY h:mm A',
},
relativeTime: {
s: '1s',
Expand Down
7 changes: 7 additions & 0 deletions lib/hooks/useNavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export default function useNavItems(): ReturnType {
icon: 'games',
isActive: pathname === '/dispute-games',
} : null;
const mudWorlds = config.features.mudFramework.isEnabled ? {
text: 'MUD worlds',
nextRoute: { pathname: '/mud-worlds' as const },
icon: 'MUD_menu',
isActive: pathname === '/mud-worlds',
} : null;

const rollupFeature = config.features.rollup;

Expand All @@ -121,6 +127,7 @@ export default function useNavItems(): ReturnType {
[
userOps,
topAccounts,
mudWorlds,
validators,
verifiedContracts,
ensLookup,
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/name-domains/[name]': 'Regular page',
'/validators': 'Root page',
'/gas-tracker': 'Root page',
'/mud-worlds': 'Root page',

// service routes, added only to make typescript happy
'/login': 'Regular page',
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/name-domains/[name]': DEFAULT_TEMPLATE,
'/validators': DEFAULT_TEMPLATE,
'/gas-tracker': DEFAULT_TEMPLATE,
'/mud-worlds': DEFAULT_TEMPLATE,

// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
Expand Down
1 change: 1 addition & 0 deletions lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/name-domains/[name]': '%network_name% %name% domain details',
'/validators': '%network_name% validators list',
'/gas-tracker': '%network_name% gas tracker - Current gas fees',
'/mud-worlds': '%network_name% MUD worlds list',

// service routes, added only to make typescript happy
'/login': '%network_name% login',
Expand Down
1 change: 1 addition & 0 deletions lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/name-domains/[name]': 'Domain details',
'/validators': 'Validators list',
'/gas-tracker': 'Gas tracker',
'/mud-worlds': 'MUD worlds',

// service routes, added only to make typescript happy
'/login': 'Login',
Expand Down
94 changes: 94 additions & 0 deletions mocks/mud/mudTables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* eslint-disable max-len */
import type { AddressMudRecord, AddressMudRecords, AddressMudRecordsItem, AddressMudTables } from 'types/api/address';
import type { MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds';

export const table1: MudWorldTable = {
table_full_name: 'tb.store.Tables',
table_id: '0x746273746f72650000000000000000005461626c657300000000000000000000',
table_name: 'Tables',
table_namespace: 'store',
table_type: 'onchain',
};

export const table2: MudWorldTable = {
table_full_name: 'ot.world.FunctionSignatur',
table_id: '0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572',
table_name: 'FunctionSignatur',
table_namespace: 'world',
table_type: 'offchain',
};

export const schema1: MudWorldSchema = {
key_names: [ 'moduleAddress', 'argumentsHash' ],
key_types: [ 'address', 'bytes32' ],
value_names: [ 'fieldLayout', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ],
value_types: [ 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ],
};

export const schema2: MudWorldSchema = {
key_names: [],
key_types: [],
value_names: [ 'value' ],
value_types: [ 'address' ],
};

export const mudTables: AddressMudTables = {
items: [
{
table: table1,
schema: schema1,
},
{
table: table2,
schema: schema2,
},
],
next_page_params: {
items_count: 50,
table_id: '1',
},
};

const record: AddressMudRecordsItem = {
decoded: {
abiEncodedFieldNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000006706c617965720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000974696d657374616d700000000000000000000000000000000000000000000000',
abiEncodedKeyNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000026964000000000000000000000000000000000000000000000000000000000000',
goldCosts: [ '100000', '150000', '200000', '250000', '400000', '550000', '700000' ],
prototypeIds: [
'0x53776f7264736d616e0000000000000000000000000000000000000000000000',
'0x50696b656d616e00000000000000000000000000000000000000000000000000',
'0x50696b656d616e00000000000000000000000000000000000000000000000000',
'0x4172636865720000000000000000000000000000000000000000000000000000',
'0x4b6e696768740000000000000000000000000000000000000000000000000000',
],
keySchema: '0x002001001f000000000000000000000000000000000000000000000000000000',
tableId: '0x6f74000000000000000000000000000044726177557064617465000000000000',
valueSchema: '0x00540300611f1f00000000000000000000000000000000000000000000000000',
},
id: '0x007a651a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007',
is_deleted: false,
timestamp: '2024-05-09T15:14:32.000000Z',
};

export const mudRecords: AddressMudRecords = {
items: [ record, record ],
next_page_params: {
items_count: 50,
key0: '1',
key1: '2',
key_bytes: '3',
},
schema: {
key_names: [ 'tableId' ],
key_types: [ 'bytes32' ],
value_names: [ 'prototypeIds', 'goldCosts', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ],
value_types: [ 'bytes32[]', 'int32[]', 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ],
},
table: table1,
};

export const mudRecord: AddressMudRecord = {
record,
schema: mudRecords.schema,
table: table1,
};
Loading

0 comments on commit c4ae053

Please sign in to comment.