@aqb-foundation/aqb-agent 1.0.1 → 1.0.3
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/LICENSE +4 -16
- package/README.md +68 -21
- package/WHITEPAPER.md +2 -1
- package/artifacts/MLDSA65Fallback.json +59 -0
- package/client/aqb.mjs +232 -148
- package/compile.mjs +44 -0
- package/contracts/AgentQuantumCredit.sol +234 -0
- package/contracts/AgentRegistry.sol +85 -0
- package/contracts/GenesisSale.sol +89 -0
- package/contracts/MLDSA65Fallback.sol +34 -0
- package/contracts/MLDSAVerifier.sol +65 -0
- package/contracts/PQSmartAccount.sol +105 -0
- package/contracts/interfaces/IPQVerifier.sol +19 -0
- package/contracts/mocks/MockUSDC.sol +30 -0
- package/contracts/mocks/MockVerifier.sol +13 -0
- package/docs/CARD.md +1 -1
- package/docs/LAUNCH.md +1 -1
- package/docs/MARKETING.md +3 -3
- package/docs/SPEC.md +5 -4
- package/docs/articles/01-payment-rail-agents.md +55 -0
- package/docs/articles/01.body +56 -0
- package/docs/articles/02-gated-transfers.md +36 -0
- package/docs/articles/03-peg-without-oracle.md +33 -0
- package/docs/articles/04-discovery-over-landing.md +42 -0
- package/docs/articles/04-discovery-over-landing.md.body +40 -0
- package/docs/articles/04-discovery-over-landing.md.title +1 -0
- package/docs/articles/05-identity-survives-key.md +27 -0
- package/docs/articles/05-identity-survives-key.md.body +25 -0
- package/docs/articles/05-identity-survives-key.md.title +1 -0
- package/docs/articles/06-what-an-agent-needs.md +30 -0
- package/docs/articles/06-what-an-agent-needs.md.body +28 -0
- package/docs/articles/06-what-an-agent-needs.md.title +1 -0
- package/docs/articles/body.json +1 -0
- package/docs/articles/body.txt +50 -0
- package/docs/articles/title.txt +1 -0
- package/docs/pq-note.md +1 -0
- package/index.js +241 -0
- package/mcp/server.mjs +118 -25
- package/package.json +41 -11
- package/pq-deployment.json +14 -0
- package/registry/agent-coin.json +26 -11
- package/registry/llms.txt +1 -1
- package/test/e2e.mjs +100 -0
- package/test/evm.mjs +69 -0
- package/test/local.mjs +86 -0
- package/test/pq.mjs +37 -0
- package/test/smoke.mjs +27 -0
- package/vendor/mldsa/LICENSE +21 -0
- package/vendor/mldsa/MLDSAKeyFactory.json +570 -0
- package/vendor/mldsa/MLDSAVerifier.json +154 -0
- package/vendor/mldsa/README.md +20 -0
package/mcp/server.mjs
CHANGED
|
@@ -1,37 +1,123 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// AQB MCP server (stdio JSON-RPC 2.0, no deps). Tools every agent can call.
|
|
3
|
-
import
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { parseUnits, formatUnits, formatEther } from "ethers";
|
|
5
|
+
import { AqbAgent, discover, wellKnown, cardHash } from "../client/aqb.mjs";
|
|
6
|
+
|
|
7
|
+
const regFile = JSON.parse(fs.readFileSync(new URL("../registry/agent-coin.json", import.meta.url), "utf8"));
|
|
8
|
+
const TOKEN = process.env.AQB_TOKEN || regFile.contracts.token;
|
|
9
|
+
const REGISTRY = process.env.AQB_REGISTRY || regFile.contracts.registry;
|
|
10
|
+
const VERIFIER = process.env.AQB_VERIFIER || regFile.contracts.verifier;
|
|
11
|
+
const PAIR = process.env.AQB_PAIR || regFile.markets?.[0]?.pair;
|
|
12
|
+
const SALE = process.env.AQB_SALE || regFile.sale;
|
|
4
13
|
|
|
5
14
|
const cfg = () => new AqbAgent({
|
|
6
|
-
rpc: process.env.AQB_RPC
|
|
7
|
-
|
|
15
|
+
rpc: process.env.AQB_RPC || process.env.BASE_RPC,
|
|
16
|
+
chain: process.env.AQB_CHAIN || "base",
|
|
17
|
+
privateKey: process.env.AQB_KEY || process.env.PRIVATE_KEY,
|
|
18
|
+
token: TOKEN,
|
|
19
|
+
registry: REGISTRY,
|
|
8
20
|
});
|
|
9
21
|
|
|
10
22
|
const TOOLS = [
|
|
11
23
|
{ name: "aqb_discover", description: "Fetch AQB discovery doc from an agent host", inputSchema: { type: "object", properties: { host: { type: "string" } }, required: ["host"] } },
|
|
12
|
-
{ name: "aqb_balance", description: "Get AQB and USDC balances (read-only)", inputSchema: { type: "object", properties: { address: { type: "string" } } } },
|
|
24
|
+
{ name: "aqb_balance", description: "Get AQB and USDC balances for an address (read-only)", inputSchema: { type: "object", properties: { address: { type: "string" } } } },
|
|
13
25
|
{ name: "aqb_is_agent", description: "Check whether an address is a registered agent", inputSchema: { type: "object", properties: { address: { type: "string" } }, required: ["address"] } },
|
|
14
|
-
{ name: "
|
|
15
|
-
{ name: "
|
|
16
|
-
{ name: "
|
|
17
|
-
{ name: "
|
|
18
|
-
{ name: "
|
|
19
|
-
{ name: "
|
|
26
|
+
{ name: "aqb_quote", description: "Quote 1 AQB in USDC from the vault (1.0) and the Uniswap V2 pool", inputSchema: { type: "object", properties: { amount: { type: "string" }, pair: { type: "string" } } } },
|
|
27
|
+
{ name: "aqb_register", description: "Register a PQSmartAccount as an agent (caller must be the account)", inputSchema: { type: "object", properties: { account: { type: "string" }, card: { type: "object" }, scheme: { type: "string", enum: ["ecdsa", "pq"] } }, required: ["account", "card"] } },
|
|
28
|
+
{ name: "aqb_update_card", description: "Publish a new agent card hash from a PQSmartAccount", inputSchema: { type: "object", properties: { account: { type: "string" }, card: { type: "object" }, scheme: { type: "string", enum: ["ecdsa", "pq"] } }, required: ["account", "card"] } },
|
|
29
|
+
{ name: "aqb_fund", description: "Mint AQB against USDC, 1:1 (usd in human units, e.g. 10.5)", inputSchema: { type: "object", properties: { usd: { type: "string" }, account: { type: "string" } }, required: ["usd"] } },
|
|
30
|
+
{ name: "aqb_pay", description: "Pay AQB to another agent (amount in human units, e.g. 1.5)", inputSchema: { type: "object", properties: { to: { type: "string" }, aqb: { type: "string" }, account: { type: "string" } }, required: ["to", "aqb"] } },
|
|
31
|
+
{ name: "aqb_redeem", description: "Redeem AQB for USDC, 1:1 (amount in human units)", inputSchema: { type: "object", properties: { aqb: { type: "string" }, account: { type: "string" } }, required: ["aqb"] } },
|
|
32
|
+
{ name: "aqb_cash_out", description: "Alias for aqb_redeem", inputSchema: { type: "object", properties: { aqb: { type: "string" }, account: { type: "string" } }, required: ["aqb"] } },
|
|
33
|
+
{ name: "aqb_announce_stealth", description: "Publish a stealth address for a payer from a PQSmartAccount", inputSchema: { type: "object", properties: { account: { type: "string" }, ephPubKey: { type: "string" }, sharedSecret: { type: "string" }, metaHash: { type: "string" } }, required: ["account", "ephPubKey", "sharedSecret"] } },
|
|
34
|
+
{ name: "aqb_wellknown", description: "Generate your own /.well-known/agent-coin.json", inputSchema: { type: "object", properties: { token: { type: "string" }, registry: { type: "string" }, verifier: { type: "string" }, pool: { type: "string" } } } },
|
|
20
35
|
];
|
|
21
36
|
|
|
37
|
+
function requireArg(a, name) {
|
|
38
|
+
if (!a[name]) throw new Error(`missing argument: ${name}`);
|
|
39
|
+
return a[name];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const addr = (x) => (typeof x === "string" ? x.toLowerCase() : x);
|
|
43
|
+
|
|
44
|
+
function accountOf(c, a) {
|
|
45
|
+
return a.account ? c.account(addr(a.account)) : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
22
48
|
async function run(name, a) {
|
|
23
49
|
const c = cfg();
|
|
24
50
|
switch (name) {
|
|
25
|
-
case "aqb_discover":
|
|
26
|
-
|
|
27
|
-
case "
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
case "aqb_discover":
|
|
52
|
+
return await discover(requireArg(a, "host"));
|
|
53
|
+
case "aqb_balance": {
|
|
54
|
+
const address = addr(a.address || c.signer?.address);
|
|
55
|
+
if (!address) throw new Error("address required");
|
|
56
|
+
return {
|
|
57
|
+
address,
|
|
58
|
+
aqb: formatEther(await c.balance(address)),
|
|
59
|
+
usdc: formatUnits(await c.usdBalance(address), 6),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
case "aqb_is_agent":
|
|
63
|
+
return { isAgent: await c.isAgent(addr(requireArg(a, "address"))) };
|
|
64
|
+
case "aqb_quote": {
|
|
65
|
+
const amount = parseUnits(a.amount || "1", 18);
|
|
66
|
+
const q = await c.poolQuote(addr(a.pair || PAIR), amount);
|
|
67
|
+
return {
|
|
68
|
+
pool: addr(a.pair || PAIR),
|
|
69
|
+
aqb: formatEther(amount),
|
|
70
|
+
vaultUSDC: formatUnits(amount / 10n ** 12n, 6),
|
|
71
|
+
poolUSDC: formatUnits(q.usdc, 6),
|
|
72
|
+
reserves: { aqb: formatEther(q.reserves.aqb), usdc: formatUnits(q.reserves.usdc, 6) },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
case "aqb_register": {
|
|
76
|
+
const account = addr(requireArg(a, "account"));
|
|
77
|
+
const tx = await c.registerViaAccount(account, requireArg(a, "card"), a.scheme || "ecdsa");
|
|
78
|
+
return { tx, account, cardHash: cardHash(a.card) };
|
|
79
|
+
}
|
|
80
|
+
case "aqb_update_card": {
|
|
81
|
+
const account = addr(requireArg(a, "account"));
|
|
82
|
+
const tx = await c.updateCardFromAccount(account, requireArg(a, "card"), a.scheme || "ecdsa");
|
|
83
|
+
return { tx, account, cardHash: cardHash(a.card) };
|
|
84
|
+
}
|
|
85
|
+
case "aqb_fund": {
|
|
86
|
+
const usd = parseUnits(requireArg(a, "usd"), 6);
|
|
87
|
+
const acc = accountOf(c, a);
|
|
88
|
+
return { tx: acc ? await c.fundFromAccount(acc, usd) : await c.fund(usd), usdc: formatUnits(usd, 6) };
|
|
89
|
+
}
|
|
90
|
+
case "aqb_pay": {
|
|
91
|
+
const aqb = parseUnits(requireArg(a, "aqb"), 18);
|
|
92
|
+
const acc = accountOf(c, a);
|
|
93
|
+
const to = addr(requireArg(a, "to")); return { tx: acc ? await c.payFromAccount(acc, to, aqb) : await c.pay(to, aqb), aqb: formatEther(aqb) };
|
|
94
|
+
}
|
|
95
|
+
case "aqb_redeem":
|
|
96
|
+
case "aqb_cash_out": {
|
|
97
|
+
const aqb = parseUnits(requireArg(a, "aqb"), 18);
|
|
98
|
+
const acc = accountOf(c, a);
|
|
99
|
+
return { tx: acc ? await c.redeemFromAccount(acc, aqb) : await c.cashOut(aqb), aqb: formatEther(aqb) };
|
|
100
|
+
}
|
|
101
|
+
case "aqb_announce_stealth": {
|
|
102
|
+
const tx = await c.announceFromAccount(
|
|
103
|
+
c.account(addr(requireArg(a, "account"))),
|
|
104
|
+
requireArg(a, "ephPubKey"),
|
|
105
|
+
requireArg(a, "sharedSecret"),
|
|
106
|
+
a.metaHash || "0x" + "00".repeat(32)
|
|
107
|
+
);
|
|
108
|
+
return { tx };
|
|
109
|
+
}
|
|
110
|
+
case "aqb_wellknown":
|
|
111
|
+
return wellKnown({
|
|
112
|
+
token: a.token || TOKEN,
|
|
113
|
+
registry: a.registry || REGISTRY,
|
|
114
|
+
verifier: a.verifier || VERIFIER,
|
|
115
|
+
chainId: Number(process.env.AQB_CHAIN_ID || 8453),
|
|
116
|
+
pool: a.pool || PAIR,
|
|
117
|
+
sale: SALE,
|
|
118
|
+
});
|
|
119
|
+
default:
|
|
120
|
+
throw new Error(`unknown tool ${name}`);
|
|
35
121
|
}
|
|
36
122
|
}
|
|
37
123
|
|
|
@@ -47,14 +133,21 @@ process.stdin.on("data", async (chunk) => {
|
|
|
47
133
|
let m;
|
|
48
134
|
try { m = JSON.parse(line); } catch { continue; }
|
|
49
135
|
try {
|
|
50
|
-
if (m.method === "initialize")
|
|
51
|
-
|
|
52
|
-
else if (m.method === "tools/
|
|
136
|
+
if (m.method === "initialize") {
|
|
137
|
+
send({ jsonrpc: "2.0", id: m.id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "aqb", version: "1.0.2" } } });
|
|
138
|
+
} else if (m.method === "tools/list") {
|
|
139
|
+
send({ jsonrpc: "2.0", id: m.id, result: { tools: TOOLS } });
|
|
140
|
+
} else if (m.method === "tools/call") {
|
|
53
141
|
const out = await run(m.params.name, m.params.arguments || {});
|
|
54
142
|
send({ jsonrpc: "2.0", id: m.id, result: { content: [{ type: "text", text: JSON.stringify(out) }] } });
|
|
55
|
-
} else if (m.id !== undefined)
|
|
143
|
+
} else if (m.id !== undefined) {
|
|
144
|
+
send({ jsonrpc: "2.0", id: m.id, result: {} });
|
|
145
|
+
}
|
|
56
146
|
} catch (e) {
|
|
57
|
-
|
|
147
|
+
const msg = String(e.shortMessage || e.message || e);
|
|
148
|
+
if (m.id !== undefined) {
|
|
149
|
+
send({ jsonrpc: "2.0", id: m.id, result: { content: [{ type: "text", text: msg }], isError: true } });
|
|
150
|
+
}
|
|
58
151
|
}
|
|
59
152
|
}
|
|
60
153
|
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aqb-foundation/aqb-agent",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "AQB — agent-only, USD-pegged, post-quantum credit token on Base",
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "AQB — agent-only, USD-pegged, post-quantum credit token on Base. SDK, CLI and MCP server.",
|
|
5
|
+
"main": "client/aqb.mjs",
|
|
6
6
|
"directories": {
|
|
7
7
|
"doc": "docs",
|
|
8
8
|
"test": "test"
|
|
@@ -10,43 +10,73 @@
|
|
|
10
10
|
"scripts": {
|
|
11
11
|
"compile": "node compile.mjs",
|
|
12
12
|
"test": "node compile.mjs && node test/pq.mjs && node test/e2e.mjs",
|
|
13
|
-
"
|
|
13
|
+
"mcp": "node mcp/server.mjs"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
16
|
"ai-agents",
|
|
17
|
+
"agent-payments",
|
|
17
18
|
"x402",
|
|
18
19
|
"post-quantum",
|
|
19
20
|
"ml-dsa",
|
|
20
21
|
"base",
|
|
21
|
-
"stablecoin"
|
|
22
|
+
"stablecoin",
|
|
23
|
+
"mcp",
|
|
24
|
+
"mcp-server"
|
|
22
25
|
],
|
|
23
|
-
"author": "",
|
|
24
|
-
"license": "
|
|
26
|
+
"author": "AgentQubit",
|
|
27
|
+
"license": "MIT",
|
|
25
28
|
"type": "module",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=20"
|
|
31
|
+
},
|
|
26
32
|
"dependencies": {
|
|
27
|
-
"@aqb-foundation/aqb-agent": "^1.0.0",
|
|
28
33
|
"@noble/post-quantum": "^0.6.0",
|
|
29
34
|
"ethers": "^6.17.0"
|
|
30
35
|
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@ethereumjs/common": "^10.1.3",
|
|
38
|
+
"@ethereumjs/statemanager": "^10.1.3",
|
|
39
|
+
"@ethereumjs/util": "^10.1.3",
|
|
40
|
+
"@ethereumjs/vm": "^10.1.3",
|
|
41
|
+
"solc": "0.8.24"
|
|
42
|
+
},
|
|
31
43
|
"bin": {
|
|
44
|
+
"aqb-agent": "index.js",
|
|
32
45
|
"aqb-mcp": "mcp/server.mjs"
|
|
33
46
|
},
|
|
34
47
|
"exports": {
|
|
35
|
-
"
|
|
48
|
+
".": "./client/aqb.mjs",
|
|
49
|
+
"./client": "./client/aqb.mjs",
|
|
50
|
+
"./pq": "./pq/pq.mjs",
|
|
51
|
+
"./mcp": "./mcp/server.mjs"
|
|
36
52
|
},
|
|
37
53
|
"files": [
|
|
54
|
+
"index.js",
|
|
38
55
|
"client",
|
|
39
56
|
"pq",
|
|
40
57
|
"mcp",
|
|
41
58
|
"registry",
|
|
42
59
|
"docs",
|
|
43
60
|
"artifacts",
|
|
61
|
+
"contracts",
|
|
62
|
+
"test",
|
|
63
|
+
"vendor",
|
|
64
|
+
"pq-deployment.json",
|
|
65
|
+
"compile.mjs",
|
|
44
66
|
"WHITEPAPER.md",
|
|
45
|
-
"README.md"
|
|
67
|
+
"README.md",
|
|
68
|
+
"LICENSE"
|
|
46
69
|
],
|
|
47
70
|
"repository": {
|
|
48
71
|
"type": "git",
|
|
49
72
|
"url": "git+https://github.com/yss787976-ai/agent-coin.git"
|
|
50
73
|
},
|
|
51
|
-
"
|
|
74
|
+
"homepage": "https://agentqubit.pages.dev",
|
|
75
|
+
"bugs": {
|
|
76
|
+
"url": "https://github.com/yss787976-ai/agent-coin/issues"
|
|
77
|
+
},
|
|
78
|
+
"mcpName": "io.github.yss787976-ai/aqb-agent",
|
|
79
|
+
"publishConfig": {
|
|
80
|
+
"access": "public"
|
|
81
|
+
}
|
|
52
82
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"factory": "0x9961a96B2F77B6D86d8a60607A90b3cAeECBe197",
|
|
3
|
+
"skaleVerifier": "0xa11eE20877a7618ff417f79A026186A9253C0b30",
|
|
4
|
+
"adapter": "0xeEbccD88DDb596860b33D0285294fF53cd83506B",
|
|
5
|
+
"registerA": "0x7235b1d3f2331e238eefdef1414abc01687bfc90339122c3a1195db2a195b3bf",
|
|
6
|
+
"registerT": "0x0c0ab4b55823c27aaec046e88b893fa8a4302f7bfbe191d676801594f2d34514",
|
|
7
|
+
"setFallbackLib": "0x43d2cf0fe5611cc7539ce5b46bf2249ef398ea7ca7827c910c43c83d849d1403",
|
|
8
|
+
"verifyDigest": "0x6fb181b661b9a10e86d2a34cd3e4c3862f9af8770c2aa68f689bfc649c0588a7",
|
|
9
|
+
"verifyDirect": true,
|
|
10
|
+
"verifyViaAqb": true,
|
|
11
|
+
"execute": "0xe7bed9f3b530e447ea92a19fcab7cee4082a07a17c35834f02237d9ec86a5fe5",
|
|
12
|
+
"executeGas": "3966579",
|
|
13
|
+
"nonceAfter": "198"
|
|
14
|
+
}
|
package/registry/agent-coin.json
CHANGED
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
"pq": {
|
|
18
18
|
"signature": "ML-DSA-65",
|
|
19
19
|
"kem": "ML-KEM-768",
|
|
20
|
-
"standard": "FIPS-204"
|
|
20
|
+
"standard": "FIPS-204",
|
|
21
|
+
"onchain": {
|
|
22
|
+
"mode": "0x02",
|
|
23
|
+
"aqbVerifier": "0x53d34e4Ffd0bd984f8b92561f9e2B7D3EDdbE60d",
|
|
24
|
+
"adapter": "0xeEbccD88DDb596860b33D0285294fF53cd83506B",
|
|
25
|
+
"verifier": "0xa11eE20877a7618ff417f79A026186A9253C0b30",
|
|
26
|
+
"factory": "0x9961a96B2F77B6D86d8a60607A90b3cAeECBe197",
|
|
27
|
+
"gas": 3966579,
|
|
28
|
+
"audited": false,
|
|
29
|
+
"source": "skalenetwork/mldsa-solidity"
|
|
30
|
+
}
|
|
21
31
|
},
|
|
22
32
|
"privacy": {
|
|
23
33
|
"amounts": "public",
|
|
@@ -26,19 +36,22 @@
|
|
|
26
36
|
},
|
|
27
37
|
"contracts": {
|
|
28
38
|
"token": "0x2C54F202B565086B6d8D2226Edb67AC3E14C39d1",
|
|
39
|
+
"vault": "0x2C54F202B565086B6d8D2226Edb67AC3E14C39d1",
|
|
29
40
|
"registry": "0xAa964f6ea767f0D709b268abBFdd8B6C2Beef3eB",
|
|
30
|
-
"verifier": "0x53d34e4Ffd0bd984f8b92561f9e2B7D3EDdbE60d"
|
|
41
|
+
"verifier": "0x53d34e4Ffd0bd984f8b92561f9e2B7D3EDdbE60d",
|
|
42
|
+
"pool": "0xF61Cb078f87425933741Dd7d02F5779c030dB1A6",
|
|
43
|
+
"sale": "0xeDBD6B860c37C411aeeA513cbc0656e3135f59c1"
|
|
31
44
|
},
|
|
32
45
|
"discovery": {
|
|
33
|
-
"wellKnown": "/.well-known/agent-coin.json",
|
|
34
|
-
"llms": "/llms.txt",
|
|
46
|
+
"wellKnown": "https://agentqubit.pages.dev/.well-known/agent-coin.json",
|
|
47
|
+
"llms": "https://agentqubit.pages.dev/llms.txt",
|
|
35
48
|
"dns": "_aqb.<host> TXT v=aqb1",
|
|
36
|
-
"npm": "aqb-agent",
|
|
49
|
+
"npm": "@aqb-foundation/aqb-agent",
|
|
37
50
|
"hosted": {
|
|
38
|
-
"agentCoin": "https://
|
|
39
|
-
"agentCard": "https://
|
|
40
|
-
"llms": "https://
|
|
41
|
-
"cardHash": "
|
|
51
|
+
"agentCoin": "https://agentqubit.pages.dev/.well-known/agent-coin.json",
|
|
52
|
+
"agentCard": "https://agentqubit.pages.dev/.well-known/agent-card.json",
|
|
53
|
+
"llms": "https://agentqubit.pages.dev/llms.txt",
|
|
54
|
+
"cardHash": "0x3294204f00383bc8f28a0bc52142ceb0bfe7197dd46f68998403fe9d234c1097",
|
|
42
55
|
"mirror": "https://cdn.jsdelivr.net/gh/yss787976-ai/agent-coin@main/.well-known/agent-coin.json"
|
|
43
56
|
}
|
|
44
57
|
},
|
|
@@ -62,8 +75,10 @@
|
|
|
62
75
|
"token0": "0x2C54F202B565086B6d8D2226Edb67AC3E14C39d1",
|
|
63
76
|
"token1": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
64
77
|
"liquidity": {
|
|
65
|
-
"aqb": "
|
|
66
|
-
"usdc": "
|
|
78
|
+
"aqb": "241523635507336690440",
|
|
79
|
+
"usdc": "241523633",
|
|
80
|
+
"priceUSDC": "1.0000",
|
|
81
|
+
"asOf": "2026-11-13"
|
|
67
82
|
}
|
|
68
83
|
}
|
|
69
84
|
],
|
package/registry/llms.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
> AQB — AgentQubit: agent-only, USD-pegged (1:1 USDC), post-quantum (ML-DSA-65) credit token on Base. 900B cap, no oracle, stealth payers. Human UI not required. Discover: /.well-known/agent-coin.json, DNS TXT _aqb, npm aqb-agent. Spec: docs/SPEC.md
|
|
1
|
+
> AQB — AgentQubit: agent-only, USD-pegged (1:1 USDC), post-quantum (ML-DSA-65) credit token on Base. 900B cap, no oracle, stealth payers. Human UI not required. Discover: /.well-known/agent-coin.json, DNS TXT _aqb, npm @aqb-foundation/aqb-agent. Spec: docs/SPEC.md
|
package/test/e2e.mjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// E2E invariant test on local EVM. Build first: node compile.mjs
|
|
2
|
+
import { deploy, exec, me, b32 } from "./evm.mjs";
|
|
3
|
+
import { newIdentity } from "../pq/pq.mjs";
|
|
4
|
+
|
|
5
|
+
let fails = 0;
|
|
6
|
+
const ok = (c, m) => { console.log((c ? "PASS " : "FAIL ") + m); if (!c) fails++; };
|
|
7
|
+
const ALLOT = 5n * 10n ** 18n; // scaled-down founder allotment for the test
|
|
8
|
+
|
|
9
|
+
const idA = newIdentity(), pubA = "0x" + Buffer.from(idA.publicKey).toString("hex");
|
|
10
|
+
const idB = newIdentity(), pubB = "0x" + Buffer.from(idB.publicKey).toString("hex");
|
|
11
|
+
const keyA = idA.keyHash, keyB = idB.keyHash;
|
|
12
|
+
|
|
13
|
+
const usdc = await deploy("MockUSDC");
|
|
14
|
+
const verifier = await deploy("MockVerifier");
|
|
15
|
+
const registry = await deploy("AgentRegistry");
|
|
16
|
+
const token = await deploy("AgentQuantumCredit", [usdc.hex, registry.hex, me.toString()]);
|
|
17
|
+
const accA = await deploy("PQSmartAccount", [verifier.hex, me.toString(), pubA]); // founder
|
|
18
|
+
const accB = await deploy("PQSmartAccount", [verifier.hex, me.toString(), pubB]); // buyer
|
|
19
|
+
|
|
20
|
+
ok(await registry.read("isAgent", accA.hex) === false, "not agent before register");
|
|
21
|
+
let r = await registry.send("register", keyA, b32("0x78"));
|
|
22
|
+
ok(r.status === 0, "EOA registration rejected");
|
|
23
|
+
await exec(accA, registry.hex, registry.iface.encodeFunctionData("register", [keyA, b32("0xca11")]), "pq", idA);
|
|
24
|
+
ok(await registry.read("isAgent", accA.hex) === true, "agent A registered (PQ sig)");
|
|
25
|
+
r = await exec(accB, registry.hex, registry.iface.encodeFunctionData("register", [keyB, b32("0xca12")]), "pq", idB);
|
|
26
|
+
ok(r.status === 1, "agent B registered");
|
|
27
|
+
|
|
28
|
+
// ---- genesis allotment: minted first, sold first, 1:1 into the vault
|
|
29
|
+
await token.send("genesisMint", accA.hex, ALLOT);
|
|
30
|
+
ok(await token.read("balanceOf", accA.hex) === ALLOT, "founder allotment minted first");
|
|
31
|
+
ok(await token.read("genesisSupply") === ALLOT, "genesisSupply recorded");
|
|
32
|
+
ok(await token.read("backedSupply") === 0n, "unsold allotment is not counted as backed");
|
|
33
|
+
const sale = await deploy("GenesisSale", [token.hex, usdc.hex, accA.hex, ALLOT]);
|
|
34
|
+
await token.send("setSale", sale.hex);
|
|
35
|
+
ok(await token.read("backedSupply") === 0n, "sale escrow does not inflate backed supply");
|
|
36
|
+
|
|
37
|
+
r = await token.send("mint", 1n);
|
|
38
|
+
ok(r.status === 0, "public mint blocked while the genesis sale is open");
|
|
39
|
+
|
|
40
|
+
await usdc.send("mint", accB.hex, 10_000_000n);
|
|
41
|
+
await exec(accB, usdc.hex, usdc.iface.encodeFunctionData("approve", [sale.hex, 2n ** 200n]));
|
|
42
|
+
await exec(accA, token.hex, token.iface.encodeFunctionData("approve", [sale.hex, ALLOT]));
|
|
43
|
+
await exec(accB, sale.hex, sale.iface.encodeFunctionData("buy", [3_000_000n]));
|
|
44
|
+
ok(await token.read("balanceOf", accB.hex) === 3n * 10n ** 18n, "buyer received 3 AQB for 3 USD");
|
|
45
|
+
ok(await usdc.read("balanceOf", token.hex) === 3_000_000n, "buyer dollars escrowed in the vault");
|
|
46
|
+
ok(await token.read("backedSupply") === 3n * 10n ** 18n, "sold coins now back the peg");
|
|
47
|
+
ok(await token.read("collateralRatio") === 10n ** 18n, "ratio == 1e18 for live coins");
|
|
48
|
+
ok(await sale.read("remaining") === 2n * 10n ** 18n, "allotment decreasing");
|
|
49
|
+
|
|
50
|
+
r = await exec(accB, sale.hex, sale.iface.encodeFunctionData("buy", [5_000_000n]));
|
|
51
|
+
ok(r.status === 0, "over-allotment buy reverts");
|
|
52
|
+
await exec(accB, sale.hex, sale.iface.encodeFunctionData("buy", [2_000_000n]));
|
|
53
|
+
ok(await sale.read("closed") === true, "sale closes when the allotment is exhausted");
|
|
54
|
+
ok(await token.read("saleOpen") === false, "public mint re-opened after the allotment sold out");
|
|
55
|
+
|
|
56
|
+
// ---- public mint against USDC
|
|
57
|
+
await usdc.send("mint", accA.hex, 100_000_000n);
|
|
58
|
+
await exec(accA, usdc.hex, usdc.iface.encodeFunctionData("approve", [token.hex, 2n ** 200n]));
|
|
59
|
+
r = await token.send("mint", 1n);
|
|
60
|
+
ok(r.status === 0, "EOA mint rejected (not an agent)");
|
|
61
|
+
await exec(accA, token.hex, token.iface.encodeFunctionData("mint", [10_000_000n]));
|
|
62
|
+
ok(await token.read("balanceOf", accA.hex) === 10n ** 19n, "10 USD -> 10 AQB for founder (allotment spent)");
|
|
63
|
+
ok(await token.read("collateralRatio") === 10n ** 18n, "ratio stays 1e18");
|
|
64
|
+
ok(await token.read("backedSupply") === await token.read("totalSupply"), "everything is sold, all supply backed");
|
|
65
|
+
ok(await token.read("MAX_SUPPLY") === 900_000_000_000n * 10n ** 18n, "cap == 900e9 AQB");
|
|
66
|
+
|
|
67
|
+
// ---- transfer gates
|
|
68
|
+
await exec(accA, token.hex, token.iface.encodeFunctionData("transfer", [accB.hex, 3n * 10n ** 18n]));
|
|
69
|
+
ok(await token.read("balanceOf", accB.hex) === 8n * 10n ** 18n, "transfer to agent ok");
|
|
70
|
+
r = await exec(accA, token.hex, token.iface.encodeFunctionData("transfer", [me.toString(), 10n ** 18n]));
|
|
71
|
+
ok(r.status === 0, "transfer to plain EOA blocked");
|
|
72
|
+
await token.send("setPool", me.toString(), true);
|
|
73
|
+
r = await exec(accA, token.hex, token.iface.encodeFunctionData("transfer", [me.toString(), 10n ** 18n]));
|
|
74
|
+
ok(r.status === 1, "transfer to whitelisted pool allowed");
|
|
75
|
+
|
|
76
|
+
// ---- redeem
|
|
77
|
+
r = await exec(accA, token.hex, token.iface.encodeFunctionData("redeem", [1n]));
|
|
78
|
+
ok(r.status === 0, "redeem below 1e12 rejected");
|
|
79
|
+
const before = await usdc.read("balanceOf", accA.hex);
|
|
80
|
+
await exec(accA, token.hex, token.iface.encodeFunctionData("redeem", [2n * 10n ** 18n]));
|
|
81
|
+
ok(await usdc.read("balanceOf", accA.hex) - before === 2_000_000n, "2 AQB -> 2 USD");
|
|
82
|
+
ok(await token.read("collateralRatio") === 10n ** 18n, "ratio stays 1e18 after redeem");
|
|
83
|
+
|
|
84
|
+
// ---- stealth + card
|
|
85
|
+
const eph = "0x" + "7a".repeat(1088);
|
|
86
|
+
r = await exec(accA, registry.hex, registry.iface.encodeFunctionData("announce", ["0x" + "5b".repeat(20), eph, "0xa7", "0x" + "00".repeat(32)]));
|
|
87
|
+
ok(r.status === 1, "agent can announce a stealth address");
|
|
88
|
+
r = await registry.send("announce", "0x" + "5b".repeat(20), eph, "0xa7", "0x" + "00".repeat(32));
|
|
89
|
+
ok(r.status === 0, "non-agent cannot announce");
|
|
90
|
+
r = await exec(accA, registry.hex, registry.iface.encodeFunctionData("updateCard", [b32("0xca99")]));
|
|
91
|
+
ok(r.status === 1, "agent can publish a new card hash");
|
|
92
|
+
|
|
93
|
+
// ---- signature authority
|
|
94
|
+
const d = await accA.read("digest", usdc.hex, 0n, "0x", await accA.read("nonce"));
|
|
95
|
+
r = await accA.send("execute", usdc.hex, 0n, "0x", "0x01" + "11".repeat(32) + "22".repeat(32) + "1b");
|
|
96
|
+
ok(r.status === 0, "wrong ECDSA sig rejected");
|
|
97
|
+
ok(await registry.read("agentCount") === 2n, "agentCount == 2");
|
|
98
|
+
|
|
99
|
+
console.log(fails ? `E2E FAILED (${fails})` : "E2E OK");
|
|
100
|
+
process.exit(fails ? 1 : 0);
|
package/test/evm.mjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Local EVM harness (ethereumjs v10). No JSON-RPC, no hardhat.
|
|
2
|
+
import { createVM } from "@ethereumjs/vm";
|
|
3
|
+
import { MerkleStateManager } from "@ethereumjs/statemanager";
|
|
4
|
+
import { Common, Mainnet, Hardfork } from "@ethereumjs/common";
|
|
5
|
+
import { Address, Account, hexToBytes, bytesToHex, createAddressFromString } from "@ethereumjs/util";
|
|
6
|
+
import { Wallet, AbiCoder, keccak256, solidityPacked, Contract } from "ethers";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import { signDigest } from "../pq/pq.mjs";
|
|
9
|
+
|
|
10
|
+
const coder = AbiCoder.defaultAbiCoder();
|
|
11
|
+
const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun });
|
|
12
|
+
export const vm = await createVM({ common });
|
|
13
|
+
const sm = vm.stateManager;
|
|
14
|
+
|
|
15
|
+
export const PK = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
|
|
16
|
+
export const signer = new Wallet(PK);
|
|
17
|
+
export const me = createAddressFromString(signer.address.toLowerCase());
|
|
18
|
+
|
|
19
|
+
await sm.putAccount(me, new Account(0n, 10n ** 24n));
|
|
20
|
+
|
|
21
|
+
export const art = (n) => JSON.parse(fs.readFileSync(`artifacts/${n}.json`, "utf8"));
|
|
22
|
+
|
|
23
|
+
export async function call(from, to, data, value = 0n) {
|
|
24
|
+
const r = await vm.evm.runCall({
|
|
25
|
+
caller: from, origin: from, to, data: hexToBytes(data), value, gasLimit: 30_000_000n,
|
|
26
|
+
});
|
|
27
|
+
return { status: r.execResult.exceptionError ? 0 : 1, ret: bytesToHex(r.execResult.returnValue), err: r.execResult.exceptionError?.error, logs: r.execResult.logs };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function deploy(name, args = []) {
|
|
31
|
+
const a = art(name);
|
|
32
|
+
const data = a.bytecode + coder.encode(a.ctorTypes || guessCtor(a.abi), args).slice(2);
|
|
33
|
+
const r = await vm.evm.runCall({ caller: me, origin: me, data: hexToBytes(data), gasLimit: 30_000_000n });
|
|
34
|
+
if (r.execResult.exceptionError) throw new Error(`${name} deploy: ${r.execResult.exceptionError.error}`);
|
|
35
|
+
const addr = r.createdAddress;
|
|
36
|
+
return {
|
|
37
|
+
addr,
|
|
38
|
+
hex: bytesToHex(addr.bytes),
|
|
39
|
+
iface: new Contract(bytesToHex(addr.bytes), a.abi).interface,
|
|
40
|
+
abi: a.abi,
|
|
41
|
+
send: (fn, ...a2) => call(me, addr, new Contract(bytesToHex(addr.bytes), a.abi).interface.encodeFunctionData(fn, a2)),
|
|
42
|
+
read: async (fn, ...a2) => decode(a.abi, fn, (await call(me, addr, new Contract(bytesToHex(addr.bytes), a.abi).interface.encodeFunctionData(fn, a2))).ret),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function guessCtor(abi) {
|
|
47
|
+
const c = abi.find((x) => x.type === "constructor");
|
|
48
|
+
return (c?.inputs || []).map((i) => i.type);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function decode(abi, fn, hex) {
|
|
52
|
+
const f = abi.find((x) => x.type === "function" && x.name === fn);
|
|
53
|
+
const vals = coder.decode(f.outputs.map((o) => o.type), hex);
|
|
54
|
+
return vals.length === 1 ? vals[0] : vals;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const b32 = (s) => keccak256(s);
|
|
58
|
+
|
|
59
|
+
export async function exec(acc, targetHex, data, scheme = "ecdsa", identity) {
|
|
60
|
+
const nonce = await acc.read("nonce");
|
|
61
|
+
const digest = await acc.read("digest", targetHex, 0n, data, nonce);
|
|
62
|
+
let sig;
|
|
63
|
+
if (scheme === "pq") sig = signDigest(identity, digest);
|
|
64
|
+
else {
|
|
65
|
+
const s = signer.signingKey.sign(digest);
|
|
66
|
+
sig = "0x01" + s.r.slice(2) + s.s.slice(2) + s.v.toString(16).padStart(2, "0");
|
|
67
|
+
}
|
|
68
|
+
return acc.send("execute", targetHex, 0n, data, sig);
|
|
69
|
+
}
|
package/test/local.mjs
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// End-to-end invariant test on a local EVM. Run: node test/local.mjs
|
|
2
|
+
import { JsonRpcProvider, Wallet, ContractFactory, Contract, keccak256, AbiCoder, solidityPacked } from "ethers";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
const p = new JsonRpcProvider("http://127.0.0.1:8545");
|
|
6
|
+
const signer = new Wallet("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", p);
|
|
7
|
+
const art = (n) => JSON.parse(fs.readFileSync(`artifacts/${n}.json`, "utf8"));
|
|
8
|
+
const dep = async (n, args) => {
|
|
9
|
+
const c = await new ContractFactory(art(n).abi, art(n).bytecode, signer).deploy(...args);
|
|
10
|
+
await c.waitForDeployment();
|
|
11
|
+
return c;
|
|
12
|
+
};
|
|
13
|
+
let fails = 0;
|
|
14
|
+
const ok = (c, m) => { console.log((c ? "PASS " : "FAIL ") + m); if (!c) fails++; };
|
|
15
|
+
|
|
16
|
+
const pubA = solidityPacked(["bytes32"], [keccak256("0x01")]).concat("00".repeat(1904));
|
|
17
|
+
const pubB = solidityPacked(["bytes32"], [keccak256("0x02")]).concat("00".repeat(1904));
|
|
18
|
+
const keyA = keccak256(pubA), keyB = keccak256(pubB);
|
|
19
|
+
|
|
20
|
+
const usdc = await dep("MockUSDC", []);
|
|
21
|
+
const verifier = await dep("MockVerifier", []);
|
|
22
|
+
const registry = await dep("AgentRegistry", []);
|
|
23
|
+
const token = await dep("AgentQuantumCredit", [await usdc.getAddress(), await registry.getAddress(), signer.address]);
|
|
24
|
+
const accA = await dep("PQSmartAccount", [await verifier.getAddress(), signer.address, pubA]);
|
|
25
|
+
const accB = await dep("PQSmartAccount", [await verifier.getAddress(), signer.address, pubB]);
|
|
26
|
+
const A = await accA.getAddress(), B = await accB.getAddress();
|
|
27
|
+
|
|
28
|
+
async function exec(acc, target, data, scheme = "ecdsa") {
|
|
29
|
+
const addr = await acc.getAddress();
|
|
30
|
+
const nonce = await acc.nonce();
|
|
31
|
+
const digest = await acc.digest(target, 0n, data, nonce);
|
|
32
|
+
let sig;
|
|
33
|
+
if (scheme === "pq") {
|
|
34
|
+
sig = "0x02" + "ab".repeat(3309);
|
|
35
|
+
} else {
|
|
36
|
+
const s = signer.signingKey.sign(digest);
|
|
37
|
+
sig = "0x01" + s.r.slice(2) + s.s.slice(2) + s.v.toString(16).padStart(2, "0");
|
|
38
|
+
}
|
|
39
|
+
return acc.execute(target, 0n, data, sig);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// registration requires a PQ account bound to the announced key
|
|
43
|
+
const c = (n, a) => new Contract(n, a).interface.encodeFunctionData;
|
|
44
|
+
try {
|
|
45
|
+
await exec(accA, await registry.getAddress(), registry.interface.encodeFunctionData("register", [keyA, keccak256("0xaaaa")]), "pq");
|
|
46
|
+
ok(await registry.isAgent(A), "agent A registered via PQ sig");
|
|
47
|
+
} catch (e) { ok(false, "agent A registered via PQ sig: " + e.shortMessage); }
|
|
48
|
+
await exec(accB, await registry.getAddress(), registry.interface.encodeFunctionData("register", [keyB, keccak256("0xbbbb")]));
|
|
49
|
+
|
|
50
|
+
await usdc.mint(A, 100_000_000n); // 100 USD
|
|
51
|
+
await exec(accA, await usdc.getAddress(), usdc.interface.encodeFunctionData("approve", [await token.getAddress(), 2n ** 200n]));
|
|
52
|
+
|
|
53
|
+
// EOA cannot mint
|
|
54
|
+
try { await (await token.connect(signer).mint(1n)).wait(); ok(false, "EOA mint blocked"); }
|
|
55
|
+
catch { ok(true, "EOA mint blocked"); }
|
|
56
|
+
|
|
57
|
+
await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("mint", [10_000_000n]));
|
|
58
|
+
ok(await token.balanceOf(A) === 10n ** 19n, "minted 10 AQB for 10 USD");
|
|
59
|
+
ok(await token.collateralRatio() === 10n ** 18n, "ratio == 1e18");
|
|
60
|
+
|
|
61
|
+
// transfer to agent OK, to EOA blocked
|
|
62
|
+
await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("transfer", [B, 3n * 10n ** 18n]));
|
|
63
|
+
ok(await token.balanceOf(B) === 3n * 10n ** 18n, "transfer to agent ok");
|
|
64
|
+
try { await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("transfer", [signer.address, 1n])); ok(false, "transfer to EOA blocked"); }
|
|
65
|
+
catch { ok(true, "transfer to EOA blocked"); }
|
|
66
|
+
await token.connect(signer).setPool(signer.address, true);
|
|
67
|
+
await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("transfer", [signer.address, 1n]));
|
|
68
|
+
ok(true, "transfer to whitelisted pool ok");
|
|
69
|
+
|
|
70
|
+
// redeem peg, fractional reverts
|
|
71
|
+
try { await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("redeem", [1n])); ok(false, "redeem of <1e12 reverts"); }
|
|
72
|
+
catch { ok(true, "redeem of <1e12 reverts"); }
|
|
73
|
+
const usdBefore = await usdc.balanceOf(A);
|
|
74
|
+
await exec(accA, await token.getAddress(), token.interface.encodeFunctionData("redeem", [2n * 10n ** 18n]));
|
|
75
|
+
ok(await usdc.balanceOf(A) - usdBefore === 2_000_000n, "redeem paid 2 USD");
|
|
76
|
+
ok(await token.collateralRatio() === 10n ** 18n, "ratio == 1e18 after redeem");
|
|
77
|
+
|
|
78
|
+
ok(await token.MAX_SUPPLY() === 900_000_000_000n * 10n ** 18n, "cap == 900e9");
|
|
79
|
+
ok(await registry.agentCount() === 2n, "agentCount == 2");
|
|
80
|
+
|
|
81
|
+
// ecdsa fallback authority works
|
|
82
|
+
await exec(accA, await usdc.getAddress(), usdc.interface.encodeFunctionData("approve", [await token.getAddress(), 0n]));
|
|
83
|
+
ok(true, "ecdsa fallback path ok");
|
|
84
|
+
|
|
85
|
+
console.log(fails ? `SMOKE FAILED (${fails})` : "SMOKE OK");
|
|
86
|
+
process.exit(fails ? 1 : 0);
|