phygital-token-mcp 0.2.2 → 0.4.0
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 +12 -15
- package/dist/index.js +154 -452
- package/dist/index.js.map +1 -1
- package/dist/lib/docs.d.ts +1 -1
- package/dist/lib/docs.js +2 -9
- package/dist/lib/docs.js.map +1 -1
- package/dist/lib/gating-json.d.ts +0 -3
- package/dist/lib/gating-json.js +1 -50
- package/dist/lib/gating-json.js.map +1 -1
- package/dist/lib/instructions.js +2 -2
- package/dist/lib/instructions.js.map +1 -1
- package/dist/lib/paths.d.ts +1 -8
- package/dist/lib/paths.js +1 -29
- package/dist/lib/paths.js.map +1 -1
- package/dist/lib/sdk-surface.d.ts +2 -8
- package/dist/lib/sdk-surface.js +4 -40
- package/dist/lib/sdk-surface.js.map +1 -1
- package/dist/lib/verification.d.ts +1 -26
- package/dist/lib/verification.js +13 -65
- package/dist/lib/verification.js.map +1 -1
- package/docs/building-on-phygital/overview.md +4 -4
- package/docs/building-on-phygital/rust-cpi.md +2 -1
- package/docs/sdk/surface-area.md +6 -13
- package/docs/verification/methods.md +16 -10
- package/docs/verification/overview.md +4 -4
- package/docs/verification/verify-asset-composable.md +1 -1
- package/package.json +4 -4
- package/dist/lib/graphify.d.ts +0 -2
- package/dist/lib/graphify.js +0 -35
- package/dist/lib/graphify.js.map +0 -1
- package/dist/lib/rpc.d.ts +0 -3
- package/dist/lib/rpc.js +0 -22
- package/dist/lib/rpc.js.map +0 -1
package/dist/lib/verification.js
CHANGED
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
export const ON_CHAIN_PATTERNS = {
|
|
2
|
-
inspect: {
|
|
3
|
-
id: "inspect",
|
|
4
|
-
name: "Pattern A — client posts verify_asset, program inspects",
|
|
5
|
-
clientTransaction: ["secp256r1_verify", "verify_asset", "your_program_ix"],
|
|
6
|
-
clientSdk: ["beginVerifyAsset", "completeVerifyAsset", "getVerifyAssetInstruction"],
|
|
7
|
-
programRust: "Scan instructions sysvar for preceding verify_asset; check message bytes",
|
|
8
|
-
reference: "programs/phygital-spend",
|
|
9
|
-
},
|
|
10
|
-
cpi: {
|
|
11
|
-
id: "cpi",
|
|
12
|
-
name: "Pattern B — client posts secp256r1_verify, program CPIs verify_asset",
|
|
13
|
-
clientTransaction: ["secp256r1_verify", "your_program_ix"],
|
|
14
|
-
clientSdk: ["beginVerifyAsset", "buildVerifyAssetArgs"],
|
|
15
|
-
programRust: "VerifyAssetCpiBuilder from phygital-token-client",
|
|
16
|
-
reference: "clients/rust/phygital-token",
|
|
17
|
-
},
|
|
18
|
-
standalone: {
|
|
19
|
-
id: "standalone",
|
|
20
|
-
name: "Standalone on-chain verify_asset (no custom program)",
|
|
21
|
-
clientTransaction: ["secp256r1_verify", "verify_asset"],
|
|
22
|
-
clientSdk: ["beginVerifyAsset", "completeVerifyAsset"],
|
|
23
|
-
programRust: "N/A",
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
1
|
const RECOMMENDATIONS = {
|
|
27
2
|
product_page_lookup: {
|
|
28
3
|
method: "identification — verifyDynamicUrl",
|
|
@@ -50,30 +25,16 @@ const RECOMMENDATIONS = {
|
|
|
50
25
|
cautions: ["Copied links can be replayed."],
|
|
51
26
|
},
|
|
52
27
|
login_ui_only: {
|
|
53
|
-
method: "authentication off-chain — startAuthentication +
|
|
28
|
+
method: "authentication off-chain — startAuthentication + verifyResponse",
|
|
54
29
|
sdkExports: [
|
|
55
|
-
"
|
|
56
|
-
"
|
|
30
|
+
"startAuthentication",
|
|
31
|
+
"verifyResponse",
|
|
57
32
|
],
|
|
58
33
|
requiresTap: true,
|
|
59
34
|
onChain: false,
|
|
60
|
-
rationale: "Server issues challenge; client taps NFC via
|
|
35
|
+
rationale: "Server issues challenge; client taps NFC via startAuthentication; server verifies with verifyResponse. No on-chain transaction.",
|
|
61
36
|
docIds: ["verification:methods", "verification:overview"],
|
|
62
|
-
cautions: ["Run
|
|
63
|
-
},
|
|
64
|
-
vault_gated_experience: {
|
|
65
|
-
method: "vault gate — server-verified tap + evaluateAssetGating",
|
|
66
|
-
sdkExports: [
|
|
67
|
-
"startAuthenticationWithChallengeResponse",
|
|
68
|
-
"verifyWithChallengeResponse",
|
|
69
|
-
"evaluateAssetGating",
|
|
70
|
-
"Gating",
|
|
71
|
-
"GatingTraitValue",
|
|
72
|
-
],
|
|
73
|
-
requiresTap: true,
|
|
74
|
-
onChain: false,
|
|
75
|
-
rationale: "Prove vault holder is present (server-verified tap), then check owner wallet holdings for tiered unlock.",
|
|
76
|
-
docIds: ["verification:methods", "verification:overview", "gating:overview"],
|
|
37
|
+
cautions: ["Run verifyResponse on your server, not in the browser."],
|
|
77
38
|
},
|
|
78
39
|
onchain_standalone_verify: {
|
|
79
40
|
method: "on-chain verify_asset only",
|
|
@@ -128,28 +89,17 @@ const RECOMMENDATIONS = {
|
|
|
128
89
|
docIds: ["verification:overview"],
|
|
129
90
|
cautions: ["Do not use verifyDynamicUrl for transfers."],
|
|
130
91
|
},
|
|
131
|
-
wallet_holdings_gate: {
|
|
132
|
-
method: "gating — evaluateAssetGating",
|
|
133
|
-
sdkExports: ["evaluateAssetGating", "Gating", "GatingTraitValue"],
|
|
134
|
-
requiresTap: false,
|
|
135
|
-
onChain: false,
|
|
136
|
-
rationale: "Wallet holdings via DAS given an asset publicKey. Pair with off-chain tap auth when you need live presence.",
|
|
137
|
-
docIds: ["gating:overview"],
|
|
138
|
-
cautions: [
|
|
139
|
-
"startAuthenticationWithChallengeResponse + verifyWithChallengeResponse first, then evaluateAssetGating.",
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
92
|
native_mobile_app: {
|
|
143
|
-
method: "authentication off-chain — startAuthentication (transceive) +
|
|
93
|
+
method: "authentication off-chain — startAuthentication (transceive) + verifyResponse",
|
|
144
94
|
sdkExports: [
|
|
145
|
-
"
|
|
146
|
-
"
|
|
95
|
+
"startAuthentication",
|
|
96
|
+
"verifyResponse",
|
|
147
97
|
],
|
|
148
98
|
requiresTap: true,
|
|
149
99
|
onChain: false,
|
|
150
|
-
rationale: "Pass transceive to
|
|
100
|
+
rationale: "Pass transceive to startAuthentication for native NFC readers; verify on server. Use beginVerifyAsset for on-chain proof.",
|
|
151
101
|
docIds: ["verification:methods"],
|
|
152
|
-
cautions: ["Run
|
|
102
|
+
cautions: ["Run verifyResponse on your server, not in the native client."],
|
|
153
103
|
},
|
|
154
104
|
};
|
|
155
105
|
export function recommendVerification(useCase) {
|
|
@@ -161,7 +111,6 @@ export function listVerificationUseCases() {
|
|
|
161
111
|
{ id: "deep_link_from_prior_scan", summary: "App opens from signed deep link" },
|
|
162
112
|
{ id: "offline_identification", summary: "Identify asset offline (weak replay)" },
|
|
163
113
|
{ id: "login_ui_only", summary: "Off-chain tap-to-login (no chain tx)" },
|
|
164
|
-
{ id: "vault_gated_experience", summary: "tap + wallet holdings check" },
|
|
165
114
|
{ id: "onchain_standalone_verify", summary: "On-chain verify_asset only" },
|
|
166
115
|
{
|
|
167
116
|
id: "onchain_inspect_verify_asset",
|
|
@@ -172,7 +121,6 @@ export function listVerificationUseCases() {
|
|
|
172
121
|
summary: "Pattern B: buildVerifyAssetArgs, your program CPIs verify_asset",
|
|
173
122
|
},
|
|
174
123
|
{ id: "transfer_ownership", summary: "Claim/transfer token to new owner" },
|
|
175
|
-
{ id: "wallet_holdings_gate", summary: "Gate by wallet NFTs/tokens" },
|
|
176
124
|
{ id: "native_mobile_app", summary: "Native app off-chain authentication" },
|
|
177
125
|
];
|
|
178
126
|
}
|
|
@@ -181,13 +129,13 @@ Identification vs Authentication
|
|
|
181
129
|
├── Need holder present NOW?
|
|
182
130
|
│ NO → verifyDynamicUrl / verifyDynamicUrlWithoutCounterCheck
|
|
183
131
|
│ YES → Need on-chain proof?
|
|
184
|
-
│ NO →
|
|
185
|
-
│ →
|
|
132
|
+
│ NO → startAuthentication (client tap)
|
|
133
|
+
│ → verifyResponse (server verify) — off-chain only
|
|
186
134
|
│ YES → beginVerifyAsset composable flow:
|
|
187
135
|
│ Pattern A: [secp256r1_verify, verify_asset, your_ix] — program inspects sysvar
|
|
188
136
|
│ Pattern B: [secp256r1_verify, your_ix] — program CPIs verify_asset
|
|
189
137
|
└── Transfer ownership? → beginTransfer → completeTransfer
|
|
190
138
|
|
|
191
|
-
|
|
139
|
+
verifyResponse never submits verify_asset. Returns { isVerified, asset } (owner is asset.owner). Run it on your server. On-chain proof always uses beginVerifyAsset.
|
|
192
140
|
`.trim();
|
|
193
141
|
//# sourceMappingURL=verification.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/lib/verification.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verification.js","sourceRoot":"","sources":["../../src/lib/verification.ts"],"names":[],"mappings":"AAwBA,MAAM,eAAe,GAA4D;IAC/E,mBAAmB,EAAE;QACnB,MAAM,EAAE,mCAAmC;QAC3C,UAAU,EAAE,CAAC,kBAAkB,CAAC;QAChC,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,KAAK;QACd,SAAS,EACP,kFAAkF;QACpF,MAAM,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;KAC1D;IACD,yBAAyB,EAAE;QACzB,MAAM,EAAE,mCAAmC;QAC3C,UAAU,EAAE,CAAC,kBAAkB,CAAC;QAChC,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,8CAA8C;QACzD,MAAM,EAAE,CAAC,sBAAsB,CAAC;KACjC;IACD,sBAAsB,EAAE;QACtB,MAAM,EAAE,sDAAsD;QAC9D,UAAU,EAAE,CAAC,qCAAqC,CAAC;QACnD,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,oDAAoD;QAC/D,MAAM,EAAE,CAAC,sBAAsB,CAAC;QAChC,QAAQ,EAAE,CAAC,+BAA+B,CAAC;KAC5C;IACD,aAAa,EAAE;QACb,MAAM,EAAE,iEAAiE;QACzE,UAAU,EAAE;YACV,qBAAqB;YACrB,gBAAgB;SACjB;QACD,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,KAAK;QACd,SAAS,EACP,iIAAiI;QACnI,MAAM,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;QACzD,QAAQ,EAAE,CAAC,wDAAwD,CAAC;KACrE;IACD,yBAAyB,EAAE;QACzB,MAAM,EAAE,4BAA4B;QACpC,UAAU,EAAE,CAAC,kBAAkB,EAAE,mCAAmC,EAAE,qBAAqB,CAAC;QAC5F,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,yDAAyD;QACpE,MAAM,EAAE,CAAC,sCAAsC,CAAC;KACjD;IACD,4BAA4B,EAAE;QAC5B,MAAM,EAAE,sEAAsE;QAC9E,UAAU,EAAE;YACV,kBAAkB;YAClB,mCAAmC;YACnC,qBAAqB;YACrB,2BAA2B;SAC5B;QACD,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,SAAS;QACzB,SAAS,EACP,mIAAmI;QACrI,MAAM,EAAE;YACN,sCAAsC;YACtC,+BAA+B;YAC/B,+BAA+B;SAChC;KACF;IACD,wBAAwB,EAAE;QACxB,MAAM,EAAE,kEAAkE;QAC1E,UAAU,EAAE;YACV,kBAAkB;YAClB,mCAAmC;YACnC,sBAAsB;SACvB;QACD,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,KAAK;QACrB,SAAS,EACP,uHAAuH;QACzH,MAAM,EAAE;YACN,sCAAsC;YACtC,+BAA+B;YAC/B,+BAA+B;SAChC;KACF;IACD,kBAAkB,EAAE;QAClB,MAAM,EAAE,6CAA6C;QACrD,UAAU,EAAE,CAAC,eAAe,EAAE,gCAAgC,EAAE,kBAAkB,CAAC;QACnF,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,sDAAsD;QACjE,MAAM,EAAE,CAAC,uBAAuB,CAAC;QACjC,QAAQ,EAAE,CAAC,4CAA4C,CAAC;KACzD;IACD,iBAAiB,EAAE;QACjB,MAAM,EAAE,8EAA8E;QACtF,UAAU,EAAE;YACV,qBAAqB;YACrB,gBAAgB;SACjB;QACD,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,KAAK;QACd,SAAS,EACP,2HAA2H;QAC7H,MAAM,EAAE,CAAC,sBAAsB,CAAC;QAChC,QAAQ,EAAE,CAAC,8DAA8D,CAAC;KAC3E;CACF,CAAC;AAEF,MAAM,UAAU,qBAAqB,CACnC,OAA4B;IAE5B,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,wBAAwB;IAItC,OAAO;QACL,EAAE,EAAE,EAAE,qBAAqB,EAAE,OAAO,EAAE,kCAAkC,EAAE;QAC1E,EAAE,EAAE,EAAE,2BAA2B,EAAE,OAAO,EAAE,iCAAiC,EAAE;QAC/E,EAAE,EAAE,EAAE,wBAAwB,EAAE,OAAO,EAAE,sCAAsC,EAAE;QACjF,EAAE,EAAE,EAAE,eAAe,EAAE,OAAO,EAAE,sCAAsC,EAAE;QACxE,EAAE,EAAE,EAAE,2BAA2B,EAAE,OAAO,EAAE,4BAA4B,EAAE;QAC1E;YACE,EAAE,EAAE,8BAA8B;YAClC,OAAO,EAAE,qEAAqE;SAC/E;QACD;YACE,EAAE,EAAE,0BAA0B;YAC9B,OAAO,EAAE,iEAAiE;SAC3E;QACD,EAAE,EAAE,EAAE,oBAAoB,EAAE,OAAO,EAAE,mCAAmC,EAAE;QAC1E,EAAE,EAAE,EAAE,mBAAmB,EAAE,OAAO,EAAE,qCAAqC,EAAE;KAC5E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;;;;;;;;CAazC,CAAC,IAAI,EAAE,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Third-party developers can:
|
|
4
4
|
|
|
5
|
-
- **
|
|
5
|
+
- **Authenticate off-chain** with a live NFC tap → `startAuthentication` + `verifyResponse`
|
|
6
6
|
- **Require live passkey presence on-chain** → composable `verify_asset` (two patterns below)
|
|
7
7
|
|
|
8
8
|
## Two on-chain composition patterns
|
|
@@ -31,10 +31,10 @@ Best when your program orchestrates verification as part of its own instruction.
|
|
|
31
31
|
|
|
32
32
|
Off-chain tap auth is split:
|
|
33
33
|
|
|
34
|
-
1. **Client:** `
|
|
35
|
-
2. **Server:** `
|
|
34
|
+
1. **Client:** `startAuthentication(expectedMessage)` — NFC tap.
|
|
35
|
+
2. **Server:** `verifyResponse({ expectedMessage, response, rpc })` — signature check → `{ isVerified, asset }` (`asset.owner` is the wallet).
|
|
36
36
|
|
|
37
|
-
Does **not** write to chain. Use for UI login
|
|
37
|
+
Does **not** write to chain. Use for UI login and vault presence checks when no program needs to inspect `verify_asset`.
|
|
38
38
|
|
|
39
39
|
## Message design checklist
|
|
40
40
|
|
|
@@ -42,13 +42,14 @@ The client obtains `secp256r1_verify_args` from TypeScript `buildVerifyAssetArgs
|
|
|
42
42
|
|
|
43
43
|
```rust
|
|
44
44
|
pub struct Secp256r1VerifyArgs {
|
|
45
|
+
pub verify_args_relative_index: i64,
|
|
45
46
|
pub signed_message_index: u8,
|
|
46
47
|
pub slot_number: u64,
|
|
47
48
|
pub client_data_json: Vec<u8>,
|
|
48
49
|
}
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
`verify_args_relative_index` is the index of the secp256r1 verify instruction relative to the phygital instruction (typically `-1` when secp immediately precedes verify/transfer).
|
|
52
53
|
|
|
53
54
|
## Testing
|
|
54
55
|
|
package/docs/sdk/surface-area.md
CHANGED
|
@@ -41,10 +41,10 @@ TypeScript package: `phygital-token-sdk` (`clients/js/phygital-token`).
|
|
|
41
41
|
|--------|---------|
|
|
42
42
|
| `verifyDynamicUrl` | Identification via signed URL (server) |
|
|
43
43
|
| `verifyDynamicUrlWithoutCounterCheck` | Identification offline |
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
44
|
+
| `startAuthentication` | Client: NFC tap trigger; returns WebAuthn response |
|
|
45
|
+
| `verifyResponse` | Server: verify tap signature; returns `{ isVerified, asset }` |
|
|
46
46
|
|
|
47
|
-
Pair `
|
|
47
|
+
Pair `startAuthentication` (client) with `verifyResponse` (server). Pass optional `transceive` for kiosk / native NFC readers. Optional `fetchAssetFromCredentialIdCallback` must return an `Asset`. Owner wallet is `asset.owner`.
|
|
48
48
|
|
|
49
49
|
On-chain proof always uses `beginVerifyAsset` / `buildVerifyAssetArgs` / `completeVerifyAsset`.
|
|
50
50
|
|
|
@@ -55,18 +55,11 @@ On-chain proof always uses `beginVerifyAsset` / `buildVerifyAssetArgs` / `comple
|
|
|
55
55
|
| `findAssetPda` | Derive asset PDA from secp256r1 pubkey |
|
|
56
56
|
| `fetchAssetFromCredentialId` | Resolve asset + pubkey from credential |
|
|
57
57
|
| `fetchAllAssetsFromOwner` | List assets by wallet owner |
|
|
58
|
-
| `
|
|
58
|
+
| `fetchAssetDisplayInfoFromPublicKey` | Rich display metadata from base64url pubkey |
|
|
59
|
+
| `fetchAssetDisplayInfo` | Rich display metadata from a decoded `Asset` account |
|
|
60
|
+
| `fetchShortcutsFromExternalUrl` | Load Phantom Shortcuts schema v2 from `{external_url}/shortcuts.json` |
|
|
59
61
|
| `resolveMedia` | Resolve media URLs from token metadata |
|
|
60
62
|
|
|
61
|
-
## Gating
|
|
62
|
-
|
|
63
|
-
| Export | Purpose |
|
|
64
|
-
|--------|---------|
|
|
65
|
-
| `evaluateAssetGating` | Full tier evaluation for asset owner wallet |
|
|
66
|
-
| `evaluateGatingTiers` / `evaluateGatingFilter` | Lower-level evaluators |
|
|
67
|
-
| `Gating` / `GatingTraitValue` | Filter builders |
|
|
68
|
-
| `summarizeGatingEvaluationFailure` | Human-readable failure reasons |
|
|
69
|
-
|
|
70
63
|
## Generated (Codama)
|
|
71
64
|
|
|
72
65
|
Re-exported from `./generated/index.js`:
|
|
@@ -16,7 +16,7 @@ All exports from `clients/js/phygital-token/src/utils/verify.ts`.
|
|
|
16
16
|
|
|
17
17
|
Off-chain authentication is **split**: NFC tap on the client, signature verification on your server.
|
|
18
18
|
|
|
19
|
-
### `
|
|
19
|
+
### `startAuthentication(message, transceive?)`
|
|
20
20
|
|
|
21
21
|
**Client — trigger the tap.**
|
|
22
22
|
|
|
@@ -24,35 +24,41 @@ Opens the system NFC modal (browser) or talks to an NFC reader via `transceive`
|
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
26
|
// Browser
|
|
27
|
-
const response = await
|
|
27
|
+
const response = await startAuthentication(message);
|
|
28
28
|
|
|
29
29
|
// Kiosk / native reader
|
|
30
|
-
const response = await
|
|
30
|
+
const response = await startAuthentication(message, transceive);
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
`message` must be the same string your server issued as the challenge (store server-side with a short TTL).
|
|
34
34
|
|
|
35
|
-
### `
|
|
35
|
+
### `verifyResponse({ rpc, expectedMessage, response, ... })`
|
|
36
36
|
|
|
37
37
|
**Server — verify the tap.**
|
|
38
38
|
|
|
39
|
-
Checks the WebAuthn signature against `expectedMessage`, resolves the vault
|
|
39
|
+
Checks the WebAuthn signature against `expectedMessage`, resolves the vault via RPC (or `fetchAssetFromCredentialIdCallback`). Returns `{ isVerified, asset }`. Owner wallet is `asset.owner`. Challenge mismatch throws (`Message mismatch.`); a bad signature returns `isVerified: false`. Does **not** submit `verify_asset`.
|
|
40
|
+
|
|
41
|
+
`fetchAssetFromCredentialIdCallback` must return a decoded on-chain `Asset`.
|
|
40
42
|
|
|
41
43
|
```ts
|
|
42
44
|
// API route after client POSTs { message, response }
|
|
43
|
-
const {
|
|
45
|
+
const { isVerified, asset } = await verifyResponse({
|
|
44
46
|
rpc,
|
|
45
47
|
expectedMessage: message,
|
|
46
48
|
response,
|
|
47
49
|
});
|
|
50
|
+
|
|
51
|
+
if (isVerified) {
|
|
52
|
+
// use asset.owner / asset fields
|
|
53
|
+
}
|
|
48
54
|
```
|
|
49
55
|
|
|
50
56
|
Typical flow:
|
|
51
57
|
|
|
52
58
|
1. Server issues `message` (e.g. `randomUUID()`), stores it for the session.
|
|
53
|
-
2. Client calls `
|
|
59
|
+
2. Client calls `startAuthentication(message)` → user taps vault.
|
|
54
60
|
3. Client POSTs `{ message, response }` to your verify API.
|
|
55
|
-
4. Server calls `
|
|
61
|
+
4. Server calls `verifyResponse`, then runs your business logic.
|
|
56
62
|
|
|
57
63
|
For on-chain proof with a bound message, use `beginVerifyAsset({ message: Uint8Array })` instead.
|
|
58
64
|
|
|
@@ -60,7 +66,7 @@ For on-chain proof with a bound message, use `beginVerifyAsset({ message: Uint8A
|
|
|
60
66
|
|
|
61
67
|
| Need | Use |
|
|
62
68
|
|------|-----|
|
|
63
|
-
| UI login / vault gate, no tx | `
|
|
69
|
+
| UI login / vault gate, no tx | `startAuthentication` + `verifyResponse` |
|
|
64
70
|
| On-chain proof — Pattern A | `completeVerifyAsset` + your ix |
|
|
65
71
|
| On-chain proof — Pattern B | `buildVerifyAssetArgs` + your ix |
|
|
66
72
|
| Transfer ownership | `beginTransfer` → `completeTransfer` |
|
|
@@ -69,7 +75,7 @@ For on-chain proof with a bound message, use `beginVerifyAsset({ message: Uint8A
|
|
|
69
75
|
|
|
70
76
|
| Context | `message` type | Effect |
|
|
71
77
|
|---------|----------------|--------|
|
|
72
|
-
| `
|
|
78
|
+
| `startAuthentication` / `verifyResponse` | `string` (`expectedMessage`) | WebAuthn challenge bytes (UTF-8); must match on client and server |
|
|
73
79
|
| `beginVerifyAsset` | `Uint8Array` | Hashed into slot-bound on-chain challenge |
|
|
74
80
|
|
|
75
81
|
An off-chain `expectedMessage` does **not** produce an on-chain `verify_asset` record. Use the composable flow when your program must inspect or CPI `verify_asset`.
|
|
@@ -20,8 +20,8 @@ Do you need the holder physically present right now?
|
|
|
20
20
|
│ └─ Offline / no backend → verifyDynamicUrlWithoutCounterCheck (weaker)
|
|
21
21
|
└─ YES → Authentication
|
|
22
22
|
├─ Off-chain only (UI login, vault gate, no chain tx)
|
|
23
|
-
│ Server issues challenge →
|
|
24
|
-
│ →
|
|
23
|
+
│ Server issues challenge → startAuthentication (client tap)
|
|
24
|
+
│ → verifyResponse (server verify) → your logic
|
|
25
25
|
├─ On-chain proof for your program → beginVerifyAsset composable flow (see below)
|
|
26
26
|
└─ Transfer ownership → beginTransfer → completeTransfer (NOT verify_asset)
|
|
27
27
|
```
|
|
@@ -30,8 +30,8 @@ Do you need the holder physically present right now?
|
|
|
30
30
|
|
|
31
31
|
Off-chain authentication uses two SDK functions:
|
|
32
32
|
|
|
33
|
-
- **`
|
|
34
|
-
- **`
|
|
33
|
+
- **`startAuthentication`** — client only; opens NFC and returns a WebAuthn response.
|
|
34
|
+
- **`verifyResponse`** — server only; checks the signature and returns `{ isVerified, asset }` (`asset.owner` is the wallet).
|
|
35
35
|
|
|
36
36
|
Neither submits a transaction. Verification should run on your backend so the client cannot fake a successful tap.
|
|
37
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Composable `verify_asset` (TypeScript)
|
|
2
2
|
|
|
3
|
-
On-chain passkey authentication for custom programs. **Not** the same as off-chain `
|
|
3
|
+
On-chain passkey authentication for custom programs. **Not** the same as off-chain `startAuthentication` + `verifyResponse`.
|
|
4
4
|
|
|
5
5
|
## Session flow
|
|
6
6
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phygital-token-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MCP server for the phygital-token Solana program, TypeScript SDK, and Rust client",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
35
|
"@noble/hashes": "^2.0.1",
|
|
36
|
-
"@solana/kit": "^
|
|
37
|
-
"
|
|
38
|
-
"
|
|
36
|
+
"@solana/kit": "^7.0.0",
|
|
37
|
+
"zod": "^3.25.76",
|
|
38
|
+
"phygital-token-sdk": "0.11.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.15.21",
|
package/dist/lib/graphify.d.ts
DELETED
package/dist/lib/graphify.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
import { pathExists, resolveGraphifyGraphPath, resolveRepoRoot } from "./paths.js";
|
|
4
|
-
const execFileAsync = promisify(execFile);
|
|
5
|
-
export async function runGraphify(mode, args) {
|
|
6
|
-
const repoRoot = resolveRepoRoot();
|
|
7
|
-
const graphPath = resolveGraphifyGraphPath();
|
|
8
|
-
const graphExists = await pathExists(graphPath);
|
|
9
|
-
if (!graphExists) {
|
|
10
|
-
throw new Error(`Graphify graph not found at ${graphPath}. Clone the phygital-token repo, run "graphify update .", and set PHYGITAL_TOKEN_REPO_ROOT to the repo root — or use search_docs / read_doc instead.`);
|
|
11
|
-
}
|
|
12
|
-
const commandArgs = [mode, ...args];
|
|
13
|
-
try {
|
|
14
|
-
const { stdout, stderr } = await execFileAsync("graphify", commandArgs, {
|
|
15
|
-
cwd: repoRoot,
|
|
16
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
17
|
-
env: process.env,
|
|
18
|
-
});
|
|
19
|
-
const output = [stdout.trim(), stderr.trim()].filter(Boolean).join("\n");
|
|
20
|
-
return output || "(no output)";
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
if (error && typeof error === "object" && "stdout" in error) {
|
|
24
|
-
const execError = error;
|
|
25
|
-
const output = [execError.stdout?.trim(), execError.stderr?.trim()]
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.join("\n");
|
|
28
|
-
if (output) {
|
|
29
|
-
return output;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=graphify.js.map
|
package/dist/lib/graphify.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graphify.js","sourceRoot":"","sources":["../../src/lib/graphify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnF,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAI1C,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAkB,EAClB,IAAc;IAEd,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,wBAAwB,EAAE,CAAC;IAC7C,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IAEhD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,+BAA+B,SAAS,sJAAsJ,CAC/L,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,WAAW,EAAE;YACtE,GAAG,EAAE,QAAQ;YACb,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;YAC3B,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,OAAO,MAAM,IAAI,aAAa,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC5D,MAAM,SAAS,GAAG,KAA+D,CAAC;YAClF,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;iBAChE,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/lib/rpc.d.ts
DELETED
package/dist/lib/rpc.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createSolanaRpc } from "@solana/kit";
|
|
2
|
-
let cachedRpc = null;
|
|
3
|
-
let cachedRpcUrl = null;
|
|
4
|
-
export function getRpcUrl(override) {
|
|
5
|
-
const url = override?.trim() ||
|
|
6
|
-
process.env.PHYGITAL_TOKEN_RPC_URL?.trim() ||
|
|
7
|
-
process.env.HELIUS_RPC_URL?.trim();
|
|
8
|
-
if (!url) {
|
|
9
|
-
throw new Error("RPC URL required. Pass rpcUrl to the tool or set PHYGITAL_TOKEN_RPC_URL (or HELIUS_RPC_URL).");
|
|
10
|
-
}
|
|
11
|
-
return url;
|
|
12
|
-
}
|
|
13
|
-
export function createRpc(override) {
|
|
14
|
-
const url = getRpcUrl(override);
|
|
15
|
-
if (cachedRpc && cachedRpcUrl === url) {
|
|
16
|
-
return cachedRpc;
|
|
17
|
-
}
|
|
18
|
-
cachedRpc = createSolanaRpc(url);
|
|
19
|
-
cachedRpcUrl = url;
|
|
20
|
-
return cachedRpc;
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=rpc.js.map
|
package/dist/lib/rpc.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/lib/rpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA+B,MAAM,aAAa,CAAC;AAE3E,IAAI,SAAS,GAA6B,IAAI,CAAC;AAC/C,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,MAAM,GAAG,GACP,QAAQ,EAAE,IAAI,EAAE;QAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE;QAC1C,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IAErC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAEhC,IAAI,SAAS,IAAI,YAAY,KAAK,GAAG,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACjC,YAAY,GAAG,GAAG,CAAC;IACnB,OAAO,SAAS,CAAC;AACnB,CAAC"}
|