hypercerts-org / hypercerts

Hypercerts are a tool to build scalable retrospective reward systems for impact.
https://hypercerts.org/
Other
92 stars 28 forks source link

Decide on hypercert claim schema #98

Closed ryscheng closed 1 year ago

ryscheng commented 1 year ago

This is what we have right now https://github.com/Network-Goods/hypercerts-sdk/blob/main/types/claimdata.d.ts

This is what's written in the paper https://docs.google.com/document/d/1456v3OWTnkQXQIo4lmud7b2qOXqsYwQo1EuN-GbsJU8/edit?disco=AAAAmz4v61w

Related to what is optional/required? https://github.com/Network-Goods/hypercerts/issues/96

We should decide on the schema soon.

holkexyz commented 1 year ago

Just to clarify (are these statements correct?):

Technical questions, esp. @ryscheng

Non-technical questions, esp. @ccerv1

ryscheng commented 1 year ago

Is it currently possible for the end of the "time of impact" to be infinity?

Yes, we currently use the value 0 to denote unspecified

Is there a technical hurdle to include the positive/negative boolean for the scopes?

That's easy to add, but can you clarify, this is for the minter to specify if this is a positive or negative contribution or impact? Shouldn't we leave that to the evaluation?

The scope tags aren't currently ordered lists, right?

It's an array right now, so they're ordered.

What does "unknown" stand for on the contributor implementation?

Contributors are an array of strings. The unknown refers to any number of additional fields we may want to add in the future, but don't want to specify it quite yet.

ryscheng commented 1 year ago

Clarified with Holke. by positive and negative, I think what he means is include vs exclude. e.g. Include "IPFS" but exclude "kubo implementation"

ccerv1 commented 1 year ago

I've created a draft metadata spec. You can view/comment on it here.

I'm also copying the most relevant parts below for easy access:

ERC-1155 properties

Property Description
name Name of the hypercert. Given that a project may create numerous hypercerts over time, consider giving the hypercert a name that represents a discrete phase or output.
external_url [optional] A URL that can be displayed next to the hypercert on webpages like OpenSea and links users to a website that has more information about the project or impact claim.
description A human readable description of the hypercert. Markdown is supported. Consider adding additional external URLs (e.g. to social media profiles) to the description.
image A URI pointing to a resource with mime type image/* that represents the hypercert's artwork, i.e. ipfs://<CID>. We recommend images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.
background_color [optional] Background color of the item for display on OpenSea. Must be a six-character hexadecimal without a pre-pended #.
properties The unique dimensions of the hypercert's impact claim (see below).

In order to perform hypercert-specific operations, including split and merge functions, and for your hypercert to robustly claim a set of coordinates in the impact space, there are six additional properties that must be included in your metadata.

Hypercert-specific properties

Property Description
impact_scope An ordered list of impact scope tags. The - prefix may be used to indicate an impact scope that is explicitly excluded from the claim. The default claim is to "all" impact, giving the owner rights to claim all potential impact created by the project.
work_scope An ordered list of work scope tags. The - prefix may be used to indicate a work scope that is explicitly excluded from the claim.
work_timeframe Date range from the start to the end of the work.
impact_timeframe Date range from the start to the end of the impact. The default claim is from the start date of work until forever.
contributors An ordered list of contributors. Contributors may be itemized as wallet addresses, ENS names, names / pseudonyms, or organizations / teams. The default claim is to the wallet address that created the hypercert contract.
rights An unordered list of usage rights tags. The default claim is solely to "public display" of the hypercert, i.e. all other rights remain with the contributors.
collection [optional] The name of a collection of related hypercerts that this hypercert belongs to. Purely for display and indexing purposes.

Here is an example of a complete set of properties included in a hypercert's metadata:

"properties": {
    "array_property": {
        "name": "Impact Scope",
        "value": ["Decentralized web infrastructure"],
        "display_value": "Decentralized web infrastructure"
    },
    "array_property": {
        "name": "Work Scope",
        "value": ["IPFS", "-go-ipfs"],
        "display_value": "IPFS (excludes go-ipfs)"
    },
    "array_property": {
        "name": "Work Timeframe",
        "value": [1380585600, 1388534399],
        "display_value": "2013-10-01 to 2013-12-31"
    },
    "array_property": {
        "name": "Impact Timeframe",
        "value": [1380585600, 0],
        "display_value": "2013-10-01 to Forever"
    },
    "array_property": {
        "name": "Contributors",
        "value": ["0xa1fa1fa000000000000000000000000000000000", "ipfsinventor.eth" , "idonthaveawallet@email.com", "Phil the Corgi"],
        "display_value": "0xa1fa1fa000000000000000000000000000000000, ipfsinventor.eth, and 2 others"
    },
    "array_property": {
        "name": "Rights",
        "value": ["public-display", "-transfers"],
        "display_value": "Public display"
    }
}

See also:

holkexyz commented 1 year ago

@bitbeckers @ryscheng we are not using the global list of scope tags anymore (as previously implemented), right?

bitbeckers commented 1 year ago

Nope, there is nothing really defined at the moment

holkexyz commented 1 year ago

Does the - prefix do the job to negate scopes? Or would it be easier to have an array with an attribute that is a boolean (splitting the scope tag from the positive or negative boolean)? Also thinking about data analytics later on

bitbeckers commented 1 year ago

Depends on the input. If it's manual, and somebody fat-fingers a space, suddenly 'public-display' becomes 'public' and '-display'. Or parsing mishaps?

Defining two seperate structures might be safer.

Would the input options be limited as well? As in, will it be drop-down/autofill or is it a textual greenfield? Because that kinda underlines or solves the point on typos.

ccerv1 commented 1 year ago

Would the input options be limited as well? As in, will it be drop-down/autofill or is it a textual greenfield? Because that kinda underlines or solves the point on typos.

Yes the current UI will be drop-down/autofill. Open question as to whether we want a drop-down for includes and a separate one for excludes. I'd like to see how this affects the user experience though. (There are already a lot of fields in the form.)

Defining two seperate structures might be safer.

So turning work_scope into an object with an includes array and an excludes array?

holkexyz commented 1 year ago

That will depend on the frontends. Since other can create different frontends in the future, we might see both at some point. The frontend could still work with prefix -, even if we store it differently. But the UI should prevent accidental negations.

I like the data structure better that makes it more explicit, but ultimately that should be a technical decision.

@ccerv1 is there an advantage of "object with an includes array and an excludes array" over array<{positive_or_negative: boolean, scope_tag: string}>?

ryscheng commented 1 year ago

@holkeb I think array<{positive_or_negative: boolean, scope_tag: string}> makes it easier to interpret. Basically you define the scope reading the array in order. e.g. yes IPFS, not kubo, yes kubo financial support.

ryscheng commented 1 year ago

One factor to keep in mind, is that we want to make sure it looks right on OpenSea. properties should show up as attributes on OpenSea. (see https://docs.opensea.io/docs/metadata-standards)

So 3 followup questions:

Worth experimenting a bit to see before we finalize

CC @bitbeckers

bitbeckers commented 1 year ago

@ryscheng we can experiment with this. If we go for traits like the rich properties facilitate we have a lot to play with : https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema

Do note the 'Array property' that displays a single value. If that is correct, we should split the positives and negatives in two separate arrays.

re: Does it make sense to hide collection and allowlist_uri somewhere else in the metadata so it doesn't show up in the NFT attributes? We could put them on the top level instead of the properties, but eventually the display is compiled by the front-end so we don't have control over that. Also, if it's a collection I think OpenSea can handle similar traits between tokens within the same contract. So that could work in our advantage.

ccerv1 commented 1 year ago

Update: I've worked through the metadata schema and moved allowlist and collection to optional properties at the end of the doc: https://hackmd.io/@ccerv1/hypercerts-metadata.

I agree with @bitbeckers that it would be helpful more often than not for the collection to display on OpenSea.

Next step: let's play with some different json blob formats (fields in properties vs top level) and see what looks best.

ccerv1 commented 1 year ago

Sample JSON with some pseudo-dummy metadata:

{
    "name": "\ud83c\udfd7  Scaffold-ETH",
    "description": "\ud83c\udfd7 Scaffold-ETH is a decentralized application template.\r\n\r\n\ud83e\uddd1\u200d\ud83c\udfeb It helps new builders learn (SpeedRunEthereum.com).\r\n\r\n\ud83d\ude80 It also makes going to production faster and prototyping easier. \r\n\r\n\ud83d\udd2d Thousands of forks later, it has become the starter kit of web3 starter kits.\r\n\r\n\ud83d\udc49 https://github.com/scaffold-eth/scaffold-eth#-scaffold-eth\r\n\r\n\ud83d\udc5b Funding will go to \ud83c\udff0BuidlGuidl builders improving \ud83c\udfd7 Scaffold-ETH and providing more educational material for the Ethereum developer ecosystem!!!",
    "external_url": "https://gitcoin.co/grants/2851/scaffold-eth",
    "image": "ipfs://QmaiaFjTYmrt97Pd5jSbKq3enfE3wcSriJy96d1Fbgyfeb",
    "background_color": "412022",
    "properties": {
        "impact_scope": {
            "name": "Impact Scope",
            "value": [
                "ETH Infra"
            ],
            "display_value": "ETH Infra"
        },
        "work_scope": {
            "name": "Work Scope",
            "value": [
                "application",
                "builders",
                "builders-learn-speedrunethereumcom",
                "decentralized-application-template",
                "developer",
                "easier",
                "ethereum-developer-ecosystem",
                "production-faster",
                "prototyping-easier-thousands",
                "starter",
                "starter-kits-funding"
            ],
            "display_value": "application, builders, builders-learn-speedrunethereumcom, decentralized-application-template, developer, easier, ethereum-developer-ecosystem, production-faster, prototyping-easier-thousands, starter, starter-kits-funding"
        },
        "work_timeframe": {
            "name": "Work Timeframe",
            "value": [
                1380585600,
                1388534399
            ],
            "display_value": "2013-10-01 to 2013-12-31"
        },
        "impact_timeframe": {
            "name": "Impact Timeframe",
            "value": [
                1380585600,
                0
            ],
            "display_value": "2013-10-01 to Forever"
        },
        "contributors": {
            "name": "Contributors",
            "value": [
                "0xa1fa1fa000000000000000000000000000000000",
                "ipfsinventor.eth",
                "idonthaveawallet@email.com",
                "Phil the Corgi"
            ],
            "display_value": "0xa1fa1fa000000000000000000000000000000000, ipfsinventor.eth, and 2 others"
        },
        "rights": {
            "name": "Rights",
            "value": [
                "public-display",
                "-transfers"
            ],
            "display_value": "Public display"
        }
    },
    "attributes": {
        "collection": "Gitcoin Alpha Round",
        "allowlist": "ipfs://bafkreiaxdog4clqiitnarc4rrzpgdlcjsg6k2nr2n2t4thwklccza34ubi"
    }
}
bitbeckers commented 1 year ago

We'll set up a simple contract on Goerli for testing JSON implementations

ccerv1 commented 1 year ago

I played around with metadata properties, and the following schema works, though I have some concerns with it.

View it on OpenSea here

Metadata here

And the JSON copied for discussion:

{
    "name": "Test Project",
    "description": "## Hypercerts\n\n \u26a1\u26a1 Hypercerts: A new primitive for impact funding systems\n\nHypercerts are an interoperable data layer for impact funding mechanisms.\n\nA single hypercert is a semi-fungible token that accounts for work that is supposed to be impactful whose ownership is fractionizable and transferable (under specific conditions).\n\nHypercerts don’t impose any specific funding mechanisms, but provide baseline invariant guarantees such that claims will not be forgotten as different mechanisms come into and out of fashion. This is also why hypercerts are especially useful for any retrospective funding mechanisms – funding can be applied to claims of the past.",
    "external_url": "https://hypercerts.xyz/",
    "image": "ipfs://bafkreib2qqcsf7si3rqivgpekniyjkz3q2txvao2pll4zi26e4mv7q6yde",
    "background_color": "00A9B7",
    "attributes": {
        "collection": "Gitcoin Alpha Round",
        "impact_scope": {
            "name": "Impact Scope",
            "value": [
                "hypercerts protocol"
            ],
            "display_value": "hypercerts protocol"
        },
        "work_scope": {
            "name": "Work Scope",
            "value": [
                "metadata experimentation",
                "goerli testnet", 
                "alpha launch prep"
            ],
            "display_value": "metadata experimentation, goerli testnet, alpha launch prep"
        },
        "work_timeframe": {
            "name": "Work Timeframe",
            "value": [
                1380585600,
                1388534399
            ],
            "display_value": "2013-10-01 to 2013-12-31"
        },
        "impact_timeframe": {
            "name": "Impact Timeframe",
            "value": [
                1380585600,
                0
            ],
            "display_value": "2013-10-01 to Forever"
        },
        "contributors": {
            "name": "Contributors",
            "value": [
                "0xa1fa1fa000000000000000000000000000000000",
                "ipfsinventor.eth",
                "idonthaveawallet@email.com",
                "Phil the Corgi"
            ],
            "display_value": "0xa1fa1fa000000000000000000000000000000000, ipfsinventor.eth, and 2 others"
        },
        "rights": {
            "name": "Rights",
            "value": [
                "public-display",
                "-transfers"
            ],
            "display_value": "Public display"
        },
        "allowlist": "ipfs://bafkreiaxdog4clqiitnarc4rrzpgdlcjsg6k2nr2n2t4thwklccza34ubi"
    }
}

Image

ryscheng commented 1 year ago

Can we add a version number somewhere? In case we change the schema in the future?

ccerv1 commented 1 year ago

Here's a new proposed schema, updated per latest discussions with @holkeb and incorporating davidad's feedback.

Changes:

{
    "name": "Project Name",
    "description": "add your markdown formatted text here",
    "external_url": "https://hypercerts.xyz",
    "image": "ipfs://bafkreicchjbpbb2hfcg5mtmlz3zktf2wt5dnux2rzx33ta7b6bhrozlbgi",
    "hypercert": {
        "impact_scope": {
            "name": "Impact Scope",
            "value": [],
            "display_value": "All",
        },
        "work_scope": {
            "name": "Work Scope",
            "value": ["Project Name"],
            "display_value": "Project Name",
        },
        "work_timeframe": {
            "name": "Work Timeframe",
            "value": [1663819200, 1673163072],
            "display_value": "2022-09-22 \u2192 2023-01-08"
        },
        "impact_timeframe": {
            "name": "Impact Timeframe",
            "value": [1673163072, 0],
            "display_value": "2023-01-08 \u2192 Indefinite"
        },
        "contributors": {
            "name": "Contributors",
            "value": ["0x799B774204A348E1182fE01074C51444bA70A149"],
            "display_value": "0x799...149"
        },
        "rights": {
            "name": "Rights",
            "value": ["public-display", "-transfers"],
            "display_value": "Public display"
        }
    },
    "creator_properties": {
        "tags": ["Gitcoin", "Alpha Round", "Open Source Software"],
        "icon": "ipfs://bafkreigdv5xynidonbbjmfeqiamlbbg3ae42zz5ckd3zbuibicziddnc7y",
        "banner": "ipfs://bafybeicecz5fdbadlzyx4gfzwqaxpm2ngvqrkhybuizkwkd5wmm2wlixam",
        "allowlist": "ipfs://bafkreiaxdog4clqiitnarc4rrzpgdlcjsg6k2nr2n2t4thwklccza34ubi"
    },
    "version": "1.0.0"
}
ryscheng commented 1 year ago

Thanks! this is helpful, couple questions:

  1. I'm curious why OpenSea won't display properties? I'm trying to reconcile this comment with the screenshot above.
  2. I'm curious if it makes sense to use an explicit "any" or "all" impact scope? An empty array to me implies a null/empty set, rather than everything to me...
holkexyz commented 1 year ago

on 2.: Yes, we can use an explicit "all"

bitbeckers commented 1 year ago

@ryscheng my understanding is that OpenSea prioritises attributes over properties and that either route supports different patterns: https://docs.opensea.io/docs/metadata-standards. But, I haven't gone through the itterations like Carl did.

@ccerv1 how is the metadata rendered on non-OpenSea platforms like Rarible and Nifty Gateway? The reason I ask is to prevent optimising for OpenSea.

ccerv1 commented 1 year ago

OpenSea's documentation is very vague on how this works.

For instance, this metadata displays correctly (here) but doesn't show any properties.

@ccerv1 how is the metadata rendered on non-OpenSea platforms like Rarible and Nifty Gateway? The reason I ask is to prevent optimising for OpenSea.

I haven't tried other platforms. I'm going to keep playing with this today. I also have all the draft hypercerts artwork combos from Sascha to experiment with.

Anyway, I'd like this issue to be done and dusted by Monday morning.

ccerv1 commented 1 year ago

Alright! I think we finally have it!

This is what it looks like on OpenSea: https://testnets.opensea.io/assets/goerli/0xffb1fbff7a40ea441aeae8036812af116f593995/57

Updates:

{
    "name": "Example Hypercert",
    "description": "This is where the description of the hypercert will go.",
    "external_url": "https://hypercerts.xyz",
    "image": "ipfs://bafybeifs7abhcooeelyjxmnlrcd5kuupfl5czhtyub2imzxzccrhzz3bem",
    "version": "1.0.0",
    "properties": [
        {
            "trait_type": "Example Property 1", 
            "value": "Some text here"
        },
        {
            "trait_type": "Example Property 2", 
            "value": "More text here"
        }
    ],
    "hypercert": {
        "impact_scope": {
            "name": "Impact Scope",
            "value": ["all"],
            "display_value": "All"
        },
        "work_scope": {
            "name": "Work Scope",
            "value": ["art design", "metadata standards"],
            "display_value": "Art Design & Metadata Standards"
        },
        "work_timeframe": {
            "name": "Work Timeframe",
            "value": [1663819200, 1673163072],
            "display_value": "2022-09-22 \u2192 2023-01-08"
        },
        "impact_timeframe": {
            "name": "Impact Timeframe",
            "value": [1673163072, 0],
            "display_value": "2023-01-08 \u2192 Indefinite"
        },
        "contributors": {
            "name": "Contributors",
            "value": ["0x799B774204A348E1182fE01074C51444bA70A149"],
            "display_value": "0x799...149"
        },
        "rights": {
            "name": "Rights",
            "value": ["public display", "¬transfers"],
            "display_value": "Public display"
        }
    }
}
holkexyz commented 1 year ago

@ccerv1 how do we currently deal with time in the date range?

Comment from davidad:

Although I don’t want to make people fiddle with a time picker or have to think about time zones, I do think we ought to standardize by fiat that the start date is implicitly attributed 00:00:00 UTC and the end date is implicitly attributed 23:59:59 UTC. If hypercerts are successful, this will eventually become relevant for a dispute at least once, and it will be really good to have a pre-existing official answer that makes sense

ryscheng commented 1 year ago

From our conversation today, I think the most recent iteration is mostly it, the only thing we're missing is a

  excludes: ["this", "that"],

that should be included with rights, impact_scopes, and work_scopes.

holkexyz commented 1 year ago

@ccerv1 Regarding the display value of the conjunctive clauses

Option 2 really would indicate a conjunctive clause. I know that many won't really know what to do with it, but I also believe it isn't distracting. Especially as we include negative terms, this makes more sense than the "&"

ccerv1 commented 1 year ago

@ryscheng I have created explicit includes/excludes list for those fields. @holkeb I have gone with the Option 2 for the display notation but with the kebab case that you had in the whitepaper, ie, "Art-Design ∧ Metadata-Standards" or "Public-Display ∧ ¬ Transfers"

The metadata for such a property would be as follows:

{
    "name": "Rights",
    "includes": ["Public Display"],
    "excludes": ["Transfers"],
    "value": ["Public Display", "¬Transfers"],
    "display_value": "Public-Display ∧ ¬Transfers"
}
ccerv1 commented 1 year ago

Adding an example of a project's metadata here

ryscheng commented 1 year ago

Here is the current schema https://github.com/Network-Goods/hypercerts-sdk/blob/main/src/types/metadata.d.ts https://github.com/Network-Goods/hypercerts-sdk/blob/main/src/types/claimdata.d.ts

Currently we just have values and excludes, where values is implicitly just the includes. In your example, it'd look like this:

{
    "name": "Rights",
    "value": ["Public Display"],
    "excludes": ["Transfers"],
    "display_value": "Public-Display ∧ ¬Transfers"
}

In the interest of not having this issue turn into a forever issue where we are constantly just evolving the schema, I'm going to close this out. CC @ccerv1 @holkeb If this is okay with you, we can leave it closed.

If you prefer one of the following options, feel free to open a new issue:

  1. having includes, excludes AND values. - I'm generally not in favor of this, redundant data usually means inconsistent data
  2. renaming values in our current implementation to includes. This makes it more explicit and we would NOT have a values field.
ryscheng commented 1 year ago

https://github.com/Network-Goods/hypercerts-sdk/pull/30