Scope: Tasks from the Binance Booster Campaign.
Goal: Let contributors independently verify that their valid data contributions were correctly, completely, and tamper-proof recorded on-chain by comparing data fingerprints for consistency.
Steps
- Open the block explorer https://basescan.org/address/0xd0A4950C096daC5Ae5A0f74Ff3eE316d74558974#readContract.
- Call the getUserRecords method.
- Enter parameters:
- user: your wallet address
- page: page index, enter 0
- size: items per page, enter 1000
- Click Query to get the result:
- rets: an array that may contain multiple records, in the shape [[submissionID, fingerprint]], where:
- submissionID: e.g., 2025082903351000xxxxxx
- fingerprint: e.g.,0x3f287de1fac99c807b8b23e67fa41b6e8547c45a7c37868f71457b1400xxxxxx
- end: whether the query is finished
- true: finished
- false: not finished — increase page and repeat until end is true. Note:
- rets: an array that may contain multiple records, in the shape [[submissionID, fingerprint]], where:
- 1 fingerprint = 1 valid contribution.
- The number of fingerprints equals the number of valid contributions recorded for that address.
Your contribution consists of X-Data and Y-Data.
- X-Data: original/raw data
- Y-Data: annotated/label data Example (food labeling task)
- X-Data
- Image (the original food image you uploaded)
- Y-Data
- Food Name
- Food Category
- Brand / Store
- Region
- Quantity of food
All of the above are user-submitted. You can reconstruct and save them yourself. Fill the template exactly as originally submitted
(replace the values in <>; if it was empty, use an empty string ""). Case, spaces, punctuation, and brackets must match exactly.
{
"brand": "<brand/store at the time; empty string if none>",
"images": [
{
"hash": "e6b024a0a5b3f202667300f3621190e666a52cadfd715664cd2e082cb0d3e03a"
}
],
"region": "the region you selected, e.g. PK",
"foodName": "the Food Name you entered at the time",
"quantity": "the exact dropdown text, e.g. Individual (1 person)",
"foodCategory": "the exact category text you selected"
}
Image hashing:
Because raw images are large, we typically include their SHA-256 hash in the fingerprint input.
- Use an online tool (drag the image to the input box to get a SHA-256 hash), or
- Compute it yourself with a script/tool of your choice.
Alongside the contribution JSON, two auxiliary fields are used to generate the fingerprint:
- Address* — your on-chain wallet address (identity).
- Quality — the rating of your contribution. After task review, the Codatta platform evaluates authenticity/completeness and assigns a rating. Only rated contributions are considered valid and recorded. For the food labeling example, ratings (high → low) are: S, A, B, C, D.
Notes & strictness
- Key names must mirror the task form exactly (e.g., foodName, foodCategory, region, quantity). Case and spelling must match the original form labels.
- Include all fields you completed on the task form. If you filled fields not listed in the template, add them with the exact original labels and values.
- Strict equality: any difference in key names / values / order (including spaces, case, punctuation) will change the fingerprint.
- If you forgot your original entries, you may look them up on the Codatta platform (if available).
Reconstruct the exact JSON from what you originally entered/uploaded in the form, then recompute the fingerprint locally using the public rules and compare it with the on-chain fingerprint, entry by entry, to complete independent verification.
- Take the self-assembled raw JSON and canonicalize it using JCS. The canonicalized string is referred to as submissionData. You can follow the https://github.com/codatta/submission-data-fingerprint/blob/main/tools/jcs.js to implement this.
Step 2 | ABI-encode the contribution data together with the auxiliary fields (Solidity) → produce encodedData
Encode the tuple using Solidity ABI. You can implement this with ethers.js; an example is shown below (running it will output encodedData).
const encodedData = ethers.AbiCoder.defaultAbiCoder().encode(
["address", "string", "string"],
[address, quality, submissionData]
);
quality accepts "S","A","B","C","D"," (use empty string "" if the task had no rating) You can follow the https://github.com/codatta/submission-data-fingerprint/blob/main/tools/fingerprint.js#L8 to implement this。
Compute it with the following ethers.js code.
const hash = ethers.keccak256(encodedData);
The calculation yields a 0x… value — that is the localFingerprint.
You can follow the https://github.com/codatta/submission-data-fingerprint/blob/main/tools/fingerprint.js#L14 to implement this。
Using the on-chain fingerprint obtained in Step 2.1, compare it with localFingerprint:
Result semantics
- localFingerprint == onChainFingerprint → Verification successful This JSON matches the fingerprint recorded on-chain; your data has not been tampered with.
- No on-chain fingerprint found for the specified address / submission → This address has no contribution attested on-chain.
- localFingerprint != onChainFingerprint → Verification failed A step may be incorrect; please recheck your JSON/address/quality or contact support.
When your locally recomputed fingerprints match the on-chain records entry by entry, it proves that all corresponding contributions have been completely, correctly, and immutably recorded on-chain.