diff --git a/skills/sybil_checker/__init__.py b/skills/sybil_checker/__init__.py new file mode 100644 index 00000000..4a316766 --- /dev/null +++ b/skills/sybil_checker/__init__.py @@ -0,0 +1,4 @@ +from .combined_checker import CombinedSybilChecker + +def get_skills(config): + return [CombinedSybilChecker(config)] diff --git a/skills/sybil_checker/base.py b/skills/sybil_checker/base.py new file mode 100644 index 00000000..f914536e --- /dev/null +++ b/skills/sybil_checker/base.py @@ -0,0 +1,5 @@ +from skills.base import Skill + +class SybilCheckerSkill(Skill): + def check_sybil(self, wallet_address: str) -> dict: + raise NotImplementedError("Must implement check_sybil method") diff --git a/skills/sybil_checker/combined_checker.py b/skills/sybil_checker/combined_checker.py new file mode 100644 index 00000000..bb7ada77 --- /dev/null +++ b/skills/sybil_checker/combined_checker.py @@ -0,0 +1,20 @@ +from .base import SybilCheckerSkill +from .gitcoin_passport_checker import GitcoinPassportChecker +from .proof_of_humanity_checker import ProofOfHumanityChecker + +class CombinedSybilChecker(SybilCheckerSkill): + def __init__(self, config): + self.gitcoin_checker = GitcoinPassportChecker(config) + self.poh_checker = ProofOfHumanityChecker(config) + + def check_sybil(self, wallet_address: str) -> dict: + gitcoin_result = self.gitcoin_checker.check_sybil(wallet_address) + poh_result = self.poh_checker.check_sybil(wallet_address) + + return { + "wallet": wallet_address, + "results": [ + gitcoin_result, + poh_result + ] + } diff --git a/skills/sybil_checker/gitcoin_logo.png b/skills/sybil_checker/gitcoin_logo.png new file mode 100644 index 00000000..79f42f2d Binary files /dev/null and b/skills/sybil_checker/gitcoin_logo.png differ diff --git a/skills/sybil_checker/gitcoin_passport_checker.py b/skills/sybil_checker/gitcoin_passport_checker.py new file mode 100644 index 00000000..8964ec80 --- /dev/null +++ b/skills/sybil_checker/gitcoin_passport_checker.py @@ -0,0 +1,22 @@ +import requests +from .base import SybilCheckerSkill + +class GitcoinPassportChecker(SybilCheckerSkill): + def __init__(self, config): + self.api_key = config.get("api_key") + self.base_url = "https://api.scorer.gitcoin.co" + + def check_sybil(self, wallet_address: str) -> dict: + headers = {"Authorization": f"Bearer {self.api_key}"} + url = f"{self.base_url}/v1/score/{wallet_address}" + response = requests.get(url, headers=headers) + if response.status_code == 200: + return { + "source": "Gitcoin Passport", + "result": response.json() + } + else: + return { + "source": "Gitcoin Passport", + "error": f"Failed with status {response.status_code}" + } diff --git a/skills/sybil_checker/proof_of_humanity_checker.py b/skills/sybil_checker/proof_of_humanity_checker.py new file mode 100644 index 00000000..94991d1e --- /dev/null +++ b/skills/sybil_checker/proof_of_humanity_checker.py @@ -0,0 +1,44 @@ +import requests +from .base import SybilCheckerSkill + +class ProofOfHumanityChecker(SybilCheckerSkill): + def __init__(self, config): + self.subgraph_url = "https://api.thegraph.com/subgraphs/name/kleros/proof-of-humanity" + + def check_sybil(self, wallet_address: str) -> dict: + query = { + "query": """ + query ($id: ID!) { + submission(id: $id) { + id + registered + submissionTime + name + } + } + """, + "variables": { + "id": wallet_address.lower() + } + } + + response = requests.post(self.subgraph_url, json=query) + data = response.json() + + submission = data.get("data", {}).get("submission") + if submission and submission.get("registered"): + return { + "source": "Proof of Humanity", + "result": { + "status": "verified", + "details": submission + } + } + else: + return { + "source": "Proof of Humanity", + "result": { + "status": "not_verified", + "details": None + } + } diff --git a/skills/sybil_checker/schema.json b/skills/sybil_checker/schema.json new file mode 100644 index 00000000..9c18ef15 --- /dev/null +++ b/skills/sybil_checker/schema.json @@ -0,0 +1,18 @@ +{ + "name": "sybil-checker", + "version": "1.0.0", + "author": "your-username", + "type": "object", + "properties": { + "api_key": { + "type": "string", + "description": "Gitcoin Passport API key" + } + }, + "required": ["api_key"], + "docs": { + "description": "Checks for Sybil resistance by combining identity signals from Gitcoin Passport and Proof of Humanity.", + "image": ".gitcoin_logo.png" + } +} + \ No newline at end of file