phygital-token-mcp 0.1.1
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/README.md +59 -0
- package/cursor-mcp.example.json +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +481 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/docs.d.ts +17 -0
- package/dist/lib/docs.js +121 -0
- package/dist/lib/docs.js.map +1 -0
- package/dist/lib/format.d.ts +13 -0
- package/dist/lib/format.js +12 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/gating-json.d.ts +127 -0
- package/dist/lib/gating-json.js +346 -0
- package/dist/lib/gating-json.js.map +1 -0
- package/dist/lib/graphify.d.ts +2 -0
- package/dist/lib/graphify.js +35 -0
- package/dist/lib/graphify.js.map +1 -0
- package/dist/lib/instructions.d.ts +131 -0
- package/dist/lib/instructions.js +232 -0
- package/dist/lib/instructions.js.map +1 -0
- package/dist/lib/paths.d.ts +14 -0
- package/dist/lib/paths.js +77 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/rpc.d.ts +3 -0
- package/dist/lib/rpc.js +22 -0
- package/dist/lib/rpc.js.map +1 -0
- package/dist/lib/sdk-surface.d.ts +33 -0
- package/dist/lib/sdk-surface.js +101 -0
- package/dist/lib/sdk-surface.js.map +1 -0
- package/dist/lib/verification.d.ts +43 -0
- package/dist/lib/verification.js +166 -0
- package/dist/lib/verification.js.map +1 -0
- package/docs/building-on-phygital/overview.md +46 -0
- package/docs/building-on-phygital/rust-cpi.md +55 -0
- package/docs/glossary.md +43 -0
- package/docs/sdk/surface-area.md +77 -0
- package/docs/verification/methods.md +58 -0
- package/docs/verification/overview.md +70 -0
- package/docs/verification/verify-asset-composable.md +97 -0
- package/package.json +52 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Verification overview
|
|
2
|
+
|
|
3
|
+
Phygital assets support two fundamentally different checks. Pick the wrong one and you either annoy users with unnecessary taps or accept replayable proofs.
|
|
4
|
+
|
|
5
|
+
## The two questions
|
|
6
|
+
|
|
7
|
+
| Kind | Question | User taps again? |
|
|
8
|
+
|------|----------|------------------|
|
|
9
|
+
| **Identification** | Which asset is this? | No |
|
|
10
|
+
| **Authentication** | Is the holder here right now? | Yes |
|
|
11
|
+
|
|
12
|
+
Think of identification like reading a signed badge from a prior scan. Think of authentication like requiring a live tap before a high-value action.
|
|
13
|
+
|
|
14
|
+
## Decision tree
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Do you need the holder physically present right now?
|
|
18
|
+
├─ NO → Identification
|
|
19
|
+
│ ├─ Online, replay protection → verifyDynamicUrl
|
|
20
|
+
│ └─ Offline / no backend → verifyDynamicUrlWithoutCounterCheck (weaker)
|
|
21
|
+
└─ YES → Authentication
|
|
22
|
+
├─ Off-chain only (UI login, no chain) → verifyWithChallengeResponse / OverNfc
|
|
23
|
+
├─ On-chain proof for your program → beginVerifyAsset composable flow (see below)
|
|
24
|
+
└─ Transfer ownership → beginTransfer → completeTransfer (NOT verify_asset)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Off-chain vs on-chain authentication
|
|
28
|
+
|
|
29
|
+
`verifyWithChallengeResponse` and `verifyWithChallengeResponseOverNfc` are **off-chain only**. They prompt an NFC tap and verify the WebAuthn signature in the client. They do **not** submit a transaction.
|
|
30
|
+
|
|
31
|
+
For on-chain proof that a passkey holder signed a specific message at a specific slot, use the composable `beginVerifyAsset` flow.
|
|
32
|
+
|
|
33
|
+
## Two ways to compose on-chain with your program
|
|
34
|
+
|
|
35
|
+
Both patterns start the same on the client: `beginVerifyAsset` → tap → `buildVerifyAssetArgs` (or `completeVerifyAsset`).
|
|
36
|
+
|
|
37
|
+
| Pattern | Client transaction | Your on-chain program |
|
|
38
|
+
|---------|-------------------|----------------------|
|
|
39
|
+
| **A — Inspect** | `[secp256r1_verify, verify_asset, your_ix]` | Scans instructions sysvar for a preceding `verify_asset`; checks `message` bytes |
|
|
40
|
+
| **B — CPI** | `[secp256r1_verify, your_ix]` | CPIs `phygital_token::verify_asset` using args from `buildVerifyAssetArgs` |
|
|
41
|
+
|
|
42
|
+
### Pattern A — Client posts `verify_asset`, program inspects
|
|
43
|
+
|
|
44
|
+
The client includes the full `verify_asset` instruction (via `completeVerifyAsset` or `getVerifyAssetInstruction`). Your program runs **after** it and reads the instructions sysvar to confirm a matching `verify_asset` ran with the expected `message`.
|
|
45
|
+
|
|
46
|
+
Reference: `phygital-spend` — `require_matching_verify_asset`.
|
|
47
|
+
|
|
48
|
+
### Pattern B — Client posts `secp256r1_verify`, program CPIs `verify_asset`
|
|
49
|
+
|
|
50
|
+
The client uses `buildVerifyAssetArgs` to get `secp256r1Verify` and the verify args, but does **not** include `verify_asset` in the transaction. Your program receives those args (via instruction data) and CPIs `verify_asset` via `VerifyAssetCpiBuilder` from `phygital-token-client`.
|
|
51
|
+
|
|
52
|
+
`secp256r1_verify` must still appear **before** your program's instruction in the transaction.
|
|
53
|
+
|
|
54
|
+
## Message binding
|
|
55
|
+
|
|
56
|
+
- **Transfer** challenge binds the asset PDA (recipient chosen later at wallet confirm).
|
|
57
|
+
- **Verify asset** challenge binds arbitrary `message` bytes. Hash on-chain: `SHA256(message)`.
|
|
58
|
+
- **Dynamic URL** binds `counter || nonce` (uint32 BE + 8 random bytes).
|
|
59
|
+
|
|
60
|
+
Embed domain-specific bytes in `message` so a proof for one action cannot authorize another.
|
|
61
|
+
|
|
62
|
+
## Slot freshness
|
|
63
|
+
|
|
64
|
+
`verify_asset` records `asset.last_transfer_slot`. Each verification must use a **strictly greater** slot. Complete the flow promptly after `beginVerifyAsset` (~512 slots).
|
|
65
|
+
|
|
66
|
+
## Next docs
|
|
67
|
+
|
|
68
|
+
- [Verification methods](./methods.md) — every `verify.ts` export
|
|
69
|
+
- [Composable verify_asset](./verify-asset-composable.md) — `buildVerifyAssetArgs` and both patterns
|
|
70
|
+
- [Building on phygital](../building-on-phygital/overview.md)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Composable `verify_asset` (TypeScript)
|
|
2
|
+
|
|
3
|
+
On-chain passkey authentication for custom programs. **Not** the same as `verifyWithChallengeResponse` (which is off-chain only).
|
|
4
|
+
|
|
5
|
+
## Session flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
beginVerifyAsset({ rpc, message })
|
|
9
|
+
↓
|
|
10
|
+
authenticatePasskeyForVerifyAsset(session) // WebAuthn NFC tap
|
|
11
|
+
↓
|
|
12
|
+
buildVerifyAssetArgs(session, response) // or completeVerifyAsset(...)
|
|
13
|
+
↓
|
|
14
|
+
assemble transaction (Pattern A or B below)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Functions
|
|
18
|
+
|
|
19
|
+
### `beginVerifyAsset({ rpc, message: Uint8Array })`
|
|
20
|
+
|
|
21
|
+
Slot-bound challenge: `SHA256("verify_asset" || SHA256(message) || slotHash)`.
|
|
22
|
+
|
|
23
|
+
### `buildVerifyAssetArgs(session, response)`
|
|
24
|
+
|
|
25
|
+
Returns `secp256r1Verify`, `signedMessageIndex`, `clientDataJson`, `asset`, `assetPda`.
|
|
26
|
+
|
|
27
|
+
### `completeVerifyAsset(session, response)`
|
|
28
|
+
|
|
29
|
+
Returns `[secp256r1Verify, verifyAssetInstruction]`.
|
|
30
|
+
|
|
31
|
+
## Pattern A — Client posts `verify_asset`, program inspects
|
|
32
|
+
|
|
33
|
+
**Client transaction order:**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
secp256r1_verify → verify_asset → your_program_ix
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const session = await beginVerifyAsset({ rpc, message });
|
|
41
|
+
const response = await authenticatePasskeyForVerifyAsset(session);
|
|
42
|
+
|
|
43
|
+
const [secp256r1Verify, verifyAssetIx] = await completeVerifyAsset(
|
|
44
|
+
session,
|
|
45
|
+
response,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const myIx = buildMyProgramInstruction(/* binds same message bytes */);
|
|
49
|
+
|
|
50
|
+
await sendTransaction([secp256r1Verify, verifyAssetIx, myIx], { feePayer });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Your Rust program:** Scan `instructions_sysvar` for the `verify_asset` instruction that ran earlier in this transaction. Decode and verify `message` matches your canonical payload. Reference: `phygital-spend`.
|
|
54
|
+
|
|
55
|
+
## Pattern B — Client posts `secp256r1_verify`, program CPIs `verify_asset`
|
|
56
|
+
|
|
57
|
+
**Client transaction order:**
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
secp256r1_verify → your_program_ix
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const session = await beginVerifyAsset({ rpc, message });
|
|
65
|
+
const response = await authenticatePasskeyForVerifyAsset(session);
|
|
66
|
+
|
|
67
|
+
const { secp256r1Verify, signedMessageIndex, clientDataJson, assetPda } =
|
|
68
|
+
await buildVerifyAssetArgs(session, response);
|
|
69
|
+
|
|
70
|
+
// Pass verify args to your program via instruction data
|
|
71
|
+
const myIx = buildMyProgramInstruction({
|
|
72
|
+
asset: assetPda,
|
|
73
|
+
secp256r1VerifyArgs: {
|
|
74
|
+
signedMessageIndex,
|
|
75
|
+
slotNumber: session.slotNumber,
|
|
76
|
+
clientDataJson,
|
|
77
|
+
},
|
|
78
|
+
message: session.message,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await sendTransaction([secp256r1Verify, myIx], { feePayer });
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Your Rust program:** CPI `verify_asset` using `VerifyAssetCpiBuilder` from `phygital-token-client`. The `secp256r1_verify` instruction must appear earlier in the same transaction (verified via instructions sysvar inside `verify_asset`).
|
|
85
|
+
|
|
86
|
+
## `verify_asset` instruction layout
|
|
87
|
+
|
|
88
|
+
Accounts: `asset` (writable), `slot_hashes`, `instructions_sysvar`.
|
|
89
|
+
|
|
90
|
+
Args: `{ secp256r1VerifyArgs: { signedMessageIndex, slotNumber, clientDataJson }, message }`.
|
|
91
|
+
|
|
92
|
+
## On-chain effects
|
|
93
|
+
|
|
94
|
+
- Verifies WebAuthn signature against slot-bound challenge
|
|
95
|
+
- Sets `asset.last_transfer_slot = slotNumber`
|
|
96
|
+
- Emits `VerifyAssetEvent`
|
|
97
|
+
- Does not change token owner or balance
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "phygital-token-mcp",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "MCP server for the phygital-token Solana program, TypeScript SDK, and Rust client",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/jychab/phygital-nfts.git",
|
|
9
|
+
"directory": "mcp/phygital-token"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"mcp",
|
|
13
|
+
"model-context-protocol",
|
|
14
|
+
"solana",
|
|
15
|
+
"phygital",
|
|
16
|
+
"webauthn",
|
|
17
|
+
"nft"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"bin": {
|
|
22
|
+
"phygital-token-mcp": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"docs",
|
|
27
|
+
"README.md",
|
|
28
|
+
"cursor-mcp.example.json"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
+
"@noble/hashes": "^2.0.1",
|
|
36
|
+
"@solana/kit": "^6.4.0",
|
|
37
|
+
"phygital-token-sdk": "^0.6.0",
|
|
38
|
+
"zod": "^3.25.76"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.15.21",
|
|
42
|
+
"typescript": "^5.7.3"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json && node scripts/add-shebang.mjs",
|
|
49
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
50
|
+
"start": "node dist/index.js"
|
|
51
|
+
}
|
|
52
|
+
}
|