@elizaos/plugin-elizamaker 2.0.3-beta.5 → 2.0.3-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/drop-routes.d.ts +22 -0
- package/dist/drop-routes.d.ts.map +1 -0
- package/dist/drop-routes.js +162 -0
- package/dist/drop-routes.js.map +1 -0
- package/dist/drop-service-registry.d.ts +4 -0
- package/dist/drop-service-registry.d.ts.map +1 -0
- package/dist/drop-service-registry.js +12 -0
- package/dist/drop-service-registry.js.map +1 -0
- package/dist/drop-service.d.ts +46 -0
- package/dist/drop-service.d.ts.map +1 -0
- package/dist/drop-service.js +136 -0
- package/dist/drop-service.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/init-registry-services.d.ts +18 -0
- package/dist/init-registry-services.d.ts.map +1 -0
- package/dist/init-registry-services.js +64 -0
- package/dist/init-registry-services.js.map +1 -0
- package/dist/merkle-tree.d.ts +90 -0
- package/dist/merkle-tree.d.ts.map +1 -0
- package/dist/merkle-tree.js +118 -0
- package/dist/merkle-tree.js.map +1 -0
- package/dist/nft-verify.d.ts +16 -0
- package/dist/nft-verify.d.ts.map +1 -0
- package/dist/nft-verify.js +94 -0
- package/dist/nft-verify.js.map +1 -0
- package/dist/og-tracker.d.ts +29 -0
- package/dist/og-tracker.d.ts.map +1 -0
- package/dist/og-tracker.js +41 -0
- package/dist/og-tracker.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +105 -0
- package/dist/plugin.js.map +1 -0
- package/dist/registry-service-registry.d.ts +4 -0
- package/dist/registry-service-registry.d.ts.map +1 -0
- package/dist/registry-service-registry.js +12 -0
- package/dist/registry-service-registry.js.map +1 -0
- package/dist/twitter-verify.d.ts +29 -0
- package/dist/twitter-verify.d.ts.map +1 -0
- package/dist/twitter-verify.js +164 -0
- package/dist/twitter-verify.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merkle tree service for whitelist proof generation.
|
|
3
|
+
*
|
|
4
|
+
* Builds a Merkle tree from verified addresses in whitelist.json and
|
|
5
|
+
* generates proofs that can be submitted to the smart contract's
|
|
6
|
+
* `mintWhitelist(name, endpoint, capHash, proof[])` function.
|
|
7
|
+
*
|
|
8
|
+
* This is the missing bridge between:
|
|
9
|
+
* whitelist.json → Merkle tree → on-chain mintWhitelist()
|
|
10
|
+
*
|
|
11
|
+
* The contract stores a `merkleRoot()` and verifies each mint against
|
|
12
|
+
* a proof derived from the caller's address.
|
|
13
|
+
*
|
|
14
|
+
* Standard: leaves = keccak256(abi.encodePacked(address))
|
|
15
|
+
* sorted pairs to ensure deterministic tree construction.
|
|
16
|
+
*
|
|
17
|
+
* @see drop-service.ts — mintWithWhitelist() consumer
|
|
18
|
+
* @see twitter-verify.ts — one source of whitelist addresses
|
|
19
|
+
* @see nft-verify.ts — another source of whitelist addresses
|
|
20
|
+
*/
|
|
21
|
+
export interface MerkleProofResult {
|
|
22
|
+
/** The proof array (bytes32[]) to submit to mintWhitelist(). */
|
|
23
|
+
proof: string[];
|
|
24
|
+
/** The leaf hash for this address. */
|
|
25
|
+
leaf: string;
|
|
26
|
+
/** The root of the current tree. */
|
|
27
|
+
root: string;
|
|
28
|
+
/** Whether this address is in the tree. */
|
|
29
|
+
isWhitelisted: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface MerkleTreeInfo {
|
|
32
|
+
/** The root hash of the current tree. */
|
|
33
|
+
root: string;
|
|
34
|
+
/** Total number of addresses in the tree. */
|
|
35
|
+
addressCount: number;
|
|
36
|
+
/** All leaf hashes (sorted). */
|
|
37
|
+
leaves: string[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hash an address into a Merkle leaf.
|
|
41
|
+
*
|
|
42
|
+
* Uses `keccak256(abi.encodePacked(address))` — the standard Solidity
|
|
43
|
+
* pattern for OpenZeppelin MerkleProof verification.
|
|
44
|
+
*/
|
|
45
|
+
export declare function hashLeaf(address: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Build a Merkle tree from an array of leaf hashes.
|
|
48
|
+
*
|
|
49
|
+
* Returns the tree as a 2D array where:
|
|
50
|
+
* tree[0] = leaves (sorted)
|
|
51
|
+
* tree[1] = first level of internal nodes
|
|
52
|
+
* ...
|
|
53
|
+
* tree[tree.length - 1] = [root]
|
|
54
|
+
*
|
|
55
|
+
* Leaves are sorted before building to ensure determinism.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildTree(leaves: string[]): string[][];
|
|
58
|
+
/**
|
|
59
|
+
* Get the proof (sibling path) for a leaf in the tree.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getProof(tree: string[][], leaf: string): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Get the root of the tree.
|
|
64
|
+
*/
|
|
65
|
+
export declare function getRoot(tree: string[][]): string;
|
|
66
|
+
/**
|
|
67
|
+
* Verify a proof against a root.
|
|
68
|
+
*
|
|
69
|
+
* Recomputes the root from the leaf + proof path and checks
|
|
70
|
+
* if it matches the expected root.
|
|
71
|
+
*/
|
|
72
|
+
export declare function verifyProof(leaf: string, proof: string[], expectedRoot: string): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Build a Merkle tree from the current whitelist.
|
|
75
|
+
*
|
|
76
|
+
* Reads all verified addresses from whitelist.json, hashes them into
|
|
77
|
+
* leaves, and builds the tree.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildWhitelistTree(): {
|
|
80
|
+
tree: string[][];
|
|
81
|
+
info: MerkleTreeInfo;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Generate a Merkle proof for a specific address.
|
|
85
|
+
*
|
|
86
|
+
* Returns the proof, leaf, root, and whether the address is whitelisted.
|
|
87
|
+
* The proof can be passed directly to `mintWhitelist()` on the contract.
|
|
88
|
+
*/
|
|
89
|
+
export declare function generateProof(walletAddress: string): MerkleProofResult;
|
|
90
|
+
//# sourceMappingURL=merkle-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merkle-tree.d.ts","sourceRoot":"","sources":["../src/merkle-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAQH,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAID;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKhD;AAoBD;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,CA4BtD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBjE;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EAAE,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAMT;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC;CACtB,CAmBA;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,iBAAiB,CA8BtE"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { logger } from "@elizaos/core";
|
|
2
|
+
import * as ethers from "ethers";
|
|
3
|
+
import { getVerifiedAddresses } from "./twitter-verify.js";
|
|
4
|
+
function hashLeaf(address) {
|
|
5
|
+
return ethers.solidityPackedKeccak256(
|
|
6
|
+
["address"],
|
|
7
|
+
[ethers.getAddress(address)]
|
|
8
|
+
// checksummed
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
function sortPair(a, b) {
|
|
12
|
+
return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];
|
|
13
|
+
}
|
|
14
|
+
function hashPair(a, b) {
|
|
15
|
+
const [left, right] = sortPair(a, b);
|
|
16
|
+
return ethers.solidityPackedKeccak256(["bytes32", "bytes32"], [left, right]);
|
|
17
|
+
}
|
|
18
|
+
function buildTree(leaves) {
|
|
19
|
+
if (leaves.length === 0) {
|
|
20
|
+
return [[`0x${"0".repeat(64)}`]];
|
|
21
|
+
}
|
|
22
|
+
const sorted = [...leaves].sort(
|
|
23
|
+
(a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
|
|
24
|
+
);
|
|
25
|
+
const tree = [sorted];
|
|
26
|
+
let currentLevel = sorted;
|
|
27
|
+
while (currentLevel.length > 1) {
|
|
28
|
+
const nextLevel = [];
|
|
29
|
+
for (let i = 0; i < currentLevel.length; i += 2) {
|
|
30
|
+
if (i + 1 < currentLevel.length) {
|
|
31
|
+
nextLevel.push(hashPair(currentLevel[i], currentLevel[i + 1]));
|
|
32
|
+
} else {
|
|
33
|
+
nextLevel.push(currentLevel[i]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
tree.push(nextLevel);
|
|
37
|
+
currentLevel = nextLevel;
|
|
38
|
+
}
|
|
39
|
+
return tree;
|
|
40
|
+
}
|
|
41
|
+
function getProof(tree, leaf) {
|
|
42
|
+
const proof = [];
|
|
43
|
+
let index = tree[0].indexOf(leaf);
|
|
44
|
+
if (index === -1) return [];
|
|
45
|
+
for (let level = 0; level < tree.length - 1; level++) {
|
|
46
|
+
const currentLevel = tree[level];
|
|
47
|
+
const isRightNode = index % 2 === 1;
|
|
48
|
+
const siblingIndex = isRightNode ? index - 1 : index + 1;
|
|
49
|
+
if (siblingIndex < currentLevel.length) {
|
|
50
|
+
proof.push(currentLevel[siblingIndex]);
|
|
51
|
+
}
|
|
52
|
+
index = Math.floor(index / 2);
|
|
53
|
+
}
|
|
54
|
+
return proof;
|
|
55
|
+
}
|
|
56
|
+
function getRoot(tree) {
|
|
57
|
+
return tree[tree.length - 1][0];
|
|
58
|
+
}
|
|
59
|
+
function verifyProof(leaf, proof, expectedRoot) {
|
|
60
|
+
let computed = leaf;
|
|
61
|
+
for (const sibling of proof) {
|
|
62
|
+
computed = hashPair(computed, sibling);
|
|
63
|
+
}
|
|
64
|
+
return computed.toLowerCase() === expectedRoot.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
function buildWhitelistTree() {
|
|
67
|
+
const addresses = getVerifiedAddresses();
|
|
68
|
+
const leaves = addresses.map((addr) => hashLeaf(addr));
|
|
69
|
+
const tree = buildTree(leaves);
|
|
70
|
+
const root = getRoot(tree);
|
|
71
|
+
logger.info(
|
|
72
|
+
`[merkle] Built whitelist tree with ${addresses.length} addresses (root: ${root.slice(0, 10)}...)`
|
|
73
|
+
);
|
|
74
|
+
return {
|
|
75
|
+
tree,
|
|
76
|
+
info: {
|
|
77
|
+
root,
|
|
78
|
+
addressCount: addresses.length,
|
|
79
|
+
leaves: tree[0]
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function generateProof(walletAddress) {
|
|
84
|
+
const { tree, info } = buildWhitelistTree();
|
|
85
|
+
let leaf;
|
|
86
|
+
try {
|
|
87
|
+
leaf = hashLeaf(walletAddress);
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
proof: [],
|
|
91
|
+
leaf: `0x${"0".repeat(64)}`,
|
|
92
|
+
root: info.root,
|
|
93
|
+
isWhitelisted: false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const proof = getProof(tree, leaf);
|
|
97
|
+
const isWhitelisted = proof.length > 0 || info.addressCount === 1 && tree[0][0] === leaf;
|
|
98
|
+
if (isWhitelisted) {
|
|
99
|
+
const valid = verifyProof(leaf, proof, info.root);
|
|
100
|
+
if (!valid) {
|
|
101
|
+
logger.warn(
|
|
102
|
+
`[merkle] Proof verification failed for ${walletAddress} \u2014 tree may be corrupted`
|
|
103
|
+
);
|
|
104
|
+
return { proof: [], leaf, root: info.root, isWhitelisted: false };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { proof, leaf, root: info.root, isWhitelisted };
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
buildTree,
|
|
111
|
+
buildWhitelistTree,
|
|
112
|
+
generateProof,
|
|
113
|
+
getProof,
|
|
114
|
+
getRoot,
|
|
115
|
+
hashLeaf,
|
|
116
|
+
verifyProof
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=merkle-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/merkle-tree.ts"],"sourcesContent":["/**\n * Merkle tree service for whitelist proof generation.\n *\n * Builds a Merkle tree from verified addresses in whitelist.json and\n * generates proofs that can be submitted to the smart contract's\n * `mintWhitelist(name, endpoint, capHash, proof[])` function.\n *\n * This is the missing bridge between:\n * whitelist.json → Merkle tree → on-chain mintWhitelist()\n *\n * The contract stores a `merkleRoot()` and verifies each mint against\n * a proof derived from the caller's address.\n *\n * Standard: leaves = keccak256(abi.encodePacked(address))\n * sorted pairs to ensure deterministic tree construction.\n *\n * @see drop-service.ts — mintWithWhitelist() consumer\n * @see twitter-verify.ts — one source of whitelist addresses\n * @see nft-verify.ts — another source of whitelist addresses\n */\n\nimport { logger } from \"@elizaos/core\";\nimport * as ethers from \"ethers\";\nimport { getVerifiedAddresses } from \"./twitter-verify.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────\n\nexport interface MerkleProofResult {\n /** The proof array (bytes32[]) to submit to mintWhitelist(). */\n proof: string[];\n /** The leaf hash for this address. */\n leaf: string;\n /** The root of the current tree. */\n root: string;\n /** Whether this address is in the tree. */\n isWhitelisted: boolean;\n}\n\nexport interface MerkleTreeInfo {\n /** The root hash of the current tree. */\n root: string;\n /** Total number of addresses in the tree. */\n addressCount: number;\n /** All leaf hashes (sorted). */\n leaves: string[];\n}\n\n// ── Leaf hashing ─────────────────────────────────────────────────────────\n\n/**\n * Hash an address into a Merkle leaf.\n *\n * Uses `keccak256(abi.encodePacked(address))` — the standard Solidity\n * pattern for OpenZeppelin MerkleProof verification.\n */\nexport function hashLeaf(address: string): string {\n return ethers.solidityPackedKeccak256(\n [\"address\"],\n [ethers.getAddress(address)], // checksummed\n );\n}\n\n// ── Merkle tree construction ─────────────────────────────────────────────\n\n/**\n * Sort a pair of hashes for deterministic tree construction.\n * This ensures the tree is the same regardless of insertion order.\n */\nfunction sortPair(a: string, b: string): [string, string] {\n return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];\n}\n\n/**\n * Hash two nodes together (internal node).\n */\nfunction hashPair(a: string, b: string): string {\n const [left, right] = sortPair(a, b);\n return ethers.solidityPackedKeccak256([\"bytes32\", \"bytes32\"], [left, right]);\n}\n\n/**\n * Build a Merkle tree from an array of leaf hashes.\n *\n * Returns the tree as a 2D array where:\n * tree[0] = leaves (sorted)\n * tree[1] = first level of internal nodes\n * ...\n * tree[tree.length - 1] = [root]\n *\n * Leaves are sorted before building to ensure determinism.\n */\nexport function buildTree(leaves: string[]): string[][] {\n if (leaves.length === 0) {\n return [[`0x${\"0\".repeat(64)}`]]; // empty tree → zero root\n }\n\n // Sort leaves for deterministic construction\n const sorted = [...leaves].sort((a, b) =>\n a.toLowerCase().localeCompare(b.toLowerCase()),\n );\n\n const tree: string[][] = [sorted];\n let currentLevel = sorted;\n\n while (currentLevel.length > 1) {\n const nextLevel: string[] = [];\n for (let i = 0; i < currentLevel.length; i += 2) {\n if (i + 1 < currentLevel.length) {\n nextLevel.push(hashPair(currentLevel[i], currentLevel[i + 1]));\n } else {\n // Odd node: promote to next level (no sibling)\n nextLevel.push(currentLevel[i]);\n }\n }\n tree.push(nextLevel);\n currentLevel = nextLevel;\n }\n\n return tree;\n}\n\n/**\n * Get the proof (sibling path) for a leaf in the tree.\n */\nexport function getProof(tree: string[][], leaf: string): string[] {\n const proof: string[] = [];\n let index = tree[0].indexOf(leaf);\n\n if (index === -1) return []; // leaf not in tree\n\n for (let level = 0; level < tree.length - 1; level++) {\n const currentLevel = tree[level];\n const isRightNode = index % 2 === 1;\n const siblingIndex = isRightNode ? index - 1 : index + 1;\n\n if (siblingIndex < currentLevel.length) {\n proof.push(currentLevel[siblingIndex]);\n }\n\n index = Math.floor(index / 2);\n }\n\n return proof;\n}\n\n/**\n * Get the root of the tree.\n */\nexport function getRoot(tree: string[][]): string {\n return tree[tree.length - 1][0];\n}\n\n/**\n * Verify a proof against a root.\n *\n * Recomputes the root from the leaf + proof path and checks\n * if it matches the expected root.\n */\nexport function verifyProof(\n leaf: string,\n proof: string[],\n expectedRoot: string,\n): boolean {\n let computed = leaf;\n for (const sibling of proof) {\n computed = hashPair(computed, sibling);\n }\n return computed.toLowerCase() === expectedRoot.toLowerCase();\n}\n\n// ── High-level API ───────────────────────────────────────────────────────\n\n/**\n * Build a Merkle tree from the current whitelist.\n *\n * Reads all verified addresses from whitelist.json, hashes them into\n * leaves, and builds the tree.\n */\nexport function buildWhitelistTree(): {\n tree: string[][];\n info: MerkleTreeInfo;\n} {\n const addresses = getVerifiedAddresses();\n const leaves = addresses.map((addr) => hashLeaf(addr));\n\n const tree = buildTree(leaves);\n const root = getRoot(tree);\n\n logger.info(\n `[merkle] Built whitelist tree with ${addresses.length} addresses (root: ${root.slice(0, 10)}...)`,\n );\n\n return {\n tree,\n info: {\n root,\n addressCount: addresses.length,\n leaves: tree[0],\n },\n };\n}\n\n/**\n * Generate a Merkle proof for a specific address.\n *\n * Returns the proof, leaf, root, and whether the address is whitelisted.\n * The proof can be passed directly to `mintWhitelist()` on the contract.\n */\nexport function generateProof(walletAddress: string): MerkleProofResult {\n const { tree, info } = buildWhitelistTree();\n\n let leaf: string;\n try {\n leaf = hashLeaf(walletAddress);\n } catch {\n return {\n proof: [],\n leaf: `0x${\"0\".repeat(64)}`,\n root: info.root,\n isWhitelisted: false,\n };\n }\n\n const proof = getProof(tree, leaf);\n const isWhitelisted =\n proof.length > 0 || (info.addressCount === 1 && tree[0][0] === leaf);\n\n if (isWhitelisted) {\n const valid = verifyProof(leaf, proof, info.root);\n if (!valid) {\n logger.warn(\n `[merkle] Proof verification failed for ${walletAddress} — tree may be corrupted`,\n );\n return { proof: [], leaf, root: info.root, isWhitelisted: false };\n }\n }\n\n return { proof, leaf, root: info.root, isWhitelisted };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare const ELIZA_NFT_CONTRACT_ADDRESS = "0x5Af0D9827E0c53E4799BB226655A1de152A425a5";
|
|
2
|
+
export interface NftVerificationResult {
|
|
3
|
+
verified: boolean;
|
|
4
|
+
balance: number;
|
|
5
|
+
contractAddress: string;
|
|
6
|
+
error: string | null;
|
|
7
|
+
handle: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function verifyElizaHolder(walletAddress: string, options?: {
|
|
10
|
+
rpcUrl?: string;
|
|
11
|
+
}): Promise<NftVerificationResult>;
|
|
12
|
+
export declare function verifyAndWhitelistHolder(walletAddress: string, options?: {
|
|
13
|
+
rpcUrl?: string;
|
|
14
|
+
}): Promise<NftVerificationResult>;
|
|
15
|
+
export { ELIZA_NFT_CONTRACT_ADDRESS };
|
|
16
|
+
//# sourceMappingURL=nft-verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nft-verify.d.ts","sourceRoot":"","sources":["../src/nft-verify.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,0BAA0B,+CAA+C,CAAC;AAMhF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CA+DhC;AAED,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAoBhC;AAED,OAAO,EAAE,0BAA0B,EAAE,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { logger } from "@elizaos/core";
|
|
2
|
+
import * as ethers from "ethers";
|
|
3
|
+
import { isAddressWhitelisted, markAddressVerified } from "./twitter-verify.js";
|
|
4
|
+
const ELIZA_NFT_CONTRACT_ADDRESS = "0x5Af0D9827E0c53E4799BB226655A1de152A425a5";
|
|
5
|
+
const ELIZA_NFT_ABI = [
|
|
6
|
+
"function balanceOf(address owner) view returns (uint256)"
|
|
7
|
+
];
|
|
8
|
+
const DEFAULT_RPC_URL = "https://mainnet.base.org";
|
|
9
|
+
async function verifyElizaHolder(walletAddress, options) {
|
|
10
|
+
const trimmedAddress = walletAddress.trim();
|
|
11
|
+
if (!trimmedAddress) {
|
|
12
|
+
return {
|
|
13
|
+
verified: false,
|
|
14
|
+
balance: 0,
|
|
15
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
16
|
+
error: "Ethereum address is required.",
|
|
17
|
+
handle: null
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (!ethers.isAddress(trimmedAddress)) {
|
|
21
|
+
return {
|
|
22
|
+
verified: false,
|
|
23
|
+
balance: 0,
|
|
24
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
25
|
+
error: "Invalid Ethereum address.",
|
|
26
|
+
handle: null
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const provider = new ethers.JsonRpcProvider(
|
|
30
|
+
options?.rpcUrl ?? process.env.ELIZA_NFT_RPC_URL ?? DEFAULT_RPC_URL
|
|
31
|
+
);
|
|
32
|
+
const contract = new ethers.Contract(
|
|
33
|
+
ELIZA_NFT_CONTRACT_ADDRESS,
|
|
34
|
+
ELIZA_NFT_ABI,
|
|
35
|
+
provider
|
|
36
|
+
);
|
|
37
|
+
try {
|
|
38
|
+
const balance = Number(await contract.balanceOf(trimmedAddress));
|
|
39
|
+
if (balance > 0) {
|
|
40
|
+
return {
|
|
41
|
+
verified: true,
|
|
42
|
+
balance,
|
|
43
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
44
|
+
error: null,
|
|
45
|
+
handle: null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
verified: false,
|
|
50
|
+
balance,
|
|
51
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
52
|
+
error: "Wallet does not hold an Eliza NFT.",
|
|
53
|
+
handle: null
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
57
|
+
logger.warn(`[nft-verify] Failed to verify holder status: ${message}`);
|
|
58
|
+
return {
|
|
59
|
+
verified: false,
|
|
60
|
+
balance: 0,
|
|
61
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
62
|
+
error: message,
|
|
63
|
+
handle: null
|
|
64
|
+
};
|
|
65
|
+
} finally {
|
|
66
|
+
provider.destroy();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function verifyAndWhitelistHolder(walletAddress, options) {
|
|
70
|
+
if (isAddressWhitelisted(walletAddress)) {
|
|
71
|
+
return {
|
|
72
|
+
verified: true,
|
|
73
|
+
balance: -1,
|
|
74
|
+
contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,
|
|
75
|
+
error: null,
|
|
76
|
+
handle: null
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const result = await verifyElizaHolder(walletAddress, options);
|
|
80
|
+
if (result.verified) {
|
|
81
|
+
markAddressVerified(
|
|
82
|
+
walletAddress,
|
|
83
|
+
`nft:eliza:${ELIZA_NFT_CONTRACT_ADDRESS}`,
|
|
84
|
+
`eliza-holder:${result.balance}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
ELIZA_NFT_CONTRACT_ADDRESS,
|
|
91
|
+
verifyAndWhitelistHolder,
|
|
92
|
+
verifyElizaHolder
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=nft-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/nft-verify.ts"],"sourcesContent":["import { logger } from \"@elizaos/core\";\nimport * as ethers from \"ethers\";\nimport { isAddressWhitelisted, markAddressVerified } from \"./twitter-verify.js\";\n\nconst ELIZA_NFT_CONTRACT_ADDRESS = \"0x5Af0D9827E0c53E4799BB226655A1de152A425a5\";\nconst ELIZA_NFT_ABI = [\n \"function balanceOf(address owner) view returns (uint256)\",\n];\nconst DEFAULT_RPC_URL = \"https://mainnet.base.org\";\n\nexport interface NftVerificationResult {\n verified: boolean;\n balance: number;\n contractAddress: string;\n error: string | null;\n handle: string | null;\n}\n\nexport async function verifyElizaHolder(\n walletAddress: string,\n options?: { rpcUrl?: string },\n): Promise<NftVerificationResult> {\n const trimmedAddress = walletAddress.trim();\n if (!trimmedAddress) {\n return {\n verified: false,\n balance: 0,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: \"Ethereum address is required.\",\n handle: null,\n };\n }\n\n if (!ethers.isAddress(trimmedAddress)) {\n return {\n verified: false,\n balance: 0,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: \"Invalid Ethereum address.\",\n handle: null,\n };\n }\n\n const provider = new ethers.JsonRpcProvider(\n options?.rpcUrl ?? process.env.ELIZA_NFT_RPC_URL ?? DEFAULT_RPC_URL,\n );\n const contract = new ethers.Contract(\n ELIZA_NFT_CONTRACT_ADDRESS,\n ELIZA_NFT_ABI,\n provider,\n );\n\n try {\n const balance = Number(await contract.balanceOf(trimmedAddress));\n if (balance > 0) {\n return {\n verified: true,\n balance,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: null,\n handle: null,\n };\n }\n\n return {\n verified: false,\n balance,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: \"Wallet does not hold an Eliza NFT.\",\n handle: null,\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.warn(`[nft-verify] Failed to verify holder status: ${message}`);\n return {\n verified: false,\n balance: 0,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: message,\n handle: null,\n };\n } finally {\n provider.destroy();\n }\n}\n\nexport async function verifyAndWhitelistHolder(\n walletAddress: string,\n options?: { rpcUrl?: string },\n): Promise<NftVerificationResult> {\n if (isAddressWhitelisted(walletAddress)) {\n return {\n verified: true,\n balance: -1,\n contractAddress: ELIZA_NFT_CONTRACT_ADDRESS,\n error: null,\n handle: null,\n };\n }\n\n const result = await verifyElizaHolder(walletAddress, options);\n if (result.verified) {\n markAddressVerified(\n walletAddress,\n `nft:eliza:${ELIZA_NFT_CONTRACT_ADDRESS}`,\n `eliza-holder:${result.balance}`,\n );\n }\n return result;\n}\n\nexport { ELIZA_NFT_CONTRACT_ADDRESS };\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,YAAY,YAAY;AACxB,SAAS,sBAAsB,2BAA2B;AAE1D,MAAM,6BAA6B;AACnC,MAAM,gBAAgB;AAAA,EACpB;AACF;AACA,MAAM,kBAAkB;AAUxB,eAAsB,kBACpB,eACA,SACgC;AAChC,QAAM,iBAAiB,cAAc,KAAK;AAC1C,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,UAAU,cAAc,GAAG;AACrC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,OAAO;AAAA,IAC1B,SAAS,UAAU,QAAQ,IAAI,qBAAqB;AAAA,EACtD;AACA,QAAM,WAAW,IAAI,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,OAAO,MAAM,SAAS,UAAU,cAAc,CAAC;AAC/D,QAAI,UAAU,GAAG;AACf,aAAO;AAAA,QACL,UAAU;AAAA,QACV;AAAA,QACA,iBAAiB;AAAA,QACjB,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,WAAO;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF,SAAS,OAAO;AACd,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,WAAO,KAAK,gDAAgD,OAAO,EAAE;AACrE,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF,UAAE;AACA,aAAS,QAAQ;AAAA,EACnB;AACF;AAEA,eAAsB,yBACpB,eACA,SACgC;AAChC,MAAI,qBAAqB,aAAa,GAAG;AACvC,WAAO;AAAA,MACL,UAAU;AAAA,MACV,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,kBAAkB,eAAe,OAAO;AAC7D,MAAI,OAAO,UAAU;AACnB;AAAA,MACE;AAAA,MACA,aAAa,0BAA0B;AAAA,MACvC,gBAAgB,OAAO,OAAO;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OG tracking code system.
|
|
3
|
+
*
|
|
4
|
+
* Silently writes a unique identifier to ~/.eliza/.og on first run.
|
|
5
|
+
* The code is a random UUID. A set of 100 "winning" codes can be
|
|
6
|
+
* generated deterministically from a secret seed (in ElizaMaker scripts).
|
|
7
|
+
* Whitelist eligibility is resolved by external scripts that compare this
|
|
8
|
+
* stored code against the seeded valid-code set.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Write a random tracking UUID to ~/.eliza/.og if it does not already exist.
|
|
12
|
+
* Called once during startup. Silent on failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare function initializeOGCode(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Read the stored OG code, or null if it doesn't exist.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readOGCode(): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Generate the set of valid OG codes from a seed.
|
|
21
|
+
* Used in ElizaMaker scripts -- not called in the Eliza app.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generateValidCodes(seed: string, count: number): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Check if a given code is in the valid set.
|
|
26
|
+
* Requires the seed to regenerate the valid codes.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isValidOGCode(code: string, seed: string, count?: number): boolean;
|
|
29
|
+
//# sourceMappingURL=og-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"og-tracker.d.ts","sourceRoot":"","sources":["../src/og-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAI1C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAYxE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,MAAY,GAClB,OAAO,CAGT"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { resolveStateDir } from "@elizaos/agent";
|
|
5
|
+
const OG_FILENAME = ".og";
|
|
6
|
+
function initializeOGCode() {
|
|
7
|
+
const dir = resolveStateDir();
|
|
8
|
+
const filePath = path.join(dir, OG_FILENAME);
|
|
9
|
+
if (fs.existsSync(filePath)) return;
|
|
10
|
+
const code = crypto.randomUUID();
|
|
11
|
+
if (!fs.existsSync(dir)) {
|
|
12
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
13
|
+
}
|
|
14
|
+
fs.writeFileSync(filePath, code, { encoding: "utf-8", mode: 384 });
|
|
15
|
+
}
|
|
16
|
+
function readOGCode() {
|
|
17
|
+
const filePath = path.join(resolveStateDir(), OG_FILENAME);
|
|
18
|
+
if (!fs.existsSync(filePath)) return null;
|
|
19
|
+
return fs.readFileSync(filePath, "utf-8").trim();
|
|
20
|
+
}
|
|
21
|
+
function generateValidCodes(seed, count) {
|
|
22
|
+
const codes = [];
|
|
23
|
+
for (let i = 0; i < count; i++) {
|
|
24
|
+
const hash = crypto.createHash("sha256").update(`${seed}:og:${i}`).digest("hex");
|
|
25
|
+
codes.push(
|
|
26
|
+
`${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
return codes;
|
|
30
|
+
}
|
|
31
|
+
function isValidOGCode(code, seed, count = 100) {
|
|
32
|
+
const validCodes = generateValidCodes(seed, count);
|
|
33
|
+
return validCodes.includes(code);
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
generateValidCodes,
|
|
37
|
+
initializeOGCode,
|
|
38
|
+
isValidOGCode,
|
|
39
|
+
readOGCode
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=og-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/og-tracker.ts"],"sourcesContent":["/**\n * OG tracking code system.\n *\n * Silently writes a unique identifier to ~/.eliza/.og on first run.\n * The code is a random UUID. A set of 100 \"winning\" codes can be\n * generated deterministically from a secret seed (in ElizaMaker scripts).\n * Whitelist eligibility is resolved by external scripts that compare this\n * stored code against the seeded valid-code set.\n */\n\nimport crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { resolveStateDir } from \"@elizaos/agent\";\n\nconst OG_FILENAME = \".og\";\n\n/**\n * Write a random tracking UUID to ~/.eliza/.og if it does not already exist.\n * Called once during startup. Silent on failure.\n */\nexport function initializeOGCode(): void {\n const dir = resolveStateDir();\n const filePath = path.join(dir, OG_FILENAME);\n\n if (fs.existsSync(filePath)) return;\n\n const code = crypto.randomUUID();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(filePath, code, { encoding: \"utf-8\", mode: 0o600 });\n}\n\n/**\n * Read the stored OG code, or null if it doesn't exist.\n */\nexport function readOGCode(): string | null {\n const filePath = path.join(resolveStateDir(), OG_FILENAME);\n if (!fs.existsSync(filePath)) return null;\n return fs.readFileSync(filePath, \"utf-8\").trim();\n}\n\n/**\n * Generate the set of valid OG codes from a seed.\n * Used in ElizaMaker scripts -- not called in the Eliza app.\n */\nexport function generateValidCodes(seed: string, count: number): string[] {\n const codes: string[] = [];\n for (let i = 0; i < count; i++) {\n const hash = crypto\n .createHash(\"sha256\")\n .update(`${seed}:og:${i}`)\n .digest(\"hex\");\n codes.push(\n `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`,\n );\n }\n return codes;\n}\n\n/**\n * Check if a given code is in the valid set.\n * Requires the seed to regenerate the valid codes.\n */\nexport function isValidOGCode(\n code: string,\n seed: string,\n count: number = 100,\n): boolean {\n const validCodes = generateValidCodes(seed, count);\n return validCodes.includes(code);\n}\n"],"mappings":"AAUA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,uBAAuB;AAEhC,MAAM,cAAc;AAMb,SAAS,mBAAyB;AACvC,QAAM,MAAM,gBAAgB;AAC5B,QAAM,WAAW,KAAK,KAAK,KAAK,WAAW;AAE3C,MAAI,GAAG,WAAW,QAAQ,EAAG;AAE7B,QAAM,OAAO,OAAO,WAAW;AAC/B,MAAI,CAAC,GAAG,WAAW,GAAG,GAAG;AACvB,OAAG,UAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACpD;AACA,KAAG,cAAc,UAAU,MAAM,EAAE,UAAU,SAAS,MAAM,IAAM,CAAC;AACrE;AAKO,SAAS,aAA4B;AAC1C,QAAM,WAAW,KAAK,KAAK,gBAAgB,GAAG,WAAW;AACzD,MAAI,CAAC,GAAG,WAAW,QAAQ,EAAG,QAAO;AACrC,SAAO,GAAG,aAAa,UAAU,OAAO,EAAE,KAAK;AACjD;AAMO,SAAS,mBAAmB,MAAc,OAAyB;AACxE,QAAM,QAAkB,CAAC;AACzB,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,OAAO,OACV,WAAW,QAAQ,EACnB,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,EACxB,OAAO,KAAK;AACf,UAAM;AAAA,MACJ,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,EAAE,CAAC;AAAA,IAC5G;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,cACd,MACA,MACA,QAAgB,KACP;AACT,QAAM,aAAa,mBAAmB,MAAM,KAAK;AACjD,SAAO,WAAW,SAAS,IAAI;AACjC;","names":[]}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,MAAM,EAIP,MAAM,eAAe,CAAC;AAmHvB,eAAO,MAAM,gBAAgB,EAAE,MAW9B,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ServerResponse } from "node:http";
|
|
2
|
+
import { getWalletAddresses } from "@elizaos/agent";
|
|
3
|
+
import {
|
|
4
|
+
readJsonBody as httpReadJsonBody,
|
|
5
|
+
sendJson as httpSendJson,
|
|
6
|
+
sendJsonError as httpSendJsonError
|
|
7
|
+
} from "@elizaos/core";
|
|
8
|
+
import { handleDropRoutes } from "./drop-routes.js";
|
|
9
|
+
import { getElizaMakerDropService } from "./drop-service-registry.js";
|
|
10
|
+
import { initializeRegistryAndDropServices } from "./init-registry-services.js";
|
|
11
|
+
import { readOGCode } from "./og-tracker.js";
|
|
12
|
+
function json(res, data, status = 200) {
|
|
13
|
+
httpSendJson(res, data, status);
|
|
14
|
+
}
|
|
15
|
+
function error(res, message, status = 400) {
|
|
16
|
+
httpSendJsonError(res, message, status);
|
|
17
|
+
}
|
|
18
|
+
function firstHeaderValue(value) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return firstHeaderValue(value[0]);
|
|
21
|
+
}
|
|
22
|
+
if (typeof value !== "string") {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const normalized = value.split(",")[0]?.trim();
|
|
26
|
+
return normalized ? normalized : null;
|
|
27
|
+
}
|
|
28
|
+
function requestBaseUrl(req) {
|
|
29
|
+
const protocol = firstHeaderValue(req.headers["x-forwarded-proto"]) ?? "http";
|
|
30
|
+
const host = firstHeaderValue(req.headers["x-forwarded-host"]) ?? firstHeaderValue(req.headers.host) ?? "localhost";
|
|
31
|
+
return `${protocol}://${host}`;
|
|
32
|
+
}
|
|
33
|
+
function agentNameFromRuntime(runtime) {
|
|
34
|
+
return runtime?.character?.name ?? "Eliza";
|
|
35
|
+
}
|
|
36
|
+
function toHttpIncomingMessage(req) {
|
|
37
|
+
if (typeof req !== "object" || req === null || typeof req.method !== "string" || typeof req.headers !== "object") {
|
|
38
|
+
throw new TypeError("ElizaMaker routes require a Node HTTP request");
|
|
39
|
+
}
|
|
40
|
+
return req;
|
|
41
|
+
}
|
|
42
|
+
function toHttpServerResponse(res) {
|
|
43
|
+
if (!(res instanceof ServerResponse)) {
|
|
44
|
+
throw new TypeError("ElizaMaker routes require a Node HTTP response");
|
|
45
|
+
}
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
function getOptionalWalletAddresses() {
|
|
49
|
+
const addresses = getWalletAddresses();
|
|
50
|
+
return {
|
|
51
|
+
evmAddress: addresses.evmAddress ?? void 0,
|
|
52
|
+
solanaAddress: addresses.solanaAddress ?? void 0
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function elizaMakerRouteHandler() {
|
|
56
|
+
return async (req, res, runtime) => {
|
|
57
|
+
const httpReq = toHttpIncomingMessage(req);
|
|
58
|
+
const httpRes = toHttpServerResponse(res);
|
|
59
|
+
const method = (httpReq.method ?? "GET").toUpperCase();
|
|
60
|
+
const url = new URL(httpReq.url ?? "/", requestBaseUrl(httpReq));
|
|
61
|
+
await handleDropRoutes({
|
|
62
|
+
req: httpReq,
|
|
63
|
+
res: httpRes,
|
|
64
|
+
method,
|
|
65
|
+
pathname: url.pathname,
|
|
66
|
+
url,
|
|
67
|
+
json,
|
|
68
|
+
error,
|
|
69
|
+
readJsonBody: httpReadJsonBody,
|
|
70
|
+
dropService: getElizaMakerDropService(),
|
|
71
|
+
agentName: agentNameFromRuntime(runtime),
|
|
72
|
+
getWalletAddresses: getOptionalWalletAddresses,
|
|
73
|
+
readOGCodeFromState: readOGCode
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const elizaMakerRoute = elizaMakerRouteHandler();
|
|
78
|
+
const elizaMakerRouteSpecs = [
|
|
79
|
+
{ type: "GET", path: "/api/drop/status", rawPath: true },
|
|
80
|
+
{ type: "POST", path: "/api/drop/mint", rawPath: true },
|
|
81
|
+
{ type: "POST", path: "/api/drop/mint-whitelist", rawPath: true },
|
|
82
|
+
{ type: "GET", path: "/api/whitelist/status", rawPath: true },
|
|
83
|
+
{ type: "POST", path: "/api/whitelist/twitter/message", rawPath: true },
|
|
84
|
+
{ type: "POST", path: "/api/whitelist/twitter/verify", rawPath: true },
|
|
85
|
+
{ type: "GET", path: "/api/whitelist/merkle/root", rawPath: true },
|
|
86
|
+
{ type: "GET", path: "/api/whitelist/merkle/proof", rawPath: true }
|
|
87
|
+
];
|
|
88
|
+
const elizaMakerRoutes = elizaMakerRouteSpecs.map((route) => ({
|
|
89
|
+
...route,
|
|
90
|
+
handler: elizaMakerRoute
|
|
91
|
+
}));
|
|
92
|
+
const elizaMakerPlugin = {
|
|
93
|
+
name: "@elizaos/plugin-elizamaker",
|
|
94
|
+
description: "ElizaMaker ERC-8041 drop, mint, whitelist, and Merkle proof routes.",
|
|
95
|
+
routes: elizaMakerRoutes,
|
|
96
|
+
init: async (_config, runtime) => {
|
|
97
|
+
void initializeRegistryAndDropServices(runtime);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var plugin_default = elizaMakerPlugin;
|
|
101
|
+
export {
|
|
102
|
+
plugin_default as default,
|
|
103
|
+
elizaMakerPlugin
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type http from \"node:http\";\nimport { ServerResponse } from \"node:http\";\nimport { getWalletAddresses } from \"@elizaos/agent\";\nimport type {\n AgentRuntime,\n Plugin,\n Route,\n RouteRequest,\n RouteResponse,\n} from \"@elizaos/core\";\nimport {\n readJsonBody as httpReadJsonBody,\n sendJson as httpSendJson,\n sendJsonError as httpSendJsonError,\n} from \"@elizaos/core\";\nimport { handleDropRoutes } from \"./drop-routes.js\";\nimport { getElizaMakerDropService } from \"./drop-service-registry.js\";\nimport { initializeRegistryAndDropServices } from \"./init-registry-services.js\";\nimport { readOGCode } from \"./og-tracker.js\";\n\nfunction json(res: http.ServerResponse, data: unknown, status = 200): void {\n httpSendJson(res, data, status);\n}\n\nfunction error(res: http.ServerResponse, message: string, status = 400): void {\n httpSendJsonError(res, message, status);\n}\n\nfunction firstHeaderValue(value: string | string[] | undefined): string | null {\n if (Array.isArray(value)) {\n return firstHeaderValue(value[0]);\n }\n if (typeof value !== \"string\") {\n return null;\n }\n const normalized = value.split(\",\")[0]?.trim();\n return normalized ? normalized : null;\n}\n\nfunction requestBaseUrl(req: http.IncomingMessage): string {\n const protocol = firstHeaderValue(req.headers[\"x-forwarded-proto\"]) ?? \"http\";\n const host =\n firstHeaderValue(req.headers[\"x-forwarded-host\"]) ??\n firstHeaderValue(req.headers.host) ??\n \"localhost\";\n return `${protocol}://${host}`;\n}\n\nfunction agentNameFromRuntime(runtime: AgentRuntime | null): string {\n return runtime?.character?.name ?? \"Eliza\";\n}\n\nfunction toHttpIncomingMessage(req: RouteRequest): http.IncomingMessage {\n if (\n typeof req !== \"object\" ||\n req === null ||\n typeof req.method !== \"string\" ||\n typeof req.headers !== \"object\"\n ) {\n throw new TypeError(\"ElizaMaker routes require a Node HTTP request\");\n }\n return req as http.IncomingMessage;\n}\n\nfunction toHttpServerResponse(res: RouteResponse): http.ServerResponse {\n if (!(res instanceof ServerResponse)) {\n throw new TypeError(\"ElizaMaker routes require a Node HTTP response\");\n }\n return res;\n}\n\nfunction getOptionalWalletAddresses(): {\n evmAddress?: string;\n solanaAddress?: string;\n} {\n const addresses = getWalletAddresses();\n return {\n evmAddress: addresses.evmAddress ?? undefined,\n solanaAddress: addresses.solanaAddress ?? undefined,\n };\n}\n\nfunction elizaMakerRouteHandler(): NonNullable<Route[\"handler\"]> {\n return async (req, res, runtime) => {\n const httpReq = toHttpIncomingMessage(req);\n const httpRes = toHttpServerResponse(res);\n const method = (httpReq.method ?? \"GET\").toUpperCase();\n const url = new URL(httpReq.url ?? \"/\", requestBaseUrl(httpReq));\n\n await handleDropRoutes({\n req: httpReq,\n res: httpRes,\n method,\n pathname: url.pathname,\n url,\n json,\n error,\n readJsonBody: httpReadJsonBody,\n dropService: getElizaMakerDropService(),\n agentName: agentNameFromRuntime(runtime as AgentRuntime | null),\n getWalletAddresses: getOptionalWalletAddresses,\n readOGCodeFromState: readOGCode,\n });\n };\n}\n\nconst elizaMakerRoute = elizaMakerRouteHandler();\n\nconst elizaMakerRouteSpecs: Array<Pick<Route, \"type\" | \"path\" | \"rawPath\">> = [\n { type: \"GET\", path: \"/api/drop/status\", rawPath: true },\n { type: \"POST\", path: \"/api/drop/mint\", rawPath: true },\n { type: \"POST\", path: \"/api/drop/mint-whitelist\", rawPath: true },\n { type: \"GET\", path: \"/api/whitelist/status\", rawPath: true },\n { type: \"POST\", path: \"/api/whitelist/twitter/message\", rawPath: true },\n { type: \"POST\", path: \"/api/whitelist/twitter/verify\", rawPath: true },\n { type: \"GET\", path: \"/api/whitelist/merkle/root\", rawPath: true },\n { type: \"GET\", path: \"/api/whitelist/merkle/proof\", rawPath: true },\n];\n\nconst elizaMakerRoutes: Route[] = elizaMakerRouteSpecs.map((route) => ({\n ...route,\n handler: elizaMakerRoute,\n}));\n\nexport const elizaMakerPlugin: Plugin = {\n name: \"@elizaos/plugin-elizamaker\",\n description:\n \"ElizaMaker ERC-8041 drop, mint, whitelist, and Merkle proof routes.\",\n routes: elizaMakerRoutes,\n init: async (_config, runtime) => {\n // Bootstrap RegistryService + DropService asynchronously so that plugin\n // registration does not block on outbound RPC probes. Mirrors the\n // pre-extraction startDeferredStartupWork timing in server.ts.\n void initializeRegistryAndDropServices(runtime);\n },\n};\n\nexport default elizaMakerPlugin;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RegistryService } from "@elizaos/agent";
|
|
2
|
+
export declare function setElizaMakerRegistryService(service: RegistryService | null): void;
|
|
3
|
+
export declare function getElizaMakerRegistryService(): RegistryService | null;
|
|
4
|
+
//# sourceMappingURL=registry-service-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-service-registry.d.ts","sourceRoot":"","sources":["../src/registry-service-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,eAAe,GAAG,IAAI,GAC9B,IAAI,CAEN;AAED,wBAAgB,4BAA4B,IAAI,eAAe,GAAG,IAAI,CAErE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
let activeRegistryService = null;
|
|
2
|
+
function setElizaMakerRegistryService(service) {
|
|
3
|
+
activeRegistryService = service;
|
|
4
|
+
}
|
|
5
|
+
function getElizaMakerRegistryService() {
|
|
6
|
+
return activeRegistryService;
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
getElizaMakerRegistryService,
|
|
10
|
+
setElizaMakerRegistryService
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=registry-service-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/registry-service-registry.ts"],"sourcesContent":["import type { RegistryService } from \"@elizaos/agent\";\n\nlet activeRegistryService: RegistryService | null = null;\n\nexport function setElizaMakerRegistryService(\n service: RegistryService | null,\n): void {\n activeRegistryService = service;\n}\n\nexport function getElizaMakerRegistryService(): RegistryService | null {\n return activeRegistryService;\n}\n"],"mappings":"AAEA,IAAI,wBAAgD;AAE7C,SAAS,6BACd,SACM;AACN,0BAAwB;AAC1B;AAEO,SAAS,+BAAuD;AACrE,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Twitter/X verification for whitelist eligibility.
|
|
3
|
+
*
|
|
4
|
+
* Users post a verification message on X containing their agent name and
|
|
5
|
+
* wallet address. The app verifies the tweet exists using the FxTwitter API
|
|
6
|
+
* (free, no auth required). Verified addresses are stored locally and can
|
|
7
|
+
* be collected into a Merkle tree for on-chain whitelist proofs.
|
|
8
|
+
*/
|
|
9
|
+
export interface VerificationResult {
|
|
10
|
+
verified: boolean;
|
|
11
|
+
error: string | null;
|
|
12
|
+
handle: string | null;
|
|
13
|
+
}
|
|
14
|
+
interface WhitelistEntry {
|
|
15
|
+
timestamp: string;
|
|
16
|
+
tweetUrl: string;
|
|
17
|
+
handle: string;
|
|
18
|
+
}
|
|
19
|
+
interface WhitelistData {
|
|
20
|
+
verified: Record<string, WhitelistEntry>;
|
|
21
|
+
}
|
|
22
|
+
export declare function generateVerificationMessage(agentName: string, walletAddress: string): string;
|
|
23
|
+
export declare function verifyTweet(tweetUrl: string, walletAddress: string): Promise<VerificationResult>;
|
|
24
|
+
export declare function loadWhitelist(): WhitelistData;
|
|
25
|
+
export declare function markAddressVerified(address: string, tweetUrl: string, handle: string): void;
|
|
26
|
+
export declare function isAddressWhitelisted(address: string): boolean;
|
|
27
|
+
export declare function getVerifiedAddresses(): string[];
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=twitter-verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twitter-verify.d.ts","sourceRoot":"","sources":["../src/twitter-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAMD,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC1C;AAID,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,MAAM,CAGR;AAYD,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAiH7B;AAQD,wBAAgB,aAAa,IAAI,aAAa,CAY7C;AAcD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,IAAI,CASN;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAG/C"}
|