arc-builder-kit 0.2.0__py3-none-any.whl

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.
Files changed (58) hide show
  1. arc_builder_kit/__init__.py +4 -0
  2. arc_builder_kit/__main__.py +6 -0
  3. arc_builder_kit/_paths.py +47 -0
  4. arc_builder_kit/cli.py +277 -0
  5. arc_builder_kit/config/arc_testnet.facts.json +31 -0
  6. arc_builder_kit/doctor.py +936 -0
  7. arc_builder_kit/examples/agent-commerce-components/components.js +200 -0
  8. arc_builder_kit/examples/agent-commerce-components/index.html +120 -0
  9. arc_builder_kit/examples/agent-commerce-flows/flows.js +271 -0
  10. arc_builder_kit/examples/agent-commerce-flows/index.html +114 -0
  11. arc_builder_kit/examples/agent-commerce-live/commerce-live.js +190 -0
  12. arc_builder_kit/examples/agent-commerce-live/index.html +105 -0
  13. arc_builder_kit/examples/agent-commerce-review-packet/index.html +96 -0
  14. arc_builder_kit/examples/agent-commerce-review-packet/packet.js +125 -0
  15. arc_builder_kit/examples/agent-identity-profile-preview/identity.js +126 -0
  16. arc_builder_kit/examples/agent-identity-profile-preview/index.html +104 -0
  17. arc_builder_kit/examples/arc-agent-treasury-lab/index.html +152 -0
  18. arc_builder_kit/examples/arc-agent-treasury-lab/treasury.js +532 -0
  19. arc_builder_kit/examples/arc-testnet-operator-evidence/evidence.example.json +47 -0
  20. arc_builder_kit/examples/arc-testnet-wallet-send-gate/index.html +233 -0
  21. arc_builder_kit/examples/arc-testnet-wallet-send-gate/live-infrastructure-policy.example.json +59 -0
  22. arc_builder_kit/examples/arc-testnet-wallet-send-gate/wallet-send-gate.js +472 -0
  23. arc_builder_kit/examples/circle-wallet-integration/index.html +155 -0
  24. arc_builder_kit/examples/circle-wallet-integration/wallet-lab.js +91 -0
  25. arc_builder_kit/examples/job-escrow-simulator/index.html +121 -0
  26. arc_builder_kit/examples/job-escrow-simulator/simulator.js +162 -0
  27. arc_builder_kit/examples/payment-intent-demo/index.html +132 -0
  28. arc_builder_kit/examples/payment-intent-playground/index.html +301 -0
  29. arc_builder_kit/examples/payment-intent-playground/playground.js +835 -0
  30. arc_builder_kit/examples/payment-intent-receipt-matcher/index.html +157 -0
  31. arc_builder_kit/examples/payment-intent-receipt-matcher/matcher.js +877 -0
  32. arc_builder_kit/examples/receipt-verifier-playground/index.html +120 -0
  33. arc_builder_kit/examples/receipt-verifier-playground/verifier.js +226 -0
  34. arc_builder_kit/examples/receipt-viewer/index.html +138 -0
  35. arc_builder_kit/examples/receipt-viewer/receipt-viewer.js +472 -0
  36. arc_builder_kit/examples/transaction-status-playground/index.html +135 -0
  37. arc_builder_kit/examples/transaction-status-playground/status.js +518 -0
  38. arc_builder_kit/examples/x402-local-challenge-server/.env.example +25 -0
  39. arc_builder_kit/examples/x402-local-challenge-server/README.md +111 -0
  40. arc_builder_kit/examples/x402-local-challenge-server/server.py +711 -0
  41. arc_builder_kit/mcp_server.py +463 -0
  42. arc_builder_kit/release_packet.py +469 -0
  43. arc_builder_kit/templates/README.md +25 -0
  44. arc_builder_kit/templates/job-escrow-starter/README.md +25 -0
  45. arc_builder_kit/templates/job-escrow-starter/index.html +41 -0
  46. arc_builder_kit/templates/job-escrow-starter/index.js +14 -0
  47. arc_builder_kit/templates/payment-intent-starter/README.md +25 -0
  48. arc_builder_kit/templates/payment-intent-starter/index.html +42 -0
  49. arc_builder_kit/templates/payment-intent-starter/index.js +7 -0
  50. arc_builder_kit/templates/x402-agent-starter/README.md +29 -0
  51. arc_builder_kit/templates/x402-agent-starter/server.py +201 -0
  52. arc_builder_kit/validate_repo.py +2212 -0
  53. arc_builder_kit-0.2.0.dist-info/METADATA +543 -0
  54. arc_builder_kit-0.2.0.dist-info/RECORD +58 -0
  55. arc_builder_kit-0.2.0.dist-info/WHEEL +5 -0
  56. arc_builder_kit-0.2.0.dist-info/entry_points.txt +3 -0
  57. arc_builder_kit-0.2.0.dist-info/licenses/LICENSE +21 -0
  58. arc_builder_kit-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,120 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Receipt Verifier Playground · Arc MCP Builder Assistant</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%232563eb'/%3E%3Cstop offset='1' stop-color='%2306b6d4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='16' fill='url(%23g)'/%3E%3Ctext x='50%25' y='55%25' text-anchor='middle' font-family='Inter,Arial,sans-serif' font-size='38' font-weight='900' fill='white'%3EA%3C/text%3E%3C/svg%3E" />
8
+ <meta name="description" content="Local-only receipt review playground for checking Arc payment receipt JSON before any future wallet or transaction-status integration." />
9
+ <meta name="author" content="Arc MCP Builder Assistant contributors" />
10
+ <meta name="robots" content="index,follow" />
11
+ <meta name="theme-color" content="#05060a" />
12
+ <meta name="color-scheme" content="dark" />
13
+ <meta name="referrer" content="no-referrer" />
14
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; upgrade-insecure-requests" />
15
+ <link rel="canonical" href="https://anstrays.github.io/arc-mcp-builder-assistant/examples/receipt-verifier-playground/" />
16
+ <meta property="og:title" content="Arc Receipt Verifier Playground" />
17
+ <meta property="og:description" content="Local-only receipt review for Arc payment JSON: chain, recipient, amount, asset, intent hash, expiry, and transaction hash." />
18
+ <meta property="og:type" content="website" />
19
+ <style>
20
+ :root { color-scheme: dark; --bg:#05060a; --panel:rgba(10,12,24,.84); --panel-2:rgba(255,255,255,.055); --text:#f7f7ff; --muted:#cbd2ff; --soft:#8e95bc; --line:rgba(255,255,255,.14); --blue:#8ea2ff; --cyan:#5eead4; --lime:#d9ff72; --red:#ff8d8d; font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif; }
21
+ * { box-sizing: border-box; }
22
+ body { margin:0; min-height:100vh; background: radial-gradient(circle at top right, rgba(94,234,212,.22), transparent 36%), radial-gradient(circle at top left, rgba(38,59,255,.76) 0, #080914 34%, var(--bg) 100%); color:var(--text); padding:32px 0; }
23
+ main { width:min(1180px, calc(100vw - 32px)); margin:0 auto; display:grid; gap:18px; }
24
+ a { color:inherit; }
25
+ a:focus-visible, button:focus-visible, textarea:focus-visible { outline:2px solid var(--cyan); outline-offset:3px; border-radius:8px; }
26
+ .breadcrumb { margin:0 0 4px; font-size:13px; color:var(--soft); }
27
+ .breadcrumb a { color:var(--muted); text-decoration:underline; text-underline-offset:3px; }
28
+ .card { border:1px solid var(--line); background:var(--panel); border-radius:24px; padding:24px; box-shadow:0 24px 80px rgba(0,0,0,.35); }
29
+ .notice { border-color:rgba(94,234,212,.32); background:rgba(94,234,212,.08); }
30
+ .danger { border-color:rgba(255,141,141,.32); background:rgba(255,141,141,.08); }
31
+ .eyebrow { color:var(--blue); text-transform:uppercase; letter-spacing:.16em; font-size:12px; font-weight:800; }
32
+ h1 { margin:8px 0 10px; font-size:clamp(34px, 6vw, 72px); line-height:.95; letter-spacing:-.05em; }
33
+ h2 { margin:8px 0 12px; font-size:clamp(24px, 3vw, 36px); letter-spacing:-.035em; }
34
+ p { color:var(--muted); line-height:1.65; }
35
+ .grid { display:grid; grid-template-columns:minmax(300px, .96fr) minmax(320px, 1.04fr); gap:18px; align-items:start; }
36
+ main > *, .grid > * { min-width:0; }
37
+ textarea { width:100%; min-height:420px; border:1px solid var(--line); border-radius:16px; background:rgba(0,0,0,.28); color:#d9fffb; padding:14px; font:13px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; resize:vertical; }
38
+ .actions { display:flex; flex-wrap:wrap; gap:12px; margin-top:14px; }
39
+ button { border:0; border-radius:999px; padding:13px 18px; font-weight:900; cursor:pointer; background:var(--text); color:#080914; }
40
+ button.secondary { background:rgba(255,255,255,.1); color:var(--text); border:1px solid var(--line); }
41
+ .pill-row { display:flex; gap:10px; flex-wrap:wrap; margin-top:14px; }
42
+ .pill { border:1px solid var(--line); background:rgba(255,255,255,.06); color:#dce2ff; border-radius:999px; padding:8px 11px; font-size:13px; }
43
+ .verdict { display:inline-flex; align-items:center; gap:8px; border:1px solid rgba(94,234,212,.3); background:rgba(94,234,212,.08); color:#d9fffb; border-radius:999px; padding:9px 12px; font-weight:900; }
44
+ .verdict::before { content:""; width:9px; height:9px; border-radius:999px; background:var(--cyan); box-shadow:0 0 18px rgba(94,234,212,.8); }
45
+ .verdict.fail { border-color:rgba(255,141,141,.42); background:rgba(255,141,141,.1); color:#ffdada; }
46
+ .verdict.fail::before { background:var(--red); box-shadow:0 0 18px rgba(255,141,141,.7); }
47
+ .mini-list { margin:12px 0 0; padding-left:18px; color:var(--muted); line-height:1.6; }
48
+ .mini-list li.pass strong { color:var(--lime); }
49
+ .mini-list li.fail strong { color:#ffdada; }
50
+ .json-preview { white-space:pre-wrap; overflow:auto; border:1px solid rgba(255,255,255,.1); border-radius:16px; padding:14px; background:rgba(0,0,0,.28); color:#d9fffb; font-size:13px; line-height:1.55; min-height:220px; }
51
+ .metric-grid { display:grid; gap:10px; grid-template-columns:repeat(3, minmax(0, 1fr)); margin:14px 0; }
52
+ .metric { border:1px solid rgba(255,255,255,.1); border-radius:16px; background:var(--panel-2); padding:13px; }
53
+ .metric span { display:block; color:var(--soft); font-size:12px; text-transform:uppercase; letter-spacing:.12em; font-weight:800; }
54
+ .metric strong { display:block; margin-top:7px; color:var(--text); overflow-wrap:anywhere; }
55
+ @media (max-width: 860px) { body { padding:18px 0; } .grid, .metric-grid { grid-template-columns:1fr; } }
56
+ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0s !important; transition: none !important; } }
57
+ </style>
58
+ <script src="./verifier.js" defer></script>
59
+ </head>
60
+ <body>
61
+ <main>
62
+ <p class="breadcrumb"><a href="../../index.html">&larr; Back to Arc MCP Builder Assistant</a></p>
63
+ <section class="card" aria-labelledby="receipt-title">
64
+ <div class="eyebrow">local-only receipt review</div>
65
+ <h1 id="receipt-title">Receipt Verifier Playground</h1>
66
+ <p>Paste a simulated Arc payment receipt JSON, inspect normalized fields, and see which checks pass before a future wallet or transaction-status PR exists. This page never connects to a wallet, never calls a backend, and never broadcasts transactions.</p>
67
+ <div class="pill-row" aria-label="Safety properties">
68
+ <span class="pill">No wallet connection</span>
69
+ <span class="pill">No backend calls</span>
70
+ <span class="pill">No transaction broadcast</span>
71
+ <span class="pill">Reviewable JSON only</span>
72
+ </div>
73
+ </section>
74
+
75
+ <section class="grid" aria-label="Receipt verification workspace">
76
+ <article class="card" aria-labelledby="input-title">
77
+ <div class="eyebrow">Receipt JSON</div>
78
+ <h2 id="input-title">Paste or edit receipt</h2>
79
+ <p>Expected shape: chain ID, recipient, amount, asset, intent hash, expiry, and optional transaction hash. The default sample is intentionally simulated.</p>
80
+ <textarea id="receipt-json" spellcheck="false" aria-label="Receipt JSON input"></textarea>
81
+ <div class="actions" aria-label="Receipt verifier controls">
82
+ <button type="button" id="verify-receipt">Verify receipt</button>
83
+ <button type="button" id="reset-receipt" class="secondary">Reset sample</button>
84
+ </div>
85
+ </article>
86
+
87
+ <article class="card notice" aria-labelledby="verdict-title">
88
+ <div class="eyebrow">Verifier output</div>
89
+ <h2 id="verdict-title">Local verdict</h2>
90
+ <p>Status: <span id="verdict-pill" class="verdict">not checked</span></p>
91
+ <ul id="receipt-check-list" class="mini-list" aria-live="polite"></ul>
92
+ <h2>Normalized receipt</h2>
93
+ <pre id="normalized-receipt" class="json-preview" aria-live="polite"></pre>
94
+ </article>
95
+ </section>
96
+
97
+ <section class="card notice" aria-labelledby="expectations-title">
98
+ <div class="eyebrow">Arc constants</div>
99
+ <h2 id="expectations-title">Verifier expectations</h2>
100
+ <p>These static constants mirror the current local Arc Testnet baseline used elsewhere in the starter kit. Re-check Arc docs/MCP before any real signing, settlement, or explorer-status PR.</p>
101
+ <div class="metric-grid" aria-label="Arc receipt verification constants">
102
+ <div class="metric"><span>Expected chain ID</span><strong>5042002 (0x4cef52)</strong></div>
103
+ <div class="metric"><span>Asset</span><strong>USDC / 6 decimals</strong></div>
104
+ <div class="metric"><span>Explorer</span><strong>https://testnet.arcscan.app</strong></div>
105
+ </div>
106
+ </section>
107
+
108
+ <section class="card danger" aria-labelledby="boundary-title">
109
+ <div class="eyebrow">Trust boundary</div>
110
+ <h2 id="boundary-title">What this does not prove</h2>
111
+ <ul class="mini-list">
112
+ <li>It does not query ArcScan, an RPC node, or a backend verifier.</li>
113
+ <li>It does not prove finality, token balance changes, or ownership of a wallet.</li>
114
+ <li>It does not sign, submit, sponsor, or broadcast any transaction.</li>
115
+ <li>Use the <a href="../../docs/view.html#receipt-verifier-playground.md">receipt verifier notes</a> before extending this into a real testnet-status path.</li>
116
+ </ul>
117
+ </section>
118
+ </main>
119
+ </body>
120
+ </html>
@@ -0,0 +1,226 @@
1
+ const receiptInput = document.querySelector('#receipt-json');
2
+ const verifyButton = document.querySelector('#verify-receipt');
3
+ const resetButton = document.querySelector('#reset-receipt');
4
+ const verdictPill = document.querySelector('#verdict-pill');
5
+ const receiptCheckList = document.querySelector('#receipt-check-list');
6
+ const normalizedReceipt = document.querySelector('#normalized-receipt');
7
+
8
+ const ARC_RECEIPT_EXPECTATIONS = Object.freeze({
9
+ network: 'Arc Testnet',
10
+ expectedChainId: 5042002,
11
+ expectedChainIdHex: '0x4cef52',
12
+ asset: 'USDC',
13
+ assetDecimals: 6,
14
+ explorerUrl: 'https://testnet.arcscan.app',
15
+ walletConnected: false,
16
+ backendCalls: false,
17
+ transactionBroadcast: false,
18
+ signingEnabled: false,
19
+ localOnly: true,
20
+ });
21
+
22
+ const RECEIPT_CHECK_IDS = Object.freeze([
23
+ { id: 'chainId' },
24
+ { id: 'recipient' },
25
+ { id: 'amount' },
26
+ { id: 'asset' },
27
+ { id: 'intentHash' },
28
+ { id: 'expiry' },
29
+ { id: 'transactionHash' },
30
+ ]);
31
+
32
+ const DEFAULT_RECEIPT_EXPIRY_MS = 24 * 60 * 60 * 1000;
33
+ const SAMPLE_RECEIPT = Object.freeze({
34
+ network: 'arc-testnet',
35
+ chainId: 5042002,
36
+ recipient: '0x1111111111111111111111111111111111111111',
37
+ amount: '5.00',
38
+ asset: 'USDC',
39
+ intentHash: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
40
+ expiry: new Date(Date.now() + DEFAULT_RECEIPT_EXPIRY_MS).toISOString(),
41
+ transactionHash: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
42
+ status: 'submitted_simulated',
43
+ safety: {
44
+ source: 'sample local receipt',
45
+ walletConnected: false,
46
+ backendCalls: false,
47
+ transactionBroadcast: false,
48
+ },
49
+ });
50
+
51
+ function isPlainObject(value) {
52
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
53
+ }
54
+
55
+ function parseReceiptJson() {
56
+ try {
57
+ const parsed = JSON.parse(receiptInput.value);
58
+ if (!isPlainObject(parsed)) {
59
+ return { ok: false, error: 'Receipt JSON must be an object.', receipt: null };
60
+ }
61
+ return { ok: true, error: '', receipt: parsed };
62
+ } catch (error) {
63
+ return { ok: false, error: `Invalid JSON: ${error.message}`, receipt: null };
64
+ }
65
+ }
66
+
67
+ function normalizeChainId(chainId) {
68
+ if (typeof chainId === 'number' && Number.isInteger(chainId)) return chainId;
69
+ const value = String(chainId || '').trim().toLowerCase();
70
+ if (/^0x[0-9a-f]+$/.test(value)) return Number.parseInt(value, 16);
71
+ if (/^[0-9]+$/.test(value)) return Number.parseInt(value, 10);
72
+ return Number.NaN;
73
+ }
74
+
75
+ function normalizeReceipt(rawReceipt) {
76
+ return {
77
+ network: String(rawReceipt.network || '').trim().toLowerCase(),
78
+ chainId: normalizeChainId(rawReceipt.chainId),
79
+ chainIdHex: Number.isFinite(normalizeChainId(rawReceipt.chainId))
80
+ ? `0x${normalizeChainId(rawReceipt.chainId).toString(16)}`
81
+ : 'invalid',
82
+ recipient: String(rawReceipt.recipient || '').trim(),
83
+ amount: String(rawReceipt.amount || '').trim(),
84
+ asset: String(rawReceipt.asset || '').trim().toUpperCase(),
85
+ intentHash: String(rawReceipt.intentHash || '').trim(),
86
+ expiry: String(rawReceipt.expiry || '').trim(),
87
+ transactionHash: String(rawReceipt.transactionHash || '').trim(),
88
+ status: String(rawReceipt.status || 'unknown').trim(),
89
+ verifier: {
90
+ expectedNetwork: ARC_RECEIPT_EXPECTATIONS.network,
91
+ expectedChainId: ARC_RECEIPT_EXPECTATIONS.expectedChainId,
92
+ expectedChainIdHex: ARC_RECEIPT_EXPECTATIONS.expectedChainIdHex,
93
+ assetDecimals: ARC_RECEIPT_EXPECTATIONS.assetDecimals,
94
+ explorerUrl: ARC_RECEIPT_EXPECTATIONS.explorerUrl,
95
+ walletConnected: ARC_RECEIPT_EXPECTATIONS.walletConnected,
96
+ backendCalls: ARC_RECEIPT_EXPECTATIONS.backendCalls,
97
+ transactionBroadcast: ARC_RECEIPT_EXPECTATIONS.transactionBroadcast,
98
+ signingEnabled: ARC_RECEIPT_EXPECTATIONS.signingEnabled,
99
+ localOnly: ARC_RECEIPT_EXPECTATIONS.localOnly,
100
+ },
101
+ };
102
+ }
103
+
104
+ function isValidAddress(value) {
105
+ const normalized = String(value || '').trim().toLowerCase();
106
+ return /^0x[a-f0-9]{40}$/.test(normalized)
107
+ && !/^0x0{40}$/.test(normalized)
108
+ && normalized !== '0x3600000000000000000000000000000000000000';
109
+ }
110
+
111
+ function isValidHash(value) {
112
+ return /^0x[a-fA-F0-9]{64}$/.test(value);
113
+ }
114
+
115
+ function isValidUsdcAmount(value) {
116
+ return /^(?:0|[1-9]\d*)(?:\.\d{1,6})?$/.test(value) && Number(value) > 0;
117
+ }
118
+
119
+ function expiryIsFuture(value) {
120
+ const expiryTime = new Date(value).getTime();
121
+ return Number.isFinite(expiryTime) && expiryTime > Date.now();
122
+ }
123
+
124
+ function check(id, label, passed, detail) {
125
+ return { id, label, passed, detail };
126
+ }
127
+
128
+ function verifyReceipt(receipt) {
129
+ const normalized = normalizeReceipt(receipt);
130
+ const checks = [
131
+ check(
132
+ 'chainId',
133
+ 'Arc Testnet chain ID',
134
+ normalized.chainId === ARC_RECEIPT_EXPECTATIONS.expectedChainId,
135
+ `Expected ${ARC_RECEIPT_EXPECTATIONS.expectedChainId} (${ARC_RECEIPT_EXPECTATIONS.expectedChainIdHex}); got ${normalized.chainIdHex}.`
136
+ ),
137
+ check(
138
+ 'recipient',
139
+ 'Recipient address',
140
+ isValidAddress(normalized.recipient),
141
+ 'Expected a non-zero 0x-prefixed recipient address that is not the USDC token contract.'
142
+ ),
143
+ check(
144
+ 'amount',
145
+ 'USDC amount units',
146
+ isValidUsdcAmount(normalized.amount),
147
+ `Expected a positive ${ARC_RECEIPT_EXPECTATIONS.asset} amount with at most ${ARC_RECEIPT_EXPECTATIONS.assetDecimals} decimals.`
148
+ ),
149
+ check(
150
+ 'asset',
151
+ 'Receipt asset',
152
+ normalized.asset === ARC_RECEIPT_EXPECTATIONS.asset,
153
+ `Expected ${ARC_RECEIPT_EXPECTATIONS.asset}; got ${normalized.asset || 'missing'}.`
154
+ ),
155
+ check(
156
+ 'intentHash',
157
+ 'Intent hash',
158
+ isValidHash(normalized.intentHash),
159
+ 'Expected a 32-byte 0x-prefixed intent hash.'
160
+ ),
161
+ check(
162
+ 'expiry',
163
+ 'Receipt expiry',
164
+ expiryIsFuture(normalized.expiry),
165
+ 'Expected an ISO-compatible future expiry timestamp.'
166
+ ),
167
+ check(
168
+ 'transactionHash',
169
+ 'Transaction hash shape',
170
+ normalized.transactionHash === '' || isValidHash(normalized.transactionHash),
171
+ 'Expected empty for local draft or a 32-byte 0x-prefixed transaction hash.'
172
+ ),
173
+ ];
174
+
175
+ return {
176
+ passed: checks.every((item) => item.passed),
177
+ checks,
178
+ normalized,
179
+ };
180
+ }
181
+
182
+ function buildCheckListItem(state, message, className) {
183
+ const listItem = document.createElement('li');
184
+ listItem.className = className;
185
+ const stateLabel = document.createElement('strong');
186
+ stateLabel.textContent = state;
187
+ listItem.append(stateLabel, ` — ${message}`);
188
+ return listItem;
189
+ }
190
+
191
+ function renderVerification(result) {
192
+ verdictPill.textContent = result.passed ? 'locally consistent' : 'needs review';
193
+ verdictPill.classList.toggle('fail', !result.passed);
194
+ receiptCheckList.replaceChildren(
195
+ ...result.checks.map((item) => {
196
+ const state = item.passed ? 'PASS' : 'REVIEW';
197
+ return buildCheckListItem(state, `${item.label}: ${item.detail}`, item.passed ? 'pass' : 'fail');
198
+ })
199
+ );
200
+ normalizedReceipt.textContent = JSON.stringify(result.normalized, null, 2);
201
+ }
202
+
203
+ function renderParseError(message) {
204
+ verdictPill.textContent = 'invalid JSON';
205
+ verdictPill.classList.add('fail');
206
+ receiptCheckList.replaceChildren(buildCheckListItem('REVIEW', message, 'fail'));
207
+ normalizedReceipt.textContent = JSON.stringify({ error: message, localOnly: true }, null, 2);
208
+ }
209
+
210
+ function verifyCurrentInput() {
211
+ const parsed = parseReceiptJson();
212
+ if (!parsed.ok) {
213
+ renderParseError(parsed.error);
214
+ return;
215
+ }
216
+ renderVerification(verifyReceipt(parsed.receipt));
217
+ }
218
+
219
+ function resetSample() {
220
+ receiptInput.value = JSON.stringify(SAMPLE_RECEIPT, null, 2);
221
+ verifyCurrentInput();
222
+ }
223
+
224
+ verifyButton.addEventListener('click', verifyCurrentInput);
225
+ resetButton.addEventListener('click', resetSample);
226
+ resetSample();
@@ -0,0 +1,138 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Agent Payment Receipt Viewer · Arc MCP Builder Assistant</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%230f766e'/%3E%3Cstop offset='1' stop-color='%232563eb'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='16' fill='url(%23g)'/%3E%3Ctext x='50%25' y='55%25' text-anchor='middle' font-family='Inter,Arial,sans-serif' font-size='34' font-weight='900' fill='white'%3ER%3C/text%3E%3C/svg%3E" />
8
+ <meta name="description" content="Read-only Arc Testnet payment receipt viewer for checking transaction receipts and highlighting USDC Transfer logs without wallets, keys, signing, or broadcast." />
9
+ <meta name="author" content="Arc MCP Builder Assistant contributors" />
10
+ <meta name="robots" content="index,follow" />
11
+ <meta name="theme-color" content="#061414" />
12
+ <meta name="color-scheme" content="dark" />
13
+ <meta name="referrer" content="no-referrer" />
14
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src 'self' https://rpc.testnet.arc.network; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'; upgrade-insecure-requests" />
15
+ <link rel="canonical" href="https://anstrays.github.io/arc-mcp-builder-assistant/examples/receipt-viewer/" />
16
+ <meta property="og:title" content="Agent Payment Receipt Viewer" />
17
+ <meta property="og:description" content="Read-only Arc Testnet receipt lookup with USDC Transfer log highlighting and explicit no-wallet boundaries." />
18
+ <meta property="og:type" content="website" />
19
+ <style>
20
+ :root { color-scheme: dark; --bg:#061414; --panel:rgba(7,17,24,.86); --panel-2:rgba(255,255,255,.055); --text:#f8fffc; --muted:#c4d9d2; --soft:#8fa8a1; --line:rgba(255,255,255,.15); --teal:#5eead4; --blue:#9ab1ff; --lime:#d9ff72; --amber:#ffd166; --red:#ff9b9b; font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif; }
21
+ * { box-sizing: border-box; }
22
+ body { margin:0; min-height:100vh; background: radial-gradient(circle at top right, rgba(94,234,212,.2), transparent 34%), linear-gradient(145deg, #061414 0, #07101c 45%, #0d1726 100%); color:var(--text); padding:32px 0; }
23
+ main { width:min(1180px, calc(100vw - 32px)); margin:0 auto; display:grid; gap:18px; }
24
+ a { color:inherit; }
25
+ a:focus-visible, button:focus-visible, input:focus-visible { outline:2px solid var(--teal); outline-offset:3px; border-radius:8px; }
26
+ .breadcrumb { margin:0 0 4px; font-size:13px; color:var(--soft); }
27
+ .breadcrumb a { color:var(--muted); text-decoration:underline; text-underline-offset:3px; }
28
+ .card { border:1px solid var(--line); background:var(--panel); border-radius:22px; padding:24px; box-shadow:0 24px 80px rgba(0,0,0,.32); }
29
+ .notice { border-color:rgba(94,234,212,.3); background:rgba(94,234,212,.075); }
30
+ .danger { border-color:rgba(255,155,155,.32); background:rgba(255,155,155,.075); }
31
+ .eyebrow { color:var(--teal); text-transform:uppercase; letter-spacing:.16em; font-size:12px; font-weight:900; }
32
+ h1 { margin:8px 0 10px; font-size:clamp(34px, 6vw, 70px); line-height:.96; }
33
+ h2 { margin:8px 0 12px; font-size:clamp(23px, 3vw, 34px); }
34
+ h3 { margin:12px 0 8px; font-size:20px; }
35
+ p { color:var(--muted); line-height:1.65; }
36
+ code { color:#d9fffb; overflow-wrap:anywhere; }
37
+ .grid { display:grid; grid-template-columns:minmax(300px, .9fr) minmax(340px, 1.1fr); gap:18px; align-items:start; }
38
+ main > *, .grid > * { min-width:0; }
39
+ label { display:block; color:var(--muted); font-weight:900; margin:0 0 8px; }
40
+ input { width:100%; border:1px solid var(--line); border-radius:14px; background:rgba(0,0,0,.28); color:#d9fffb; padding:14px; font:14px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
41
+ .actions { display:flex; flex-wrap:wrap; gap:12px; margin-top:14px; }
42
+ button { border:0; border-radius:999px; padding:13px 18px; font-weight:900; cursor:pointer; background:var(--text); color:#07101c; }
43
+ button.secondary { background:rgba(255,255,255,.1); color:var(--text); border:1px solid var(--line); }
44
+ button:disabled { opacity:.55; cursor:not-allowed; }
45
+ .pill-row { display:flex; gap:10px; flex-wrap:wrap; margin-top:14px; }
46
+ .pill { border:1px solid var(--line); background:rgba(255,255,255,.06); color:#dce2ff; border-radius:999px; padding:8px 11px; font-size:13px; }
47
+ .status { display:inline-flex; align-items:center; gap:8px; border:1px solid rgba(255,255,255,.22); background:rgba(255,255,255,.08); color:#eef2ff; border-radius:999px; padding:9px 12px; font-weight:900; overflow-wrap:anywhere; }
48
+ .status::before { content:""; width:9px; height:9px; border-radius:999px; background:var(--soft); box-shadow:0 0 18px rgba(143,168,161,.7); flex:0 0 auto; }
49
+ .status.checking::before { background:var(--amber); box-shadow:0 0 18px rgba(255,209,102,.7); }
50
+ .status.success::before { background:var(--teal); box-shadow:0 0 18px rgba(94,234,212,.8); }
51
+ .status.revert::before { background:var(--red); box-shadow:0 0 18px rgba(255,155,155,.75); }
52
+ .status.not_found::before, .status.unknown::before { background:var(--amber); box-shadow:0 0 18px rgba(255,209,102,.7); }
53
+ .mini-list { margin:12px 0 0; padding-left:18px; color:var(--muted); line-height:1.6; }
54
+ .mini-list li { margin:6px 0; overflow-wrap:anywhere; }
55
+ .mini-list li.pass strong { color:var(--lime); }
56
+ .mini-list li.warn strong { color:var(--amber); }
57
+ .mini-list li.fail strong { color:#ffdada; }
58
+ .json-preview { white-space:pre-wrap; overflow:auto; border:1px solid rgba(255,255,255,.1); border-radius:16px; padding:14px; background:rgba(0,0,0,.28); color:#d9fffb; font-size:13px; line-height:1.55; min-height:320px; }
59
+ .metric-grid { display:grid; gap:10px; grid-template-columns:repeat(3, minmax(0, 1fr)); margin:14px 0; }
60
+ .metric { border:1px solid rgba(255,255,255,.1); border-radius:16px; background:var(--panel-2); padding:13px; }
61
+ .metric span { display:block; color:var(--soft); font-size:12px; text-transform:uppercase; letter-spacing:.12em; font-weight:900; }
62
+ .metric strong { display:block; margin-top:7px; color:var(--text); overflow-wrap:anywhere; }
63
+ @media (max-width: 860px) { body { padding:18px 0; } .grid, .metric-grid { grid-template-columns:1fr; } }
64
+ @media (prefers-reduced-motion: reduce) { * { animation-duration: 0s !important; transition: none !important; } }
65
+ </style>
66
+ <script src="./receipt-viewer.js" integrity="sha384-JaKYiQKsMvVE2asRi35IsLclmdnzN1WXI7gwN+9Qq3K11fLzIH+NjuLhx8eBgde4" crossorigin="anonymous" defer></script>
67
+ </head>
68
+ <body>
69
+ <main>
70
+ <p class="breadcrumb"><a href="../../index.html">&larr; Back to Arc MCP Builder Assistant</a></p>
71
+ <section class="card" aria-labelledby="viewer-title">
72
+ <div class="eyebrow">Read-only Arc Testnet RPC</div>
73
+ <h1 id="viewer-title">Agent Payment Receipt Viewer</h1>
74
+ <p>Paste an Arc Testnet transaction hash and inspect its receipt status, block number, gas used, raw logs, and any pinned USDC Transfer events. The viewer never connects to a wallet, handles private keys, signs, or broadcasts.</p>
75
+ <div class="pill-row" aria-label="Safety properties">
76
+ <span class="pill">No wallet connection</span>
77
+ <span class="pill">No private keys</span>
78
+ <span class="pill">No signing</span>
79
+ <span class="pill">No transaction broadcast</span>
80
+ <span class="pill">Read-only RPC only</span>
81
+ </div>
82
+ </section>
83
+
84
+ <section class="grid" aria-label="Receipt viewer workspace">
85
+ <article class="card" aria-labelledby="input-title">
86
+ <div class="eyebrow">Transaction hash</div>
87
+ <h2 id="input-title">Lookup receipt</h2>
88
+ <p>The sample hash is a placeholder. Use a real Arc Testnet hash only if you already have one from a wallet or ArcScan.</p>
89
+ <label for="transaction-hash">Arc Testnet transaction hash</label>
90
+ <input id="transaction-hash" autocomplete="off" spellcheck="false" value="0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" />
91
+ <div class="actions" aria-label="Receipt viewer controls">
92
+ <button type="button" id="load-receipt">Load receipt</button>
93
+ <button type="button" id="reset-receipt" class="secondary">Reset sample</button>
94
+ </div>
95
+ </article>
96
+
97
+ <article class="card notice" aria-labelledby="result-title">
98
+ <div class="eyebrow">Receipt evidence</div>
99
+ <h2 id="result-title">Read-only verdict</h2>
100
+ <p>Status: <span id="status-pill" class="status">not_checked</span></p>
101
+ <ul id="receipt-summary-list" class="mini-list" aria-live="polite"></ul>
102
+ <h3>USDC Transfer logs</h3>
103
+ <ul id="transfer-log-list" class="mini-list" aria-live="polite"></ul>
104
+ </article>
105
+ </section>
106
+
107
+ <section class="card notice" aria-labelledby="states-title">
108
+ <div class="eyebrow">Receipt states</div>
109
+ <h2 id="states-title">What the viewer reports</h2>
110
+ <div class="metric-grid" aria-label="Arc receipt states">
111
+ <div class="metric"><span>success</span><strong>Receipt exists and status is 0x1.</strong></div>
112
+ <div class="metric"><span>revert</span><strong>Receipt exists and status is 0x0.</strong></div>
113
+ <div class="metric"><span>not_found</span><strong>No receipt yet, or the hash has no receipt.</strong></div>
114
+ <div class="metric"><span>unknown</span><strong>RPC unavailable, wrong chain, malformed envelope, or ambiguous status.</strong></div>
115
+ <div class="metric"><span>USDC logs</span><strong>Highlights Transfer events from 0x3600000000000000000000000000000000000000.</strong></div>
116
+ <div class="metric"><span>Chain</span><strong>5042002 / 0x4cef52</strong></div>
117
+ </div>
118
+ </section>
119
+
120
+ <section class="card" aria-labelledby="json-title">
121
+ <div class="eyebrow">Machine-readable output</div>
122
+ <h2 id="json-title">Receipt JSON</h2>
123
+ <pre id="receipt-json" class="json-preview" aria-live="polite"></pre>
124
+ </section>
125
+
126
+ <section class="card danger" aria-labelledby="boundary-title">
127
+ <div class="eyebrow">Trust boundary</div>
128
+ <h2 id="boundary-title">What this does not prove</h2>
129
+ <ul class="mini-list">
130
+ <li>A receipt status does not prove business acceptance, offchain fulfillment, or payment intent review.</li>
131
+ <li>USDC Transfer logs show raw event data only; they do not replace a product settlement policy.</li>
132
+ <li>The viewer does not estimate gas, simulate calls, submit transactions, or retry failed payments.</li>
133
+ <li>Use the <a href="../../docs/view.html#receipt-viewer.md">receipt viewer notes</a> before wiring this evidence into an agent workflow.</li>
134
+ </ul>
135
+ </section>
136
+ </main>
137
+ </body>
138
+ </html>