fix(explorer): improve chain event tx view for contract calls (#5243)
This commit is contained in:
parent
a070504d2e
commit
96658134ee
@ -15,6 +15,7 @@ import isUndefined from 'lodash/isUndefined';
|
|||||||
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
|
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
|
||||||
import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal';
|
import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal';
|
||||||
import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist';
|
import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist';
|
||||||
|
import { TxDetailsContractCall } from './tx-contract-call';
|
||||||
|
|
||||||
interface ChainEventProps {
|
interface ChainEventProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@ -38,7 +39,7 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { builtin, erc20, erc20Multisig, stakingEvent } =
|
const { builtin, erc20, erc20Multisig, stakingEvent, contractCall } =
|
||||||
txData.command.chainEvent;
|
txData.command.chainEvent;
|
||||||
|
|
||||||
// Builtin Asset events
|
// Builtin Asset events
|
||||||
@ -140,6 +141,10 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contractCall) {
|
||||||
|
return <TxDetailsContractCall contractCall={contractCall} />;
|
||||||
|
}
|
||||||
|
|
||||||
// If we hit this return, tx-shared-details should give a basic overview
|
// If we hit this return, tx-shared-details should give a basic overview
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
import { decodeEthCallResult } from './tx-contract-call';
|
||||||
|
import { base64 } from 'ethers/lib/utils';
|
||||||
|
import { defaultAbiCoder } from '@ethersproject/abi';
|
||||||
|
import { BigNumber } from '@ethersproject/bignumber';
|
||||||
|
|
||||||
|
describe('decodeEthCallResult', () => {
|
||||||
|
it('should decode contractData correctly (mocked)', () => {
|
||||||
|
const mockContractData = base64.encode(
|
||||||
|
defaultAbiCoder.encode(['int256'], [BigNumber.from(123)])
|
||||||
|
);
|
||||||
|
const result = decodeEthCallResult(mockContractData);
|
||||||
|
expect(result).toBe('123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should decode contractData correctly (known data)', () => {
|
||||||
|
const mockContractData = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADH8cueyY=';
|
||||||
|
const result = decodeEthCallResult(mockContractData);
|
||||||
|
expect(result).toBe('3435020581670');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "-" when an error occurs', () => {
|
||||||
|
const mockContractData = 'invalid_data';
|
||||||
|
const result = decodeEthCallResult(mockContractData);
|
||||||
|
expect(result).toBe('-');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,88 @@
|
|||||||
|
import { TableCell, TableRow } from '../../../table';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import {
|
||||||
|
EthExplorerLink,
|
||||||
|
EthExplorerLinkTypes,
|
||||||
|
} from '../../../links/eth-explorer-link/eth-explorer-link';
|
||||||
|
import type { components } from '../../../../../types/explorer';
|
||||||
|
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
||||||
|
import { BigNumber } from 'ethers';
|
||||||
|
import OracleLink from '../../../links/oracle-link/oracle-link';
|
||||||
|
import { useExplorerOracleSpecByIdQuery } from '../../../../routes/oracles/__generated__/Oracles';
|
||||||
|
import { OracleEthSource } from '../../../../routes/oracles/components/oracle-eth-source';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the b64/ABIcoded result from an eth cal
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function decodeEthCallResult(contractData: string): string {
|
||||||
|
try {
|
||||||
|
const rawResult = defaultAbiCoder.decode(
|
||||||
|
['int256'],
|
||||||
|
base64.decode(contractData)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Finally, convert the resulting BigNumber in to a string
|
||||||
|
const res = BigNumber.from(rawResult[0]).toString();
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TxDetailsContractCallProps {
|
||||||
|
contractCall: components['schemas']['vegaEthContractCallEvent'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TxDetailsContractCall = ({
|
||||||
|
contractCall,
|
||||||
|
}: TxDetailsContractCallProps) => {
|
||||||
|
const { data } = useExplorerOracleSpecByIdQuery({
|
||||||
|
variables: {
|
||||||
|
id: contractCall.specId || '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!contractCall || !contractCall.result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{contractCall.specId && (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('Oracle')}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<OracleLink
|
||||||
|
id={contractCall.specId}
|
||||||
|
hasSeenOracleReports={true}
|
||||||
|
status={data?.oracleSpec?.dataSourceSpec.spec.status || '-'}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
{contractCall.blockHeight && (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('ETH block')}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<EthExplorerLink
|
||||||
|
id={contractCall.blockHeight}
|
||||||
|
type={EthExplorerLinkTypes.block}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
{data?.oracleSpec?.dataSourceSpec && (
|
||||||
|
<OracleEthSource
|
||||||
|
sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('Result')}</TableCell>
|
||||||
|
<TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -1,51 +1,11 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableWithTbody } from '../../table';
|
import { TableWithTbody } from '../../table';
|
||||||
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
|
||||||
import { ChainEvent } from './chain-events';
|
import { ChainEvent } from './chain-events';
|
||||||
import { BigNumber } from 'ethers';
|
|
||||||
|
|
||||||
import type { AbiType } from '../../../lib/encoders/abis/abi-types';
|
|
||||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||||
|
|
||||||
interface AbiOutput {
|
|
||||||
type: AbiType;
|
|
||||||
internalType: AbiType;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decodes the b64/ABIcoded result from an eth cal
|
|
||||||
* @param data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function decodeEthCallResult(
|
|
||||||
data: BlockExplorerTransactionResult
|
|
||||||
): string {
|
|
||||||
const ethResult = data.command.chainEvent?.contractCall.result;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode the result string: base64 => uint8array
|
|
||||||
const data = base64.decode(ethResult);
|
|
||||||
|
|
||||||
// Parse the escaped ABI in to an object
|
|
||||||
const abi = JSON.parse(
|
|
||||||
'[{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]'
|
|
||||||
);
|
|
||||||
// Pull the expected types out of the Oracles ABI
|
|
||||||
const types: AbiType[] = abi[0].outputs.map((o: AbiOutput) => o.type);
|
|
||||||
|
|
||||||
const rawResult = defaultAbiCoder.decode(types, data);
|
|
||||||
|
|
||||||
// Finally, convert the resulting BigNumber in to a string
|
|
||||||
const res = BigNumber.from(rawResult[0]).toString();
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TxDetailsChainEventProps {
|
interface TxDetailsChainEventProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
pubKey: string | undefined;
|
pubKey: string | undefined;
|
||||||
|
Loading…
Reference in New Issue
Block a user