explorer icon indicating copy to clipboard operation
explorer copied to clipboard

Ownership/Identity Metadata

Open jmank88 opened this issue 7 years ago • 7 comments

We should consider integrating ownership and identity metadata for contracts and accounts into explorer. Often this information is publicly available, but not directly associated with the chain data. When viewing a smart contract, or a public account address, explorer could display e.g. a short blurb about the owner/operator, it's purpose/function, and a link to a website. The rich list could label which of the whale accounts are actually exchange cold wallets. All of this would be opt-in only. There are a few different ways we could collect and verify this information:

  1. Manual verification (off-chain): We could simply have interested parties send us the desired info directly and provide some verification that they control the associated keys, and that data would go directly into explorer. This is a simple solution, but requires manual steps (initially and for updates) and leaves a transparency gap.

  2. Extended smart contracts (on-chain): We could define a smart contract interface for providing certain meta data fields (e.g. name, description, url) which explorer would recognize and display in the interface automatically. This would be public, transparent, updatable, and automatic, but would only work for smart contracts, not standard accounts.

  3. Custom registry contract (on-chain): We could deploy our own custom registry contract which allows users to associate metadata with both smart contracts and standard account addresses. This would be public, transparent, updatable, and automatic (just like 2) but more flexible and general.

I'm favoring 3, since it's the most flexible and backwards compatible. I think it could be as simple as a few maps with proper authentication for updates: one mapping an address to a set of metadata, and one or two more mapping contracts and accounts to those metadata keys (so that multiple can map to the same metadata w/o duplication).

jmank88 avatar Oct 02 '18 19:10 jmank88

All for this. It would be really nice to identify exchange's cold/hot wallets like Etherscan does.

cryptolarity avatar Oct 02 '18 20:10 cryptolarity

WIP contract for 3:

pragma solidity ^0.5.2;

struct ID {
  address addr;
  address owner; // Null if self-owned.
  string name;
  string url;
  string description;
}

interface Registry {
  // info identifies an address by returning the ID and the addresses of any contracts it owns.
  function info(address addr) external view returns (ID id, address[] owns);
  // register associates the caller address with the provided info and (optional) owner.
  function register(address owner, string name, string url, string description) external view returns (bool);

  // A Registration event is emitted when address information is registered.
  event Registration(ID id);
}

contract ExplorerRegistry is Registry {
  // Array of ids.
  ID[] private _id;
  // Maps addresses to their ID index.
  mapping (address => uint256) private _idx;
  // Maps owner address to sets of owned addresses.
  mapping (address => address[]) private _own;

  function info(address addr) public view returns (ID id, address[] owns) {
    idx = _idx[addr];
    if idx == 0 {
      return null, null
    }
    id = _id[idx]
    owns = _own[addr];
    return id, owns;
  }

  function infoByIdx(uint256 idx) public view returns (ID id, address[] owns) {
    if idx >= _id.length {
      return null, null
    }
    id = _id[idx]
    owns = _own[id.addr]
    return id, owns
  }

  function count() public view returns (uint256) {
    returns _id.length
  }

  // register registers this address with an owner (can be self or null) and ID info.
  //TODO limit name and description length?
  function register(address owner, string name, string url, string description) public returns (bool) {
    id = ID({addr:address(this), owner:owner, name:name, url:url, description:description});
    idx = _id[id.addr]
    if idx == 0 {
      // Create.
      idx = _ids.push(id) - 1;
      _idx[id.addr]=idx;
      if owner != null {
        _owns[owner].push(owns);
      }
    } else {
      // Update.
      oldOwner = _id[idx].owner
      _id[idx] = id
      if owner != oldOwner {
        own = _own[oldOwner];
        for (i=0;i<own.length;i++) {
          o = owns[i]
          if o.addr = id.addr {
            own[i] = own[owns.length-1]
            delete own[owns.length-1]
            own.length--
            break
          }
        }        
        _own[owner].push(owns);
      }
    }
    if owner != null {
      _own[owner].push(own);
    }

    emit Registration(owner, id);
    return true;
  }
}

Explorer could passively call info when loading pages for a specific address, and it could also crawl over infoByidx up to count periodically.

jmank88 avatar Jan 14 '19 14:01 jmank88

Interesting idea.

For 3:

  • would that be one massive smart contract for the entire chain?
  • how could you know if someone actually owns a contract?

For metadata, what if the data was an IPFS hash or URL which would hold a structured JSON object with all the data to keep on chain data small and allow for easy extensibility in the future (ie: nothing on chain would have to change and this contract interfact wouldn't have to change). This is somewhat similar to ERC721 metadata, you can read more about that here: https://docs.opensea.io/docs/2-adding-metadata

treeder avatar Jan 16 '19 14:01 treeder

would that be one massive smart contract for the entire chain?

Yes, one big registry that anyone can read/write.

how could you know if someone actually owns a contract?

Only the creator's account will be allowed to register a contract. The WIP code needs a registerContract variant which takes an additional address contract parameter to use in ID.addr, and which verifies that the caller is the creator of the contract.

I definitely like the idea of off-chain, extensible metadata. This on-chain contract data could be reduced to a single mapping (address => string). Maybe even only for accounts - with contract mappings being implicit (e.g. append /0x123 to the URL).

jmank88 avatar Jan 16 '19 15:01 jmank88

contract MetadataRegistry {
  mapping (address => string) private _uri;

  function uri(address addr) public view returns (string) {
    return _uri[addr];
  }

  function registerURI(string uri) public view returns (bool) {
    _uri[address(this)] = uri;
    emit URIRegistration(address(this), uri);
    return true;
  }
}

jmank88 avatar Jan 16 '19 15:01 jmank88

@jmank88 Is this still relevant?

bergauz avatar Mar 27 '19 10:03 bergauz

Yes, but it's just a convenience feature, not high priority.

jmank88 avatar Mar 27 '19 12:03 jmank88