Skip to content

Commit

Permalink
Support custom MUD system ABI (#2296)
Browse files Browse the repository at this point in the history
* MUD system tab placeholder

* implement MUD system selector and fix proxy contract read/write

* rollback changes of target contract address
  • Loading branch information
tom2drum authored Oct 28, 2024
1 parent 9337b03 commit b5d6ec9
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 110 deletions.
14 changes: 14 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ import type {
SmartContract,
SmartContractVerificationConfigRaw,
SmartContractSecurityAudits,
SmartContractMudSystemsResponse,
SmartContractMudSystemInfo,
} from 'types/api/contract';
import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts';
import type {
Expand Down Expand Up @@ -767,6 +769,16 @@ export const RESOURCES = {
pathParams: [ 'hash' as const, 'table_id' as const, 'record_id' as const ],
},

contract_mud_systems: {
path: '/api/v2/mud/worlds/:hash/systems',
pathParams: [ 'hash' as const ],
},

contract_mud_system_info: {
path: '/api/v2/mud/worlds/:hash/systems/:system_address',
pathParams: [ 'hash' as const, 'system_address' as const ],
},

// arbitrum L2
arbitrum_l2_messages: {
path: '/api/v2/arbitrum/messages/:direction',
Expand Down Expand Up @@ -1195,6 +1207,8 @@ 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 :
Q extends 'contract_mud_systems' ? SmartContractMudSystemsResponse :
Q extends 'contract_mud_system_info' ? SmartContractMudSystemInfo :
Q extends 'address_epoch_rewards' ? AddressEpochRewardsResponse :
Q extends 'withdrawals' ? WithdrawalsResponse :
Q extends 'withdrawals_counters' ? WithdrawalsCounters :
Expand Down
34 changes: 32 additions & 2 deletions lib/hooks/useContractTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import useSocketChannel from 'lib/socket/useSocketChannel';
import * as stubs from 'stubs/contract';
import ContractCode from 'ui/address/contract/ContractCode';
import ContractMethodsCustom from 'ui/address/contract/methods/ContractMethodsCustom';
import ContractMethodsMudSystem from 'ui/address/contract/methods/ContractMethodsMudSystem';
import ContractMethodsProxy from 'ui/address/contract/methods/ContractMethodsProxy';
import ContractMethodsRegular from 'ui/address/contract/methods/ContractMethodsRegular';
import { divideAbiIntoMethodTypes } from 'ui/address/contract/methods/utils';
import ContentLoader from 'ui/shared/ContentLoader';

const CONTRACT_TAB_IDS = [
'contract_code',
Expand All @@ -24,6 +26,7 @@ const CONTRACT_TAB_IDS = [
'write_contract_rpc',
'write_proxy',
'write_custom_methods',
'mud_system',
] as const;

interface ContractTab {
Expand All @@ -37,7 +40,7 @@ interface ReturnType {
isLoading: boolean;
}

export default function useContractTabs(data: Address | undefined, isPlaceholderData: boolean): ReturnType {
export default function useContractTabs(data: Address | undefined, isPlaceholderData: boolean, hasMudTab?: boolean): ReturnType {
const [ isQueryEnabled, setIsQueryEnabled ] = React.useState(false);

const router = useRouter();
Expand Down Expand Up @@ -65,6 +68,15 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder
},
});

const mudSystemsQuery = useApiQuery('contract_mud_systems', {
pathParams: { hash: data?.hash },
queryOptions: {
enabled: isEnabled && isQueryEnabled && hasMudTab,
refetchOnMount: false,
placeholderData: stubs.MUD_SYSTEMS,
},
});

const channel = useSocketChannel({
topic: `addresses:${ data?.hash?.toLowerCase() }`,
isDisabled: !isEnabled,
Expand Down Expand Up @@ -136,8 +148,26 @@ export default function useContractTabs(data: Address | undefined, isPlaceholder
/>
),
},
hasMudTab && {
id: 'mud_system' as const,
title: 'MUD System',
component: mudSystemsQuery.isPlaceholderData ?
<ContentLoader/> :
<ContractMethodsMudSystem items={ mudSystemsQuery.data?.items ?? [] }/>,
},
].filter(Boolean),
isLoading: contractQuery.isPlaceholderData,
};
}, [ contractQuery, channel, data?.hash, verifiedImplementations, methods.read, methods.write, methodsCustomAbi.read, methodsCustomAbi.write ]);
}, [
contractQuery,
channel,
data?.hash,
methods.read,
methods.write,
methodsCustomAbi.read,
methodsCustomAbi.write,
verifiedImplementations,
mudSystemsQuery,
hasMudTab,
]);
}
13 changes: 11 additions & 2 deletions stubs/contract.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { SmartContract } from 'types/api/contract';
import type { SmartContract, SmartContractMudSystemsResponse } from 'types/api/contract';
import type { VerifiedContract, VerifiedContractsCounters } from 'types/api/contracts';

import type { SolidityScanReport } from 'lib/solidityScan/schema';

import { ADDRESS_PARAMS } from './addressParams';
import { ADDRESS_PARAMS, ADDRESS_HASH } from './addressParams';

export const CONTRACT_CODE_UNVERIFIED = {
creation_bytecode: '0x60806040526e',
Expand Down Expand Up @@ -98,3 +98,12 @@ export const SOLIDITY_SCAN_REPORT: SolidityScanReport = {
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
},
};

export const MUD_SYSTEMS: SmartContractMudSystemsResponse = {
items: [
{
name: 'sy.AccessManagement',
address: ADDRESS_HASH,
},
],
};
16 changes: 16 additions & 0 deletions types/api/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,19 @@ export type SmartContractSecurityAuditSubmission = {
'audit_publish_date': string;
'comment'?: string;
}

// MUD SYSTEM

export interface SmartContractMudSystemsResponse {
items: Array<SmartContractMudSystemItem>;
}

export interface SmartContractMudSystemItem {
address: string;
name: string;
}

export interface SmartContractMudSystemInfo {
name: string;
abi: Abi;
}
4 changes: 3 additions & 1 deletion ui/address/contract/methods/ContractAbi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ interface Props {
abi: Array<SmartContractMethod>;
addressHash: string;
tab: string;
sourceAddress?: string;
}

const ContractAbi = ({ abi, addressHash, tab }: Props) => {
const ContractAbi = ({ abi, addressHash, sourceAddress, tab }: Props) => {
const [ expandedSections, setExpandedSections ] = React.useState<Array<number>>(abi.length === 1 ? [ 0 ] : []);
const [ id, setId ] = React.useState(0);

Expand Down Expand Up @@ -61,6 +62,7 @@ const ContractAbi = ({ abi, addressHash, tab }: Props) => {
id={ id }
index={ index }
addressHash={ addressHash }
sourceAddress={ sourceAddress }
tab={ tab }
onSubmit={ handleFormSubmit }
/>
Expand Down
6 changes: 4 additions & 2 deletions ui/address/contract/methods/ContractAbiItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ interface Props {
index: number;
id: number;
addressHash: string;
sourceAddress?: string;
tab: string;
onSubmit: FormSubmitHandler;
}

const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props) => {
const ContractAbiItem = ({ data, index, id, addressHash, sourceAddress, tab, onSubmit }: Props) => {
const [ attempt, setAttempt ] = React.useState(0);

const url = React.useMemo(() => {
Expand All @@ -36,10 +37,11 @@ const ContractAbiItem = ({ data, index, id, addressHash, tab, onSubmit }: Props)
query: {
hash: addressHash ?? '',
tab,
...(sourceAddress ? { source_address: sourceAddress } : {}),
},
hash: data.method_id,
});
}, [ addressHash, data, tab ]);
}, [ addressHash, data, tab, sourceAddress ]);

const handleCopyLinkClick = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
Expand Down
66 changes: 0 additions & 66 deletions ui/address/contract/methods/ContractImplementationAddress.tsx

This file was deleted.

8 changes: 5 additions & 3 deletions ui/address/contract/methods/ContractMethods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ interface Props {
isLoading?: boolean;
isError?: boolean;
type: MethodType;
sourceAddress?: string;
}

const ContractMethods = ({ abi, isLoading, isError, type }: Props) => {
const ContractMethods = ({ abi, isLoading, isError, type, sourceAddress }: Props) => {

const router = useRouter();

Expand All @@ -32,10 +33,11 @@ const ContractMethods = ({ abi, isLoading, isError, type }: Props) => {
}

if (abi.length === 0) {
return <span>No public { type } functions were found for this contract.</span>;
const typeText = type === 'all' ? '' : type;
return <span>No public { typeText } functions were found for this contract.</span>;
}

return <ContractAbi abi={ abi } tab={ tab } addressHash={ addressHash }/>;
return <ContractAbi abi={ abi } tab={ tab } addressHash={ addressHash } sourceAddress={ sourceAddress }/>;
};

export default React.memo(ContractMethods);
68 changes: 68 additions & 0 deletions ui/address/contract/methods/ContractMethodsMudSystem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';

import type { SmartContractMudSystemItem } from 'types/api/contract';

import useApiQuery from 'lib/api/useApiQuery';
import getQueryParamString from 'lib/router/getQueryParamString';

import ContractConnectWallet from './ContractConnectWallet';
import ContractMethods from './ContractMethods';
import type { Item } from './ContractSourceAddressSelector';
import ContractSourceAddressSelector from './ContractSourceAddressSelector';
import { enrichWithMethodId, isMethod } from './utils';

interface Props {
items: Array<SmartContractMudSystemItem>;
}

const ContractMethodsMudSystem = ({ items }: Props) => {

const router = useRouter();

const addressHash = getQueryParamString(router.query.hash);
const contractAddress = getQueryParamString(router.query.source_address);

const [ selectedItem, setSelectedItem ] = React.useState(items.find((item) => item.address === contractAddress) || items[0]);

const systemInfoQuery = useApiQuery('contract_mud_system_info', {
pathParams: { hash: addressHash, system_address: selectedItem.address },
queryOptions: {
enabled: Boolean(selectedItem?.address),
refetchOnMount: false,
},
});

const handleItemSelect = React.useCallback((item: Item) => {
setSelectedItem(item as SmartContractMudSystemItem);
}, []);

if (items.length === 0) {
return <span>No MUD System found for this contract.</span>;
}

const abi = systemInfoQuery.data?.abi?.filter(isMethod).map(enrichWithMethodId) || [];

return (
<Box>
<ContractConnectWallet/>
<ContractSourceAddressSelector
items={ items }
selectedItem={ selectedItem }
onItemSelect={ handleItemSelect }
label="System address"
/>
<ContractMethods
key={ selectedItem.address }
abi={ abi }
isLoading={ systemInfoQuery.isPending }
isError={ systemInfoQuery.isError }
sourceAddress={ selectedItem.address }
type="all"
/>
</Box>
);
};

export default React.memo(ContractMethodsMudSystem);
Loading

0 comments on commit b5d6ec9

Please sign in to comment.