-
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.
Relocate protocol eas and merkle proofs to core
- Loading branch information
Showing
8 changed files
with
217 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { getSchemaUID } from '@ethereum-attestation-service/eas-sdk'; | ||
|
||
export const NULL_EAS_REF_UID = '0x0000000000000000000000000000000000000000000000000000000000000000'; | ||
|
||
export const NULL_EVM_ADDRESS = '0x0000000000000000000000000000000000000000'; | ||
|
||
// Obtained from https://github.com/ethereum-attestation-service/eas-contracts/blob/558250dae4cb434859b1ac3b6d32833c6448be21/deploy/scripts/000004-name-initial-schemas.ts#L10C1-L11C1 | ||
export const NAME_SCHEMA_UID = getSchemaUID('bytes32 schemaId,string name', NULL_EVM_ADDRESS, true); |
56 changes: 56 additions & 0 deletions
56
src/lib/protocol/easSchemas/githubContributionReceiptSchema.ts
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,56 @@ | ||
import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk'; | ||
|
||
export const githubContributionReceiptEASSchema = | ||
'bytes32 userRefUID,string description,string url,string metadataUrl,uint256 value,string type'; | ||
|
||
export const githubContributionReceiptSchemaName = 'Github Contribution Receipt'; | ||
|
||
export type GithubContributionReceiptAttestation = { | ||
userRefUID: string; | ||
description: string; | ||
url: string; | ||
metadataUrl: string; | ||
value: number; | ||
type: string; | ||
}; | ||
|
||
const encoder = new SchemaEncoder(githubContributionReceiptEASSchema); | ||
|
||
export function encodeGithubContributionReceiptAttestation( | ||
attestation: GithubContributionReceiptAttestation | ||
): `0x${string}` { | ||
const encodedData = encoder.encodeData([ | ||
{ name: 'userRefUID', type: 'string', value: attestation.userRefUID }, | ||
{ name: 'description', type: 'string', value: attestation.description }, | ||
{ | ||
name: 'url', | ||
type: 'string', | ||
value: attestation.url | ||
}, | ||
{ | ||
name: 'metadataUrl', | ||
type: 'string', | ||
value: attestation.metadataUrl | ||
}, | ||
{ name: 'value', type: 'uint256', value: attestation.value }, | ||
{ name: 'type', type: 'string', value: attestation.type } | ||
]); | ||
|
||
return encodedData as `0x${string}`; | ||
} | ||
|
||
export function decodeGithubContributionReceiptAttestation(rawData: string): GithubContributionReceiptAttestation { | ||
const parsed = encoder.decodeData(rawData); | ||
const values = parsed.reduce((acc, item) => { | ||
const key = item.name as keyof GithubContributionReceiptAttestation; | ||
|
||
if (key === 'value') { | ||
acc[key] = parseInt(item.value.value as string); | ||
} else { | ||
acc[key] = item.value.value as string; | ||
} | ||
return acc; | ||
}, {} as GithubContributionReceiptAttestation); | ||
|
||
return values as GithubContributionReceiptAttestation; | ||
} |
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,3 @@ | ||
export * from './constants'; | ||
export * from './githubContributionReceiptSchema'; | ||
export * from './scoutGameUserProfileSchema'; |
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,34 @@ | ||
import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk'; | ||
|
||
export const scoutGameUserProfileEASSchema = 'string id,string metadataUrl'; | ||
|
||
export const scoutGameUserProfileSchemaName = 'Scout Game User Profile'; | ||
|
||
export type ScoutGameUserProfileAttestation = { | ||
id: string; | ||
metadataUrl: string; | ||
}; | ||
|
||
const encoder = new SchemaEncoder(scoutGameUserProfileEASSchema); | ||
|
||
export function encodeScoutGameUserProfileAttestation(attestation: ScoutGameUserProfileAttestation): `0x${string}` { | ||
const encodedData = encoder.encodeData([ | ||
{ name: 'id', type: 'string', value: attestation.id }, | ||
{ name: 'metadataUrl', type: 'string', value: attestation.metadataUrl } | ||
]); | ||
|
||
return encodedData as `0x${string}`; | ||
} | ||
|
||
export function decodeScoutGameUserProfileAttestation(rawData: string): ScoutGameUserProfileAttestation { | ||
const parsed = encoder.decodeData(rawData); | ||
const values = parsed.reduce((acc, item) => { | ||
const key = item.name as keyof ScoutGameUserProfileAttestation; | ||
|
||
acc[key] = item.value.value as string; | ||
|
||
return acc; | ||
}, {} as ScoutGameUserProfileAttestation); | ||
|
||
return values as ScoutGameUserProfileAttestation; | ||
} |
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,70 @@ | ||
import { generateMerkleTree, getMerkleProofs, verifyMerkleClaim, type ProvableClaim } from '../merkleTree'; | ||
|
||
const claimsInput: ProvableClaim[] = [ | ||
{ | ||
// Key here so we can copy to other tests: 57b7b9b29419b66ac8156f844a7b0eb18d94f729699b3f15a3d8817d3f5980a3 | ||
address: '0x3F2A655d4e39E6c4470703e1063e9a843586886A', | ||
amount: 100 | ||
}, | ||
{ | ||
// Key here so we can copy to other tests: aa03d22263ff3e4df4105a20d08f62873f5e100974862fdc1f99083ba11e6adc | ||
address: '0x2Fe1B8C9C8722f0D3e5B9a9D4115559bB8f04931', | ||
amount: 200 | ||
}, | ||
{ | ||
// Key here so we can copy to other tests: c674865dde0163f480f818a78fc4d316c64d60b05666600734df8e8f37147f64 | ||
address: '0x03F8B139fF6dbbb7475bAA5A71c16fcDD9495cc4', | ||
amount: 300 | ||
}, | ||
{ | ||
address: '0x36446eF671954753801f9d73C415a80C0e550b32', | ||
amount: 400 | ||
}, | ||
{ | ||
address: '0xD02953857250D32EC72064d9E2320B43296E52C0', | ||
amount: 500 | ||
} | ||
]; | ||
|
||
describe('verifyMerkleClaim', () => { | ||
it('should return true if the claim is valid', () => { | ||
const { tree } = generateMerkleTree(claimsInput); | ||
const claim = claimsInput[0]; | ||
const proofs = getMerkleProofs(tree, claim); | ||
|
||
expect(verifyMerkleClaim(tree, claim, proofs)).toBe(true); | ||
}); | ||
|
||
it('should return false if the claim is invalid', () => { | ||
const { tree } = generateMerkleTree(claimsInput); | ||
const claim: ProvableClaim = { | ||
address: '0x36446eF671954753801f9d73C415a80C0e550b32', | ||
amount: 200 | ||
}; | ||
const proof = getMerkleProofs(tree, claim); | ||
expect(verifyMerkleClaim(tree, claim, proof)).toBe(false); | ||
}); | ||
|
||
it('should sort inputs so that it is not reliant on ordering of the claims', () => { | ||
function shuffleArray<T>(array: T[]): T[] { | ||
const newArray = [...array]; // Create a copy of the array to avoid mutating the original | ||
for (let i = newArray.length - 1; i > 0; i--) { | ||
const j = Math.floor(Math.random() * (i + 1)); | ||
[newArray[i], newArray[j]] = [newArray[j], newArray[i]]; // Swap elements | ||
} | ||
return newArray; | ||
} | ||
|
||
const shuffledOne = shuffleArray(claimsInput); | ||
|
||
const shuffledTwo = shuffleArray(claimsInput); | ||
|
||
// Make sure sorting worked | ||
expect(JSON.stringify(shuffledOne)).not.toEqual(JSON.stringify(shuffledTwo)); | ||
|
||
const { rootHash: rootHashOne } = generateMerkleTree(shuffledOne); | ||
const { rootHash: rootHashTwo } = generateMerkleTree(shuffledTwo); | ||
|
||
expect(rootHashOne).toEqual(rootHashTwo); | ||
}); | ||
}); |
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,37 @@ | ||
import { MerkleTree } from 'merkletreejs'; | ||
import type { Address } from 'viem'; | ||
import { keccak256, encodePacked } from 'viem/utils'; | ||
|
||
export type ProvableClaim = { | ||
address: Address; | ||
amount: number; | ||
}; | ||
|
||
function hashLeaf(claim: ProvableClaim): string { | ||
// Mimic Solidity's keccak256(abi.encodePacked(address, amount)) | ||
const packedData = encodePacked(['address', 'uint256'], [claim.address, BigInt(claim.amount)]); | ||
return keccak256(packedData); | ||
} | ||
|
||
export function generateMerkleTree(claims: ProvableClaim[]): { tree: MerkleTree; rootHash: string } { | ||
const inputs = claims.map(hashLeaf); | ||
|
||
const tree = new MerkleTree(inputs, keccak256, { | ||
sort: true | ||
// concatenator: ([left, right]) => Buffer.from(hashLeaf(Buffer.concat([left, right]).toString())) | ||
}); | ||
|
||
return { | ||
tree, | ||
rootHash: tree.getRoot().toString('hex') | ||
}; | ||
} | ||
|
||
export function getMerkleProofs(tree: MerkleTree, claim: ProvableClaim): string[] { | ||
return tree.getHexProof(hashLeaf(claim)); | ||
} | ||
|
||
export function verifyMerkleClaim(tree: MerkleTree, claim: ProvableClaim, proof: string[]): boolean { | ||
const root = tree.getRoot().toString('hex'); | ||
return tree.verify(proof, hashLeaf(claim), root); | ||
} |
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,2 @@ | ||
export * from './lib/protocol/proofs/merkleTree'; | ||
export * from './lib/protocol/easSchemas/index'; |