@fangorn-network/westmarch 0.0.19-dev → 2026.10.2-0.0-dev
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 +33 -3
- package/docs/app-to-agent.md +100 -27
- package/package.json +9 -5
- package/plugin/scripts/fx.mjs +7 -9
- package/site/dist/assets/AccountNotFoundScreen-C4njXc69-RUyAWRzV.js +1 -0
- package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-ZV-LlYBe.js +31 -0
- package/site/dist/assets/Address-H8kD1d9O-CGQnt7Pu.js +17 -0
- package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-CKn2JLwq.js +6 -0
- package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-9oIP9fma.js +1 -0
- package/site/dist/assets/ArrowsRightLeftIcon-BEhBD_6N.js +1 -0
- package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-D1wLlSJs.js +1 -0
- package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-CSBbHTyz.js +1 -0
- package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-BsRq-xBt.js +1 -0
- package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-BPdhsKvN.js +103 -0
- package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-CEGNdrkr.js +1 -0
- package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-Da9P7eSx.js +68 -0
- package/site/dist/assets/CaptchaScreen-DbpvkIGx-BecgMnD_.js +1 -0
- package/site/dist/assets/CheckCircleIcon-DBiWFjO6.js +1 -0
- package/site/dist/assets/CheckCircleIcon-DzG2ROJZ.js +1 -0
- package/site/dist/assets/Checkbox-D1EDeo41-DMHcj9o_.js +52 -0
- package/site/dist/assets/ChevronDownIcon-2Os56gUt.js +1 -0
- package/site/dist/assets/Chip-CZKIKt9K-BWzs9fj5.js +26 -0
- package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-CFvcSiDs.js +16 -0
- package/site/dist/assets/ConnectEmailForm-B-VRE0MN-BozkCg8q.js +3 -0
- package/site/dist/assets/ConnectLedgerScreen-DWHABGot-DaCnbBzh.js +10 -0
- package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-DUkCLkxD.js +1 -0
- package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-Dv_5nMkZ.js +1 -0
- package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-DSGEeB3Y.js +1 -0
- package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-C-s4s_Lq.js +95 -0
- package/site/dist/assets/ConnectWalletView-DqI-PTvX-BKX7fdYr.js +74 -0
- package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-DSoAuMFZ.js +7 -0
- package/site/dist/assets/CopyToClipboard-i_OQSBJr-CZTXyfV0.js +39 -0
- package/site/dist/assets/CopyableText-CQapvaMr-DHO818I0.js +28 -0
- package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-DW4nRGfz.js +1 -0
- package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-DH8Nnb4Y.js +22 -0
- package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-gfUS_uV5.js +1 -0
- package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-DZj9HTHc.js +1 -0
- package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-eHYsDK9W.js +187 -0
- package/site/dist/assets/DepositAddressScreen-DBLRs7aI-DK_XVgVk.js +8 -0
- package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-6gRZzhLe.js +1 -0
- package/site/dist/assets/{EmailInputForm-Cqo1mda8-B2NG3K5V.js → EmailInputForm-Cqo1mda8-CZD4r5LM.js} +11 -11
- package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-BZu2ae0k.js +1 -0
- package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-86pviemJ.js +1 -0
- package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-vDcvT53Y.js +23 -0
- package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-rNJAtSJc.js +1 -0
- package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-I8Q95280.js +1 -0
- package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-Coi7OxoI.js +1 -0
- package/site/dist/assets/EnrollTotp-Dibew4dp-DC7FrDgo.js +66 -0
- package/site/dist/assets/EnvelopeIcon-DY6ERnW7.js +1 -0
- package/site/dist/assets/ErrorBanner-BcpGRt0h-BvESwN99.js +21 -0
- package/site/dist/assets/{ErrorMessage-D8VaAP5m-CrZOnUyg.js → ErrorMessage-D8VaAP5m-oSruKN8L.js} +2 -2
- package/site/dist/assets/ErrorScreen-DipdTWzE-DnKGxH1Z.js +4 -0
- package/site/dist/assets/ExclamationCircleIcon-BZhlqH0r.js +1 -0
- package/site/dist/assets/ExclamationTriangleIcon-CkAOJIYV.js +1 -0
- package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-4pi7jooW.js +75 -0
- package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-z2XCMaLL.js +28 -0
- package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-CBVA9v1-.js +491 -0
- package/site/dist/assets/FingerPrintIcon-ra2dqmJO.js +1 -0
- package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-DwAwc6Ny.js +1 -0
- package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-CZszQsZf.js +1 -0
- package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-DfX6nvFQ.js +58 -0
- package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-CDS_sk_t.js +1 -0
- package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-CPfNQemT.js +1 -0
- package/site/dist/assets/GlobeAltIcon-SEUEIe_E.js +1 -0
- package/site/dist/assets/GooglePay-B53WnudL-aap7zwag.js +1 -0
- package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-DgSjCjQI.js +4 -0
- package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-DO4wICml.js +1 -0
- package/site/dist/assets/InfoBanner-Cb3p1z12-CDWt2ADo.js +22 -0
- package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-Ba19XoBr.js +1 -0
- package/site/dist/assets/InstallWalletScreen-DTA8dwLv-DGpewEch.js +1 -0
- package/site/dist/assets/{JsonTree-BHzNC-ic-DcNcbSoM.js → JsonTree-BHzNC-ic-pAEq1bl9.js} +3 -3
- package/site/dist/assets/{LabelXs-oqZNqbm_-iafrj25N.js → LabelXs-oqZNqbm_-D09YjVR4.js} +2 -2
- package/site/dist/assets/LandingScreen-CGWUoSUG-CUOCWUXd.js +1 -0
- package/site/dist/assets/{Layouts-BMRfo5hw-CeCOyCLK.js → Layouts-BMRfo5hw-D2u1-w8g.js} +16 -16
- package/site/dist/assets/Link-BdDilT2T-BYiP1zvW.js +40 -0
- package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-Tb6cAepT.js +77 -0
- package/site/dist/assets/LinkEmailScreen-dKo9frwc-pq0NF2JO.js +1 -0
- package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-D9MOmDqg.js +117 -0
- package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-B80UJqAZ.js +1 -0
- package/site/dist/assets/{LoadingSkeleton-BMsgO5PV-CiEyS5PZ.js → LoadingSkeleton-BMsgO5PV-BRMh4Fv4.js} +6 -6
- package/site/dist/assets/LockClosedIcon-BqkoEL9J.js +1 -0
- package/site/dist/assets/LoginFailedScreen-CN0dJV6U-DlnOM-Kh.js +1 -0
- package/site/dist/assets/ManualTransferScreen-CHqcuqbs-DSShupq9.js +1 -0
- package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-DUPV_SLA.js +1 -0
- package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-D96GpzXX.js +1 -0
- package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-CYQDa4ND.js +1 -0
- package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-CJtSAXGN.js +1 -0
- package/site/dist/assets/ModalFooter-BCuEK_nz-D2I8KWtk.js +403 -0
- package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-Bx5aHy-z.js +16 -0
- package/site/dist/assets/OAuthStatusScreen-ufbOa-da-CS6988X2.js +1 -0
- package/site/dist/assets/OpenLink-CUpJ1mOr-EZWVSX2r.js +39 -0
- package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-BY4eK5Jh.js +1 -0
- package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-LXys76u9.js +6 -0
- package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-RczvLvmo.js +20 -0
- package/site/dist/assets/{PhArrowCircleDown-D97G-BOA.js → PhArrowCircleDown-BtlNV0TZ.js} +5 -5
- package/site/dist/assets/PhArrowClockwise-mOiWYLMV.js +14 -0
- package/site/dist/assets/PhArrowDown-CkoOD89h.js +14 -0
- package/site/dist/assets/PhArrowLeft-C0jlxVhM.js +14 -0
- package/site/dist/assets/PhArrowRight-BI-V03Xf.js +14 -0
- package/site/dist/assets/{PhArrowSquareOut-Bk2CMG3I.js → PhArrowSquareOut-DnQ7n_B4.js} +5 -5
- package/site/dist/assets/PhArrowUp-izKvURUW.js +14 -0
- package/site/dist/assets/PhArrowUpRight-Bn-JhHsY.js +14 -0
- package/site/dist/assets/{PhArrowsClockwise-BXRn1GEl.js → PhArrowsClockwise-Brtx2Re-.js} +5 -5
- package/site/dist/assets/{PhArrowsDownUp-DM09Jo_3.js → PhArrowsDownUp-PZosTqZG.js} +5 -5
- package/site/dist/assets/PhArrowsLeftRight-CYsRQ2qh.js +14 -0
- package/site/dist/assets/{PhBank-fHbgCqqZ.js → PhBank-hhPAIPVb.js} +5 -5
- package/site/dist/assets/PhBrowser-BjzAmrHO.js +14 -0
- package/site/dist/assets/PhCaretDown-ddSjwcwm.js +14 -0
- package/site/dist/assets/PhCaretLeft-B7YLoNEF.js +14 -0
- package/site/dist/assets/PhCaretRight-BY8D5MIg.js +14 -0
- package/site/dist/assets/PhCaretUp-BG10nSut.js +14 -0
- package/site/dist/assets/PhCheck-DrKd7pBC.js +14 -0
- package/site/dist/assets/{PhCircleHalf-MJZeRBGF.js → PhCircleHalf-Bk9C4DvT.js} +5 -5
- package/site/dist/assets/PhClock-Sj17weCB.js +14 -0
- package/site/dist/assets/{PhCompass-hYn3HhGN.js → PhCompass-Df1ugFO-.js} +5 -5
- package/site/dist/assets/PhCopy-rgttky8E.js +14 -0
- package/site/dist/assets/{PhCreditCard-Fy4pzwtt.js → PhCreditCard-CZpdid2n.js} +5 -5
- package/site/dist/assets/{PhCurrencyDollar-BgiaenCi.js → PhCurrencyDollar-B6IKKjue.js} +5 -5
- package/site/dist/assets/{PhDesktop-CxrzCSv5.js → PhDesktop-BuCDXYJj.js} +5 -5
- package/site/dist/assets/PhDeviceMobile-CnybhXY-.js +14 -0
- package/site/dist/assets/PhDotsThree-CSAEcba1.js +14 -0
- package/site/dist/assets/PhEnvelope-SmaNeXoQ.js +14 -0
- package/site/dist/assets/PhFunnelSimple-DKx35ITq.js +14 -0
- package/site/dist/assets/{PhGlobe-DvmSc9_A.js → PhGlobe-q1mqj65V.js} +5 -5
- package/site/dist/assets/{PhIdentificationCard-CfkZm-5K.js → PhIdentificationCard-BeLXJpfZ.js} +5 -5
- package/site/dist/assets/{PhImage-Cf0J2ZfJ.js → PhImage-BYrKfN9k.js} +5 -5
- package/site/dist/assets/{PhInfo-Bwi51-2X.js → PhInfo-X33f06Ej.js} +5 -5
- package/site/dist/assets/{PhLightbulb-C4FZV7Es.js → PhLightbulb-DMZJsowR.js} +5 -5
- package/site/dist/assets/PhMagnifyingGlass-B6D0sWFL.js +14 -0
- package/site/dist/assets/{PhPaperPlaneRight-zxQVvPLX.js → PhPaperPlaneRight-Dvo-nXr9.js} +5 -5
- package/site/dist/assets/PhPlus-CBvZvLax.js +14 -0
- package/site/dist/assets/{PhPower-CFyWDmgD.js → PhPower-D8-AcNM6.js} +5 -5
- package/site/dist/assets/{PhPuzzlePiece-BjoNVfST.js → PhPuzzlePiece-C-7mYqGO.js} +5 -5
- package/site/dist/assets/{PhQrCode-CP3tFauY.js → PhQrCode-uIXlI5ER.js} +5 -5
- package/site/dist/assets/{PhQuestion-wOWJBd3G.js → PhQuestion-rUoqC2sC.js} +5 -5
- package/site/dist/assets/{PhQuestionMark-CV0k9C0Y.js → PhQuestionMark-W55Txvvb.js} +5 -5
- package/site/dist/assets/{PhSealCheck-vIKzREuN.js → PhSealCheck-BeVLq9PH.js} +5 -5
- package/site/dist/assets/{PhSignOut-CxELV-i0.js → PhSignOut-Cq2PivQ6.js} +5 -5
- package/site/dist/assets/{PhSpinner-CGDmbzUd.js → PhSpinner-CZMtAFdX.js} +5 -5
- package/site/dist/assets/{PhTrash-C9PD0SAt.js → PhTrash-BamrmMsN.js} +5 -5
- package/site/dist/assets/{PhUser-0sWNXQeZ.js → PhUser-A2fxqIV0.js} +5 -5
- package/site/dist/assets/{PhVault-6JTskUcC.js → PhVault-B8m1usHw.js} +5 -5
- package/site/dist/assets/{PhWarning-CoKs3gz6.js → PhWarning-CpkID9oi.js} +5 -5
- package/site/dist/assets/PhWarningCircle-Cm58gXb9.js +14 -0
- package/site/dist/assets/PhX-C3b4cCEy.js +14 -0
- package/site/dist/assets/PhoneIcon-CzbssTb8.js +1 -0
- package/site/dist/assets/PinInput-DbZ0b1i1-1DtLWL81.js +247 -0
- package/site/dist/assets/QrCode-DbnZwlkH-ChwcUk_h.js +22 -0
- package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-C79jd345.js +1 -0
- package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-C9c0x-yq.js +1 -0
- package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-PNqHb6CI.js +7 -0
- package/site/dist/assets/{Screen-D30meWsY-CCeVNuWs.js → Screen-D30meWsY-DDum2qlY.js} +22 -22
- package/site/dist/assets/{ScreenHeader-CHmc4-Lu-DjyrH7IK.js → ScreenHeader-CHmc4-Lu-BqrAEXbV.js} +4 -4
- package/site/dist/assets/ScreenLayout-OwaQQcxH-B4Z8FjFp.js +1 -0
- package/site/dist/assets/{SelectSourceAsset-CWFog6gm-RKjBsB6m.js → SelectSourceAsset-CWFog6gm-8iXuqbAs.js} +18 -18
- package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-CUpQZ9wq.js +14 -0
- package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-ZzvPLj2s.js +15 -0
- package/site/dist/assets/ShieldCheckIcon-Bjwe_-PV.js +1 -0
- package/site/dist/assets/SignRequestScreen-BHKv32KR-0qGeNipB.js +26 -0
- package/site/dist/assets/{StackedContainer-B2vaEl56-eQCM2cmv.js → StackedContainer-B2vaEl56-C-ojzCoQ.js} +2 -2
- package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-C3lgJb68.js +20 -0
- package/site/dist/assets/{Subtitle-CV-2yKE4-zxWw2m1Q.js → Subtitle-CV-2yKE4-o-1FQLlX.js} +2 -2
- package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-l35_C2DG.js +1 -0
- package/site/dist/assets/{Title-BnzYV3Is-B_JpTatT.js → Title-BnzYV3Is-RX1f0RV-.js} +2 -2
- package/site/dist/assets/TodoList-DnyULl18-JxWWU9KA.js +36 -0
- package/site/dist/assets/TransactionDetails-e9nesl30-C91nJPM9.js +109 -0
- package/site/dist/assets/TransactionErrorView-BDBNqA_f-Cpm-4xzm.js +178 -0
- package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-BWCH7-Si.js +31 -0
- package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-BWMeJabk.js +6 -0
- package/site/dist/assets/TurnstileWrapper-B7sNddys-Dgr80Gih.js +1 -0
- package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-Clk4zk-8.js +1 -0
- package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-BgRsoR3B.js +1 -0
- package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-D-SU-vAz.js +1 -0
- package/site/dist/assets/{Value-DTgR824E-BRuIGiec.js → Value-DTgR824E-DZpvBCWi.js} +7 -7
- package/site/dist/assets/WalletCards-DH1rqayz-UuZ4i_Ze.js +1 -0
- package/site/dist/assets/WalletIcon-B1i03Vqc.js +1 -0
- package/site/dist/assets/WalletInfoCard-sq_2wgFg-TXTxmb8L.js +29 -0
- package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-CbrFb0ON.js +1 -0
- package/site/dist/assets/WalletLink-DK5LB5Ha-BYQZV7UV.js +20 -0
- package/site/dist/assets/WalletOverflowButton-bfCnp-9T-CNgSPnTS.js +1 -0
- package/site/dist/assets/WarningBanner-ZZqCEtZK-DNZUUOph.js +21 -0
- package/site/dist/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/site/dist/assets/analytics-mkkvFRju-DOXuftJB.js +1 -0
- package/site/dist/assets/basic-C9wn6QoQ.js +1575 -0
- package/site/dist/assets/bytes-CjWw8TJh.js +1 -0
- package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-tV1Idewc.js +1 -0
- package/site/dist/assets/ccip-BhHEqVIW.js +1 -0
- package/site/dist/assets/check-C_DTyjLS.js +1 -0
- package/site/dist/assets/chevron-down-iklXXH-z.js +1 -0
- package/site/dist/assets/circle-alert-KEOUF48T.js +1 -0
- package/site/dist/assets/circle-check-big-BJ_ADwB4.js +1 -0
- package/site/dist/assets/circle-x-BSfWmhYk.js +1 -0
- package/site/dist/assets/client-BnDcP42J.js +9 -0
- package/site/dist/assets/client-DH86IrJA.js +125 -0
- package/site/dist/assets/coinbase-DCPC2L_R-2ivYfoMv.js +3 -0
- package/site/dist/assets/copy-sK00DOfH.js +1 -0
- package/site/dist/assets/core-CRE213RM.js +211 -0
- package/site/dist/assets/createLucideIcon-D9OK_uxG.js +1 -0
- package/site/dist/assets/custom-V1wex-Di.js +1 -0
- package/site/dist/assets/dijkstra-COg3n3zL.js +1 -0
- package/site/dist/assets/embed.worker-BRIYO7m8.js +1 -0
- package/site/dist/assets/encodePacked-DyXYq3Cc.js +1 -0
- package/site/dist/assets/ethers-zTjbodk5-DSYOi6tU.js +1 -0
- package/site/dist/assets/external-link-Cdp8NBio.js +1 -0
- package/site/dist/assets/fallback-DiFue5Zm.js +1 -0
- package/site/dist/assets/farcaster-DPlSjvF5-DP2DLoHZ.js +1 -0
- package/site/dist/assets/features-B6BTj_BL.js +5 -0
- package/site/dist/assets/fingerprint-pattern-gFJTz5e4.js +1 -0
- package/site/dist/assets/floating-ui.react-D9ndYqYK.js +1 -0
- package/site/dist/assets/floating-ui.react-dom-CNbOgOFt.js +1 -0
- package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-CkAr4Oc0.js +1 -0
- package/site/dist/assets/getChainName-DjpPdUSc-c2urPd0g.js +1 -0
- package/site/dist/assets/getErc20Balance-jXNpFf0N-mFQ9Iqk-.js +1 -0
- package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-Dx5he_uG.js +1 -0
- package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-C446pzMl.js +1 -0
- package/site/dist/assets/getTransactionCount-80UHlwAV.js +1 -0
- package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-FDalV59C.js +1 -0
- package/site/dist/assets/hashTypedData-Beq57Ogb.js +22 -0
- package/site/dist/assets/hooks.module-ClpaQ1Tg.js +1 -0
- package/site/dist/assets/hourglass-CB7z_I9H.js +1 -0
- package/site/dist/assets/http-BocEfKYb.js +19 -0
- package/site/dist/assets/index-0knDOgU9.js +1 -0
- package/site/dist/assets/index-9jNeS_nC.js +1 -0
- package/site/dist/assets/index-B--T-Fmt.js +16 -0
- package/site/dist/assets/index-BDkarAkX.js +2 -0
- package/site/dist/assets/index-BMfMy017.js +679 -0
- package/site/dist/assets/index-BOd8jv8-.js +1 -0
- package/site/dist/assets/index-BdEw5wyl-2VGMzHIs.js +1 -0
- package/site/dist/assets/index-C8ThRelw.js +9 -0
- package/site/dist/assets/{index-CWARkn2w-Cezl_uBr.js → index-CWARkn2w-D6ikWJWj.js} +7 -7
- package/site/dist/assets/{wui-ux-by-reown-fVu0QkI4.js → index-CztIxJB6.js} +296 -296
- package/site/dist/assets/index-D4LdLFDC.js +1 -0
- package/site/dist/assets/index-D6T-s6sR.js +1 -0
- package/site/dist/assets/index-D7396TWe.js +1 -0
- package/site/dist/assets/index-DdxtHe81.js +2 -0
- package/site/dist/assets/index-DvMddJcd-D7AEQrzj.js +12 -0
- package/site/dist/assets/index-nibyPLVP.js +1 -0
- package/site/dist/assets/index.browser-B6Bt5URJ.js +1 -0
- package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-D7xtzRqA.js +1 -0
- package/site/dist/assets/landmark-CMLKifhk.js +1 -0
- package/site/dist/assets/lock-BkyjNE8h.js +1 -0
- package/site/dist/assets/mail-B6-Tq9Ld.js +1 -0
- package/site/dist/assets/native-CJ5et6AR.js +1 -0
- package/site/dist/assets/nonceManager-zkW7cM66.js +1 -0
- package/site/dist/assets/parseEther-C7aOvuze.js +1 -0
- package/site/dist/assets/parseSignature-BIjcuWdB.js +1 -0
- package/site/dist/assets/preload-helper-PPVm8Dsz.js +1 -0
- package/site/dist/assets/privateKeyToAccount-CRvvpHWf.js +1 -0
- package/site/dist/assets/property-CnFDNXw5.js +3 -0
- package/site/dist/assets/react-auth_false-DedQs79w.js +1 -0
- package/site/dist/assets/reservoir-x-nGuZkT-DFxAFt2v.js +1 -0
- package/site/dist/assets/roles-DV_6FRBZ.js +1 -0
- package/site/dist/assets/safe-url-D7SRPu33-A8zMgOTd.js +1 -0
- package/site/dist/assets/serializeErc6492Signature-CeJeL3h6.js +1 -0
- package/site/dist/assets/serializeTransaction-L-_Ej-jW.js +8 -0
- package/site/dist/assets/{shared-DRZUmnL5-xqAUARWo.js → shared-DRZUmnL5-MWD-C-pp.js} +33 -33
- package/site/dist/assets/{shared-FM0rljBt--Avg6q2R.js → shared-FM0rljBt-c4eSjzGj.js} +6 -6
- package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-CF2QteTJ.js +1 -0
- package/site/dist/assets/simulateTransaction-CfuOrae4-BHUMPf6Q.js +1 -0
- package/site/dist/assets/smartphone-DAN5rY0T.js +1 -0
- package/site/dist/assets/stripe.esm-Dv8C3q95.js +2 -0
- package/site/dist/assets/styles-BsotlekN-Cjuu2SDV.js +9 -0
- package/site/dist/assets/{styles-Cm9H63WO-D90SsXOs.js → styles-Cm9H63WO-Cv1TbqYi.js} +17 -17
- package/site/dist/assets/{styles-DVyDvTdj-BTABn2K8.js → styles-DVyDvTdj-CI4xZNHp.js} +8 -8
- package/site/dist/assets/{styles-hslXy6xG-D-49b1rk.js → styles-hslXy6xG-DeVcFoDD.js} +23 -23
- package/site/dist/assets/telegram-B-JqnkqZ-DTjJLkIF.js +1 -0
- package/site/dist/assets/to-ui-error-DWe-qwS3-Dx3yzcV0.js +4 -0
- package/site/dist/assets/toAccount-Bq6rbpbI.js +1 -0
- package/site/dist/assets/transaction-BNTP-bFm-CrebgH-D.js +1 -0
- package/site/dist/assets/transactionReceipt-BwJl-gjg.js +1 -0
- package/site/dist/assets/transfer-6YztDh-t-DU6hH7CZ.js +1 -0
- package/site/dist/assets/transformers.web-BEKVh7pD.js +40 -0
- package/site/dist/assets/transformers.web-aWa49tv1.js +40 -0
- package/site/dist/assets/triangle-alert-ndoiBs4y.js +1 -0
- package/site/dist/assets/twitch-5IOe4sIQ-BXviIywg.js +1 -0
- package/site/dist/assets/useGetSolPrice-x7gfUIHJ-C7axYJ--.js +1 -0
- package/site/dist/assets/useGetTokenPrice-CaoUkb79-CR4CrguU.js +1 -0
- package/site/dist/assets/useI18n-C-5n_noF-C_t8QqBJ.js +1 -0
- package/site/dist/assets/useWalletBalance-CZnP5f_f-BnSHLVI_.js +1 -0
- package/site/dist/assets/w3m-modal-Bz5KWTQ-.js +1174 -0
- package/site/dist/assets/wallet-DTM31trz.js +2 -0
- package/site/dist/assets/wallet-UESEqPei.js +1 -0
- package/site/dist/assets/wallet-connect-BvJtUsDt-CnvUnFbz.js +1 -0
- package/site/dist/assets/walletconnect-v2-3frQFwbg-CzQk99gS.js +15 -0
- package/site/dist/assets/x-B2_XdOVQ.js +1 -0
- package/site/dist/index.html +120 -8
- package/site/feed.js +16 -1
- package/site/no-model.js +4 -0
- package/site/worker.js +597 -58
- package/src/agent/agent-card.js +20 -11
- package/src/agent/mcp.js +150 -316
- package/src/agent/view-tools.js +53 -12
- package/src/agent/x402.js +379 -44
- package/src/core/embed.js +51 -52
- package/src/core/embed.worker.js +1 -1
- package/src/core/shard.js +7 -3
- package/src/discover/apps.js +4 -3
- package/src/discover/explore.js +36 -22
- package/src/market/reactions.js +2 -2
- package/src/publish/devnet.js +95 -0
- package/src/publish/lint.js +2 -1
- package/src/publish/ship.js +517 -236
- package/src/publish/view-cli.js +11 -10
- package/src/publish/view.js +359 -206
- package/src/taste/taste-doc.js +3 -2
- package/src/taste/taste.js +2 -2
- package/src/taste/vault.js +3 -1
- package/site/dist/assets/AccountNotFoundScreen-C4njXc69-BOfmiSH1.js +0 -1
- package/site/dist/assets/AddFundsSelectionScreen-RAoCn7ej-B6uvYyqa.js +0 -31
- package/site/dist/assets/Address-H8kD1d9O-DcHv7A8C.js +0 -17
- package/site/dist/assets/AffirmativeConsentScreen-BG0MJxku-v2wz-N-s.js +0 -6
- package/site/dist/assets/AllowlistRejectionScreen-Cae6e1Od-DhVy9iGN.js +0 -1
- package/site/dist/assets/ApiController-BbhXeqTV.js +0 -5
- package/site/dist/assets/ArrowsRightLeftIcon-cp60yobq.js +0 -1
- package/site/dist/assets/AuthenticateWithWalletScreen-CU6hIcFT-B0eT1zE6.js +0 -1
- package/site/dist/assets/AwaitingEvmToSolBridgingScreen-D0e_sZMS-DL5PH-v_.js +0 -1
- package/site/dist/assets/AwaitingExternalEthereumTransferScreen-ur5HL4R7-n1BsRPqc.js +0 -1
- package/site/dist/assets/AwaitingPasswordlessCodeScreen-DRHz76mM-BVl2BJDY.js +0 -103
- package/site/dist/assets/AwaitingSolToEvmBridgingScreen-CdLRrGD2-BD-OZUQb.js +0 -1
- package/site/dist/assets/BridgeNetworkSelectionView-CFGjB2nO-CaYrzRje.js +0 -68
- package/site/dist/assets/CaptchaScreen-DbpvkIGx-BQJ7inx1.js +0 -1
- package/site/dist/assets/CheckCircleIcon-BM409mGI.js +0 -1
- package/site/dist/assets/CheckCircleIcon-NCOQc0Gs.js +0 -1
- package/site/dist/assets/Checkbox-D1EDeo41-rHlvsZzP.js +0 -52
- package/site/dist/assets/ChevronDownIcon-CW-4MSpE.js +0 -1
- package/site/dist/assets/Chip-CZKIKt9K-CutJG010.js +0 -26
- package/site/dist/assets/CoinbaseOnrampStatusScreen-BbyCJDXL-DDaMC7cl.js +0 -16
- package/site/dist/assets/ConnectEmailForm-B-VRE0MN-DVSf4tFJ.js +0 -3
- package/site/dist/assets/ConnectLedgerScreen-DWHABGot-h0uYIUi6.js +0 -10
- package/site/dist/assets/ConnectOnlyLandingScreen-CZYx9Bmp-DP19hJq5.js +0 -1
- package/site/dist/assets/ConnectOnlyStatusScreen-DCRzjxHx-DHvijlTx.js +0 -1
- package/site/dist/assets/ConnectOrCreateScreen-CUUYUO07-ewmXYPTQ.js +0 -1
- package/site/dist/assets/ConnectPhoneForm-Dek5xn4C-DUYdmVtr.js +0 -95
- package/site/dist/assets/ConnectWalletView-DqI-PTvX-RobGErVW.js +0 -74
- package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-Bbiq3hY6.js +0 -1
- package/site/dist/assets/ConnectionStatusScreen-fUfROm9m-DPkOGNWl.js +0 -7
- package/site/dist/assets/ConnectorUtil-BO1L6VvV.js +0 -21
- package/site/dist/assets/ConstantsUtil-DmLsRHAl.js +0 -198
- package/site/dist/assets/CopyToClipboard-i_OQSBJr-DujUHNzW.js +0 -39
- package/site/dist/assets/CopyableText-CQapvaMr-DMIdpDaU.js +0 -28
- package/site/dist/assets/CrossAppAuthScreen-CLw7ymZq-DbGy4QyA.js +0 -1
- package/site/dist/assets/CustomLandingScreenView-Cm_MBSyX-B3WqJZBh.js +0 -22
- package/site/dist/assets/DelegatedActionsConsentScreen-DYF2U4y5-CmUDoS0C.js +0 -1
- package/site/dist/assets/DelegatedActionsRevokeScreen-bPp6Uwyh-fcO51q5H.js +0 -1
- package/site/dist/assets/DepositAddressAddressStepView-4vdkb55L-ClAb_j1B.js +0 -187
- package/site/dist/assets/DepositAddressScreen-DBLRs7aI-CN1P_KY9.js +0 -8
- package/site/dist/assets/DepositAddressV3Screen-CDqIEMQw-BtDZmuEN.js +0 -1
- package/site/dist/assets/EmbeddedWalletConnectingScreen-D4T7DUzs-D3pExuaW.js +0 -1
- package/site/dist/assets/EmbeddedWalletCreatedScreen-D7WhW5ve-CLsJbPYQ.js +0 -1
- package/site/dist/assets/EmbeddedWalletKeyExportScreen-CqV4NYQ3-DI6bu-oi.js +0 -23
- package/site/dist/assets/EmbeddedWalletOnAccountCreateScreen-BoKb3Q2S-BQ9Ygr0P.js +0 -1
- package/site/dist/assets/EmbeddedWalletPasswordUpdateScreen-D3rMlqS7-CxrfLr5q.js +0 -1
- package/site/dist/assets/EmbeddedWalletPasswordUpdateSplashScreen-BPFkWJeE-DHZRGoTN.js +0 -1
- package/site/dist/assets/EnrollTotp-Dibew4dp-7gVtJ1Jt.js +0 -66
- package/site/dist/assets/EnvelopeIcon-Dh2vSVj4.js +0 -1
- package/site/dist/assets/ErrorBanner-BcpGRt0h-BKOYTyCi.js +0 -21
- package/site/dist/assets/ErrorScreen-DipdTWzE-BT8TtRTx.js +0 -4
- package/site/dist/assets/ErrorScreen-DipdTWzE-DAam41Xl.js +0 -1
- package/site/dist/assets/ExclamationCircleIcon-DUA2wMfO.js +0 -1
- package/site/dist/assets/ExclamationTriangleIcon-eZfaqcFq.js +0 -1
- package/site/dist/assets/FarcasterConnectStatusScreen-51tjUxqq-BPCBi37Q.js +0 -75
- package/site/dist/assets/FarcasterSignerStatusScreen-6x-Uh95G-XCmWpnUd.js +0 -28
- package/site/dist/assets/FiatOnrampScreen-BAhYeiTI-DJRcUh2N.js +0 -491
- package/site/dist/assets/FingerPrintIcon-Dm6kxmg7.js +0 -1
- package/site/dist/assets/FundSolWalletWithExternalSolanaWallet-WtLwYaOl-BfNVSTb1.js +0 -1
- package/site/dist/assets/FundWalletMethodHeader-BWNkokCT-BQdbJ5Bb.js +0 -1
- package/site/dist/assets/FundWithBankDepositScreen-DajkHxBD-BfvMcHb6.js +0 -58
- package/site/dist/assets/FundingEditAmountScreen-CpvpiO4M-VBfbahnA.js +0 -1
- package/site/dist/assets/FundingMethodSelectionScreen-Db7vhRlE-CMuBYf64.js +0 -1
- package/site/dist/assets/GlobeAltIcon-vqrVV5ho.js +0 -1
- package/site/dist/assets/GooglePay-B53WnudL-BX5_WtCx.js +0 -1
- package/site/dist/assets/HCaptchaWrapper-CUi9LJsp-D-I8j7NO.js +0 -4
- package/site/dist/assets/InAppBrowserLoginNotPossible-BMR9br1T-BY3j50El.js +0 -1
- package/site/dist/assets/InfoBanner-Cb3p1z12-DLBl_Ywy.js +0 -22
- package/site/dist/assets/InjectedWalletIcon-DLcYOGDj-DLSV_NjP.js +0 -1
- package/site/dist/assets/InstallWalletScreen-DTA8dwLv-CxreyXoD.js +0 -1
- package/site/dist/assets/LandingScreen-CGWUoSUG-Q7hgXKvM.js +0 -1
- package/site/dist/assets/Link-BdDilT2T-BEDEdHuS.js +0 -40
- package/site/dist/assets/LinkConflictScreen-C3ZEPsgl-DK1z_v7y.js +0 -77
- package/site/dist/assets/LinkEmailScreen-dKo9frwc-BYYpJ5-6.js +0 -1
- package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-uhub9C26.js +0 -1
- package/site/dist/assets/LinkPasskeyScreen-zbrl_AI_-uid22VTN.js +0 -117
- package/site/dist/assets/LinkPhoneScreen-oIUpfj0w-CVQibNi9.js +0 -1
- package/site/dist/assets/Loader-UAZqxsnw-Cy7hEE2f.js +0 -62
- package/site/dist/assets/LockClosedIcon-BgY_5v_Z.js +0 -1
- package/site/dist/assets/LoginFailedScreen-CN0dJV6U-DhUMiJZJ.js +0 -1
- package/site/dist/assets/ManualTransferScreen-CHqcuqbs-DMygAXMU.js +0 -1
- package/site/dist/assets/MfaAuthEnrollmentFlowScreen-Dj7gPTRC-_1GFUSIg.js +0 -1
- package/site/dist/assets/MfaAuthVerifyFlowScreen-DbFpyWMy-DLnBJ5b8.js +0 -1
- package/site/dist/assets/MfaEnrollmentFlowScreen-DhLnGgnr-BUJY1lzs.js +0 -1
- package/site/dist/assets/MfaVerifyFlowScreen-C_XPjMRP-25l5fVun.js +0 -1
- package/site/dist/assets/ModalFooter-BCuEK_nz-c-khvG4Y.js +0 -403
- package/site/dist/assets/MoonpayStatusScreen-CzTUtokc-BRFN2eXr.js +0 -16
- package/site/dist/assets/OAuthStatusScreen-ufbOa-da-DL30vu07.js +0 -1
- package/site/dist/assets/OpenLink-CUpJ1mOr-DfAvJRyK.js +0 -39
- package/site/dist/assets/PasskeySelectSignupOrLogin-xh2ScC_l-DtxhRQyw.js +0 -1
- package/site/dist/assets/PasskeyStatusScreen-Bla8MaOk-CNfu8ujT.js +0 -6
- package/site/dist/assets/PasswordRecoveryScreen-awGXZkQk-BR6z6xE8.js +0 -20
- package/site/dist/assets/PhArrowClockwise-DVgyFuez.js +0 -14
- package/site/dist/assets/PhArrowDown-87b1BizM.js +0 -14
- package/site/dist/assets/PhArrowLeft-Da0MfnbK.js +0 -14
- package/site/dist/assets/PhArrowRight-AdrIL4DW.js +0 -14
- package/site/dist/assets/PhArrowUp-4sgyKPGx.js +0 -14
- package/site/dist/assets/PhArrowUpRight-BNghD2zq.js +0 -14
- package/site/dist/assets/PhArrowsLeftRight-MuKBLUoF.js +0 -14
- package/site/dist/assets/PhBrowser-2-9m-AkM.js +0 -14
- package/site/dist/assets/PhCaretDown-BDNabFuL.js +0 -14
- package/site/dist/assets/PhCaretLeft-CI460R-A.js +0 -14
- package/site/dist/assets/PhCaretRight-D7ZS3Ls8.js +0 -14
- package/site/dist/assets/PhCaretUp-C-iIfyvO.js +0 -14
- package/site/dist/assets/PhCheck-DxQXn57r.js +0 -14
- package/site/dist/assets/PhClock-x3MS3Mwi.js +0 -14
- package/site/dist/assets/PhCopy-CWNYcABI.js +0 -14
- package/site/dist/assets/PhDeviceMobile-CsyA5Goi.js +0 -14
- package/site/dist/assets/PhDotsThree-D-k1BiYg.js +0 -14
- package/site/dist/assets/PhEnvelope-D8kzeNox.js +0 -14
- package/site/dist/assets/PhFunnelSimple-CfQs5DAW.js +0 -14
- package/site/dist/assets/PhMagnifyingGlass-C9u8jSCJ.js +0 -14
- package/site/dist/assets/PhPlus-C4UoW-2n.js +0 -14
- package/site/dist/assets/PhWarningCircle-BXHw1fCt.js +0 -14
- package/site/dist/assets/PhX-DX0SFIqC.js +0 -14
- package/site/dist/assets/PhoneIcon-CvIiPwYQ.js +0 -1
- package/site/dist/assets/PinInput-DbZ0b1i1-CDXZBdQx.js +0 -247
- package/site/dist/assets/PrivyPluginContext-DA-yS5vO-CtQWBiV2.js +0 -1
- package/site/dist/assets/QrCode-DbnZwlkH-BymHrKY7.js +0 -22
- package/site/dist/assets/RecoveryOAuthStatusScreen-CsgtTI2x-Dro1btv5.js +0 -1
- package/site/dist/assets/RecoveryPasswordCreateScreen-Be-qJ-LU-C1q1hiaN.js +0 -1
- package/site/dist/assets/RecoverySelectionScreen-D1iGtpmi-C21D20gD.js +0 -7
- package/site/dist/assets/SIWXUtil-CHZVugix.js +0 -1
- package/site/dist/assets/ScreenLayout-OwaQQcxH-CrQM6kvA.js +0 -1
- package/site/dist/assets/SetAutomaticRecoveryScreen-BO_OEqAZ-BcWHJoEG.js +0 -14
- package/site/dist/assets/SetWalletPasswordForm-DwCTVtAh-kq1zvA7d.js +0 -11
- package/site/dist/assets/ShieldCheckIcon-o6RHYev1.js +0 -1
- package/site/dist/assets/SignRequestScreen-BHKv32KR-iQCaBkcz.js +0 -26
- package/site/dist/assets/SolanaWalletConnector-C7Lqk-Wz-BUdJJ0ep.js +0 -1
- package/site/dist/assets/StandardSignAndSendTransactionScreen-CfhIXVtd-nART-xUU.js +0 -20
- package/site/dist/assets/TelegramAuthScreen-UL9zLfO8-Dw9geyK3.js +0 -1
- package/site/dist/assets/TodoList-DnyULl18-C8Eyy28X.js +0 -36
- package/site/dist/assets/TransactionDetails-e9nesl30-DeEL1JQS.js +0 -109
- package/site/dist/assets/TransactionErrorView-BDBNqA_f-Dffuo7I4.js +0 -178
- package/site/dist/assets/TransferFromWalletScreen-Qvn1Yuno-CWLWUiHy.js +0 -31
- package/site/dist/assets/TransferOrBridgeLoadingScreen-Dl1I_iIz-Bo-tm6wr.js +0 -6
- package/site/dist/assets/TurnstileWrapper-B7sNddys-Jwy1tJtm.js +0 -1
- package/site/dist/assets/UpdateEmailScreen-DVOhPV0H-gFbSIUhh.js +0 -1
- package/site/dist/assets/UpdatePhoneScreen-CXRswTNZ-Coy4E1NQ.js +0 -1
- package/site/dist/assets/UserLimitReachedScreen-5we2DHn8-D7m70C4b.js +0 -1
- package/site/dist/assets/WalletCards-DH1rqayz-Bgp9Fsgs.js +0 -1
- package/site/dist/assets/WalletIcon-gKf0gTw6.js +0 -1
- package/site/dist/assets/WalletInfoCard-sq_2wgFg-ISBsQt8p.js +0 -29
- package/site/dist/assets/WalletInterstitialScreen-Sh7LfsAh-DG-RuO-F.js +0 -1
- package/site/dist/assets/WalletLink-DK5LB5Ha-CXBqCHf3.js +0 -20
- package/site/dist/assets/WalletOverflowButton-bfCnp-9T-DP0-PoZL.js +0 -1
- package/site/dist/assets/WarningBanner-ZZqCEtZK-2eU4Qo74.js +0 -21
- package/site/dist/assets/__vite-browser-external-CitKSmYk.js +0 -1
- package/site/dist/assets/_esm-CJe2F7LL.js +0 -7
- package/site/dist/assets/_esm-zhBBffjV.js +0 -1
- package/site/dist/assets/accounts-Bq8t1wAD.js +0 -1
- package/site/dist/assets/accounts-D96UKWkC.js +0 -1
- package/site/dist/assets/analytics-mkkvFRju-BRBcadka.js +0 -1
- package/site/dist/assets/baseSepolia-DX-qGsik.js +0 -1
- package/site/dist/assets/basic-MlZ7f4s9.js +0 -1575
- package/site/dist/assets/capitalizeFirstLetter-DmLYqXsO-DtOXx-2n.js +0 -1
- package/site/dist/assets/ccip-BI9LWBBg.js +0 -1
- package/site/dist/assets/ccip-C-59SNEF.js +0 -7
- package/site/dist/assets/ccip-y08wK9du.js +0 -1
- package/site/dist/assets/chain-DM3oH6Px.js +0 -8
- package/site/dist/assets/check-BPgcwEPP.js +0 -1
- package/site/dist/assets/chevron-down-BSsbXkK-.js +0 -1
- package/site/dist/assets/circle-alert-DWaeLZNj.js +0 -1
- package/site/dist/assets/circle-check-big-Chb1ivw9.js +0 -1
- package/site/dist/assets/circle-x-AV9Cet0R.js +0 -1
- package/site/dist/assets/client-D0IV9lV9.js +0 -2
- package/site/dist/assets/client-Dsa7_3V3.js +0 -9
- package/site/dist/assets/coinbase-DCPC2L_R-Rs9nVCfm.js +0 -3
- package/site/dist/assets/copy-Ckr0AkL6.js +0 -1
- package/site/dist/assets/core-zQ1tZtVZ.js +0 -12
- package/site/dist/assets/createLucideIcon-IPW50ckY.js +0 -1
- package/site/dist/assets/defineChain-CiuCXUZW.js +0 -1
- package/site/dist/assets/dijkstra-Drfl93V4.js +0 -1
- package/site/dist/assets/dist-BxQqwnue.js +0 -16
- package/site/dist/assets/dist-Cm7pNAtL.js +0 -1
- package/site/dist/assets/dist-CoS6E-3x.js +0 -1
- package/site/dist/assets/dist-Dl07uGq1.js +0 -1
- package/site/dist/assets/embed.worker-ugGhEqCV.js +0 -1
- package/site/dist/assets/errors-BQ3N-RVt-BLqjTPNR.js +0 -1
- package/site/dist/assets/esm-C3DslVJx.js +0 -1
- package/site/dist/assets/esm-Ci1ogKuK.js +0 -1
- package/site/dist/assets/esm-DXsETz3H.js +0 -1
- package/site/dist/assets/esm-n2lWo7kz.js +0 -1
- package/site/dist/assets/esm-vs5jNx_Y.js +0 -37
- package/site/dist/assets/ethers-zTjbodk5-B7BBFFKf.js +0 -1
- package/site/dist/assets/eventemitter3-CL76jPsW.js +0 -1
- package/site/dist/assets/external-link-D-uehwM2.js +0 -1
- package/site/dist/assets/farcaster-DPlSjvF5-BqTvDXBR.js +0 -1
- package/site/dist/assets/features-CYXRa3_m.js +0 -2
- package/site/dist/assets/fingerprint-pattern-D8ytA16Q.js +0 -1
- package/site/dist/assets/floating-ui.react-dom-DKWOL9UG.js +0 -1
- package/site/dist/assets/floating-ui.react-xRkl9O3u.js +0 -1
- package/site/dist/assets/formatErc20TokenAmount-BuPk9xcy-BVqOKQif.js +0 -1
- package/site/dist/assets/formatter-CVbp4SlJ.js +0 -1
- package/site/dist/assets/get-entropy-details-for-user-dHRH5Gpi-Dukf7_kJ.js +0 -1
- package/site/dist/assets/getChainName-DjpPdUSc-DUyKDrnp.js +0 -1
- package/site/dist/assets/getErc20Balance-jXNpFf0N-V2btxzrJ.js +0 -1
- package/site/dist/assets/getErc20TokenInfo-BNTrzLMP-CtCwQuGA.js +0 -1
- package/site/dist/assets/getFormattedUsdFromLamports-De3U9GlO-Gx4_wxON.js +0 -1
- package/site/dist/assets/getUsdcMintAddress-BVgm9GRo-r2UnDeEy.js +0 -1
- package/site/dist/assets/guards-BIW-GiMJ-CHfXo8XI.js +0 -1
- package/site/dist/assets/hooks.module-BYZGfwDl.js +0 -1
- package/site/dist/assets/hourglass-CPG1HZmf.js +0 -1
- package/site/dist/assets/id-UqEwL2BI-CdKXapd-.js +0 -1
- package/site/dist/assets/index-BdEw5wyl-ChSJvPZk.js +0 -1
- package/site/dist/assets/index-CKdAF_dK.js +0 -8
- package/site/dist/assets/index-DvMddJcd-D-4mRLKK.js +0 -12
- package/site/dist/assets/index-YiCw0Y_x-BX1zsiqG.js +0 -437
- package/site/dist/assets/index.browser-B-i-Lj3J.js +0 -1
- package/site/dist/assets/index.browser-BMmUcykv.js +0 -1
- package/site/dist/assets/index.es-BmqyoBEM.js +0 -2
- package/site/dist/assets/index.es-C1v1or--.js +0 -3
- package/site/dist/assets/isPaymentRequestAvailable-Bq1cemEn-By6r0dVB.js +0 -1
- package/site/dist/assets/jsx-runtime-BbDfbRii.js +0 -1
- package/site/dist/assets/landmark-CH6LV6F0.js +0 -1
- package/site/dist/assets/lib-BsS8z8Lj.js +0 -1
- package/site/dist/assets/localBatchGatewayRequest-5YxrERBj.js +0 -32
- package/site/dist/assets/lock-DcskI4Bb.js +0 -1
- package/site/dist/assets/logger-CsXm3oIv-B0UYpNFI.js +0 -1
- package/site/dist/assets/mail-Bs0r5yOi.js +0 -1
- package/site/dist/assets/modal-context-CwmSeFcv-DruX9J1E.js +0 -1
- package/site/dist/assets/native-BKj27wfc.js +0 -1
- package/site/dist/assets/ofetch.CWycOUEr-CHp-crOQ.js +0 -1
- package/site/dist/assets/parseAbi-Csg-8t59.js +0 -2
- package/site/dist/assets/parseAbiParameters-BWbIMYe4.js +0 -1
- package/site/dist/assets/parseEther-CGP3gnf2.js +0 -1
- package/site/dist/assets/pkce-DXF0yKjP-Bqz4wAsq.js +0 -1
- package/site/dist/assets/preload-helper-BaNbYf_w.js +0 -1
- package/site/dist/assets/privy-context-81KmsBw2-CjWGzyg7.js +0 -2
- package/site/dist/assets/property-DuZ9DLP4.js +0 -3
- package/site/dist/assets/react-DCXkXSrv.js +0 -1
- package/site/dist/assets/react-DQnMl2Xv.js +0 -1
- package/site/dist/assets/react-DrJ0MmMO.js +0 -1
- package/site/dist/assets/react-auth-sHnipzbS.js +0 -1
- package/site/dist/assets/react-dom-5dW7aKoP.js +0 -1
- package/site/dist/assets/reservoir-x-nGuZkT-BMX127i6.js +0 -1
- package/site/dist/assets/roles-32M1l9jI.js +0 -1
- package/site/dist/assets/roles-DKPW79Ca.js +0 -1
- package/site/dist/assets/rolldown-runtime-BcKkbAw3.js +0 -1
- package/site/dist/assets/safe-url-D7SRPu33-CkMedGro.js +0 -1
- package/site/dist/assets/secp256k1-C4nzcPo-.js +0 -1
- package/site/dist/assets/secp256k1-D92aGNnU.js +0 -1
- package/site/dist/assets/secp256k1-DmbwyFau.js +0 -1
- package/site/dist/assets/secp256k1-_MleGpqL.js +0 -1
- package/site/dist/assets/secp256k1-xl2og-wO.js +0 -1
- package/site/dist/assets/serializeAuthorizationList-CX8hGL56.js +0 -1
- package/site/dist/assets/serializeSignature-DfxkPnu-.js +0 -2
- package/site/dist/assets/sha2-BU_QyRTx.js +0 -1
- package/site/dist/assets/shouldProceedtoEmbeddedWalletCreationFlow-B3vyavrh-ByFOTrQK.js +0 -1
- package/site/dist/assets/simulateTransaction-CfuOrae4-CMFGDgT_.js +0 -1
- package/site/dist/assets/smartphone-CcEZ-XAy.js +0 -1
- package/site/dist/assets/src-BBjkfi-Q.js +0 -1
- package/site/dist/assets/storage-Ufl800Ne-EalZTH9q.js +0 -9
- package/site/dist/assets/stringify-BlDm7tuc.js +0 -21
- package/site/dist/assets/stripe.esm-CZRDpc-N.js +0 -2
- package/site/dist/assets/styles-BsotlekN-CXEB4Ud9.js +0 -9
- package/site/dist/assets/telegram-B-JqnkqZ-RBoY3PLx.js +0 -1
- package/site/dist/assets/to-ui-error-DWe-qwS3-DgCO-Y70.js +0 -4
- package/site/dist/assets/toViemAccount-B5iDGq1b-DZ1MBqUW.js +0 -2
- package/site/dist/assets/transaction-BNTP-bFm-M5tJx8vY.js +0 -1
- package/site/dist/assets/transfer-6YztDh-t-BVQkTvG-.js +0 -1
- package/site/dist/assets/transformers.web-D4RvOP4-.js +0 -40
- package/site/dist/assets/transformers.web-D8W1u_I2.js +0 -49
- package/site/dist/assets/triangle-alert-CWjYH75H.js +0 -1
- package/site/dist/assets/twitch-5IOe4sIQ-lDtgk0os.js +0 -1
- package/site/dist/assets/types-4CuOD23i.js +0 -1
- package/site/dist/assets/use-headless-crypto-deposit-Yn-QRLB3-Dn6_8w-Y.js +0 -1
- package/site/dist/assets/use-unlink-wallet-BKK4_5Ur-DgsQ75lh.js +0 -1
- package/site/dist/assets/useActiveWallet-B-yZYGc6-B6JWREbk.js +0 -123
- package/site/dist/assets/useGetSolPrice-x7gfUIHJ-D1hmQfpA.js +0 -1
- package/site/dist/assets/useGetTokenPrice-CaoUkb79-ZnbuJ-vP.js +0 -1
- package/site/dist/assets/useI18n-C-5n_noF-BcqC76yj.js +0 -1
- package/site/dist/assets/useWalletBalance-CZnP5f_f-L3V0jDjL.js +0 -1
- package/site/dist/assets/useWallets-X1ndvJ_U-CLwUcevw.js +0 -1
- package/site/dist/assets/utils-BkR7EQjg.js +0 -1
- package/site/dist/assets/utils-DJhBNOZA.js +0 -1
- package/site/dist/assets/utils-DdckVfQr.js +0 -1
- package/site/dist/assets/w3m-modal-BMubXAoG.js +0 -1174
- package/site/dist/assets/wallet-Bn85Sg2W.js +0 -1
- package/site/dist/assets/wallet-connect-BvJtUsDt-Y03hhqFN.js +0 -1
- package/site/dist/assets/wallet-uSKqQsgP.js +0 -2
- package/site/dist/assets/walletconnect-registry-xxG8XLCb-D1SSFvMp.js +0 -1
- package/site/dist/assets/walletconnect-v2-3frQFwbg-Dv6JY_ko.js +0 -12
- package/site/dist/assets/x-rlSp9U7W.js +0 -1
package/src/agent/view-tools.js
CHANGED
|
@@ -69,9 +69,10 @@ const WHERE = { type: "object", description: 'Equality filters, e.g. {"phase":"P
|
|
|
69
69
|
* The tools for one app, or null when its card names no views.
|
|
70
70
|
* `app` is `{ name, desc, views }` from a verified card. `fetchCatalog(url)` may
|
|
71
71
|
* answer from a stale copy: the survey only describes and routes, and loading a
|
|
72
|
-
* view re-reads its catalog through shard.js either way.
|
|
72
|
+
* view re-reads its catalog through shard.js either way. `queryVector(q)` embeds a
|
|
73
|
+
* query (null to rank by words); a Worker passes its own encoder.
|
|
73
74
|
*/
|
|
74
|
-
export function viewTools(app, { fetchCatalog = fetch } = {}) {
|
|
75
|
+
export function viewTools(app, { fetchCatalog = fetch, queryVector: embedQ = queryVector } = {}) {
|
|
75
76
|
if (!app.views?.length) return null;
|
|
76
77
|
const names = new Map(); // view url → its name in every result
|
|
77
78
|
const label = (url) => names.get(url) ?? url.split("/").pop();
|
|
@@ -115,7 +116,7 @@ export function viewTools(app, { fetchCatalog = fetch } = {}) {
|
|
|
115
116
|
}),
|
|
116
117
|
search: async ({ query, limit = 10, views, where: w }) => {
|
|
117
118
|
if (!query) throw new Error("query is required");
|
|
118
|
-
const qv = await
|
|
119
|
+
const qv = await embedQ(query);
|
|
119
120
|
const ranked = views?.length ? await pick(views) : (await route(query, qv)).slice(0, ROUTE_TOP);
|
|
120
121
|
const others = views?.length ? [] : (await route(query, qv)).slice(ROUTE_TOP, ROUTE_TOP + 5).map((v) => v.name);
|
|
121
122
|
const per = await Promise.all(ranked.map(async (v) =>
|
|
@@ -125,7 +126,9 @@ export function viewTools(app, { fetchCatalog = fetch } = {}) {
|
|
|
125
126
|
next_views: others, hits };
|
|
126
127
|
},
|
|
127
128
|
get: async ({ id, view }) => {
|
|
128
|
-
|
|
129
|
+
// Nothing loaded (a fresh Worker isolate: each request may land on a new one): load every view.
|
|
130
|
+
// ponytail: downloads every view for an app with many; route by id when that shows.
|
|
131
|
+
for (const v of view ? await pick([view]) : loaded.size ? [] : await survey()) await load(v);
|
|
129
132
|
for (const [v, rows] of loaded) { const r = getRow(rows, id, rolesOf(v)); if (r) return r; }
|
|
130
133
|
return { error: `${id} is not in a loaded view. Search for it, or pass its view.`, loaded: [...loaded.keys()].map(label) };
|
|
131
134
|
},
|
|
@@ -180,21 +183,59 @@ export function viewTools(app, { fetchCatalog = fetch } = {}) {
|
|
|
180
183
|
const p = app.paid, dec = p.decimals ?? 6;
|
|
181
184
|
const price = `${(Number(p.price) / 10 ** dec).toFixed(dec).replace(/0+$/, "").replace(/\.$/, "")} ${p.symbol ?? "USDC"}`;
|
|
182
185
|
const cap = BigInt(Math.round(Number(process.env.FANGORN_MCP_MAX_PRICE ?? "0.10") * 10 ** dec));
|
|
183
|
-
|
|
184
|
-
if (!id) throw new Error("id is required");
|
|
186
|
+
const resolve = (id) => {
|
|
185
187
|
const hit = loadedRows().find(({ r, view }) => r.id === id || r[rolesOf(view).identity] === id);
|
|
186
|
-
|
|
188
|
+
return { key: hit ? hit.r[rolesOf(hit.view).identity] ?? id : id, want: hit?.r.paid_sha256, ct: hit?.r.paid_ct };
|
|
189
|
+
};
|
|
190
|
+
run.buy = async ({ id, ids }) => {
|
|
191
|
+
const { buyBatch, withLock, readAsMember } = await import("./x402.js");
|
|
192
|
+
const rs = (ids?.length ? ids : id ? [id] : []).map(resolve);
|
|
193
|
+
const ct = Object.fromEntries(rs.filter((r) => r.ct).map((r) => [r.key, r.ct]));
|
|
194
|
+
const want = Object.fromEntries(rs.filter((r) => r.want).map((r) => [r.key, r.want]));
|
|
195
|
+
const verdict = (records) => { const bad = records.filter((x) => x.verified === false).map((x) => x.id);
|
|
196
|
+
return bad.length ? `MISMATCH for ${bad.join(", ")}` : "every record opened matches its published sha256"; };
|
|
197
|
+
// A membership the agent was named on (FANGORN_MCP_READER_KEY + FANGORN_MCP_MEMBERSHIP,
|
|
198
|
+
// the token id): read for free, nothing paid.
|
|
199
|
+
if (process.env.FANGORN_MCP_READER_KEY && process.env.FANGORN_MCP_MEMBERSHIP && p.membership) {
|
|
200
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
201
|
+
const r = await readAsMember(p, rs.map((r) => r.key).filter((k) => ct[k]), { reader: privateKeyToAccount(process.env.FANGORN_MCP_READER_KEY),
|
|
202
|
+
tokenId: process.env.FANGORN_MCP_MEMBERSHIP, ct, want });
|
|
203
|
+
return { read: r.records.length, with: "membership", until: r.until, verified: verdict(r.records), records: r.records.map(({ id, record, error }) => ({ id, record, error })) };
|
|
204
|
+
}
|
|
205
|
+
if (ids?.length) {
|
|
206
|
+
// Many at once, one price: from the allowance file (FANGORN_MCP_VOUCHERS, vouchers the
|
|
207
|
+
// person's wallet signed) if there is one, else one payment from the wallet key.
|
|
208
|
+
const fs = await import("node:fs");
|
|
209
|
+
const vf = process.env.FANGORN_MCP_VOUCHERS;
|
|
210
|
+
const opts = { ct, want, privateKey: process.env.FANGORN_MCP_WALLET_KEY, maxPrice: cap * BigInt(rs.length) };
|
|
211
|
+
// The file is read, spent from and saved under one lock, and saved even when the
|
|
212
|
+
// purchase fails: buyBatch drops the vouchers the seller settled or refused either way.
|
|
213
|
+
const r = vf
|
|
214
|
+
? await withLock(vf, async () => {
|
|
215
|
+
const vouchers = JSON.parse(fs.readFileSync(vf, "utf8"));
|
|
216
|
+
try { return await buyBatch(p, rs.map((r) => r.key), { ...opts, vouchers }); }
|
|
217
|
+
finally { fs.writeFileSync(vf, JSON.stringify(vouchers)); }
|
|
218
|
+
})
|
|
219
|
+
: await buyBatch(p, rs.map((r) => r.key), opts);
|
|
220
|
+
return { bought: r.records.length, missing: r.missing, verified: verdict(r.records),
|
|
221
|
+
...(r.undelivered.length ? { not_bought: r.undelivered, why: r.error } : {}),
|
|
222
|
+
manifest: r.manifest, records: r.records.map(({ id, record, error }) => ({ id, record, error })) };
|
|
223
|
+
}
|
|
224
|
+
if (!id) throw new Error("id or ids is required");
|
|
225
|
+
const { key, want: w, ct: c } = resolve(id);
|
|
226
|
+
if (!c) return { id: key, error: "search for the record first: its sealed copy (paid_ct) comes from the row, and nothing is bought without it" };
|
|
187
227
|
const { buyRecord } = await import("./x402.js");
|
|
188
|
-
const
|
|
189
|
-
const r = await buyRecord(p, key, { want, privateKey: process.env.FANGORN_MCP_WALLET_KEY, maxPrice: cap });
|
|
228
|
+
const r = await buyRecord(p, key, { ct: c, want: w, privateKey: process.env.FANGORN_MCP_WALLET_KEY, maxPrice: cap });
|
|
190
229
|
if (r.error) return r;
|
|
191
230
|
return { id: key, paid: r.price ? price : "free", transaction: r.receipt?.transaction ?? null, network: r.receipt?.network ?? p.network,
|
|
192
|
-
verified:
|
|
231
|
+
verified: w ? (r.verified ? "matches the published sha256" : `MISMATCH: published ${w}, got ${r.digest}`) : "not checked (the row has no paid_sha256)",
|
|
193
232
|
record: r.record };
|
|
194
233
|
};
|
|
195
234
|
tools.push({ name: "buy", description: `Buy one ${n} record's paid detail for ${price} (x402 on ${p.network}): ${p.description || "the structured record"}. ` +
|
|
196
|
-
"Pass an id from search
|
|
197
|
-
|
|
235
|
+
"Pass an id from search, or ids for up to 1000 at once (one price, one manifest of hashes and transactions). " +
|
|
236
|
+
"The record is already in the row, sealed; this buys its key and opens it. " +
|
|
237
|
+
"Read free with a membership the agent was named on (FANGORN_MCP_READER_KEY + FANGORN_MCP_MEMBERSHIP); otherwise paid from the allowance (FANGORN_MCP_VOUCHERS) or this server's wallet (FANGORN_MCP_WALLET_KEY), never above FANGORN_MCP_MAX_PRICE a record.",
|
|
238
|
+
inputSchema: { type: "object", properties: { id: str("A record id from search or get"), ids: { type: "array", items: { type: "string" }, description: "Several record ids, bought together" } } } });
|
|
198
239
|
}
|
|
199
240
|
|
|
200
241
|
return { tools, call: async (name, args = {}) => {
|
package/src/agent/x402.js
CHANGED
|
@@ -1,76 +1,300 @@
|
|
|
1
|
-
// Paying for a resource over x402 (
|
|
1
|
+
// Paying for a resource over x402 (v2, "exact" scheme on an EVM chain): the buyer's half.
|
|
2
2
|
//
|
|
3
3
|
// const { body, receipt, price } = await payAndFetch(url, { privateKey, maxPrice: 50000n });
|
|
4
4
|
//
|
|
5
|
-
// 1. GET url → 402 with
|
|
5
|
+
// 1. GET url → 402 with its PaymentRequired in the PAYMENT-REQUIRED header (base64 JSON):
|
|
6
|
+
// `accepts` says what it costs, in which token, on which chain ("eip155:84532"), to whom.
|
|
6
7
|
// 2. Sign an EIP-3009 transferWithAuthorization for exactly that (EIP-712, the token's own
|
|
7
|
-
// domain). Nothing is sent on chain by the buyer: the seller's facilitator
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// domain, named in `extra`). Nothing is sent on chain by the buyer: the seller's facilitator
|
|
9
|
+
// submits it.
|
|
10
|
+
// 3. GET url again with PAYMENT-SIGNATURE: the seller verifies, settles, and returns the
|
|
11
|
+
// resource, with the settlement (the tx hash) in PAYMENT-RESPONSE.
|
|
10
12
|
//
|
|
11
13
|
// `maxPrice` (token base units) is checked BEFORE signing: a seller cannot ask for more
|
|
12
14
|
// than the caller agreed to, whatever its 402 says.
|
|
13
15
|
|
|
14
|
-
const CHAIN_IDS = { "arbitrum-sepolia": 421614, arbitrum: 42161, "base-sepolia": 84532, base: 8453 };
|
|
15
16
|
const b64 = (s) => (typeof btoa === "function" ? btoa(s) : Buffer.from(s, "utf8").toString("base64"));
|
|
16
17
|
const unb64 = (s) => (typeof atob === "function" ? atob(s) : Buffer.from(s, "base64").toString("utf8"));
|
|
18
|
+
const EVM = /^eip155:(\d+)$/;
|
|
17
19
|
|
|
18
|
-
/** The one requirement we can pay: exact scheme,
|
|
19
|
-
export function choose(accepts = [], { maxPrice
|
|
20
|
-
const ok = accepts.filter((a) => a.scheme === "exact" &&
|
|
20
|
+
/** The one requirement we can pay: exact scheme, an EVM chain, within `maxPrice`. */
|
|
21
|
+
export function choose(accepts = [], { maxPrice } = {}) {
|
|
22
|
+
const ok = accepts.filter((a) => a.scheme === "exact" && EVM.test(a.network));
|
|
21
23
|
if (!ok.length) throw new Error(`no payable option: ${JSON.stringify(accepts.map((a) => `${a.scheme}/${a.network}`))}`);
|
|
22
|
-
const cheapest = ok.sort((a, b) => (BigInt(a.
|
|
23
|
-
if (maxPrice != null && BigInt(cheapest.
|
|
24
|
-
throw new Error(`price ${cheapest.
|
|
24
|
+
const cheapest = ok.sort((a, b) => (BigInt(a.amount) < BigInt(b.amount) ? -1 : 1))[0];
|
|
25
|
+
if (maxPrice != null && BigInt(cheapest.amount) > BigInt(maxPrice))
|
|
26
|
+
throw new Error(`price ${cheapest.amount} exceeds the cap ${maxPrice} (token base units)`);
|
|
25
27
|
return cheapest;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
/**
|
|
29
|
-
export
|
|
30
|
-
const
|
|
31
|
-
if (!
|
|
30
|
+
/** A 402's PaymentRequired, from its PAYMENT-REQUIRED header. */
|
|
31
|
+
export function paymentRequired(res) {
|
|
32
|
+
const h = res.headers.get("PAYMENT-REQUIRED");
|
|
33
|
+
if (!h) throw new Error(`HTTP ${res.status} has no PAYMENT-REQUIRED header (not x402 v2)`);
|
|
34
|
+
return JSON.parse(unb64(h));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The PAYMENT-SIGNATURE header value for requirement `req`, signed by `account` (a viem account). */
|
|
38
|
+
export async function paymentHeader(req, account, opts) {
|
|
39
|
+
return b64(JSON.stringify(await signPayment(req, account, opts)));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A PaymentPayload for `req` (one of a 402's `accepts`), for the 402's `resource`. */
|
|
43
|
+
export async function signPayment(req, account, { resource, now = Math.floor(Date.now() / 1000) } = {}) {
|
|
44
|
+
const chainId = Number((EVM.exec(req.network) ?? [])[1]);
|
|
45
|
+
if (!chainId) throw new Error(`not an EVM network: ${req.network}`);
|
|
32
46
|
const nonce = `0x${[...crypto.getRandomValues(new Uint8Array(32))].map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
33
47
|
const authorization = {
|
|
34
|
-
from: account.address, to: req.payTo, value: String(req.
|
|
48
|
+
from: account.address, to: req.payTo, value: String(req.amount),
|
|
35
49
|
validAfter: String(now - 600), validBefore: String(now + (req.maxTimeoutSeconds ?? 120)), nonce,
|
|
36
50
|
};
|
|
51
|
+
// The token's EIP-712 name differs by chain (Base Sepolia's USDC is "USDC", Base's "USD
|
|
52
|
+
// Coin"), so a seller that names none is refused rather than guessed at.
|
|
53
|
+
if (!req.extra?.name || !req.extra?.version) throw new Error(`${req.network}: the seller names no EIP-712 domain for ${req.asset} (extra.name, extra.version)`);
|
|
37
54
|
const signature = await account.signTypedData({
|
|
38
|
-
domain: { name: req.extra
|
|
55
|
+
domain: { name: req.extra.name, version: req.extra.version, chainId, verifyingContract: req.asset },
|
|
39
56
|
types: { TransferWithAuthorization: [
|
|
40
57
|
{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" },
|
|
41
58
|
{ name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }] },
|
|
42
59
|
primaryType: "TransferWithAuthorization",
|
|
43
60
|
message: { ...authorization, value: BigInt(authorization.value), validAfter: BigInt(authorization.validAfter), validBefore: BigInt(authorization.validBefore) },
|
|
44
61
|
});
|
|
45
|
-
return
|
|
62
|
+
return { x402Version: 2, ...(resource ? { resource } : {}), accepted: req, payload: { signature, authorization } };
|
|
46
63
|
}
|
|
47
64
|
|
|
48
|
-
/** GET `url
|
|
49
|
-
export async function payAndFetch(url, { privateKey, account, maxPrice, fetchImpl = fetch } = {}) {
|
|
50
|
-
const first = await fetchImpl(url);
|
|
65
|
+
/** GET `url` (or `method` it), paying once if it answers 402. Returns { status, body, price, receipt }. */
|
|
66
|
+
export async function payAndFetch(url, { privateKey, account, maxPrice, fetchImpl = fetch, method = "GET" } = {}) {
|
|
67
|
+
const first = await fetchImpl(url, { method });
|
|
51
68
|
if (first.status !== 402) return { status: first.status, body: await first.text(), price: null, receipt: null };
|
|
52
|
-
const offer =
|
|
69
|
+
const offer = paymentRequired(first);
|
|
53
70
|
const req = choose(offer.accepts, { maxPrice });
|
|
54
71
|
if (!account) {
|
|
55
|
-
if (!privateKey) throw new Error(`${url} costs ${req.
|
|
72
|
+
if (!privateKey) throw new Error(`${url} costs ${req.amount} (base units of ${req.asset} on ${req.network}); no wallet configured to pay`);
|
|
56
73
|
account = (await import("viem/accounts")).privateKeyToAccount(privateKey);
|
|
57
74
|
}
|
|
58
|
-
const paid = await fetchImpl(url, { headers: { "
|
|
59
|
-
const receiptHeader = paid.headers.get("
|
|
75
|
+
const paid = await fetchImpl(url, { method, headers: { "PAYMENT-SIGNATURE": await paymentHeader(req, account, { resource: offer.resource }) } });
|
|
76
|
+
const receiptHeader = paid.headers.get("PAYMENT-RESPONSE");
|
|
60
77
|
const body = await paid.text();
|
|
61
|
-
if (paid.status === 402) throw new Error(`payment refused: ${
|
|
62
|
-
return { status: paid.status, body, price: req.
|
|
78
|
+
if (paid.status === 402) throw new Error(`payment refused: ${paymentRequired(paid).error ?? body}`);
|
|
79
|
+
return { status: paid.status, body, price: req.amount, receipt: receiptHeader ? JSON.parse(unb64(receiptHeader)) : null };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Sealed records ────────────────────────────────────────────────────────────────────────
|
|
83
|
+
// An app's paid record is in its committed rows already, sealed: `paid_ct` is
|
|
84
|
+
// base64(nonce[12] ‖ AES-256-GCM(key, record)), with key = HMAC-SHA256(PAID_SECRET, id), and
|
|
85
|
+
// `paid_sha256` is the sha256 of the record itself. A purchase buys the key, never the data,
|
|
86
|
+
// so the buyer has the ciphertext (and so knows the record exists) before paying, and checks
|
|
87
|
+
// what it decrypts against the committed hash after. Sources seal with the same derivation.
|
|
88
|
+
|
|
89
|
+
const hex = (u8) => [...u8].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
const unhex = (h) => Uint8Array.from(h.match(/../g).map((x) => parseInt(x, 16)));
|
|
91
|
+
const fromB64 = (s) => Uint8Array.from(unb64bytes(s));
|
|
92
|
+
function unb64bytes(s) { return typeof atob === "function" ? [...atob(s)].map((c) => c.charCodeAt(0)) : [...Buffer.from(s, "base64")]; }
|
|
93
|
+
const toB64 = (u8) => (typeof btoa === "function" ? btoa(String.fromCharCode(...u8)) : Buffer.from(u8).toString("base64"));
|
|
94
|
+
const sha256 = async (bytes) => hex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
|
|
95
|
+
|
|
96
|
+
/** A record's key, hex: HMAC-SHA256(secret, id). The worker's keyFor; here for sources and tests. */
|
|
97
|
+
export async function paidKey(secret, id) {
|
|
98
|
+
const k = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
99
|
+
return hex(new Uint8Array(await crypto.subtle.sign("HMAC", k, new TextEncoder().encode(id))));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Seal `text` under `keyHex`: what a source writes as `paid_ct`. */
|
|
103
|
+
export async function sealPaid(keyHex, text, nonce = crypto.getRandomValues(new Uint8Array(12))) {
|
|
104
|
+
const k = await crypto.subtle.importKey("raw", unhex(keyHex), "AES-GCM", false, ["encrypt"]);
|
|
105
|
+
const ct = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, k, new TextEncoder().encode(text)));
|
|
106
|
+
const out = new Uint8Array(12 + ct.length);
|
|
107
|
+
out.set(nonce); out.set(ct, 12);
|
|
108
|
+
return toB64(out);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Open a sealed record and check it against the committed sha256 (`want`, if known). */
|
|
112
|
+
export async function openPaid(ct, keyHex, want) {
|
|
113
|
+
const bytes = fromB64(ct);
|
|
114
|
+
const k = await crypto.subtle.importKey("raw", unhex(keyHex), "AES-GCM", false, ["decrypt"]);
|
|
115
|
+
const plain = new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv: bytes.slice(0, 12) }, k, bytes.slice(12)));
|
|
116
|
+
const sha = await sha256(plain);
|
|
117
|
+
return { sha256: sha, verified: want ? want === sha : null, record: JSON.parse(new TextDecoder().decode(plain)) };
|
|
63
118
|
}
|
|
64
119
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
120
|
+
/** A private app's pass (its card's `access`): one payment at POST `pass_url`, a bearer token back. */
|
|
121
|
+
export async function buyPass(access, { account, privateKey, maxPrice = access.pass, fetchImpl } = {}) {
|
|
122
|
+
const r = await payAndFetch(access.pass_url, { method: "POST", account, privateKey, maxPrice, fetchImpl });
|
|
123
|
+
if (r.status !== 200) throw new Error(`no pass: HTTP ${r.status} ${r.body.slice(0, 200)}`);
|
|
124
|
+
return { ...JSON.parse(r.body), price: r.price, receipt: r.receipt };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Private apps ──────────────────────────────────────────────────────────────────────────
|
|
128
|
+
// A private app's rows are sealed whole before they reach IPFS: the payload on chain is
|
|
129
|
+
// `{sealed}`, base64(nonce[12] ‖ AES-256-GCM(rowKey, JSON(payload))), with rowKey =
|
|
130
|
+
// HMAC-SHA256(PAID_SECRET, "fangorn.row.v1"). The app's Worker holds the secret, opens the rows
|
|
131
|
+
// in its cron and serves the view only to those who pay. The nonce is the first 12 bytes of
|
|
132
|
+
// HMAC(rowKey, plaintext): an unchanged row seals to the same bytes, so the same CID, and is not
|
|
133
|
+
// embedded again; two different rows never share a nonce. (Equal rows look equal: that is all.)
|
|
134
|
+
|
|
135
|
+
const ROW = "fangorn.row.v1";
|
|
136
|
+
async function rowNonce(keyHex, text) {
|
|
137
|
+
const k = await crypto.subtle.importKey("raw", unhex(keyHex), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
138
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", k, new TextEncoder().encode(text))).slice(0, 12);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The payload a private app commits for `payload`. */
|
|
142
|
+
export async function sealRow(secret, payload) {
|
|
143
|
+
const key = await paidKey(secret, ROW), text = JSON.stringify(payload);
|
|
144
|
+
return { sealed: await sealPaid(key, text, await rowNonce(key, text)) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A row as committed, opened; a payload that is not sealed comes back as it is. */
|
|
148
|
+
export async function openRow(secret, payload) {
|
|
149
|
+
if (typeof payload?.sealed !== "string") return payload;
|
|
150
|
+
return (await openPaid(payload.sealed, await paidKey(secret, ROW))).record;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Open every record a seller sent a key for. `ct[id]` is the row's paid_ct, `want[id]` its paid_sha256. */
|
|
154
|
+
async function openAll(keys, ct, want) {
|
|
155
|
+
const out = [];
|
|
156
|
+
for (const [id, key] of Object.entries(keys ?? {})) {
|
|
157
|
+
if (!ct[id]) { out.push({ id, key, error: "no sealed record for this id in the rows read" }); continue; }
|
|
158
|
+
try { out.push({ id, ...(await openPaid(ct[id], key, want[id])) }); }
|
|
159
|
+
catch (e) { out.push({ id, key, error: `could not open: ${e.message}` }); }
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Buy one record an app sells (its agent card's `paid`): pay for its key, open `ct` (the
|
|
165
|
+
* row's paid_ct) and check it against `want` (its paid_sha256). Shared by fangorn-mcp and the page. */
|
|
166
|
+
export async function buyRecord(paid, key, { ct, want, account, privateKey, maxPrice = paid.price, fetchImpl } = {}) {
|
|
167
|
+
if (!ct) return { id: key, error: "this row carries no sealed record (paid_ct): nothing to buy" };
|
|
69
168
|
const r = await payAndFetch(paid.url.replace("{id}", encodeURIComponent(key)), { account, privateKey, maxPrice, fetchImpl });
|
|
70
169
|
if (r.status !== 200) return { id: key, error: `HTTP ${r.status}: ${r.body.slice(0, 200)}` };
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
170
|
+
const opened = await openPaid(ct, JSON.parse(r.body).key, want);
|
|
171
|
+
return { id: key, price: r.price, receipt: r.receipt, digest: opened.sha256, verified: opened.verified, record: opened.record };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The typed data a reader signs to read with a membership: a copy of @fangorn-network/barliman's
|
|
175
|
+
* src/read.js, which its /check verifies. ponytail: import it once barliman is on npm. */
|
|
176
|
+
export const readTypedData = ({ registry, chainId, appId, tokenId, ids, issuedAt }) => ({
|
|
177
|
+
domain: { name: "Fangorn Read", version: "1", chainId: Number(chainId), verifyingContract: registry },
|
|
178
|
+
types: { Read: [{ name: "app", type: "bytes32" }, { name: "tokenId", type: "uint256" }, { name: "ids", type: "string[]" }, { name: "issuedAt", type: "uint256" }] },
|
|
179
|
+
primaryType: "Read",
|
|
180
|
+
message: { app: appId, tokenId: BigInt(tokenId), ids, issuedAt: BigInt(issuedAt) },
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
/** Read records with a membership: `reader` (the holder, or the agent it named) signs the
|
|
184
|
+
* request, the app's worker has Barliman check it, and the keys come back. Nothing is paid. */
|
|
185
|
+
export async function readAsMember(paid, ids, { reader, tokenId, ct = {}, want = {}, fetchImpl = fetch } = {}) {
|
|
186
|
+
const m = paid.membership;
|
|
187
|
+
if (!m) throw new Error("this app sells no memberships");
|
|
188
|
+
const issuedAt = Math.floor(Date.now() / 1000);
|
|
189
|
+
const signature = await reader.signTypedData(readTypedData({ registry: m.registry, chainId: m.chainId, appId: m.appId, tokenId, ids, issuedAt }));
|
|
190
|
+
const url = paid.key_url ?? paid.url.replace(/\/paid\/\{id\}$/, "/key");
|
|
191
|
+
const res = await fetchImpl(url, { method: "POST", headers: { "content-type": "application/json" },
|
|
192
|
+
body: JSON.stringify({ ids, tokenId: String(tokenId), issuedAt, signature }) });
|
|
193
|
+
const body = await res.json();
|
|
194
|
+
if (res.status !== 200) throw new Error(`membership refused: ${body.error ?? res.status}`);
|
|
195
|
+
return { records: await openAll(body.keys, ct, want), until: body.until };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ── Batches and vouchers ──────────────────────────────────────────────────────────────────
|
|
199
|
+
// `POST <app>/paid {ids}` answers 402 with one price for every record it has; PAYMENT-SIGNATURE may then
|
|
200
|
+
// carry several payments (a JSON array) that add up to exactly that price. A payment signs
|
|
201
|
+
// payee, amount and expiry but not the resource (checked against the PayAI facilitator,
|
|
202
|
+
// 2026-10-07), so a person's wallet can sign a stack of them ahead of time ("vouchers") and an
|
|
203
|
+
// agent spends them later with no key: each pays only the app, for a fixed amount, until it
|
|
204
|
+
// expires. Holding them is holding the right to that much of the person's money, nothing more.
|
|
205
|
+
|
|
206
|
+
/** Sign vouchers worth `total` (base units) in `denominations` (ascending), payable to `req.payTo`
|
|
207
|
+
* for `days`. Enough of each small one to make any multiple of the smallest up to `total`. */
|
|
208
|
+
export async function signVouchers(req, account, { total, denominations, days = 30 } = {}) {
|
|
209
|
+
const out = [], ds = denominations.map(BigInt);
|
|
210
|
+
let left = BigInt(total);
|
|
211
|
+
for (let i = 0; i < ds.length; i++) {
|
|
212
|
+
let n = left / ds[i];
|
|
213
|
+
if (i < ds.length - 1) { // keep a full set of this size, the rest in multiples of the next
|
|
214
|
+
const keep = ds[i + 1] / ds[i] - 1n; // ponytail: assumes each denomination divides the next
|
|
215
|
+
n = (left - (left - (keep < n ? keep : n) * ds[i]) / ds[i + 1] * ds[i + 1]) / ds[i];
|
|
216
|
+
}
|
|
217
|
+
for (let k = 0n; k < n; k++)
|
|
218
|
+
out.push(await signPayment({ ...req, amount: String(ds[i]), maxTimeoutSeconds: days * 86400 }, account));
|
|
219
|
+
left -= n * ds[i];
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Vouchers adding up to exactly `amount`, largest first; null if they cannot. */
|
|
225
|
+
export function pick(vouchers, amount) {
|
|
226
|
+
let left = BigInt(amount);
|
|
227
|
+
const out = [];
|
|
228
|
+
for (const v of [...vouchers].sort((a, b) => { const x = BigInt(a.payload.authorization.value), y = BigInt(b.payload.authorization.value); return x > y ? -1 : x < y ? 1 : 0; })) { // largest first; equal ones keep their order
|
|
229
|
+
const x = BigInt(v.payload.authorization.value);
|
|
230
|
+
if (x <= left) { out.push(v); left -= x; }
|
|
231
|
+
}
|
|
232
|
+
return left === 0n ? out : null; // ponytail: greedy; exact for denominations that divide each other
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Run `fn` holding `<path>.lock` (Node only): two fangorn-mcp processes sharing one allowance
|
|
236
|
+
* file would otherwise pick the same vouchers and race. A lock older than `staleMs` belongs to
|
|
237
|
+
* a crashed purchase and is taken over.
|
|
238
|
+
* ponytail: a takeover can race a third process for the same stale lock; add a pid check if
|
|
239
|
+
* more than two servers ever share a file. */
|
|
240
|
+
export async function withLock(path, fn, { staleMs = 120_000, waitMs = 30_000 } = {}) {
|
|
241
|
+
const fs = await import("node:fs"), lock = `${path}.lock`, start = Date.now();
|
|
242
|
+
for (;;) {
|
|
243
|
+
try { fs.closeSync(fs.openSync(lock, "wx")); break; } catch (e) {
|
|
244
|
+
if (e.code !== "EEXIST") throw e;
|
|
245
|
+
let age = 0;
|
|
246
|
+
try { age = Date.now() - fs.statSync(lock).mtimeMs; } catch { continue; } // released meanwhile
|
|
247
|
+
if (age > staleMs) { fs.rmSync(lock, { force: true }); continue; }
|
|
248
|
+
if (Date.now() - start > waitMs) throw new Error(`the allowance is in use by another purchase (${lock})`);
|
|
249
|
+
await new Promise((ok) => setTimeout(ok, 100));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
try { return await fn(); } finally { fs.rmSync(lock, { force: true }); }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Buy many records at once. Pays from `vouchers` (spent ones are removed from the array) or
|
|
256
|
+
* signs one exact payment with `account`/`privateKey`. Each key opens `ct[id]` (the row's
|
|
257
|
+
* paid_ct), checked against `want[id]` (its paid_sha256). Returns the records and a manifest a
|
|
258
|
+
* third party can check. */
|
|
259
|
+
export async function buyBatch(paid, allIds, { ct = {}, want = {}, vouchers, account, privateKey, maxPrice, fetchImpl = fetch } = {}) {
|
|
260
|
+
const url = paid.url.replace(/\/\{id\}$/, "");
|
|
261
|
+
// Only ids whose sealed record is in hand are bought: anything else would pay for nothing.
|
|
262
|
+
const ids = allIds.filter((id) => ct[id]), unsealed = allIds.filter((id) => !ct[id]);
|
|
263
|
+
if (!ids.length) throw new Error(`none of these rows carries a sealed record (paid_ct): ${unsealed.join(", ")}`);
|
|
264
|
+
const post = (headers = {}) => fetchImpl(url, { method: "POST", headers: { "content-type": "application/json", ...headers }, body: JSON.stringify({ ids }) });
|
|
265
|
+
const first = await post();
|
|
266
|
+
const body0 = await first.json();
|
|
267
|
+
if (first.status !== 402) throw new Error(`expected a price, got HTTP ${first.status}: ${JSON.stringify(body0).slice(0, 200)}`);
|
|
268
|
+
const offer = { ...body0, ...paymentRequired(first) };
|
|
269
|
+
const req = choose(offer.accepts, { maxPrice });
|
|
270
|
+
let payments;
|
|
271
|
+
if (vouchers) {
|
|
272
|
+
payments = pick(vouchers, req.amount);
|
|
273
|
+
if (!payments) throw new Error(`the allowance cannot pay exactly ${req.amount} (base units); add funds or buy fewer records`);
|
|
274
|
+
} else {
|
|
275
|
+
account ??= privateKey ? (await import("viem/accounts")).privateKeyToAccount(privateKey) : null;
|
|
276
|
+
if (!account) throw new Error(`${offer.found.length} record(s) cost ${req.amount} (base units); no wallet or allowance to pay`);
|
|
277
|
+
payments = [await signPayment(req, account, { resource: offer.resource })];
|
|
278
|
+
}
|
|
279
|
+
const res = await post({ "PAYMENT-SIGNATURE": b64(JSON.stringify(payments)) });
|
|
280
|
+
const body = await res.json();
|
|
281
|
+
// Drop every voucher the seller says it settled (`paid`) or could not (`rejected`: spent, or
|
|
282
|
+
// the wallet cannot cover it), on success, partial delivery and refusal alike, so a dead
|
|
283
|
+
// voucher is never offered twice. A seller that names none: all of them on a 200.
|
|
284
|
+
const nonce = (p) => p.payload.authorization.nonce;
|
|
285
|
+
const paidNonces = new Set(body.paid ?? (res.status === 200 ? payments.map(nonce) : []));
|
|
286
|
+
if (vouchers) for (const n of [...paidNonces, ...(body.rejected ?? [])]) {
|
|
287
|
+
const k = vouchers.findIndex((v) => nonce(v) === n);
|
|
288
|
+
if (k >= 0) vouchers.splice(k, 1);
|
|
289
|
+
}
|
|
290
|
+
if (res.status !== 200) throw new Error(`payment refused: ${body.error ?? JSON.stringify(body).slice(0, 200)}`);
|
|
291
|
+
const spent = payments.filter((p) => paidNonces.has(nonce(p))).reduce((s, p) => s + BigInt(p.payload.authorization.value), 0n);
|
|
292
|
+
const records = await openAll(body.keys, ct, want);
|
|
293
|
+
// `undelivered` + `error`: a payment failed after others settled; these ids were not paid for.
|
|
294
|
+
return { records, missing: [...unsealed, ...(body.missing ?? [])], undelivered: body.undelivered ?? [], error: body.error ?? null,
|
|
295
|
+
manifest: { app: url, bought: new Date().toISOString(), price: String(spent),
|
|
296
|
+
asset: req.asset, network: req.network, transactions: body.receipts.map((r) => r.transaction),
|
|
297
|
+
records: records.map(({ id, sha256 }) => ({ id, sha256 })) } };
|
|
74
298
|
}
|
|
75
299
|
|
|
76
300
|
// ── self-check: `node src/agent/x402.js` — a stand-in seller that checks the signature ──
|
|
@@ -78,28 +302,139 @@ if (typeof process !== "undefined" && import.meta.url === `file://${process.argv
|
|
|
78
302
|
const { privateKeyToAccount, generatePrivateKey } = await import("viem/accounts");
|
|
79
303
|
const { verifyTypedData } = await import("viem");
|
|
80
304
|
const buyer = privateKeyToAccount(generatePrivateKey());
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
305
|
+
// Base Sepolia's USDC, whose EIP-712 name is "USDC" (Base's is "USD Coin").
|
|
306
|
+
const USDC = "0x036CbD53842c5426634e7929541eC2318f3dCF7e", SELLER = `0x${"5e".repeat(20)}`, CHAIN = 84532, DOMAIN = { name: "USDC", version: "2" };
|
|
307
|
+
const req = { scheme: "exact", network: `eip155:${CHAIN}`, amount: "10000", payTo: SELLER, asset: USDC, maxTimeoutSeconds: 120, extra: DOMAIN };
|
|
84
308
|
let settled = null;
|
|
85
309
|
const seller = async (url, init = {}) => {
|
|
86
|
-
const h = init.headers?.["
|
|
87
|
-
if (!h) return new Response(JSON.stringify({ x402Version:
|
|
88
|
-
const
|
|
310
|
+
const h = init.headers?.["PAYMENT-SIGNATURE"];
|
|
311
|
+
if (!h) return new Response("{}", { status: 402, headers: { "PAYMENT-REQUIRED": b64(JSON.stringify({ x402Version: 2, resource: { url }, accepts: [req] })) } });
|
|
312
|
+
const sent = JSON.parse(unb64(h)), p = sent.payload, a = p.authorization;
|
|
313
|
+
if (sent.x402Version !== 2 || sent.resource?.url !== url || sent.accepted?.amount !== "10000") return new Response("{}", { status: 400 });
|
|
89
314
|
const ok = await verifyTypedData({ address: a.from, signature: p.signature,
|
|
90
|
-
domain: {
|
|
315
|
+
domain: { ...DOMAIN, chainId: CHAIN, verifyingContract: USDC },
|
|
91
316
|
types: { TransferWithAuthorization: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" },
|
|
92
317
|
{ name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }] },
|
|
93
318
|
primaryType: "TransferWithAuthorization",
|
|
94
319
|
message: { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) } });
|
|
95
|
-
if (!ok || a.to !== SELLER || a.value !== "10000") return new Response(JSON.stringify({ error: "bad payment"
|
|
320
|
+
if (!ok || a.to !== SELLER || a.value !== "10000") return new Response("{}", { status: 402, headers: { "PAYMENT-REQUIRED": b64(JSON.stringify({ x402Version: 2, error: "bad payment", accepts: [req] })) } });
|
|
96
321
|
settled = a;
|
|
97
|
-
return new Response('{"secret":42}', { status: 200, headers: { "
|
|
322
|
+
return new Response('{"secret":42}', { status: 200, headers: { "PAYMENT-RESPONSE": b64(JSON.stringify({ success: true, transaction: "0xabc" })) } });
|
|
98
323
|
};
|
|
99
324
|
const r = await payAndFetch("https://x/paid/1", { account: buyer, maxPrice: 10000n, fetchImpl: seller });
|
|
100
325
|
if (r.body !== '{"secret":42}' || r.receipt.transaction !== "0xabc" || settled.from !== buyer.address) throw new Error(`paid fetch: ${JSON.stringify(r)}`);
|
|
101
326
|
let refused = false;
|
|
102
327
|
try { await payAndFetch("https://x/paid/1", { account: buyer, maxPrice: 9999n, fetchImpl: seller }); } catch (e) { refused = /exceeds the cap/.test(e.message); }
|
|
103
328
|
if (!refused) throw new Error("a price over the cap must be refused before signing");
|
|
329
|
+
|
|
330
|
+
// Batches through the real worker (CONFIG as ship deploys it), a stand-in facilitator
|
|
331
|
+
// that checks signatures and refuses a nonce settled twice, and vouchers signed ahead.
|
|
332
|
+
const { mkdtempSync, writeFileSync } = await import("node:fs");
|
|
333
|
+
const { tmpdir } = await import("node:os");
|
|
334
|
+
const membership = { registry: `0x${"12".repeat(20)}`, barliman: "https://barliman", chainId: CHAIN, appId: `0x${"ab".repeat(32)}` };
|
|
335
|
+
const sale = { price: "100000", network: `eip155:${CHAIN}`, asset: USDC, payTo: SELLER, facilitator: "https://fac", ...DOMAIN, description: "", membership };
|
|
336
|
+
// Not a literal: the page bundles this module, and a bundler would follow it into the Worker.
|
|
337
|
+
const WORKER = "../../site/worker.js";
|
|
338
|
+
const worker = (await import(/* @vite-ignore */ WORKER)).default;
|
|
339
|
+
// The rows as a buyer reads them: each record sealed under its key, with the plaintext's sha256.
|
|
340
|
+
const SECRET = "s3cret", ct = {}, want = {};
|
|
341
|
+
for (const id of ["a", "b", "c"]) {
|
|
342
|
+
const plain = JSON.stringify({ id, amount: 1 });
|
|
343
|
+
ct[id] = await sealPaid(await paidKey(SECRET, id), plain);
|
|
344
|
+
want[id] = await sha256(new TextEncoder().encode(plain));
|
|
345
|
+
}
|
|
346
|
+
const env = { PAID_SECRET: SECRET, CONFIG: { name: "Test", paid: sale } };
|
|
347
|
+
const member = privateKeyToAccount(generatePrivateKey()), memberToken = "77";
|
|
348
|
+
const spent = new Set(), facilitated = [];
|
|
349
|
+
const realFetch = globalThis.fetch;
|
|
350
|
+
const { recoverTypedDataAddress } = await import("viem");
|
|
351
|
+
globalThis.fetch = async (u, init) => {
|
|
352
|
+
if (u === "https://barliman/check") { // a stand-in Barliman: the member's token reads, nobody else's
|
|
353
|
+
const b = JSON.parse(init.body);
|
|
354
|
+
const reader = await recoverTypedDataAddress({ ...readTypedData({ registry: membership.registry, chainId: CHAIN, ...b }), signature: b.signature });
|
|
355
|
+
const ok = reader === member.address && b.tokenId === memberToken && b.appId === membership.appId;
|
|
356
|
+
return new Response(JSON.stringify({ ok, reader, until: ok ? "1900000000" : "0" }));
|
|
357
|
+
}
|
|
358
|
+
const { x402Version, paymentPayload: p, paymentRequirements: q } = JSON.parse(init.body), a = p.payload.authorization;
|
|
359
|
+
if (x402Version !== 2 || p.x402Version !== 2) throw new Error("the facilitator is spoken to in v2");
|
|
360
|
+
facilitated.push(p);
|
|
361
|
+
if (u.endsWith("/verify")) {
|
|
362
|
+
const ok = await verifyTypedData({ address: a.from, signature: p.payload.signature,
|
|
363
|
+
domain: { ...DOMAIN, chainId: CHAIN, verifyingContract: USDC },
|
|
364
|
+
types: { TransferWithAuthorization: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" },
|
|
365
|
+
{ name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }] },
|
|
366
|
+
primaryType: "TransferWithAuthorization",
|
|
367
|
+
message: { ...a, value: BigInt(a.value), validAfter: BigInt(a.validAfter), validBefore: BigInt(a.validBefore) } });
|
|
368
|
+
return new Response(JSON.stringify({ isValid: ok && a.value === q.amount, invalidReason: "bad" }));
|
|
369
|
+
}
|
|
370
|
+
if (spent.has(a.nonce)) return new Response(JSON.stringify({ success: false, errorReason: "nonce used" }));
|
|
371
|
+
spent.add(a.nonce);
|
|
372
|
+
return new Response(JSON.stringify({ success: true, transaction: `0x${spent.size}` }));
|
|
373
|
+
};
|
|
374
|
+
const viaWorker = (u, init) => worker.fetch(new Request(u, init), env);
|
|
375
|
+
const paidCard = { url: "https://x/paid/{id}", key_url: "https://x/key", price: sale.price, membership };
|
|
376
|
+
try {
|
|
377
|
+
// One record, one payment: the key opens the row's sealed record, which matches its hash.
|
|
378
|
+
const single = await buyRecord(paidCard, "a", { ct: ct.a, want: want.a, account: buyer, maxPrice: 100000n, fetchImpl: viaWorker });
|
|
379
|
+
if (single.verified !== true || single.record.id !== "a") throw new Error(`single key: ${JSON.stringify(single)}`);
|
|
380
|
+
if (facilitated.some((p) => p.extensions)) throw new Error("GET /paid/<id> must not ask to be listed: its URL differs per record");
|
|
381
|
+
// A probe with no body (x402scan, a Bazaar crawler) gets a 402 for one record, Bazaar extension and all.
|
|
382
|
+
const probe = await viaWorker("https://x/paid", { method: "POST" });
|
|
383
|
+
const asked = paymentRequired(probe);
|
|
384
|
+
if (probe.status !== 402 || asked.accepts[0].amount !== sale.price || asked.extensions?.bazaar?.info?.input?.method !== "POST")
|
|
385
|
+
throw new Error(`probe: ${probe.status} ${JSON.stringify(asked)}`);
|
|
386
|
+
// One exact payment from a wallet: 2 of 3 ids are sealed in the rows, 2 × $0.10, the
|
|
387
|
+
// third is never quoted (no ciphertext in hand, nothing to pay for).
|
|
388
|
+
const one = await buyBatch(paidCard, ["a", "b", "zz"], { account: buyer, maxPrice: 200000n, ct, want, fetchImpl: viaWorker });
|
|
389
|
+
if (one.records.length !== 2 || one.missing[0] !== "zz" || one.manifest.price !== "200000" || !one.records.every((r) => r.verified === true))
|
|
390
|
+
throw new Error(`batch with a wallet: ${JSON.stringify(one)}`);
|
|
391
|
+
// The facilitator lists what it settles with the Bazaar extension: every batch payment carries it.
|
|
392
|
+
const batchSent = facilitated.slice(-2); // its one payment: verify, settle
|
|
393
|
+
if (!batchSent.every((p) => p.extensions?.bazaar && p.resource.url === "https://x/paid" && p.accepted.amount === "200000"))
|
|
394
|
+
throw new Error(`batch payloads: ${JSON.stringify(batchSent.map((p) => [p.resource?.url, p.accepted?.amount, !!p.extensions]))}`);
|
|
395
|
+
// $2 of vouchers in $0.10 / $1, 30 days: any multiple of $0.10 can be paid exactly.
|
|
396
|
+
const vs = await signVouchers({ ...paidCard, scheme: "exact", network: sale.network, payTo: SELLER, asset: USDC, extra: DOMAIN },
|
|
397
|
+
buyer, { total: 2000000n, denominations: [100000n, 1000000n] });
|
|
398
|
+
if (vs.length !== 11 || pick(vs, 1700000n).length !== 8 || pick(vs, 2100000n) !== null) throw new Error(`vouchers: ${vs.length}`);
|
|
399
|
+
const before = [...vs];
|
|
400
|
+
const three = await buyBatch(paidCard, ["a", "b", "c"], { vouchers: vs, ct, want, fetchImpl: viaWorker });
|
|
401
|
+
if (three.records.length !== 3 || vs.length !== 8 || three.manifest.transactions.length !== 3) throw new Error(`batch with vouchers: ${JSON.stringify(three.manifest)}`);
|
|
402
|
+
// A spent $0.10 voucher riding along with a fresh one: both verify, settle refuses, nothing sent.
|
|
403
|
+
const usedOne = before.find((v) => !vs.includes(v) && v.payload.authorization.value === "100000");
|
|
404
|
+
const fresh = vs.find((v) => v.payload.authorization.value === "100000");
|
|
405
|
+
const replay = await viaWorker("https://x/paid", { method: "POST", body: JSON.stringify({ ids: ["a", "b"] }), headers: { "PAYMENT-SIGNATURE": b64(JSON.stringify([usedOne, fresh])) } });
|
|
406
|
+
if (replay.status !== 402 || (await replay.json()).keys) throw new Error("a spent voucher must not buy anything");
|
|
407
|
+
const short = await viaWorker("https://x/paid", { method: "POST", body: JSON.stringify({ ids: ["a", "b"] }), headers: { "PAYMENT-SIGNATURE": b64(JSON.stringify([fresh])) } });
|
|
408
|
+
if (short.status !== 402) throw new Error("payments short of the price must be refused");
|
|
409
|
+
// A spent voucher still in the stack, behind a fresh one (a crash before the file was
|
|
410
|
+
// saved): the fresh one settles and buys one record, the spent one is refused, and both
|
|
411
|
+
// leave the stack so neither is offered again.
|
|
412
|
+
const fresh2 = vs.find((v) => v !== fresh && v.payload.authorization.value === "100000");
|
|
413
|
+
const stack = [fresh2, usedOne];
|
|
414
|
+
const half = await buyBatch(paidCard, ["a", "b"], { vouchers: stack, ct, want, fetchImpl: viaWorker });
|
|
415
|
+
if (half.records.length !== 1 || half.undelivered[0] !== "b" || !half.error || half.manifest.price !== "100000" || stack.length !== 0)
|
|
416
|
+
throw new Error(`partial settle: ${JSON.stringify({ ...half, stack: stack.length })}`);
|
|
417
|
+
// The spent voucher first: nothing settles, nothing is sent, and it is dropped.
|
|
418
|
+
const stack2 = [usedOne, fresh];
|
|
419
|
+
let threw = false;
|
|
420
|
+
try { await buyBatch(paidCard, ["a", "b"], { vouchers: stack2, ct, want, fetchImpl: viaWorker }); } catch { threw = true; }
|
|
421
|
+
if (!threw || stack2.length !== 1 || stack2[0] !== fresh) throw new Error(`refused batch must drop only the spent voucher: ${stack2.length}`);
|
|
422
|
+
// A member reads for free: the signed request names the token; a stranger's is refused.
|
|
423
|
+
const read = await readAsMember(paidCard, ["a", "c"], { reader: member, tokenId: memberToken, ct, want, fetchImpl: viaWorker });
|
|
424
|
+
if (read.records.length !== 2 || !read.records.every((r) => r.verified === true)) throw new Error(`member read: ${JSON.stringify(read)}`);
|
|
425
|
+
let stranger = false;
|
|
426
|
+
try { await readAsMember(paidCard, ["a"], { reader: buyer, tokenId: memberToken, ct, want, fetchImpl: viaWorker }); } catch { stranger = true; }
|
|
427
|
+
if (!stranger) throw new Error("a reader the membership does not name must be refused");
|
|
428
|
+
// The allowance lock: two purchases at once take turns; a crashed one's lock is taken over.
|
|
429
|
+
const vfile = `${mkdtempSync(`${tmpdir()}/wm-`)}/vouchers.json`, order = [];
|
|
430
|
+
const turn = (name) => withLock(vfile, async () => { order.push(`${name}+`); await new Promise((ok) => setTimeout(ok, 50)); order.push(`${name}-`); });
|
|
431
|
+
await Promise.all([turn("a"), turn("b")]);
|
|
432
|
+
if (order.join() !== "a+,a-,b+,b-" && order.join() !== "b+,b-,a+,a-") throw new Error(`lock interleaved: ${order}`);
|
|
433
|
+
writeFileSync(`${vfile}.lock`, "");
|
|
434
|
+
const { utimesSync, existsSync } = await import("node:fs");
|
|
435
|
+
utimesSync(`${vfile}.lock`, new Date(Date.now() - 300_000), new Date(Date.now() - 300_000));
|
|
436
|
+
if (await withLock(vfile, async () => 7) !== 7 || existsSync(`${vfile}.lock`)) throw new Error("a stale lock must be taken over and released");
|
|
437
|
+
} finally { globalThis.fetch = realFetch; }
|
|
438
|
+
console.log("x402.js batch self-check ok — keys, not records: each opens the row's sealed record and matches its hash; a member reads free and a stranger is refused; one price for the ids sealed in the rows, paid by one signature or by vouchers signed ahead, spent vouchers removed and refused if replayed, short payment refused, a half-settled batch delivers what was paid and drops spent vouchers, the allowance file is locked per purchase");
|
|
104
439
|
console.log("x402.js self-check ok — a signed EIP-3009 authorization the seller can verify, exactly the asked amount to the asked payee, the receipt read back, and a price over the cap refused before signing");
|
|
105
440
|
}
|