Skip to content

Commit

Permalink
feat: detect contract addresses that are Gnosis Safe (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
CJ42 authored Jun 14, 2024
1 parent b45a447 commit e1a3e0a
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 5 deletions.
17 changes: 14 additions & 3 deletions components/AddressInfos/AddressInfos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
LSP1_GRAVE_FORWARDER,
UP_RECOVERY_ADDRESSES,
} from '../../globals';
import { checkInterface, getData } from '../../utils/web3';
import { checkInterface, getData, checkIsGnosisSafe } from '../../utils/web3';

import LSP7Artifact from '@lukso/lsp-smart-contracts/artifacts/LSP7DigitalAsset.json';
import { AbiItem } from 'web3-utils';
Expand Down Expand Up @@ -125,6 +125,7 @@ const AddressInfos: React.FC<Props> = ({ assetAddress, userAddress = '' }) => {
const [assetName, setAssetName] = useState('');
const [assetSymbol, setAssetSymbol] = useState('');
const [isLSP1GraveForwarder, setIsLSP1GraveForwarder] = useState(false);
const [isGnosisSafe, setIsGnosisSafe] = useState(false);

const checkAddressInterface = async (_address: string) => {
if (!web3 || !_address) {
Expand All @@ -145,6 +146,9 @@ const AddressInfos: React.FC<Props> = ({ assetAddress, userAddress = '' }) => {
setIsLSP7(isLsp7DigitalAsset);
setIsLSP8(isLsp8IdentifiableDigitalAsset);

const isGnosisSafeContract = await checkIsGnosisSafe(_address, web3);
setIsGnosisSafe(isGnosisSafeContract);

const nameBytesValue = await getData(
assetAddress,
ERC725YDataKeys.LSP4.LSP4TokenName,
Expand Down Expand Up @@ -184,7 +188,7 @@ const AddressInfos: React.FC<Props> = ({ assetAddress, userAddress = '' }) => {

const explorerLink = `${
EXPLORER_BASE_URL[network.name]
}/assetAddress/${assetAddress}`;
}/address/${assetAddress}`;

const renderTags = () => {
if (isLoading) {
Expand Down Expand Up @@ -215,7 +219,14 @@ const AddressInfos: React.FC<Props> = ({ assetAddress, userAddress = '' }) => {
text="👻 - LSP1 Grave Forwarder"
colorClass="is-danger"
isLight={true}
contractVersion="0.14.0"
/>
)}

{isGnosisSafe && (
<AddressTypeBadge
text="🥝 - Gnosis Safe"
colorClass="is-success"
isLight={true}
/>
)}

Expand Down
9 changes: 8 additions & 1 deletion globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ export const LSP1_DELEGATE_VERSIONS = {
'0x7870C5B8BC9572A8001C3f96f7ff59961B23500D': '0.14.0',
};

// Mainnet
// Mainnet only
export const LSP1_GRAVE_FORWARDER =
'0x42562196ee7AaC3E8501db777B25ffc976ed8463';

// Mainnet AND Testnet
export const GNOSIS_SAFE_IMPLEMENTATION =
'0x3E5c63644E683549055b9Be8653de26E0B4CD36E';

export const GNOSIS_SAFE_PROXY_DEPLOYED_BYTECODE =
'0x608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea2646970667358221220d1429297349653a4918076d650332de1a1068c5f3e07c5c82360c277770b955264736f6c63430007060033';
5 changes: 4 additions & 1 deletion pages/inspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,10 @@ const Home: NextPage = () => {
</li>
<li>
<strong>Contract type:</strong>{' '}
<code>ERC725-compatible</code>
<code>
{isLSP0ERC725Account && '🆙 Universal Profile'}{' '}
(ERC725-compatible)
</code>
</li>
</ul>
<AddressButtons
Expand Down
29 changes: 29 additions & 0 deletions utils/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import {
aggregateABI,
} from '../constants';
import { AbiItem } from 'web3-utils';
import {
GNOSIS_SAFE_IMPLEMENTATION,
GNOSIS_SAFE_PROXY_DEPLOYED_BYTECODE,
} from '../globals';

export const getDataBatch = async (
address: string,
Expand Down Expand Up @@ -176,3 +180,28 @@ export const getVersion = async (
return 'unknown';
}
};

export async function checkIsGnosisSafe(
address: string,
web3: Web3,
): Promise<boolean> {
const codeAt = await web3.eth.getCode(address);

if (codeAt != GNOSIS_SAFE_PROXY_DEPLOYED_BYTECODE) {
return false;
}

// in the Solidity code of the Gnosis Safe proxy, the address of the singleton (= implementation)
// is always the first declared variable. This variable is set to `internal` and does not have a getter
// to reduce deployment costs.
//
// example: https://explorer.execution.mainnet.lukso.network/address/0x14C2041eD166e00A8Ed2adad8c9C7389b3Dd87fb?tab=contract
const valueAtStorageSlot0 = await web3.eth.getStorageAt(address, 0);

const { implementationAddress } = web3.eth.abi.decodeParameter(
'address',
valueAtStorageSlot0,
);

return implementationAddress == GNOSIS_SAFE_IMPLEMENTATION;
}

0 comments on commit e1a3e0a

Please sign in to comment.