-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from hypercerts-org/feat/attestation_validator
Implement attestation validator
- Loading branch information
Showing
11 changed files
with
582 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// https://github.com/hypercerts-org/hypercerts/blob/7671d06762c929bc2890a31e5dc392f8a30065c6/contracts/test/foundry/protocol/Bitshifting.t.sol | ||
|
||
/** | ||
* The maximum value that can be represented as an uint256. | ||
* @type {BigInt} | ||
*/ | ||
const MAX = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); | ||
|
||
/** | ||
* A mask that represents the base id of the token. It is created by shifting the maximum uint256 value left by 128 bits. | ||
* @type {BigInt} | ||
*/ | ||
const TYPE_MASK = MAX << BigInt(128); | ||
|
||
/** | ||
* A mask that represents the index of a non-fungible token. It is created by shifting the maximum uint256 value right by 128 bits. | ||
* @type {BigInt} | ||
*/ | ||
const NF_INDEX_MASK = MAX >> BigInt(128); | ||
|
||
/** | ||
* Checks if a token ID represents a base type token. | ||
* | ||
* A token ID is considered to represent a base type token if: | ||
* - The bitwise AND of the token ID and the TYPE_MASK equals the token ID. | ||
* - The bitwise AND of the token ID and the NF_INDEX_MASK equals 0. | ||
* | ||
* @param {BigInt} id - The token ID to check. | ||
* @returns {boolean} - Returns true if the token ID represents a base type token, false otherwise. | ||
*/ | ||
const isBaseType = (id: bigint) => { | ||
return (id & TYPE_MASK) === id && (id & NF_INDEX_MASK) === BigInt(0); | ||
}; | ||
|
||
/** | ||
* Checks if a token ID represents a claim token. | ||
* | ||
* A token ID is considered to represent a claim token if it is not null and it represents a base type token. | ||
* | ||
* @param {BigInt} tokenId - The token ID to check. It can be undefined. | ||
* @returns {boolean} - Returns true if the token ID represents a claim token, false otherwise. | ||
*/ | ||
export const isHypercertToken = (tokenId?: bigint) => { | ||
if (!tokenId) { | ||
return false; | ||
} | ||
return isBaseType(tokenId); | ||
}; | ||
|
||
/** | ||
* Gets the claim token ID from a given token ID. | ||
* | ||
* The claim token ID is obtained by applying the TYPE_MASK to the given token ID using the bitwise AND operator. | ||
* The result is logged to the console for debugging purposes. | ||
* | ||
* @param {BigInt} tokenId - The token ID to get the claim token ID from. | ||
* @returns {BigInt} - Returns the claim token ID. | ||
*/ | ||
export const getHypercertTokenId = (tokenId: bigint) => { | ||
return tokenId & TYPE_MASK; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { z } from "zod"; | ||
import { DEPLOYMENTS } from "../../constants"; | ||
import { ZodSchemaValidator } from "../base/SchemaValidator"; | ||
import { isHypercertToken } from "src/utils/tokenIds"; | ||
|
||
const AttestationSchema = z | ||
.object({ | ||
chain_id: z.coerce.bigint(), | ||
contract_address: z.string(), | ||
token_id: z.coerce.bigint(), | ||
}) | ||
.passthrough() | ||
.refine( | ||
(data) => { | ||
return Number(data.chain_id) in DEPLOYMENTS; | ||
}, | ||
(data) => ({ | ||
code: "INVALID_CHAIN_ID", | ||
message: `Chain ID ${data.chain_id.toString()} is not supported`, | ||
path: ["chain_id"], | ||
}), | ||
) | ||
.refine( | ||
(data) => { | ||
const deployment = DEPLOYMENTS[Number(data.chain_id) as keyof typeof DEPLOYMENTS]; | ||
if (!deployment?.addresses) { | ||
return false; | ||
} | ||
const knownAddresses = Object.values(deployment.addresses).map((addr) => addr.toLowerCase()); | ||
return knownAddresses.includes(data.contract_address.toLowerCase()); | ||
}, | ||
(data) => ({ | ||
code: "INVALID_CONTRACT_ADDRESS", | ||
message: `Contract address ${data.contract_address} is not deployed on chain ${data.chain_id.toString()}`, | ||
path: ["contract_address"], | ||
}), | ||
) | ||
.refine( | ||
(data) => { | ||
return isHypercertToken(data.token_id); | ||
}, | ||
(data) => ({ | ||
code: "INVALID_TOKEN_ID", | ||
message: `Token ID ${data.token_id.toString()} is not a valid hypercert token`, | ||
path: ["token_id"], | ||
}), | ||
); | ||
|
||
type AttestationData = z.infer<typeof AttestationSchema>; | ||
|
||
// Example raw attestation | ||
|
||
// { | ||
// "uid": "0x4f923f7485e013d3c64b55268304c0773bb84d150b4289059c77af0e28aea3f6", | ||
// "data": "0x000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000822f17a9a5eecfd66dbaff7946a8071c265d1d0700000000000000000000000000009c0900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b5a757a616c752032303233000000000000000000000000000000000000000000", | ||
// "time": 1727969021, | ||
// "refUID": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
// "schema": "0x2f4f575d5df78ac52e8b124c4c900ec4c540f1d44f5b8825fac0af5308c91449", | ||
// "attester": "0x676703E18b2d03Aa36d6A3124B4F58716dBf61dB", | ||
// "recipient": "0x0000000000000000000000000000000000000000", | ||
// "revocable": false, | ||
// "expirationTime": 0, | ||
// "revocationTime": 0 | ||
// } | ||
|
||
// Example decoded attestation data | ||
|
||
// { | ||
// "tags": [ | ||
// "Zuzalu 2023" | ||
// ], | ||
// "chain_id": 10, | ||
// "comments": "", | ||
// "token_id": 1.3592579146656887e+43, | ||
// "evaluate_work": 1, | ||
// "evaluate_basic": 1, | ||
// "contract_address": "0x822F17A9A5EeCFd66dBAFf7946a8071C265D1d07", | ||
// "evaluate_properties": 1, | ||
// "evaluate_contributors": 1 | ||
// } | ||
|
||
// Example raw attestation data | ||
|
||
// { | ||
// "uid": "0xc6b717cfbf9df516c0cbdc670fdd7d098ae0a7d30b2fb2c1ff7bd15a822bf1f4", | ||
// "data": "0x0000000000000000000000000000000000000000000000000000000000aa36a7000000000000000000000000a16dfb32eb140a6f3f2ac68f41dad8c7e83c4941000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000001e54657374696e67206164646974696f6e616c206174746573746174696f6e0000000000000000000000000000000000000000000000000000000000000000000877757575757575740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000003a7b2274797065223a2275726c222c22737263223a2268747470733a2f2f7078686572652e636f6d2f656e2f70686f746f2f31333833373237227d00000000000000000000000000000000000000000000000000000000000000000000000000b27b2274797065223a22696d6167652f6a706567222c226e616d65223a22676f61745f62726f776e5f616e696d616c5f6e61747572655f62696c6c795f676f61745f6d616d6d616c5f63726561747572655f686f726e732d3635363235322d313533313531373336392e6a7067222c22737263223a226261666b72656964676d613237367a326d756178717a79797467676979647437627a617073736479786b7333376737736f37377372347977776775227d0000000000000000000000000000", | ||
// "time": 1737648084, | ||
// "refUID": "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
// "schema": "0x48e3e1be1e08084b408a7035ac889f2a840b440bbf10758d14fb722831a200c3", | ||
// "attester": "0xdf2C3dacE6F31e650FD03B8Ff72beE82Cb1C199A", | ||
// "recipient": "0x0000000000000000000000000000000000000000", | ||
// "revocable": false, | ||
// "expirationTime": 0, | ||
// "revocationTime": 0 | ||
// } | ||
|
||
// Example decoded attestation data | ||
|
||
// { | ||
// "title": "Testing additional attestation", | ||
// "sources": [ | ||
// "{\"type\":\"url\",\"src\":\"https://pxhere.com/en/photo/1383727\"}", | ||
// "{\"type\":\"image/jpeg\",\"name\":\"goat_brown_animal_nature_billy_goat_mammal_creature_horns-656252-1531517369.jpg\",\"src\":\"bafkreidgma276z2muaxqzyytggiydt7bzapssdyxks37g7so77sr4ywwgu\"}" | ||
// ], | ||
// "chain_id": 11155111, | ||
// "token_id": 2.0416942015256308e+41, | ||
// "description": "wuuuuuut", | ||
// "contract_address": "0xa16DFb32Eb140a6f3F2AC68f41dAd8c7e83C4941" | ||
// } | ||
|
||
export class AttestationValidator extends ZodSchemaValidator<AttestationData> { | ||
constructor() { | ||
super(AttestationSchema); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { expect, it, describe } from "vitest"; | ||
|
||
import { isHypercertToken, getHypercertTokenId } from "../../src/utils/tokenIds"; | ||
|
||
const claimTokenId = 340282366920938463463374607431768211456n; | ||
const fractionTokenId = 340282366920938463463374607431768211457n; | ||
|
||
describe("isClaimTokenId", () => { | ||
it("should return true for a claim token id", () => { | ||
expect(isHypercertToken(claimTokenId)).toBe(true); | ||
}); | ||
|
||
it("should return false for a non-claim token id", () => { | ||
expect(isHypercertToken(fractionTokenId)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("getClaimTokenId", () => { | ||
it("should return the claim token id", () => { | ||
expect(getHypercertTokenId(claimTokenId)).toBe(claimTokenId); | ||
}); | ||
|
||
it("should return the claim token id for a fraction token id", () => { | ||
expect(getHypercertTokenId(fractionTokenId)).toBe(claimTokenId); | ||
}); | ||
}); |
Oops, something went wrong.