Skip to content

Commit

Permalink
Merge pull request #2296 from 0xCardinalError/feat/gitcoin_passport_p…
Browse files Browse the repository at this point in the history
…lugin

feat: Gitcoin passport
  • Loading branch information
wtfsayo authored Jan 14, 2025
2 parents 0404e29 + 67ea56b commit 58e9f7c
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 2 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,9 @@ GIPHY_API_KEY=
# OpenWeather
OPEN_WEATHER_API_KEY= # OpenWeather API key


#GITCOIN Passport
PASSPORT_API_KEY= #Gitcoin Passport key
PASSPORT_SCORER= #Scorer number

# EchoChambers Configuration
ECHOCHAMBERS_API_URL=http://127.0.0.1:3333
Expand Down
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@elizaos/plugin-flow": "workspace:*",
"@elizaos/plugin-gitbook": "workspace:*",
"@elizaos/plugin-story": "workspace:*",
"@elizaos/plugin-gitcoin-passport": "workspace:*",
"@elizaos/plugin-goat": "workspace:*",
"@elizaos/plugin-lensNetwork": "workspace:*",
"@elizaos/plugin-icp": "workspace:*",
Expand Down
6 changes: 5 additions & 1 deletion agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import { flowPlugin } from "@elizaos/plugin-flow";
import { fuelPlugin } from "@elizaos/plugin-fuel";
import { genLayerPlugin } from "@elizaos/plugin-genlayer";
import { giphyPlugin } from "@elizaos/plugin-giphy";
import { gitcoinPassportPlugin } from "@elizaos/plugin-gitcoin-passport";
import { hyperliquidPlugin } from "@elizaos/plugin-hyperliquid";
import { imageGenerationPlugin } from "@elizaos/plugin-image-generation";
import { lensPlugin } from "@elizaos/plugin-lensNetwork";
Expand Down Expand Up @@ -825,7 +826,7 @@ export async function createAgent(
getSecret(character, "ABSTRACT_PRIVATE_KEY")
? abstractPlugin
: null,
getSecret(character, "B2_PRIVATE_KEY") ? b2Plugin: null,
getSecret(character, "B2_PRIVATE_KEY") ? b2Plugin : null,
getSecret(character, "BINANCE_API_KEY") &&
getSecret(character, "BINANCE_SECRET_KEY")
? binancePlugin
Expand Down Expand Up @@ -868,6 +869,9 @@ export async function createAgent(
getSecret(character, "LETZAI_API_KEY") ? letzAIPlugin : null,
getSecret(character, "STARGAZE_ENDPOINT") ? stargazePlugin : null,
getSecret(character, "GIPHY_API_KEY") ? giphyPlugin : null,
getSecret(character, "PASSPORT_API_KEY")
? gitcoinPassportPlugin
: null,
getSecret(character, "GENLAYER_PRIVATE_KEY")
? genLayerPlugin
: null,
Expand Down
38 changes: 38 additions & 0 deletions packages/plugin-gitcoin-passport/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# `@elizaos/plugin-passport`

This plugin provides actions for interacting with Gitcoin passport
https://docs.passport.xyz/building-with-passport/passport-api/overview

---

## Installation

Just add it under your character profile in plugins as

```
"plugins": [
"@elizaos/plugin-gitcoin-passport"
],
```

## Configuration

Getting Your API Key

1. Log in to the developer portal: Go to developer.passport.xyz and log in to your account by connecting your wallet.
2. Navigate to the API Keys section: After logging in, go to the "API Keys" section.
3. Generate an API key: Click on the "+ Create a Key" button to generate a unique API key for your account.
4. Store your API key securely: Store your API key in a secure place, as it will be used to access the Passport API.

Getting your Scorer ID

1. Log in to the Developer Portal: Go to developer.passport.xyz and log in to your account by connecting your wallet.
2. Navigate to the Scorer section: After logging in, go to the "Scorer" section
3. Create a Scorer: Click on the "+ Create a Scorer" button and input a Scorer name and description. Make sure you use the Unique Humanity scorer, and not the Binary scorer.
4. Find your Scorer ID: Click on the newly created Scorer and you will see the Scorer ID in the page URL.
Example: https://developer.passport.xyz/dashboard/scorer/{scorer_id}

## Usage

Results are saved as message and agents can retrive it from there for different use cases.
Default passport treshold of 20 is used, but you can pick your own value and match it agains that
3 changes: 3 additions & 0 deletions packages/plugin-gitcoin-passport/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import eslintGlobalConfig from "../../eslint.config.mjs";

export default [...eslintGlobalConfig];
20 changes: 20 additions & 0 deletions packages/plugin-gitcoin-passport/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@elizaos/plugin-gitcoin-passport",
"version": "0.1.7-alpha.2",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",
"dependencies": {
"@elizaos/core": "workspace:*",
"tsup": "8.3.5"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"test": "vitest run",
"lint": "eslint --fix --cache ."
},
"peerDependencies": {
"whatwg-url": "7.1.0"
}
}
137 changes: 137 additions & 0 deletions packages/plugin-gitcoin-passport/src/actions/getScore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {
Action,
elizaLogger,
IAgentRuntime,
Memory,
HandlerCallback,
State,
getEmbeddingZeroVector,
Content,
composeContext,
generateMessageResponse,
ModelClass,
} from "@elizaos/core";

interface PassportScore {
address: string;
score: string;
threshold: string;
passing_score: string;
}

const createTokenMemory = async (
runtime: IAgentRuntime,
_message: Memory,
formattedOutput: string
) => {
const memory: Memory = {
userId: _message.userId,
agentId: _message.agentId,
roomId: _message.roomId,
content: { text: formattedOutput },
createdAt: Date.now(),
embedding: getEmbeddingZeroVector(),
};
await runtime.messageManager.createMemory(memory);
};

export const addressTemplate = `From previous sentence extract only the Ethereum address being asked about.
Respond with a JSON markdown block containing only the extracted value:
\`\`\`json
{
"address": string | null
}
\`\`\`
`;

export const getPassportScoreAction: Action = {
name: "GET_PASSPORT_SCORE",
description: "Get score from Passport API for an address",
validate: async (runtime: IAgentRuntime, _message: Memory) => {
elizaLogger.log("Validating runtime for GET_PASSPORT_SCORE...");
const apiKey = runtime.getSetting("PASSPORT_API_KEY");
const scorerId = runtime.getSetting("PASSPORT_SCORER");
if (!apiKey || !scorerId) {
elizaLogger.error(
"Missing PASSPORT_API_KEY or PASSPORT_SCORER settings"
);
return false;
}
return true;
},
handler: async (
runtime: IAgentRuntime,
_message: Memory,
state: State,
_options: any,
callback: HandlerCallback
) => {
elizaLogger.log("Starting GET_PASSPORT_SCORE handler...");
const apiKey = runtime.getSetting("PASSPORT_API_KEY");
const scorerId = runtime.getSetting("PASSPORT_SCORER");

if (!state) {
state = (await runtime.composeState(_message)) as State;
} else {
state = await runtime.updateRecentMessageState(state);
}

const context = composeContext({
state,
template: `${_message.content.text}\n${addressTemplate}`,
});

const addressRequest = await generateMessageResponse({
runtime,
context,
modelClass: ModelClass.SMALL,
});

const address = addressRequest.address as string;

if (!address) {
callback({ text: "Address is required." }, []);
return;
}

try {
const response = await fetch(
`https://api.passport.xyz/v2/stamps/${scorerId}/score/${address}`,
{
method: "GET",
headers: {
"X-API-KEY": apiKey,
accept: "application/json",
},
}
);

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data: PassportScore = await response.json();
const formattedOutput = `Address: ${data.address}\nScore: ${data.score}${data.passing_score ? "\nScore is above threshold" : `\nScore is below threshold (${data.threshold})`}`;

await createTokenMemory(runtime, _message, formattedOutput);

callback({ text: formattedOutput }, []);
} catch (error) {
elizaLogger.error("Error fetching Passport score:", error);
callback(
{
text: "Failed to fetch Passport score. Please check the logs for more details.",
},
[]
);
}
},
examples: [],
similes: [
"GET_PASSPORT_SCORE",
"FETCH_PASSPORT_SCORE",
"CHECK_PASSPORT_SCORE",
"VIEW_PASSPORT_SCORE",
],
};
15 changes: 15 additions & 0 deletions packages/plugin-gitcoin-passport/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export * from "./actions/getScore";

import type { Plugin } from "@elizaos/core";
import { getPassportScoreAction } from "./actions/getScore";

export const gitcoinPassportPlugin: Plugin = {
name: "passport",
description: "Gitcoin passport integration plugin",
providers: [],
evaluators: [],
services: [],
actions: [getPassportScoreAction],
};

export default gitcoinPassportPlugin;
15 changes: 15 additions & 0 deletions packages/plugin-gitcoin-passport/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../core/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "./src",
"typeRoots": [
"./node_modules/@types",
"./src/types"
],
"declaration": true
},
"include": [
"src"
]
}
21 changes: 21 additions & 0 deletions packages/plugin-gitcoin-passport/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from "tsup";

export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
sourcemap: true,
clean: true,
format: ["esm"], // Ensure you're targeting CommonJS
external: [
"dotenv", // Externalize dotenv to prevent bundling
"fs", // Externalize fs to use Node.js built-in module
"path", // Externalize other built-ins if necessary
"@reflink/reflink",
"@node-llama-cpp",
"https",
"http",
"agentkeepalive",
"viem",
"@lifi/sdk",
],
});
15 changes: 15 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 58e9f7c

Please sign in to comment.