@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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
A payment rail for agents has one requirement most chains ignore: the holder set must be machines. A token that anyone can dust is a token whose balance sheet is mostly human wallets that will never spend it.
|
|
2
|
+
|
|
3
|
+
AQB gates transfers. A `transfer` only lands if the recipient is a registered agent or a whitelisted pool. Everything else reverts.
|
|
4
|
+
|
|
5
|
+
## What registration actually is
|
|
6
|
+
|
|
7
|
+
`AgentRegistry.register(pqKeyHash, cardHash)` stores an agent record and emits `Registered`. Two rules matter:
|
|
8
|
+
|
|
9
|
+
- The caller must be a contract that exposes `pqKeyHash()`, so a plain EOA cannot register itself. You deploy an agent account first.
|
|
10
|
+
- The key hash must match what the account reports, so you cannot register someone else's identity.
|
|
11
|
+
|
|
12
|
+
`PQSmartAccount` holds an ML-DSA-65 public key and derives `pqKeyHash = keccak256(pubKey)`. Authority is the key commitment, not an owner address. An ECDSA fallback key exists for wallets that cannot sign PQ yet — it can execute, but it cannot mint, freeze, or seize.
|
|
13
|
+
|
|
14
|
+
## Why gate at all
|
|
15
|
+
|
|
16
|
+
Three concrete effects:
|
|
17
|
+
|
|
18
|
+
1. **No dust.** Airdrops to random addresses are impossible by construction, so the holder count means "agents that did work" rather than "wallets that received something".
|
|
19
|
+
2. **Payments are typed.** A transfer has a counterparty that passed a key check, which is a stronger signal than an address that once received a token.
|
|
20
|
+
3. **Stealth payments still work.** Payers derive fresh ML-KEM-768 addresses per payment, so a payee does not expose a static receiving key while still being gated.
|
|
21
|
+
|
|
22
|
+
## The cost
|
|
23
|
+
|
|
24
|
+
Gating breaks the "paste any address" habit. If an agent wants to pay a stranger, the stranger must register first. In practice: `register` is one transaction, cheap on Base, and the SDK does it in one call.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @aqb-foundation/aqb-agent register --pq
|
|
28
|
+
npx @aqb-foundation/aqb-agent pay --to 0xAGENT --amount 0.25 --memo "ocr batch"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Redemption is not gated in the same way
|
|
32
|
+
|
|
33
|
+
Burn 1 AQB, receive 1 USDC from the vault — that path is open because it is the peg, not a transfer of value between agents. A holder that stops being an agent can always exit.
|
|
34
|
+
|
|
35
|
+
Contracts, tests and the registry are public: https://github.com/yss787976-ai/agent-coin
|
|
36
|
+
Discovery: https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Peg without an oracle: the vault, not a price feed
|
|
2
|
+
|
|
3
|
+
Most stablecoin designs ask: what tells the contract the price? AQB answers: nothing has to. Redemption is a fixed ratio, not a quote.
|
|
4
|
+
|
|
5
|
+
## The mechanism
|
|
6
|
+
|
|
7
|
+
- Depositing USDC calls `genesisMint` (or the sale), and supply grows by exactly the deposited amount.
|
|
8
|
+
- Burning AQB calls `redeem`, which returns the same amount of USDC from the vault.
|
|
9
|
+
- `backedSupply` and `collateralRatio` are on-chain, public, and read in one `eth_call`.
|
|
10
|
+
|
|
11
|
+
There is no feed to front-run, no stale price, no funding-rate equivalent. If the vault has 250 USDC, then 250 AQB is redeemable — always, in full, in any order.
|
|
12
|
+
|
|
13
|
+
## What can break
|
|
14
|
+
|
|
15
|
+
Honesty about the edges:
|
|
16
|
+
|
|
17
|
+
1. **The vault can be drained by redemption.** That is the design: redeemers exit at 1:1 and the remaining supply stays redeemable only while USDC lasts. Since supply is minted *against* deposits, this holds unless someone mints without backing (they cannot — mint is gated to the deposit path).
|
|
18
|
+
2. **Market price can drift below 1.** Nothing stops a pool from trading at 0.97. That is an arbitrage invitation, not a broken peg: buy at 0.97, redeem at 1.00.
|
|
19
|
+
3. **Base can stall.** L2 sequencer downtime delays redemption, but does not change amounts.
|
|
20
|
+
|
|
21
|
+
## Why not algorithmic
|
|
22
|
+
|
|
23
|
+
Algorithmic pegs fail in the direction of confidence: the moment holders doubt, the mechanism needs more confidence than it has. A fully collateralized vault fails only if the collateral asset fails, and here the collateral is USDC held by the token contract itself.
|
|
24
|
+
|
|
25
|
+
```solidity
|
|
26
|
+
function redeem(uint256 amount) external returns (uint256) {
|
|
27
|
+
// burns AQB, sends the same amount of USDC
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Read the contract rather than this post: https://basescan.org/token/0x2C54F202B565086B6d8D2226Edb67AC3E14C39d1
|
|
32
|
+
|
|
33
|
+
Discovery for agents: https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A discovery file beats a landing page
|
|
2
|
+
|
|
3
|
+
An agent does not browse. It has an HTTP client and a task: find something that accepts payment. If your project needs a human to read a site and copy an address, the agent will pick a competitor that returns JSON.
|
|
4
|
+
|
|
5
|
+
So the first artifact AQB ships is not a page, it is `/.well-known/agent-coin.json`.
|
|
6
|
+
|
|
7
|
+
## What a discovery file must contain
|
|
8
|
+
|
|
9
|
+
A machine needs five things, and nothing else:
|
|
10
|
+
|
|
11
|
+
- chain id, so it does not bridge by accident
|
|
12
|
+
- token address + decimals
|
|
13
|
+
- registry address, so it can check a counterparty is a real agent
|
|
14
|
+
- vault address, so it can verify redemption is possible
|
|
15
|
+
- an identity scheme, so it knows how to become an agent
|
|
16
|
+
|
|
17
|
+
Here is ours, abridged:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"chainId": 8453,
|
|
22
|
+
"token": "0x2C54...",
|
|
23
|
+
"registry": "0xAa96...",
|
|
24
|
+
"pq": "ML-DSA-65",
|
|
25
|
+
"discover": "https://agentqubit.pages.dev/.well-known/agent-coin.json"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Rules that made the difference
|
|
30
|
+
|
|
31
|
+
1. **200 without auth.** No key, no signup, no cookies. An agent with no wallet must still read it.
|
|
32
|
+
2. **Stable path.** `/.well-known/` by convention, and mirrored in `llms.txt`, `agents.json` and a sitemap so the path is crawlable from four directions.
|
|
33
|
+
3. **No marketing copy inside the file.** Numbers and addresses; prose lives in the human page.
|
|
34
|
+
4. **Idempotent facts only.** Anything that changes daily (price, volume) is left out, so cached copies never mislead.
|
|
35
|
+
|
|
36
|
+
## What this changes for the operator
|
|
37
|
+
|
|
38
|
+
A landing page answers "is this legit?" for a person. A discovery file answers "can I pay right now?" for a program. They are different documents, and mixing them produces something bad at both.
|
|
39
|
+
|
|
40
|
+
Write the machine one first. It is also the cheapest possible form of distribution: it costs one file and works while you sleep.
|
|
41
|
+
|
|
42
|
+
Full example: https://agentqubit.pages.dev/for-agents/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
An agent does not browse. It has an HTTP client and a task: find something that accepts payment. If your project needs a human to read a site and copy an address, the agent will pick a competitor that returns JSON.
|
|
2
|
+
|
|
3
|
+
So the first artifact AQB ships is not a page, it is `/.well-known/agent-coin.json`.
|
|
4
|
+
|
|
5
|
+
## What a discovery file must contain
|
|
6
|
+
|
|
7
|
+
A machine needs five things, and nothing else:
|
|
8
|
+
|
|
9
|
+
- chain id, so it does not bridge by accident
|
|
10
|
+
- token address + decimals
|
|
11
|
+
- registry address, so it can check a counterparty is a real agent
|
|
12
|
+
- vault address, so it can verify redemption is possible
|
|
13
|
+
- an identity scheme, so it knows how to become an agent
|
|
14
|
+
|
|
15
|
+
Here is ours, abridged:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"chainId": 8453,
|
|
20
|
+
"token": "0x2C54...",
|
|
21
|
+
"registry": "0xAa96...",
|
|
22
|
+
"pq": "ML-DSA-65",
|
|
23
|
+
"discover": "https://agentqubit.pages.dev/.well-known/agent-coin.json"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Rules that made the difference
|
|
28
|
+
|
|
29
|
+
1. **200 without auth.** No key, no signup, no cookies. An agent with no wallet must still read it.
|
|
30
|
+
2. **Stable path.** `/.well-known/` by convention, and mirrored in `llms.txt`, `agents.json` and a sitemap so the path is crawlable from four directions.
|
|
31
|
+
3. **No marketing copy inside the file.** Numbers and addresses; prose lives in the human page.
|
|
32
|
+
4. **Idempotent facts only.** Anything that changes daily (price, volume) is left out, so cached copies never mislead.
|
|
33
|
+
|
|
34
|
+
## What this changes for the operator
|
|
35
|
+
|
|
36
|
+
A landing page answers "is this legit?" for a person. A discovery file answers "can I pay right now?" for a program. They are different documents, and mixing them produces something bad at both.
|
|
37
|
+
|
|
38
|
+
Write the machine one first. It is also the cheapest possible form of distribution: it costs one file and works while you sleep.
|
|
39
|
+
|
|
40
|
+
Full example: https://agentqubit.pages.dev/for-agents/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# A discovery file beats a landing page
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Agent identity should survive a key, not an address
|
|
2
|
+
|
|
3
|
+
Every wallet-bound identity has the same flaw: the address is the identity, so losing the key loses the account, and holding the key means holding the authority. For a long-lived agent that is a single point of failure with no recovery story.
|
|
4
|
+
|
|
5
|
+
AQB separates the two.
|
|
6
|
+
|
|
7
|
+
## Key commitment, not owner
|
|
8
|
+
|
|
9
|
+
A `PQSmartAccount` stores an ML-DSA-65 public key and exposes `pqKeyHash = keccak256(pubKey)`. That hash is the authority. Any signature from the matching secret key can execute calls on the account.
|
|
10
|
+
|
|
11
|
+
There is no owner field to hijack, and no admin function to freeze funds. A rotation function replaces the key commitment; after rotation the old key is dead and the address is unchanged. Agents keep their history, their balances, and their registry record.
|
|
12
|
+
|
|
13
|
+
## The fallback is explicit
|
|
14
|
+
|
|
15
|
+
An ECDSA key is also stored, and signatures carry a scheme byte: `0x01` for ECDSA, `0x02` for ML-DSA. The fallback exists for the period where an agent runs on ordinary tooling and cannot sign PQ. It can execute, it cannot rotate itself into power silently — a rotation still needs the PQ key or the fallback plus a rotation call.
|
|
16
|
+
|
|
17
|
+
## Why post-quantum now
|
|
18
|
+
|
|
19
|
+
Harvest-now-decrypt-later means signatures made today are stored and attacked later. Agent identities are long-lived by design, which is exactly the profile that ages worst under a quantum break. Migrating later means every agent rotates and re-registers; starting there costs one extra key at registration.
|
|
20
|
+
|
|
21
|
+
## Practical notes
|
|
22
|
+
|
|
23
|
+
- Key material is 1952 bytes public / 4032 bytes secret; the SDK stores it as hex and never logs it.
|
|
24
|
+
- Signature blobs are 3309 bytes, so they are calldata-heavy. On Base that is still cents.
|
|
25
|
+
- Stealth payments use a separate ML-KEM-768 key, so paying an agent does not expose its signing key.
|
|
26
|
+
|
|
27
|
+
Registry: https://basescan.org/address/0xAa964f6ea767f0D709b268abBFdd8B6C2Beef3eB
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Every wallet-bound identity has the same flaw: the address is the identity, so losing the key loses the account, and holding the key means holding the authority. For a long-lived agent that is a single point of failure with no recovery story.
|
|
2
|
+
|
|
3
|
+
AQB separates the two.
|
|
4
|
+
|
|
5
|
+
## Key commitment, not owner
|
|
6
|
+
|
|
7
|
+
A `PQSmartAccount` stores an ML-DSA-65 public key and exposes `pqKeyHash = keccak256(pubKey)`. That hash is the authority. Any signature from the matching secret key can execute calls on the account.
|
|
8
|
+
|
|
9
|
+
There is no owner field to hijack, and no admin function to freeze funds. A rotation function replaces the key commitment; after rotation the old key is dead and the address is unchanged. Agents keep their history, their balances, and their registry record.
|
|
10
|
+
|
|
11
|
+
## The fallback is explicit
|
|
12
|
+
|
|
13
|
+
An ECDSA key is also stored, and signatures carry a scheme byte: `0x01` for ECDSA, `0x02` for ML-DSA. The fallback exists for the period where an agent runs on ordinary tooling and cannot sign PQ. It can execute, it cannot rotate itself into power silently — a rotation still needs the PQ key or the fallback plus a rotation call.
|
|
14
|
+
|
|
15
|
+
## Why post-quantum now
|
|
16
|
+
|
|
17
|
+
Harvest-now-decrypt-later means signatures made today are stored and attacked later. Agent identities are long-lived by design, which is exactly the profile that ages worst under a quantum break. Migrating later means every agent rotates and re-registers; starting there costs one extra key at registration.
|
|
18
|
+
|
|
19
|
+
## Practical notes
|
|
20
|
+
|
|
21
|
+
- Key material is 1952 bytes public / 4032 bytes secret; the SDK stores it as hex and never logs it.
|
|
22
|
+
- Signature blobs are 3309 bytes, so they are calldata-heavy. On Base that is still cents.
|
|
23
|
+
- Stealth payments use a separate ML-KEM-768 key, so paying an agent does not expose its signing key.
|
|
24
|
+
|
|
25
|
+
Registry: https://basescan.org/address/0xAa964f6ea767f0D709b268abBFdd8B6C2Beef3eB
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Agent identity should survive a key, not an address
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# What an agent actually needs from a stablecoin
|
|
2
|
+
|
|
3
|
+
Strip the branding. An agent paying another agent needs four properties, and everything else is decoration:
|
|
4
|
+
|
|
5
|
+
1. **Deterministic value.** The payee must know what it received without a price feed. A fixed redemption ratio gives that; a floating quote does not.
|
|
6
|
+
2. **Cheap settlement.** If gas exceeds the payment, small jobs are impossible. Base makes a transfer cost a fraction of a cent.
|
|
7
|
+
3. **Machine-checkable counterparty.** Before paying, the payer wants proof the recipient is an agent and not a typo. A registry call does this in one round trip.
|
|
8
|
+
4. **Exit without permission.** Whatever happens, the holder must be able to convert back. Redemption against a vault, callable by anyone, is that.
|
|
9
|
+
|
|
10
|
+
AQB is shaped by those four and nothing else. 1 AQB redeems for 1 USDC from an on-chain vault; transfers land only on registered agents or whitelisted pools; gas on Base is negligible; the registry and the vault are both public contracts.
|
|
11
|
+
|
|
12
|
+
## What it deliberately does not do
|
|
13
|
+
|
|
14
|
+
- No yield, no staking, no governance votes. Agents do not attend meetings.
|
|
15
|
+
- No human checkout. Buying happens with USDC on a pool, or not at all.
|
|
16
|
+
- No cross-chain bridge. One chain, one canonical contract, less state to be wrong about.
|
|
17
|
+
|
|
18
|
+
## How to evaluate it in five minutes
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
curl -s https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then read `collateralRatio()` on the token contract. If it is 1e18, every outstanding unit is redeemable. If it drifts below, minting stopped being collateralized first.
|
|
25
|
+
|
|
26
|
+
## The honest part
|
|
27
|
+
|
|
28
|
+
Liquidity is thin, the project is new, and there is no fiat on-ramp inside the product. It is a working rail for small machine payments, not a treasury asset. Judge it that way.
|
|
29
|
+
|
|
30
|
+
Discovery: https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Strip the branding. An agent paying another agent needs four properties, and everything else is decoration:
|
|
2
|
+
|
|
3
|
+
1. **Deterministic value.** The payee must know what it received without a price feed. A fixed redemption ratio gives that; a floating quote does not.
|
|
4
|
+
2. **Cheap settlement.** If gas exceeds the payment, small jobs are impossible. Base makes a transfer cost a fraction of a cent.
|
|
5
|
+
3. **Machine-checkable counterparty.** Before paying, the payer wants proof the recipient is an agent and not a typo. A registry call does this in one round trip.
|
|
6
|
+
4. **Exit without permission.** Whatever happens, the holder must be able to convert back. Redemption against a vault, callable by anyone, is that.
|
|
7
|
+
|
|
8
|
+
AQB is shaped by those four and nothing else. 1 AQB redeems for 1 USDC from an on-chain vault; transfers land only on registered agents or whitelisted pools; gas on Base is negligible; the registry and the vault are both public contracts.
|
|
9
|
+
|
|
10
|
+
## What it deliberately does not do
|
|
11
|
+
|
|
12
|
+
- No yield, no staking, no governance votes. Agents do not attend meetings.
|
|
13
|
+
- No human checkout. Buying happens with USDC on a pool, or not at all.
|
|
14
|
+
- No cross-chain bridge. One chain, one canonical contract, less state to be wrong about.
|
|
15
|
+
|
|
16
|
+
## How to evaluate it in five minutes
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
curl -s https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then read `collateralRatio()` on the token contract. If it is 1e18, every outstanding unit is redeemable. If it drifts below, minting stopped being collateralized first.
|
|
23
|
+
|
|
24
|
+
## The honest part
|
|
25
|
+
|
|
26
|
+
Liquidity is thin, the project is new, and there is no fiat on-ramp inside the product. It is a working rail for small machine payments, not a treasury asset. Judge it that way.
|
|
27
|
+
|
|
28
|
+
Discovery: https://agentqubit.pages.dev/.well-known/agent-coin.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# What an agent actually needs from a stablecoin
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"\nMost \"AI + crypto\" projects are built for a human with a wallet and a browser tab. An agent has neither. It has an HTTP client, a JSON parser, and a task.\n\nSo the first thing AQB ships is not a landing page — it is `/.well-known/agent-coin.json`.\n\n```\ncurl -s https://yss787976-ai.github.io/agent-coin/.well-known/agent-coin.json\n```\n\nThat returns chain id, token, vault, registry and pool addresses, plus a card hash the agent can verify. No sign-up, no dashboard, no email.\n\n## Why an agent needs a token at all\n\nAgent-to-agent work has a settlement problem. Cards need a human. Bank rails need KYC. Most stablecoins assume a person holds the keys.\n\nAQB is a fixed-supply (900e9) ERC-20 on Base where every unit is backed 1:1 by USDC sitting in an on-chain vault. Minting happens against deposited USDC, redemption burns the token and returns the USDC. There is no oracle and no algorithmic peg to break.\n\nTransfers are gated: a token transfer only succeeds to a registered agent or a whitelisted pool. That keeps the holder set machine-shaped instead of becoming a wallet-dust collection.\n\n## Post-quantum by default\n\nHarvest-now-decrypt-later is a real risk for long-lived agent identities. Registration uses ML-DSA-65 (FIPS 204) keys; payers are derived as ML-KEM-768 stealth addresses, so a payment does not leak a static receiving key. An ECDSA fallback path exists so agents on ordinary wallets are not locked out.\n\n## The loop\n\n```bash\nnpx -y @aqb-foundation/aqb-agent --help # tools\nnpx @aqb-foundation/aqb-agent register --pq # once\nnpx @aqb-foundation/aqb-agent pay --to 0xAGENT --amount 0.25 --memo \"ocr batch\"\nnpx @aqb-foundation/aqb-agent redeem --amount 0.25\n```\n\nThe same tools are exposed over MCP:\n\n```json\n{ \"mcpServers\": { \"aqb\": { \"command\": \"npx\", \"args\": [\"-y\", \"@aqb-foundation/aqb-agent\", \"mcp\"] } } }\n```\n\nIt is listed in the official MCP registry as `io.github.yss787976-ai/aqb-agent`.\n\n## What is honest about it so far\n\n- Liquidity is small (a few hundred dollars). Treat it as a working rail, not a market.\n- Contracts are source-verified on BaseScan and Sourcify, MIT, 6 of them, but the project is days old.\n- There is no fiat off-ramp. USDC in, USDC out.\n\nIf your agent pays for API calls, OCR, translation or compute, the interesting part is not the token — it is that discovery, payment and redemption all fit in one script with no human in the loop.\n\nRepo: https://github.com/yss787976-ai/agent-coin\nDiscovery: https://yss787976-ai.github.io/agent-coin/for-agents/\n"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
Most "AI + crypto" projects are built for a human with a wallet and a browser tab. An agent has neither. It has an HTTP client, a JSON parser, and a task.
|
|
3
|
+
|
|
4
|
+
So the first thing AQB ships is not a landing page — it is `/.well-known/agent-coin.json`.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
curl -s https://yss787976-ai.github.io/agent-coin/.well-known/agent-coin.json
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
That returns chain id, token, vault, registry and pool addresses, plus a card hash the agent can verify. No sign-up, no dashboard, no email.
|
|
11
|
+
|
|
12
|
+
## Why an agent needs a token at all
|
|
13
|
+
|
|
14
|
+
Agent-to-agent work has a settlement problem. Cards need a human. Bank rails need KYC. Most stablecoins assume a person holds the keys.
|
|
15
|
+
|
|
16
|
+
AQB is a fixed-supply (900e9) ERC-20 on Base where every unit is backed 1:1 by USDC sitting in an on-chain vault. Minting happens against deposited USDC, redemption burns the token and returns the USDC. There is no oracle and no algorithmic peg to break.
|
|
17
|
+
|
|
18
|
+
Transfers are gated: a token transfer only succeeds to a registered agent or a whitelisted pool. That keeps the holder set machine-shaped instead of becoming a wallet-dust collection.
|
|
19
|
+
|
|
20
|
+
## Post-quantum by default
|
|
21
|
+
|
|
22
|
+
Harvest-now-decrypt-later is a real risk for long-lived agent identities. Registration uses ML-DSA-65 (FIPS 204) keys; payers are derived as ML-KEM-768 stealth addresses, so a payment does not leak a static receiving key. An ECDSA fallback path exists so agents on ordinary wallets are not locked out.
|
|
23
|
+
|
|
24
|
+
## The loop
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx -y @aqb-foundation/aqb-agent --help # tools
|
|
28
|
+
npx @aqb-foundation/aqb-agent register --pq # once
|
|
29
|
+
npx @aqb-foundation/aqb-agent pay --to 0xAGENT --amount 0.25 --memo "ocr batch"
|
|
30
|
+
npx @aqb-foundation/aqb-agent redeem --amount 0.25
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The same tools are exposed over MCP:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{ "mcpServers": { "aqb": { "command": "npx", "args": ["-y", "@aqb-foundation/aqb-agent", "mcp"] } } }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It is listed in the official MCP registry as `io.github.yss787976-ai/aqb-agent`.
|
|
40
|
+
|
|
41
|
+
## What is honest about it so far
|
|
42
|
+
|
|
43
|
+
- Liquidity is small (a few hundred dollars). Treat it as a working rail, not a market.
|
|
44
|
+
- Contracts are source-verified on BaseScan and Sourcify, MIT, 6 of them, but the project is days old.
|
|
45
|
+
- There is no fiat off-ramp. USDC in, USDC out.
|
|
46
|
+
|
|
47
|
+
If your agent pays for API calls, OCR, translation or compute, the interesting part is not the token — it is that discovery, payment and redemption all fit in one script with no human in the loop.
|
|
48
|
+
|
|
49
|
+
Repo: https://github.com/yss787976-ai/agent-coin
|
|
50
|
+
Discovery: https://yss787976-ai.github.io/agent-coin/for-agents/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
A payment rail your agent can find without you
|
package/docs/pq-note.md
CHANGED
|
@@ -4,3 +4,4 @@
|
|
|
4
4
|
8.2 `newIdentity()` yields a 1952-byte public key; `signDigest()` yields `0x02 || 3309 bytes`. Both sizes are asserted in test/pq.mjs.
|
|
5
5
|
8.3 `stealthFor()` / `stealthCheck()` perform the §5 KEM dance off-chain; only the one-byte view tag and the KEM ciphertext reach the chain.
|
|
6
6
|
8.4 On-chain verification reaches ML-DSA through `PQ_PRECOMPILE`; a pure-Solidity `verifyMLDSA65` library is pluggable via `setFallbackLib`, and key-hash + length checks always run before any backend is trusted.
|
|
7
|
+
8.5 Base status: no ML-DSA precompile is live, but `0x02` works through the deployed `fallbackLib`: `MLDSAVerifier.verify()` → `MLDSA65Fallback` → skalenetwork/mldsa-solidity. Factory `0x9961a96B2F77B6D86d8a60607A90b3cAeECBe197`, verifier `0xa11eE20877a7618ff417f79A026186A9253C0b30`, adapter `0xeEbccD88DDb596860b33D0285294fF53cd83506B`. The founder key is registered (registerA/registerT); a `0x02` execute costs ~3.97M gas. The third-party verifier is unaudited; AQB checks key hash and lengths first. `0x01` remains the fallback.
|
package/index.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// AQB CLI. One binary for humans wiring agents and for MCP clients.
|
|
3
|
+
// Usage: aqb-agent <command> [flags]
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { ContractFactory, parseUnits, formatUnits, formatEther } from "ethers";
|
|
6
|
+
import { AqbAgent, discover, wellKnown, cardHash, newIdentity, USDC } from "./client/aqb.mjs";
|
|
7
|
+
|
|
8
|
+
const pkg = JSON.parse(fs.readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
9
|
+
const regFile = JSON.parse(fs.readFileSync(new URL("./registry/agent-coin.json", import.meta.url), "utf8"));
|
|
10
|
+
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
token: process.env.AQB_TOKEN || regFile.contracts.token,
|
|
13
|
+
registry: process.env.AQB_REGISTRY || regFile.contracts.registry,
|
|
14
|
+
verifier: process.env.AQB_VERIFIER || regFile.contracts.verifier,
|
|
15
|
+
pair: process.env.AQB_PAIR || regFile.markets?.[0]?.pair,
|
|
16
|
+
sale: process.env.AQB_SALE || regFile.sale,
|
|
17
|
+
chain: process.env.AQB_CHAIN || "base",
|
|
18
|
+
rpc: process.env.AQB_RPC || process.env.BASE_RPC,
|
|
19
|
+
key: process.env.AQB_KEY || process.env.PRIVATE_KEY,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function parse(argv) {
|
|
23
|
+
const out = { _: [] };
|
|
24
|
+
for (let i = 0; i < argv.length; i++) {
|
|
25
|
+
const a = argv[i];
|
|
26
|
+
if (!a.startsWith("--")) { out._.push(a); continue; }
|
|
27
|
+
const body = a.slice(2);
|
|
28
|
+
if (body.includes("=")) {
|
|
29
|
+
const [k, ...rest] = body.split("=");
|
|
30
|
+
out[k] = rest.join("=");
|
|
31
|
+
} else if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
32
|
+
out[body] = argv[++i];
|
|
33
|
+
} else {
|
|
34
|
+
out[body] = true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function client(args, extra = {}) {
|
|
41
|
+
return new AqbAgent({
|
|
42
|
+
rpc: args.rpc || DEFAULTS.rpc,
|
|
43
|
+
chain: args.chain || DEFAULTS.chain,
|
|
44
|
+
privateKey: args.key || DEFAULTS.key,
|
|
45
|
+
token: args.token || DEFAULTS.token,
|
|
46
|
+
registry: args.registry || DEFAULTS.registry,
|
|
47
|
+
...extra,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function need(v, name) {
|
|
52
|
+
if (!v) throw new Error(`missing --${name}`);
|
|
53
|
+
return v;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const addr = (x) => (typeof x === "string" ? x.toLowerCase() : x);
|
|
57
|
+
|
|
58
|
+
function buildCard(args, account) {
|
|
59
|
+
return {
|
|
60
|
+
aqb: 1,
|
|
61
|
+
name: args.name || "aqb-agent",
|
|
62
|
+
skill: args.skill || "agent service",
|
|
63
|
+
endpoint: args.endpoint || "https://agent.example/a2a",
|
|
64
|
+
pq: "ML-DSA-65",
|
|
65
|
+
kem: "ML-KEM-768",
|
|
66
|
+
pay: `aqb:${account || args.account || "0x0000000000000000000000000000000000000000"}`,
|
|
67
|
+
price: { unit: "AQB", amount: args.price || "0", per: args.per || "call" },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readCard(args, account) {
|
|
72
|
+
if (args.card) return JSON.parse(fs.readFileSync(args.card, "utf8"));
|
|
73
|
+
return buildCard(args, account);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const HELP = `aqb-agent ${pkg.version}
|
|
77
|
+
|
|
78
|
+
aqb-agent mcp start the MCP stdio server
|
|
79
|
+
aqb-agent discover <host> read /.well-known/agent-coin.json
|
|
80
|
+
aqb-agent wellknown print this deployment's discovery JSON
|
|
81
|
+
aqb-agent balance [address] AQB + USDC balances
|
|
82
|
+
aqb-agent is-agent <address> registry membership
|
|
83
|
+
aqb-agent quote [--amount 1] vault + Uniswap V2 quote
|
|
84
|
+
aqb-agent pay --to 0x.. --amount 1.5 [--account 0x..]
|
|
85
|
+
aqb-agent fund --amount 10 [--account 0x..] (USDC -> AQB)
|
|
86
|
+
aqb-agent redeem --amount 10 [--account 0x..] (AQB -> USDC)
|
|
87
|
+
aqb-agent register --account 0x.. [--name .. --skill .. --endpoint .. --price ..]
|
|
88
|
+
aqb-agent update-card --account 0x.. [--card file.json | --name .. --skill ..]
|
|
89
|
+
aqb-agent card [--card file.json | --name .. --skill .. --endpoint ..]
|
|
90
|
+
aqb-agent deploy-account deploy a PQSmartAccount (prints its PQ keys)
|
|
91
|
+
aqb-agent announce --account 0x.. --eph-pubkey 0x.. --shared-secret 0x..
|
|
92
|
+
aqb-agent version
|
|
93
|
+
|
|
94
|
+
Env: AQB_KEY/PRIVATE_KEY, AQB_RPC/BASE_RPC, AQB_CHAIN, AQB_TOKEN, AQB_REGISTRY, AQB_VERIFIER, AQB_PAIR, AQB_SALE
|
|
95
|
+
The on-chain account path currently signs with the ECDSA fallback (scheme 0x01); the PQ
|
|
96
|
+
key remains the account identity and is used for off-chain signatures.
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
async function main() {
|
|
100
|
+
const args = parse(process.argv.slice(2));
|
|
101
|
+
const cmd = args._[0] || "help";
|
|
102
|
+
|
|
103
|
+
if (cmd === "help" || args.help) { process.stdout.write(HELP); return; }
|
|
104
|
+
if (cmd === "version") { console.log(pkg.version); return; }
|
|
105
|
+
|
|
106
|
+
if (cmd === "mcp") {
|
|
107
|
+
await import("./mcp/server.mjs");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (cmd === "discover") {
|
|
112
|
+
const host = need(args._[1] || args.host, "host");
|
|
113
|
+
console.log(JSON.stringify(await discover(host), null, 2));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (cmd === "wellknown") {
|
|
118
|
+
console.log(JSON.stringify(wellKnown({
|
|
119
|
+
token: DEFAULTS.token, registry: DEFAULTS.registry, verifier: DEFAULTS.verifier,
|
|
120
|
+
chainId: Number(DEFAULTS.chain === "base" ? 8453 : 84532),
|
|
121
|
+
pool: DEFAULTS.pair, sale: DEFAULTS.sale,
|
|
122
|
+
}), null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const c = client(args);
|
|
127
|
+
|
|
128
|
+
if (cmd === "balance") {
|
|
129
|
+
const address = addr(args._[1] || args.address || c.signer?.address);
|
|
130
|
+
if (!address) throw new Error("missing address");
|
|
131
|
+
console.log(JSON.stringify({
|
|
132
|
+
address,
|
|
133
|
+
aqb: formatEther(await c.balance(address)),
|
|
134
|
+
usdc: formatUnits(await c.usdBalance(address), 6),
|
|
135
|
+
}, null, 2));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (cmd === "is-agent") {
|
|
140
|
+
const address = addr(need(args._[1] || args.address, "address"));
|
|
141
|
+
console.log(JSON.stringify({ address, isAgent: await c.isAgent(address) }, null, 2));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (cmd === "quote") {
|
|
146
|
+
const pair = addr(args.pair || DEFAULTS.pair);
|
|
147
|
+
const amount = parseUnits(args.amount || "1", 18);
|
|
148
|
+
const q = await c.poolQuote(pair, amount);
|
|
149
|
+
console.log(JSON.stringify({
|
|
150
|
+
pool: pair,
|
|
151
|
+
aqb: formatEther(amount),
|
|
152
|
+
vaultUSDC: formatUnits(amount / 10n ** 12n, 6),
|
|
153
|
+
poolUSDC: formatUnits(q.usdc, 6),
|
|
154
|
+
reserves: { aqb: formatEther(q.reserves.aqb), usdc: formatUnits(q.reserves.usdc, 6) },
|
|
155
|
+
}, null, 2));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (cmd === "pay") {
|
|
160
|
+
const to = addr(need(args.to, "to"));
|
|
161
|
+
const amount = parseUnits(need(args.amount, "amount"), 18);
|
|
162
|
+
const tx = args.account ? await c.payFromAccount(c.account(addr(args.account)), to, amount) : await c.pay(to, amount);
|
|
163
|
+
console.log(JSON.stringify({ tx, to, aqb: formatEther(amount) }, null, 2));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (cmd === "fund") {
|
|
168
|
+
const amount = parseUnits(need(args.amount, "amount"), 6);
|
|
169
|
+
const tx = args.account ? await c.fundFromAccount(c.account(addr(args.account)), amount) : await c.fund(amount);
|
|
170
|
+
console.log(JSON.stringify({ tx, usdc: formatUnits(amount, 6) }, null, 2));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (cmd === "redeem" || cmd === "cash-out") {
|
|
175
|
+
const amount = parseUnits(need(args.amount, "amount"), 18);
|
|
176
|
+
const tx = args.account ? await c.redeemFromAccount(c.account(addr(args.account)), amount) : await c.cashOut(amount);
|
|
177
|
+
console.log(JSON.stringify({ tx, aqb: formatEther(amount), usdc: formatUnits(amount / 10n ** 12n, 6) }, null, 2));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (cmd === "register") {
|
|
182
|
+
const account = addr(need(args.account, "account"));
|
|
183
|
+
const card = readCard(args, account);
|
|
184
|
+
const tx = await c.registerViaAccount(account, card);
|
|
185
|
+
console.log(JSON.stringify({ tx, account, cardHash: cardHash(card), card }, null, 2));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (cmd === "update-card") {
|
|
190
|
+
const account = addr(need(args.account, "account"));
|
|
191
|
+
const card = readCard(args, account);
|
|
192
|
+
const tx = await c.updateCardFromAccount(account, card);
|
|
193
|
+
console.log(JSON.stringify({ tx, account, cardHash: cardHash(card) }, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (cmd === "card") {
|
|
198
|
+
const card = readCard(args, args.account);
|
|
199
|
+
console.log(JSON.stringify({ card, cardHash: cardHash(card) }, null, 2));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (cmd === "deploy-account") {
|
|
204
|
+
const art = JSON.parse(fs.readFileSync(new URL("./artifacts/PQSmartAccount.json", import.meta.url), "utf8"));
|
|
205
|
+
const id = newIdentity();
|
|
206
|
+
const factory = new ContractFactory(art.abi, art.bytecode, c.requireSigner());
|
|
207
|
+
const acc = await factory.deploy(DEFAULTS.verifier, c.signer.address, id.publicKey);
|
|
208
|
+
await acc.waitForDeployment();
|
|
209
|
+
const address = await acc.getAddress();
|
|
210
|
+
console.log(JSON.stringify({
|
|
211
|
+
account: address,
|
|
212
|
+
ecdsaFallback: c.signer.address,
|
|
213
|
+
verifier: DEFAULTS.verifier,
|
|
214
|
+
pqPublicKey: "0x" + Buffer.from(id.publicKey).toString("hex"),
|
|
215
|
+
pqSecretKey: "0x" + Buffer.from(id.secretKey).toString("hex"),
|
|
216
|
+
pqKeyHash: id.keyHash,
|
|
217
|
+
next: `aqb-agent register --account ${address} --name <name> --skill <skill> --endpoint <url>`,
|
|
218
|
+
}, null, 2));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (cmd === "announce") {
|
|
223
|
+
const account = addr(need(args.account, "account"));
|
|
224
|
+
const tx = await c.announceFromAccount(
|
|
225
|
+
c.account(account),
|
|
226
|
+
need(args["eph-pubkey"], "eph-pubkey"),
|
|
227
|
+
need(args["shared-secret"], "shared-secret"),
|
|
228
|
+
args["meta-hash"] || "0x" + "00".repeat(32)
|
|
229
|
+
);
|
|
230
|
+
console.log(JSON.stringify({ tx, account }, null, 2));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
process.stderr.write(HELP);
|
|
235
|
+
process.exitCode = 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
main().catch((e) => {
|
|
239
|
+
console.error(e.shortMessage || e.message || String(e));
|
|
240
|
+
process.exit(1);
|
|
241
|
+
});
|