diff --git a/.env.example b/.env.example
index ff8490f..2307286 100644
--- a/.env.example
+++ b/.env.example
@@ -24,3 +24,7 @@ DEMO_AAVE_POOL=
CHAIN=robinhood # robinhood (chain 46630) | arbitrumSepolia
GUARDIAN_IMPL= # required: GuardianModule impl on the target chain (printed by DeployGuardian)
RULES_ENGINE= # RulesEngineV1 (printed by DeployRules); also set VITE_RULES_ENGINE in site/.env
+VAULT_FACTORY= # SafeVaultFactory (printed by DeployFactory); also set VITE_VAULT_FACTORY in site/.env
+
+# ── Gasless onboarding relayer (site/api/onboard.ts; set on the host, e.g. Vercel) ──
+RELAYER_PRIVATE_KEY= # a funded testnet key that sponsors the delegate+configure tx
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 350cb94..f8546b1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -59,4 +59,5 @@ jobs:
cache-dependency-path: site/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- run: pnpm build
+ - run: pnpm typecheck:api
- run: pnpm lint
diff --git a/README.md b/README.md
index d56d85a..20c8a77 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
[](https://coincoin-five.vercel.app/)
[-7af7c0?style=flat-square)](#deployed-addresses)
[](contracts/)
-[](#testing)
+[](https://github.com/gamween/coincoin/actions/workflows/ci.yml)
[](LICENSE)
@@ -49,7 +49,7 @@ coincoin closes the gap by enforcing at the account level via **EIP-7702** (a pr
Four parts. The product is the watcher daemon and the contracts; there is no required UI.
-1. **Delegate (once).** An EOA signs an EIP-7702 authorization pointing at `GuardianModule`, and in the same transaction calls `configure()` to set its policy: a **frozen** safe vault (settable once — a leaked key cannot repoint it) and an authorized **keeper** set. Policies can also be installed from an **EIP-712 signature** a relayer submits (`configureWithSig`), for gasless onboarding.
+1. **Delegate (once).** An EOA signs an EIP-7702 authorization pointing at `GuardianModule`, and in the same transaction calls `configure()` to set its policy: a **frozen** safe vault (settable once — a leaked key cannot repoint it) and an authorized **keeper** set. Policies can also be installed from an **EIP-712 signature** a relayer submits (`configureWithSig`) — this powers the **one-click, gasless onboarding** in the `/app` dashboard (the wallet just signs; a relayer pays).
2. **Watch.** A TypeScript/viem daemon (`ChainThreatSource`) polls `Drained` logs of the monitored protocols directly from chain — no third-party feed, no websocket dependency. It catches up to head in bounded `getLogs` windows.
3. **React.** On a verified threat, the **keeper** — which is cryptographically bounded to two actions and nothing else — calls `exitAaveV3()` to unwind deposited positions back into the account, then `evacuateERC20()` to sweep every token to the safe vault.
4. **Firewall (proactive).** Calls routed through `execute()` are scored by a stateless `RulesEngineV1`; an unlimited `approve` / `increaseAllowance` / EIP-2612 `permit` / blanket `setApprovalForAll` to an untrusted spender reverts at the account level before it lands.
@@ -93,13 +93,14 @@ Deployed and exercised on **Robinhood Chain testnet (chain 46630)** unless noted
|---|---|
| ✅ | EIP-7702 delegation + self-config · ERC-20 sweep · approval revocation |
| ✅ | Frozen vault · signed multi-keeper policy (EIP-712 `configureWithSig`) |
+| ✅ | **Gasless in-browser onboarding** — the wallet signs (EIP-712 policy + EIP-7702 authorization); a relayer (`site/api`) deploys the user's deterministic `SafeVaultFactory` vault and sponsors the delegate+configure tx (zero gas for the user) |
| ✅ | Local firewall (`RulesEngineV1`) — unlimited-approval / `permit` / `setApprovalForAll` rules |
| ✅ | Real on-chain detection → rescue loop, run end-to-end (funds at rest **and** a deposited DeFi position rescued in one keeper-driven sequence) |
| ✅ | DeFi-exit engine (`exitAaveV3`) — built and verified against the **live Aave V3 Pool** via an Arbitrum One fork test |
| 🛣️ | **Native Aave V3 integration** — built and ready; goes live the day Aave V3 is deployed on Robinhood Chain. Waiting on availability, not on code. |
| 🛣️ | **GMX V2 position exit** — same pattern, once GMX is available on the target chain |
| 🛣️ | Broader firewall coverage — Permit2, multicall, direct-transfer heuristics |
-| 🛣️ | Policy asset/protocol scoping · Stylus rules engine · in-browser 7702 onboarding · security audit |
+| 🛣️ | Policy asset/protocol scoping · Stylus rules engine · security audit |
> [!NOTE]
> Aave V3 and GMX V2 are not deployed on Robinhood Chain testnet, so the live end-to-end run exercises the exit engine against an Aave-V3-shaped lending pool. The exit code itself is verified against the real Aave V3 Pool in the fork test (`AaveRealFork.t.sol`) — it ships against the real protocol the moment one is available on the chain coincoin runs on.
@@ -117,11 +118,11 @@ Deployed and exercised on **Robinhood Chain testnet (chain 46630)** unless noted
```
coincoin/
-├── contracts/ Foundry — GuardianModule (EIP-7702), RulesEngineV1, SafeVault, mocks, deploy scripts
+├── contracts/ Foundry — GuardianModule (EIP-7702), RulesEngineV1, SafeVault(+Factory), mocks, scripts
├── watcher/ TypeScript/viem detection → rescue daemon (onboard · watch · exploit · revoke)
-├── site/ Vite/React/Tailwind landing + /app dashboard
+├── site/ Vite/React/Tailwind landing + /app dashboard + api/ (gasless onboarding relayer)
├── deployments/ On-chain address records (robinhood-testnet.json, arbitrum-sepolia.json)
-├── docs/ Brand kit, design specs, submission notes
+├── docs/readme/ README images
├── video/ Remotion pitch + demo videos (code → MP4)
├── .env.example Config template (RPC, disposable keys, demo addresses)
└── LICENSE MIT
@@ -160,9 +161,10 @@ Robinhood Chain testnet (chain `46630`) — [explorer](https://explorer.testnet.
| Contract | Address |
|---|---|
-| `GuardianModule` (EIP-7702 guardian) | [`0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77`](https://explorer.testnet.chain.robinhood.com/address/0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77) |
+| `GuardianModule` (EIP-7702 guardian) | [`0x9953BB30cFef2ac842C74417eA6DC661b492E8dA`](https://explorer.testnet.chain.robinhood.com/address/0x9953BB30cFef2ac842C74417eA6DC661b492E8dA) |
| `RulesEngineV1` (firewall) | [`0xc20A9d7D38B07a9C74A1fD87A2e25CA1973Cbc52`](https://explorer.testnet.chain.robinhood.com/address/0xc20A9d7D38B07a9C74A1fD87A2e25CA1973Cbc52) |
-| `SafeVault` (demo) | [`0x49be3DC48fC0540346A064fCC6Fc94FBaE62f479`](https://explorer.testnet.chain.robinhood.com/address/0x49be3DC48fC0540346A064fCC6Fc94FBaE62f479) |
+| `SafeVaultFactory` (deterministic per-user vault) | [`0x1ef2B2539fa842A9c7e4EA07790aA6dBc47ec4A5`](https://explorer.testnet.chain.robinhood.com/address/0x1ef2B2539fa842A9c7e4EA07790aA6dBc47ec4A5) |
+| `SafeVault` (demo) | [`0x530921CFFCeCc01B3Ad20E48A8c1707d27204b91`](https://explorer.testnet.chain.robinhood.com/address/0x530921CFFCeCc01B3Ad20E48A8c1707d27204b91) |
The `GuardianModule` was also initially deployed and **Arbiscan-verified** on Arbitrum Sepolia at [`0x6671…200F`](https://sepolia.arbiscan.io/address/0x6671b4B73b79c284A710B00ef777d8E65f55200F).
@@ -187,15 +189,15 @@ Non-custodial by construction, but **experimental and unaudited** — a research
## Testing
-90 tests, written test-first.
+95 tests, written test-first.
```bash
-cd contracts && forge test # 64 unit/integration tests (+1 fork test, gated on ARBITRUM_ONE_RPC)
+cd contracts && forge test # 69 unit/integration tests (+1 fork test, gated on ARBITRUM_ONE_RPC)
ARBITRUM_ONE_RPC= forge test # includes AaveRealFork.t.sol against the live Aave V3 Pool
cd ../watcher && pnpm test # 25 watcher tests (vitest)
```
-`AaveRealFork.t.sol` exits a real position against the **live Aave V3 Pool on Arbitrum One** (forked) — the same code path the guardian runs. The watcher suite covers the alert schema, exposure registry, keeper client, and the end-to-end orchestrator.
+`AaveRealFork.t.sol` exits a real position against the **live Aave V3 Pool on Arbitrum One** (forked) — the same code path the guardian runs. The watcher suite covers the alert schema, exposure registry, keeper client, and the end-to-end orchestrator. The gasless onboarding path is proven end-to-end on testnet by `pnpm onboard:gasless` (a fresh, unfunded EOA signs; a relayer sponsors the delegate+configure tx).
## Buildathon
@@ -206,5 +208,5 @@ Built for **[Arbitrum Open House London](https://arbitrum-london.hackquest.io/)*
[MIT](LICENSE) © 2026 coincoin
-Built for ETHGlobal New York 2026 · Hedera — Tokenization track
+Built for Arbitrum Open House London 2026 · Robinhood Chain track
diff --git a/contracts/script/DeployFactory.s.sol b/contracts/script/DeployFactory.s.sol
new file mode 100644
index 0000000..6bc5bd7
--- /dev/null
+++ b/contracts/script/DeployFactory.s.sol
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.24;
+
+import {Script, console2} from "forge-std/Script.sol";
+import {SafeVaultFactory} from "../src/SafeVaultFactory.sol";
+
+/// @notice Deploys the SafeVaultFactory (deterministic per-owner SafeVault deployer).
+/// Set its address as `VAULT_FACTORY` (.env) and `VITE_VAULT_FACTORY` (site)
+/// to enable the gasless in-browser onboarding flow.
+contract DeployFactory is Script {
+ function run() external returns (SafeVaultFactory factory) {
+ uint256 pk = vm.envUint("DEPLOYER_PRIVATE_KEY");
+ vm.startBroadcast(pk);
+ factory = new SafeVaultFactory();
+ vm.stopBroadcast();
+ console2.log("SafeVaultFactory deployed at:", address(factory));
+ }
+}
diff --git a/contracts/src/GuardianModule.sol b/contracts/src/GuardianModule.sol
index 397d0ce..18d6ba8 100644
--- a/contracts/src/GuardianModule.sol
+++ b/contracts/src/GuardianModule.sol
@@ -89,7 +89,7 @@ contract GuardianModule {
if (block.timestamp > deadline) revert Expired();
if (nonce != policyNonce) revert BadNonce();
bytes32 structHash = keccak256(
- abi.encode(POLICY_TYPEHASH, p.safeVault, keccak256(abi.encodePacked(p.keepers)), nonce, deadline)
+ abi.encode(POLICY_TYPEHASH, p.safeVault, _hashKeepers(p.keepers), nonce, deadline)
);
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), structHash));
if (ECDSA.recover(digest, sig) != address(this)) revert BadSignature();
@@ -99,6 +99,17 @@ contract GuardianModule {
_applyPolicy(p.safeVault, p.keepers);
}
+ /// @dev EIP-712 hash of the `address[] keepers` field: keccak256 of each address encoded as a
+ /// 32-byte word, concatenated — the standard array encoding, so a wallet's `signTypedData`
+ /// (e.g. in-browser gasless onboarding) recovers correctly.
+ function _hashKeepers(address[] calldata keepers_) internal pure returns (bytes32) {
+ bytes32[] memory words = new bytes32[](keepers_.length);
+ for (uint256 i; i < keepers_.length; ++i) {
+ words[i] = bytes32(uint256(uint160(keepers_[i])));
+ }
+ return keccak256(abi.encodePacked(words));
+ }
+
/// @notice The full authorized keeper set.
function keepers() external view returns (address[] memory) {
return _keeperList;
diff --git a/contracts/src/SafeVaultFactory.sol b/contracts/src/SafeVaultFactory.sol
new file mode 100644
index 0000000..8b2aff3
--- /dev/null
+++ b/contracts/src/SafeVaultFactory.sol
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.24;
+
+import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
+import {SafeVault} from "./SafeVault.sol";
+
+/// @title SafeVaultFactory
+/// @notice Deploys one SafeVault per owner at a deterministic CREATE2 address. The
+/// address is therefore known (counterfactual) BEFORE deployment, which is
+/// what the gasless onboarding flow needs: the user signs an EIP-712 policy
+/// against `vaultOf(user)`, then a relayer deploys the vault and configures
+/// the guardian in one sponsored transaction.
+contract SafeVaultFactory {
+ event VaultDeployed(address indexed owner, address vault);
+
+ /// @dev One vault per owner → the salt is the owner address.
+ function _salt(address owner) internal pure returns (bytes32) {
+ return bytes32(uint256(uint160(owner)));
+ }
+
+ /// @dev Init code = SafeVault creation bytecode + abi-encoded constructor arg (owner).
+ function _initCode(address owner) internal pure returns (bytes memory) {
+ return abi.encodePacked(type(SafeVault).creationCode, abi.encode(owner));
+ }
+
+ /// @notice The deterministic SafeVault address for `owner`, deployed or not.
+ function vaultOf(address owner) public view returns (address) {
+ return Create2.computeAddress(_salt(owner), keccak256(_initCode(owner)));
+ }
+
+ /// @notice Whether `owner`'s SafeVault has already been deployed.
+ function isDeployed(address owner) external view returns (bool) {
+ return vaultOf(owner).code.length != 0;
+ }
+
+ /// @notice Deploy `owner`'s SafeVault if it doesn't exist yet; returns its address.
+ /// Idempotent: a second call just returns the existing vault.
+ function deploy(address owner) external returns (address vault) {
+ vault = vaultOf(owner);
+ if (vault.code.length == 0) {
+ address deployed = Create2.deploy(0, _salt(owner), _initCode(owner));
+ require(deployed == vault, "factory: address mismatch");
+ emit VaultDeployed(owner, vault);
+ }
+ }
+}
diff --git a/contracts/test/PreAuthRegistry.t.sol b/contracts/test/PreAuthRegistry.t.sol
index 022aeb9..bbfe34e 100644
--- a/contracts/test/PreAuthRegistry.t.sol
+++ b/contracts/test/PreAuthRegistry.t.sol
@@ -139,7 +139,10 @@ contract PreAuthSigTest is Test {
bytes32 domainSep = keccak256(
abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("coincoin GuardianModule")), keccak256(bytes("1")), block.chainid, user)
);
- bytes32 structHash = keccak256(abi.encode(POLICY_TYPEHASH, v, keccak256(abi.encodePacked(ks)), nonce, deadline));
+ bytes32[] memory words = new bytes32[](ks.length);
+ for (uint256 i; i < ks.length; ++i) words[i] = bytes32(uint256(uint160(ks[i])));
+ bytes32 keepersHash = keccak256(abi.encodePacked(words)); // standard EIP-712 address[] hash
+ bytes32 structHash = keccak256(abi.encode(POLICY_TYPEHASH, v, keepersHash, nonce, deadline));
return keccak256(abi.encodePacked("\x19\x01", domainSep, structHash));
}
diff --git a/contracts/test/SafeVaultFactory.t.sol b/contracts/test/SafeVaultFactory.t.sol
new file mode 100644
index 0000000..d5f2e29
--- /dev/null
+++ b/contracts/test/SafeVaultFactory.t.sol
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.24;
+
+import {Test} from "forge-std/Test.sol";
+import {SafeVaultFactory} from "../src/SafeVaultFactory.sol";
+import {SafeVault} from "../src/SafeVault.sol";
+
+contract SafeVaultFactoryTest is Test {
+ SafeVaultFactory factory;
+ address alice = address(0xA11CE);
+ address bob = address(0xB0B);
+
+ function setUp() public {
+ factory = new SafeVaultFactory();
+ }
+
+ function test_VaultOfMatchesDeployedAddress() public {
+ address predicted = factory.vaultOf(alice);
+ assertEq(predicted.code.length, 0, "should not exist yet");
+ address deployed = factory.deploy(alice);
+ assertEq(deployed, predicted, "deployed address must equal vaultOf");
+ assertGt(deployed.code.length, 0, "vault should have code");
+ }
+
+ function test_DeployedVaultIsOwnedByOwner() public {
+ address vault = factory.deploy(alice);
+ assertEq(SafeVault(payable(vault)).owner(), alice, "vault owner must be the user");
+ }
+
+ function test_DeployIsIdempotent() public {
+ address first = factory.deploy(alice);
+ address second = factory.deploy(alice); // must not revert
+ assertEq(first, second, "second deploy returns the same vault");
+ }
+
+ function test_IsDeployedReflectsState() public {
+ assertFalse(factory.isDeployed(alice));
+ factory.deploy(alice);
+ assertTrue(factory.isDeployed(alice));
+ }
+
+ function test_DistinctOwnersGetDistinctVaults() public {
+ assertTrue(factory.vaultOf(alice) != factory.vaultOf(bob), "vaults must differ per owner");
+ }
+}
diff --git a/deployments/robinhood-testnet.json b/deployments/robinhood-testnet.json
index 3e8fa90..5cd8f10 100644
--- a/deployments/robinhood-testnet.json
+++ b/deployments/robinhood-testnet.json
@@ -3,19 +3,21 @@
"network": "robinhood-chain-testnet",
"rpc": "https://rpc.testnet.chain.robinhood.com/rpc",
"contracts": {
- "GuardianModule": "0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77",
- "RulesEngineV1": "0xc20A9d7D38B07a9C74A1fD87A2e25CA1973Cbc52"
+ "GuardianModule": "0x9953BB30cFef2ac842C74417eA6DC661b492E8dA",
+ "RulesEngineV1": "0xc20A9d7D38B07a9C74A1fD87A2e25CA1973Cbc52",
+ "SafeVaultFactory": "0x1ef2B2539fa842A9c7e4EA07790aA6dBc47ec4A5"
},
"demo": {
"_comment": "Live end-to-end detection->evacuation + DeFi-exit scenario (pnpm onboard/watch/exploit). MockAavePool holds a deposited position credited to the victim (exitAaveV3 target).",
- "SafeVault": "0x49be3DC48fC0540346A064fCC6Fc94FBaE62f479",
- "MockERC20": "0xC32C2eB815F1413ee2c7A68d2EFf3760d828841E",
- "MockVulnerableProtocol": "0x6e8086CF791754b93bAf04F039b9f72e2bCF80Db",
- "MockAavePool": "0xaf57676673B71CED42767841F1317A20484052BE",
- "victim": "0xfa142e801447fc359E54e8E797357aA7cBf23368"
+ "SafeVault": "0x530921CFFCeCc01B3Ad20E48A8c1707d27204b91",
+ "MockERC20": "0x31052145BBFB8aA9B4e3713a2fD34e57b3A942f3",
+ "MockVulnerableProtocol": "0x43FfEEA2eAea3e2FB504cFF746b77171f86f6Ec0",
+ "MockAavePool": "0x7E8c3a15E45418c4B187505Bb21FBB05015aca93",
+ "victim": "0x46d167056f22BFB13bc2F940ae42b29c514280Cc"
},
- "deployedAt": "2026-06-14",
+ "deployedAt": "2026-06-16",
"deployer": "0xdae8992a9b5Fe850bE63781d1c2e65a3e496F728",
- "explorer": "https://explorer.testnet.chain.robinhood.com/address/0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77",
- "note": "Robinhood Chain testnet (Arbitrum Orbit). This GuardianModule is the CURRENT build: EIP-7702 delegation + self-config, ERC-20 sweep, approval revocation, on-chain detection->evacuation, frozen vault, signed multi-keeper policy (configureWithSig), exitAaveV3 (DeFi exit), and the local firewall (execute + RulesEngineV1). The DeFi exit is also fork-verified against real Aave V3 on Arbitrum One (Aave isn't deployed on this chain; MockAavePool stands in here). Supersedes the earlier build 0x6671b4B73b79c284A710B00ef777d8E65f55200F (delegation + sweep + revoke only)."
+ "keeper": "0x627872F35b724222413e7421C9e40A26B2762B9e",
+ "explorer": "https://explorer.testnet.chain.robinhood.com/address/0x9953BB30cFef2ac842C74417eA6DC661b492E8dA",
+ "note": "Robinhood Chain testnet (Arbitrum Orbit). Current GuardianModule build: EIP-7702 delegation + self-config, ERC-20 sweep, approval revocation, on-chain detection->evacuation, frozen vault, signed multi-keeper policy (configureWithSig, EIP-712 standard encoding so a browser wallet's signTypedData verifies), exitAaveV3 (DeFi exit), and the local firewall (execute + RulesEngineV1). SafeVaultFactory deploys a deterministic per-user SafeVault for gasless in-browser onboarding (relayer-sponsored). Supersedes 0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77 (pre-EIP-712-fix)."
}
diff --git a/site/.env.example b/site/.env.example
index ae83752..5c9c6a5 100644
--- a/site/.env.example
+++ b/site/.env.example
@@ -3,3 +3,7 @@
# RulesEngineV1 address on Robinhood Chain — enables the dashboard's firewall controls.
# Deploy it with contracts/script/DeployRules.s.sol; empty = the Enable button stays off.
VITE_RULES_ENGINE=
+
+# SafeVaultFactory address (gasless onboarding). Optional — falls back to the deployed constant
+# in src/app/contracts.ts. Deploy with contracts/script/DeployFactory.s.sol.
+VITE_VAULT_FACTORY=
diff --git a/site/api/onboard.ts b/site/api/onboard.ts
new file mode 100644
index 0000000..dd11cc0
--- /dev/null
+++ b/site/api/onboard.ts
@@ -0,0 +1,116 @@
+import type { VercelRequest, VercelResponse } from "@vercel/node";
+import {
+ createPublicClient,
+ createWalletClient,
+ http,
+ encodeFunctionData,
+ parseAbi,
+ isAddress,
+ defineChain,
+ type SignedAuthorization,
+} from "viem";
+import { verifyAuthorization } from "viem/utils";
+import { privateKeyToAccount } from "viem/accounts";
+
+/// Gasless onboarding relayer. The browser sends the user's two off-chain signatures (an EIP-7702
+/// authorization delegating to the GuardianModule + an EIP-712 Policy for `configureWithSig`). The
+/// relayer validates them against the canonical policy, deploys the user's deterministic SafeVault if
+/// needed, then submits ONE sponsored type-4 transaction that delegates AND configures. The user
+/// pays no gas. The relayer can only ever apply the canonical policy (official keeper + the user's
+/// own factory vault) — it cannot delegate an account to arbitrary code or redirect funds.
+
+const CHAIN_ID = 46630;
+const GUARDIAN = (process.env.GUARDIAN_IMPL ?? "0x9953BB30cFef2ac842C74417eA6DC661b492E8dA").toLowerCase();
+const FACTORY = (process.env.VAULT_FACTORY ?? "0x1ef2B2539fa842A9c7e4EA07790aA6dBc47ec4A5") as `0x${string}`;
+const KEEPER = (process.env.KEEPER_ADDRESS ?? "0x627872F35b724222413e7421C9e40A26B2762B9e").toLowerCase();
+const RPC = process.env.ROBINHOOD_TESTNET_RPC ?? "https://rpc.testnet.chain.robinhood.com/rpc";
+const ORBIT_GAS = 5_000_000n;
+
+const robinhood = defineChain({
+ id: CHAIN_ID,
+ name: "Robinhood Chain Testnet",
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
+ rpcUrls: { default: { http: [RPC] } },
+});
+
+const FACTORY_ABI = parseAbi([
+ "function vaultOf(address owner) view returns (address)",
+ "function deploy(address owner) returns (address)",
+ "function isDeployed(address owner) view returns (bool)",
+]);
+const GUARDIAN_ABI = parseAbi([
+ "function configureWithSig((address safeVault, address[] keepers) p, uint256 nonce, uint256 deadline, bytes sig)",
+]);
+
+type Body = {
+ owner: `0x${string}`;
+ authorization: SignedAuthorization;
+ policy: { safeVault: `0x${string}`; keepers: `0x${string}`[] };
+ nonce: string | number;
+ deadline: string | number;
+ sig: `0x${string}`;
+};
+
+export default async function handler(req: VercelRequest, res: VercelResponse) {
+ if (req.method !== "POST") return res.status(405).json({ error: "POST only" });
+ const relayerKey = process.env.RELAYER_PRIVATE_KEY as `0x${string}` | undefined;
+ if (!relayerKey) return res.status(500).json({ error: "relayer not configured" });
+
+ let body: Body;
+ try {
+ body = typeof req.body === "string" ? JSON.parse(req.body) : (req.body as Body);
+ } catch {
+ return res.status(400).json({ error: "invalid JSON" });
+ }
+ const { owner, authorization, policy, nonce, deadline, sig } = body ?? ({} as Body);
+
+ // ── validate ──
+ if (!owner || !isAddress(owner)) return res.status(400).json({ error: "bad owner" });
+ if (!policy || !isAddress(policy.safeVault) || !Array.isArray(policy.keepers)) return res.status(400).json({ error: "bad policy" });
+ if (Number(deadline) <= Math.floor(Date.now() / 1000)) return res.status(400).json({ error: "deadline passed" });
+ if (policy.keepers.length !== 1 || policy.keepers[0].toLowerCase() !== KEEPER) {
+ return res.status(400).json({ error: "policy must use the canonical keeper" });
+ }
+ if (!authorization || (authorization.address ?? "").toLowerCase() !== GUARDIAN) {
+ return res.status(400).json({ error: "authorization must target the GuardianModule" });
+ }
+ if (authorization.chainId !== 0 && authorization.chainId !== CHAIN_ID) {
+ return res.status(400).json({ error: "authorization chainId mismatch" });
+ }
+
+ const pub = createPublicClient({ chain: robinhood, transport: http(RPC) });
+
+ // the policy must point at the user's OWN deterministic factory vault — nothing else
+ const expectedVault = (await pub.readContract({ address: FACTORY, abi: FACTORY_ABI, functionName: "vaultOf", args: [owner] })) as `0x${string}`;
+ if (policy.safeVault.toLowerCase() !== expectedVault.toLowerCase()) {
+ return res.status(400).json({ error: "safeVault is not the caller's factory vault" });
+ }
+ // the authorization must have been signed by the owner
+ let validSigner: boolean;
+ try {
+ validSigner = await verifyAuthorization({ address: owner, authorization });
+ } catch {
+ validSigner = false;
+ }
+ if (!validSigner) return res.status(400).json({ error: "authorization not signed by owner" });
+
+ // ── relay (pay gas) ──
+ const relayer = privateKeyToAccount(relayerKey);
+ const wallet = createWalletClient({ account: relayer, chain: robinhood, transport: http(RPC) });
+ try {
+ const deployed = (await pub.readContract({ address: FACTORY, abi: FACTORY_ABI, functionName: "isDeployed", args: [owner] })) as boolean;
+ if (!deployed) {
+ const dtx = await wallet.writeContract({ address: FACTORY, abi: FACTORY_ABI, functionName: "deploy", args: [owner], gas: ORBIT_GAS });
+ await pub.waitForTransactionReceipt({ hash: dtx });
+ }
+ const data = encodeFunctionData({
+ abi: GUARDIAN_ABI,
+ functionName: "configureWithSig",
+ args: [{ safeVault: policy.safeVault, keepers: policy.keepers }, BigInt(nonce), BigInt(deadline), sig],
+ });
+ const txHash = await wallet.sendTransaction({ to: owner, data, authorizationList: [authorization], gas: ORBIT_GAS });
+ return res.status(200).json({ ok: true, txHash, vault: expectedVault });
+ } catch (e) {
+ return res.status(500).json({ error: "relay failed", detail: (e as Error).message });
+ }
+}
diff --git a/site/package.json b/site/package.json
index a804d72..cc7e799 100644
--- a/site/package.json
+++ b/site/package.json
@@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
+ "typecheck:api": "tsc -p tsconfig.api.json",
"lint": "eslint .",
"preview": "vite preview"
},
@@ -23,6 +24,7 @@
"@types/node": "^24.12.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
+ "@vercel/node": "^5.8.17",
"@vitejs/plugin-react": "^6.0.1",
"autoprefixer": "^10.5.0",
"eslint": "^10.3.0",
diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml
index 370063e..089fa62 100644
--- a/site/pnpm-lock.yaml
+++ b/site/pnpm-lock.yaml
@@ -42,9 +42,12 @@ importers:
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.17)
+ '@vercel/node':
+ specifier: ^5.8.17
+ version: 5.8.17
'@vitejs/plugin-react':
specifier: ^6.0.1
- version: 6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@1.21.7))
+ version: 6.0.2(vite@8.0.16(@types/node@24.13.2)(esbuild@0.27.0)(jiti@1.21.7)(tsx@4.21.0))
autoprefixer:
specifier: ^10.5.0
version: 10.5.0(postcss@8.5.15)
@@ -65,7 +68,7 @@ importers:
version: 8.5.15
tailwindcss:
specifier: ^3.4.19
- version: 3.4.19
+ version: 3.4.19(tsx@4.21.0)
typescript:
specifier: ~6.0.2
version: 6.0.3
@@ -74,7 +77,7 @@ importers:
version: 8.61.0(eslint@10.5.0(jiti@1.21.7))(typescript@6.0.3)
vite:
specifier: ^8.0.12
- version: 8.0.16(@types/node@24.13.2)(jiti@1.21.7)
+ version: 8.0.16(@types/node@24.13.2)(esbuild@0.27.0)(jiti@1.21.7)(tsx@4.21.0)
packages:
@@ -152,6 +155,29 @@ packages:
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'}
+ '@bytecodealliance/preview2-shim@0.17.6':
+ resolution: {integrity: sha512-n3cM88gTen5980UOBAD6xDcNNL3ocTK8keab21bpx1ONdA+ARj7uD1qoFxOWCyKlkpSi195FH+GeAut7Oc6zZw==}
+
+ '@edge-runtime/format@2.2.1':
+ resolution: {integrity: sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g==}
+ engines: {node: '>=16'}
+
+ '@edge-runtime/node-utils@2.3.0':
+ resolution: {integrity: sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ==}
+ engines: {node: '>=16'}
+
+ '@edge-runtime/ponyfill@2.4.2':
+ resolution: {integrity: sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA==}
+ engines: {node: '>=16'}
+
+ '@edge-runtime/primitives@4.1.0':
+ resolution: {integrity: sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==}
+ engines: {node: '>=16'}
+
+ '@edge-runtime/vm@3.2.0':
+ resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==}
+ engines: {node: '>=16'}
+
'@emnapi/core@1.10.0':
resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
@@ -161,6 +187,162 @@ packages:
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+ '@esbuild/aix-ppc64@0.27.0':
+ resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.0':
+ resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.0':
+ resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.0':
+ resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.0':
+ resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.0':
+ resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.0':
+ resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.0':
+ resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.0':
+ resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.0':
+ resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.0':
+ resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.0':
+ resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.0':
+ resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.0':
+ resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.0':
+ resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.0':
+ resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.0':
+ resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.0':
+ resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.0':
+ resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.0':
+ resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.0':
+ resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.0':
+ resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.0':
+ resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.0':
+ resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.0':
+ resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.0':
+ resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
'@eslint-community/eslint-utils@4.9.1':
resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -200,6 +382,10 @@ packages:
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ '@fastify/busboy@2.1.1':
+ resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
+ engines: {node: '>=14'}
+
'@humanfs/core@0.19.2':
resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
engines: {node: '>=18.18.0'}
@@ -220,6 +406,18 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
+ '@isaacs/balanced-match@4.0.1':
+ resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
+ engines: {node: 20 || >=22}
+
+ '@isaacs/brace-expansion@5.0.1':
+ resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==}
+ engines: {node: 20 || >=22}
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -236,6 +434,11 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@mapbox/node-pre-gyp@2.0.3':
+ resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
'@napi-rs/wasm-runtime@1.1.5':
resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
peerDependencies:
@@ -269,6 +472,10 @@ packages:
'@oxc-project/types@0.133.0':
resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+ '@renovatebot/pep440@4.2.1':
+ resolution: {integrity: sha512-2FK1hF93Fuf1laSdfiEmJvSJPVIDHEUTz68D3Fi9s0IZrrpaEcj6pTFBTbYvsgC5du4ogrtf5re7yMMvrKNgkw==}
+ engines: {node: ^20.9.0 || ^22.11.0 || ^24, pnpm: ^10.0.0}
+
'@rolldown/binding-android-arm64@1.0.3':
resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -367,6 +574,15 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
+ '@rollup/pluginutils@5.4.0':
+ resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
'@scure/base@1.2.6':
resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==}
@@ -384,6 +600,9 @@ packages:
peerDependencies:
react: ^18 || ^19
+ '@ts-morph/common@0.11.1':
+ resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==}
+
'@tybys/wasm-util@0.10.2':
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
@@ -396,6 +615,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/node@20.11.0':
+ resolution: {integrity: sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==}
+
'@types/node@24.13.2':
resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==}
@@ -466,6 +688,26 @@ packages:
resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@vercel/build-utils@13.30.0':
+ resolution: {integrity: sha512-fLIa8cpELsSoWbcxshaqegwlTCfKnbgsDmA6uIb+oA797xvSmYkPu5a781aRYCRdghgyOZF7NCSVgtZjHjunnQ==}
+
+ '@vercel/error-utils@2.2.0':
+ resolution: {integrity: sha512-WFWiRxfPzoYWYifaj4thSKvAaZZwUOqD4k5GINRIgZgCiS2E3iAJbWbIsIZmkQdTecWFHcWGA6q48CjisgpOBA==}
+
+ '@vercel/nft@1.10.0':
+ resolution: {integrity: sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA==}
+ engines: {node: '>=20'}
+ hasBin: true
+
+ '@vercel/node@5.8.17':
+ resolution: {integrity: sha512-n2DVzblqS43LTs4BV1iLfx8tjjrTegcSsyDgRzn70h6tQePYWjl+h0bU3X1stpITZtaYJSZYwVevuX1BJNZHHg==}
+
+ '@vercel/python-analysis@0.11.1':
+ resolution: {integrity: sha512-EPPLuXJQhIDUx08H9nG76AR2HSgBquwe3OAX5s2w20M923iaWeGGVkhX/4yZ89CJfXEZgE1Aj/mX7lVHOVIcYA==}
+
+ '@vercel/static-config@3.4.0':
+ resolution: {integrity: sha512-wCq90CMUB//ggnFh77NQO1xaLFsS4LigQIqKrH6ohnr9Br/KI1FhlErx62WfCOuueWaW+LVsbLOqNXIUjK8t6A==}
+
'@vitejs/plugin-react@6.0.2':
resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -528,6 +770,10 @@ packages:
typescript:
optional: true
+ abbrev@3.0.1:
+ resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+
abitype@1.2.3:
resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==}
peerDependencies:
@@ -539,6 +785,11 @@ packages:
zod:
optional: true
+ acorn-import-attributes@1.9.5:
+ resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
+ peerDependencies:
+ acorn: ^8
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -549,9 +800,16 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
ajv@6.15.0:
resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+ ajv@8.6.3:
+ resolution: {integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==}
+
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@@ -562,6 +820,20 @@ packages:
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ async-listen@3.0.0:
+ resolution: {integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==}
+ engines: {node: '>= 14'}
+
+ async-listen@3.0.1:
+ resolution: {integrity: sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==}
+ engines: {node: '>= 14'}
+
+ async-sema@3.1.1:
+ resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==}
+
autoprefixer@10.5.0:
resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==}
engines: {node: ^10 || ^12 || >=14}
@@ -569,6 +841,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
@@ -582,6 +857,12 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
+ bindings@1.5.0:
+ resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+
+ brace-expansion@1.1.15:
+ resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
+
brace-expansion@5.0.6:
resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
engines: {node: 18 || 20 || >=22}
@@ -606,10 +887,31 @@ packages:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ cjs-module-lexer@1.2.3:
+ resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==}
+
+ code-block-writer@10.1.1:
+ resolution: {integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==}
+
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ consola@3.4.2:
+ resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
+ convert-hrtime@3.0.0:
+ resolution: {integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==}
+ engines: {node: '>=8'}
+
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
@@ -647,6 +949,11 @@ packages:
dlv@1.1.3:
resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+ edge-runtime@2.5.9:
+ resolution: {integrity: sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg==}
+ engines: {node: '>=16'}
+ hasBin: true
+
electron-to-chromium@1.5.372:
resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==}
@@ -654,6 +961,17 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
+ es-module-lexer@1.4.1:
+ resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==}
+
+ es-module-lexer@1.5.0:
+ resolution: {integrity: sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==}
+
+ esbuild@0.27.0:
+ resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
@@ -711,10 +1029,17 @@ packages:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
+ estree-walker@2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
@@ -747,6 +1072,9 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
+ file-uri-to-path@1.0.0:
+ resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -779,6 +1107,10 @@ packages:
react-dom:
optional: true
+ fs-extra@11.1.1:
+ resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
+ engines: {node: '>=14.14'}
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -791,6 +1123,9 @@ packages:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
+ get-tsconfig@4.14.0:
+ resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
+
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -799,10 +1134,17 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
+ glob@13.0.6:
+ resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
+ engines: {node: 18 || 20 || >=22}
+
globals@17.6.0:
resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==}
engines: {node: '>=18'}
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
gsap@3.15.0:
resolution: {integrity: sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==}
@@ -816,6 +1158,10 @@ packages:
hermes-parser@0.25.1:
resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -863,6 +1209,10 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ js-yaml@4.1.1:
+ resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
+ hasBin: true
+
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -871,9 +1221,15 @@ packages:
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+ json-schema-to-ts@1.6.4:
+ resolution: {integrity: sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==}
+
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+ json-schema-traverse@1.0.0:
+ resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -882,6 +1238,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ jsonfile@6.2.1:
+ resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@@ -974,6 +1333,10 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
+ lru-cache@11.5.1:
+ resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+ engines: {node: 20 || >=22}
+
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -985,10 +1348,33 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ minimatch@10.1.1:
+ resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==}
+ engines: {node: 20 || >=22}
+
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
engines: {node: 18 || 20 || >=22}
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+ minipass@7.1.3:
+ resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.1.0:
+ resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
+ engines: {node: '>= 18'}
+
mipd@0.0.7:
resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==}
peerDependencies:
@@ -997,12 +1383,21 @@ packages:
typescript:
optional: true
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
motion-dom@12.40.0:
resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==}
motion-utils@12.39.0:
resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==}
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -1017,10 +1412,28 @@ packages:
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ node-fetch@2.6.9:
+ resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
node-releases@2.0.47:
resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==}
engines: {node: '>=18'}
+ nopt@8.1.0:
+ resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==}
+ engines: {node: ^18.17.0 || >=20.5.0}
+ hasBin: true
+
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -1053,6 +1466,13 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+ parse-ms@2.1.0:
+ resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==}
+ engines: {node: '>=6'}
+
+ path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -1064,6 +1484,19 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ path-scurry@2.0.2:
+ resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
+ engines: {node: 18 || 20 || >=22}
+
+ path-to-regexp@6.1.0:
+ resolution: {integrity: sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==}
+
+ path-to-regexp@6.3.0:
+ resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
+
+ picocolors@1.0.0:
+ resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -1134,6 +1567,10 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ pretty-ms@7.0.1:
+ resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==}
+ engines: {node: '>=10'}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -1157,6 +1594,17 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
resolve@1.22.12:
resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
engines: {node: '>= 0.4'}
@@ -1194,6 +1642,14 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
+ signal-exit@4.0.2:
+ resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==}
+ engines: {node: '>=14'}
+
+ smol-toml@1.5.2:
+ resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==}
+ engines: {node: '>= 18'}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -1212,6 +1668,10 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
+ tar@7.5.16:
+ resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==}
+ engines: {node: '>=18'}
+
thenify-all@1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'}
@@ -1219,6 +1679,10 @@ packages:
thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+ time-span@4.0.0:
+ resolution: {integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==}
+ engines: {node: '>=10'}
+
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
@@ -1227,6 +1691,9 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
ts-api-utils@2.5.0:
resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
engines: {node: '>=18.12'}
@@ -1236,9 +1703,20 @@ packages:
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+ ts-morph@12.0.0:
+ resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==}
+
+ ts-toolbelt@6.15.5:
+ resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==}
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+ tsx@4.21.0:
+ resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -1250,14 +1728,30 @@ packages:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'}
hasBin: true
+ undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+ undici@5.28.4:
+ resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
+ engines: {node: '>=14.0'}
+
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
update-browserslist-db@1.2.3:
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
hasBin: true
@@ -1337,6 +1831,12 @@ packages:
typescript:
optional: true
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -1361,6 +1861,10 @@ packages:
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@@ -1371,6 +1875,9 @@ packages:
peerDependencies:
zod: ^3.25.0 || ^4.0.0
+ zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
@@ -1498,6 +2005,20 @@ snapshots:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
+ '@bytecodealliance/preview2-shim@0.17.6': {}
+
+ '@edge-runtime/format@2.2.1': {}
+
+ '@edge-runtime/node-utils@2.3.0': {}
+
+ '@edge-runtime/ponyfill@2.4.2': {}
+
+ '@edge-runtime/primitives@4.1.0': {}
+
+ '@edge-runtime/vm@3.2.0':
+ dependencies:
+ '@edge-runtime/primitives': 4.1.0
+
'@emnapi/core@1.10.0':
dependencies:
'@emnapi/wasi-threads': 1.2.1
@@ -1514,6 +2035,84 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@esbuild/aix-ppc64@0.27.0':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/android-arm@0.27.0':
+ optional: true
+
+ '@esbuild/android-x64@0.27.0':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.0':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.0':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.0':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.0':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.0':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.0':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.0':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.0':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.0':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.0':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.0':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.0':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.0':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.0':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.0':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.0':
+ optional: true
+
'@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@1.21.7))':
dependencies:
eslint: 10.5.0(jiti@1.21.7)
@@ -1548,6 +2147,8 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
+ '@fastify/busboy@2.1.1': {}
+
'@humanfs/core@0.19.2':
dependencies:
'@humanfs/types': 0.15.0
@@ -1564,6 +2165,16 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
+ '@isaacs/balanced-match@4.0.1': {}
+
+ '@isaacs/brace-expansion@5.0.1':
+ dependencies:
+ '@isaacs/balanced-match': 4.0.1
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.3
+
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -1583,6 +2194,19 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@mapbox/node-pre-gyp@2.0.3':
+ dependencies:
+ consola: 3.4.2
+ detect-libc: 2.1.2
+ https-proxy-agent: 7.0.6
+ node-fetch: 2.6.9
+ nopt: 8.1.0
+ semver: 7.8.4
+ tar: 7.5.16
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
'@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
dependencies:
'@emnapi/core': 1.10.0
@@ -1612,6 +2236,8 @@ snapshots:
'@oxc-project/types@0.133.0': {}
+ '@renovatebot/pep440@4.2.1': {}
+
'@rolldown/binding-android-arm64@1.0.3':
optional: true
@@ -1663,6 +2289,12 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
+ '@rollup/pluginutils@5.4.0':
+ dependencies:
+ '@types/estree': 1.0.9
+ estree-walker: 2.0.2
+ picomatch: 4.0.4
+
'@scure/base@1.2.6': {}
'@scure/bip32@1.7.0':
@@ -1683,6 +2315,13 @@ snapshots:
'@tanstack/query-core': 5.101.0
react: 19.2.7
+ '@ts-morph/common@0.11.1':
+ dependencies:
+ fast-glob: 3.3.3
+ minimatch: 3.1.5
+ mkdirp: 1.0.4
+ path-browserify: 1.0.1
+
'@tybys/wasm-util@0.10.2':
dependencies:
tslib: 2.8.1
@@ -1694,6 +2333,10 @@ snapshots:
'@types/json-schema@7.0.15': {}
+ '@types/node@20.11.0':
+ dependencies:
+ undici-types: 5.26.5
+
'@types/node@24.13.2':
dependencies:
undici-types: 7.18.2
@@ -1797,10 +2440,82 @@ snapshots:
'@typescript-eslint/types': 8.61.0
eslint-visitor-keys: 5.0.1
- '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.13.2)(jiti@1.21.7))':
+ '@vercel/build-utils@13.30.0':
+ dependencies:
+ '@vercel/python-analysis': 0.11.1
+ cjs-module-lexer: 1.2.3
+ es-module-lexer: 1.5.0
+
+ '@vercel/error-utils@2.2.0': {}
+
+ '@vercel/nft@1.10.0':
+ dependencies:
+ '@mapbox/node-pre-gyp': 2.0.3
+ '@rollup/pluginutils': 5.4.0
+ acorn: 8.17.0
+ acorn-import-attributes: 1.9.5(acorn@8.17.0)
+ async-sema: 3.1.1
+ bindings: 1.5.0
+ estree-walker: 2.0.2
+ glob: 13.0.6
+ graceful-fs: 4.2.11
+ node-gyp-build: 4.8.4
+ picomatch: 4.0.4
+ resolve-from: 5.0.0
+ transitivePeerDependencies:
+ - encoding
+ - rollup
+ - supports-color
+
+ '@vercel/node@5.8.17':
+ dependencies:
+ '@edge-runtime/node-utils': 2.3.0
+ '@edge-runtime/primitives': 4.1.0
+ '@edge-runtime/vm': 3.2.0
+ '@types/node': 20.11.0
+ '@vercel/build-utils': 13.30.0
+ '@vercel/error-utils': 2.2.0
+ '@vercel/nft': 1.10.0
+ '@vercel/static-config': 3.4.0
+ async-listen: 3.0.0
+ cjs-module-lexer: 1.2.3
+ edge-runtime: 2.5.9
+ es-module-lexer: 1.4.1
+ esbuild: 0.27.0
+ etag: 1.8.1
+ mime-types: 2.1.35
+ node-fetch: 2.6.9
+ path-to-regexp: 6.1.0
+ path-to-regexp-updated: path-to-regexp@6.3.0
+ ts-morph: 12.0.0
+ tsx: 4.21.0
+ typescript: 5.9.3
+ undici: 5.28.4
+ transitivePeerDependencies:
+ - encoding
+ - rollup
+ - supports-color
+
+ '@vercel/python-analysis@0.11.1':
+ dependencies:
+ '@bytecodealliance/preview2-shim': 0.17.6
+ '@renovatebot/pep440': 4.2.1
+ fs-extra: 11.1.1
+ js-yaml: 4.1.1
+ minimatch: 10.1.1
+ smol-toml: 1.5.2
+ zod: 3.22.4
+
+ '@vercel/static-config@3.4.0':
+ dependencies:
+ ajv: 8.6.3
+ json-schema-to-ts: 1.6.4
+ ts-morph: 12.0.0
+
+ '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@24.13.2)(esbuild@0.27.0)(jiti@1.21.7)(tsx@4.21.0))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@24.13.2)(jiti@1.21.7)
+ vite: 8.0.16(@types/node@24.13.2)(esbuild@0.27.0)(jiti@1.21.7)(tsx@4.21.0)
'@wagmi/connectors@8.0.15(@wagmi/core@3.5.0(@tanstack/query-core@5.101.0)(@types/react@19.2.17)(react@19.2.7)(typescript@6.0.3)(use-sync-external-store@1.4.0(react@19.2.7))(viem@2.52.2(typescript@6.0.3)(zod@4.4.3)))(typescript@6.0.3)(viem@2.52.2(typescript@6.0.3)(zod@4.4.3))':
dependencies:
@@ -1824,17 +2539,25 @@ snapshots:
- react
- use-sync-external-store
+ abbrev@3.0.1: {}
+
abitype@1.2.3(typescript@6.0.3)(zod@4.4.3):
optionalDependencies:
typescript: 6.0.3
zod: 4.4.3
+ acorn-import-attributes@1.9.5(acorn@8.17.0):
+ dependencies:
+ acorn: 8.17.0
+
acorn-jsx@5.3.2(acorn@8.17.0):
dependencies:
acorn: 8.17.0
acorn@8.17.0: {}
+ agent-base@7.1.4: {}
+
ajv@6.15.0:
dependencies:
fast-deep-equal: 3.1.3
@@ -1842,6 +2565,13 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
+ ajv@8.6.3:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ uri-js: 4.4.1
+
any-promise@1.3.0: {}
anymatch@3.1.3:
@@ -1851,6 +2581,14 @@ snapshots:
arg@5.0.2: {}
+ argparse@2.0.1: {}
+
+ async-listen@3.0.0: {}
+
+ async-listen@3.0.1: {}
+
+ async-sema@3.1.1: {}
+
autoprefixer@10.5.0(postcss@8.5.15):
dependencies:
browserslist: 4.28.2
@@ -1860,12 +2598,23 @@ snapshots:
postcss: 8.5.15
postcss-value-parser: 4.2.0
+ balanced-match@1.0.2: {}
+
balanced-match@4.0.4: {}
baseline-browser-mapping@2.10.37: {}
binary-extensions@2.3.0: {}
+ bindings@1.5.0:
+ dependencies:
+ file-uri-to-path: 1.0.0
+
+ brace-expansion@1.1.15:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
brace-expansion@5.0.6:
dependencies:
balanced-match: 4.0.4
@@ -1898,8 +2647,20 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ chownr@3.0.0: {}
+
+ cjs-module-lexer@1.2.3: {}
+
+ code-block-writer@10.1.1: {}
+
commander@4.1.1: {}
+ concat-map@0.0.1: {}
+
+ consola@3.4.2: {}
+
+ convert-hrtime@3.0.0: {}
+
convert-source-map@2.0.0: {}
cross-spawn@7.0.6:
@@ -1924,10 +2685,55 @@ snapshots:
dlv@1.1.3: {}
+ edge-runtime@2.5.9:
+ dependencies:
+ '@edge-runtime/format': 2.2.1
+ '@edge-runtime/ponyfill': 2.4.2
+ '@edge-runtime/vm': 3.2.0
+ async-listen: 3.0.1
+ mri: 1.2.0
+ picocolors: 1.0.0
+ pretty-ms: 7.0.1
+ signal-exit: 4.0.2
+ time-span: 4.0.0
+
electron-to-chromium@1.5.372: {}
es-errors@1.3.0: {}
+ es-module-lexer@1.4.1: {}
+
+ es-module-lexer@1.5.0: {}
+
+ esbuild@0.27.0:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.0
+ '@esbuild/android-arm': 0.27.0
+ '@esbuild/android-arm64': 0.27.0
+ '@esbuild/android-x64': 0.27.0
+ '@esbuild/darwin-arm64': 0.27.0
+ '@esbuild/darwin-x64': 0.27.0
+ '@esbuild/freebsd-arm64': 0.27.0
+ '@esbuild/freebsd-x64': 0.27.0
+ '@esbuild/linux-arm': 0.27.0
+ '@esbuild/linux-arm64': 0.27.0
+ '@esbuild/linux-ia32': 0.27.0
+ '@esbuild/linux-loong64': 0.27.0
+ '@esbuild/linux-mips64el': 0.27.0
+ '@esbuild/linux-ppc64': 0.27.0
+ '@esbuild/linux-riscv64': 0.27.0
+ '@esbuild/linux-s390x': 0.27.0
+ '@esbuild/linux-x64': 0.27.0
+ '@esbuild/netbsd-arm64': 0.27.0
+ '@esbuild/netbsd-x64': 0.27.0
+ '@esbuild/openbsd-arm64': 0.27.0
+ '@esbuild/openbsd-x64': 0.27.0
+ '@esbuild/openharmony-arm64': 0.27.0
+ '@esbuild/sunos-x64': 0.27.0
+ '@esbuild/win32-arm64': 0.27.0
+ '@esbuild/win32-ia32': 0.27.0
+ '@esbuild/win32-x64': 0.27.0
+
escalade@3.2.0: {}
escape-string-regexp@4.0.0: {}
@@ -2011,8 +2817,12 @@ snapshots:
estraverse@5.3.0: {}
+ estree-walker@2.0.2: {}
+
esutils@2.0.3: {}
+ etag@1.8.1: {}
+
eventemitter3@5.0.1: {}
fast-deep-equal@3.1.3: {}
@@ -2041,6 +2851,8 @@ snapshots:
dependencies:
flat-cache: 4.0.1
+ file-uri-to-path@1.0.0: {}
+
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -2068,6 +2880,12 @@ snapshots:
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
+ fs-extra@11.1.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.1
+ universalify: 2.0.1
+
fsevents@2.3.3:
optional: true
@@ -2075,6 +2893,10 @@ snapshots:
gensync@1.0.0-beta.2: {}
+ get-tsconfig@4.14.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -2083,8 +2905,16 @@ snapshots:
dependencies:
is-glob: 4.0.3
+ glob@13.0.6:
+ dependencies:
+ minimatch: 10.2.5
+ minipass: 7.1.3
+ path-scurry: 2.0.2
+
globals@17.6.0: {}
+ graceful-fs@4.2.11: {}
+
gsap@3.15.0: {}
hasown@2.0.4:
@@ -2097,6 +2927,13 @@ snapshots:
dependencies:
hermes-estree: 0.25.1
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
ignore@5.3.2: {}
ignore@7.0.5: {}
@@ -2129,16 +2966,33 @@ snapshots:
js-tokens@4.0.0: {}
+ js-yaml@4.1.1:
+ dependencies:
+ argparse: 2.0.1
+
jsesc@3.1.0: {}
json-buffer@3.0.1: {}
+ json-schema-to-ts@1.6.4:
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ts-toolbelt: 6.15.5
+
json-schema-traverse@0.4.1: {}
+ json-schema-traverse@1.0.0: {}
+
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
+ jsonfile@6.2.1:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
keyv@4.5.4:
dependencies:
json-buffer: 3.0.1
@@ -2205,6 +3059,8 @@ snapshots:
dependencies:
p-locate: 5.0.0
+ lru-cache@11.5.1: {}
+
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -2216,20 +3072,44 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.2
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ minimatch@10.1.1:
+ dependencies:
+ '@isaacs/brace-expansion': 5.0.1
+
minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.6
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.15
+
+ minipass@7.1.3: {}
+
+ minizlib@3.1.0:
+ dependencies:
+ minipass: 7.1.3
+
mipd@0.0.7(typescript@6.0.3):
optionalDependencies:
typescript: 6.0.3
+ mkdirp@1.0.4: {}
+
motion-dom@12.40.0:
dependencies:
motion-utils: 12.39.0
motion-utils@12.39.0: {}
+ mri@1.2.0: {}
+
ms@2.1.3: {}
mz@2.7.0:
@@ -2242,8 +3122,18 @@ snapshots:
natural-compare@1.4.0: {}
+ node-fetch@2.6.9:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-gyp-build@4.8.4: {}
+
node-releases@2.0.47: {}
+ nopt@8.1.0:
+ dependencies:
+ abbrev: 3.0.1
+
normalize-path@3.0.0: {}
object-assign@4.1.1: {}
@@ -2282,12 +3172,27 @@ snapshots:
dependencies:
p-limit: 3.1.0
+ parse-ms@2.1.0: {}
+
+ path-browserify@1.0.1: {}
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
path-parse@1.0.7: {}
+ path-scurry@2.0.2:
+ dependencies:
+ lru-cache: 11.5.1
+ minipass: 7.1.3
+
+ path-to-regexp@6.1.0: {}
+
+ path-to-regexp@6.3.0: {}
+
+ picocolors@1.0.0: {}
+
picocolors@1.1.1: {}
picomatch@2.3.2: {}
@@ -2310,12 +3215,13 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.5.15
- postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15):
+ postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15)(tsx@4.21.0):
dependencies:
lilconfig: 3.1.3
optionalDependencies:
jiti: 1.21.7
postcss: 8.5.15
+ tsx: 4.21.0
postcss-nested@6.2.0(postcss@8.5.15):
dependencies:
@@ -2337,6 +3243,10 @@ snapshots:
prelude-ls@1.2.1: {}
+ pretty-ms@7.0.1:
+ dependencies:
+ parse-ms: 2.1.0
+
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
@@ -2356,6 +3266,12 @@ snapshots:
dependencies:
picomatch: 2.3.2
+ require-from-string@2.0.2: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
resolve@1.22.12:
dependencies:
es-errors: 1.3.0
@@ -2402,6 +3318,10 @@ snapshots:
shebang-regex@3.0.0: {}
+ signal-exit@4.0.2: {}
+
+ smol-toml@1.5.2: {}
+
source-map-js@1.2.1: {}
sucrase@3.35.1:
@@ -2416,7 +3336,7 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- tailwindcss@3.4.19:
+ tailwindcss@3.4.19(tsx@4.21.0):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
@@ -2435,7 +3355,7 @@ snapshots:
postcss: 8.5.15
postcss-import: 15.1.0(postcss@8.5.15)
postcss-js: 4.1.0(postcss@8.5.15)
- postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15)
+ postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15)(tsx@4.21.0)
postcss-nested: 6.2.0(postcss@8.5.15)
postcss-selector-parser: 6.1.4
resolve: 1.22.12
@@ -2444,6 +3364,14 @@ snapshots:
- tsx
- yaml
+ tar@7.5.16:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.3
+ minizlib: 3.1.0
+ yallist: 5.0.0
+
thenify-all@1.6.0:
dependencies:
thenify: 3.3.1
@@ -2452,6 +3380,10 @@ snapshots:
dependencies:
any-promise: 1.3.0
+ time-span@4.0.0:
+ dependencies:
+ convert-hrtime: 3.0.0
+
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
@@ -2461,14 +3393,30 @@ snapshots:
dependencies:
is-number: 7.0.0
+ tr46@0.0.3: {}
+
ts-api-utils@2.5.0(typescript@6.0.3):
dependencies:
typescript: 6.0.3
ts-interface-checker@0.1.13: {}
+ ts-morph@12.0.0:
+ dependencies:
+ '@ts-morph/common': 0.11.1
+ code-block-writer: 10.1.1
+
+ ts-toolbelt@6.15.5: {}
+
tslib@2.8.1: {}
+ tsx@4.21.0:
+ dependencies:
+ esbuild: 0.27.0
+ get-tsconfig: 4.14.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -2484,10 +3432,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ typescript@5.9.3: {}
+
typescript@6.0.3: {}
+ undici-types@5.26.5: {}
+
undici-types@7.18.2: {}
+ undici@5.28.4:
+ dependencies:
+ '@fastify/busboy': 2.1.1
+
+ universalify@2.0.1: {}
+
update-browserslist-db@1.2.3(browserslist@4.28.2):
dependencies:
browserslist: 4.28.2
@@ -2521,7 +3479,7 @@ snapshots:
- utf-8-validate
- zod
- vite@8.0.16(@types/node@24.13.2)(jiti@1.21.7):
+ vite@8.0.16(@types/node@24.13.2)(esbuild@0.27.0)(jiti@1.21.7)(tsx@4.21.0):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -2530,8 +3488,10 @@ snapshots:
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.13.2
+ esbuild: 0.27.0
fsevents: 2.3.3
jiti: 1.21.7
+ tsx: 4.21.0
wagmi@3.6.16(@tanstack/query-core@5.101.0)(@tanstack/react-query@5.101.0(react@19.2.7))(@types/react@19.2.17)(react@19.2.7)(typescript@6.0.3)(viem@2.52.2(typescript@6.0.3)(zod@4.4.3)):
dependencies:
@@ -2556,6 +3516,13 @@ snapshots:
- immer
- porto
+ webidl-conversions@3.0.1: {}
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -2566,12 +3533,16 @@ snapshots:
yallist@3.1.1: {}
+ yallist@5.0.0: {}
+
yocto-queue@0.1.0: {}
zod-validation-error@4.0.2(zod@4.4.3):
dependencies:
zod: 4.4.3
+ zod@3.22.4: {}
+
zod@4.4.3: {}
zustand@5.0.0(@types/react@19.2.17)(react@19.2.7)(use-sync-external-store@1.4.0(react@19.2.7)):
diff --git a/site/src/app/contracts.ts b/site/src/app/contracts.ts
index 20837dc..aa03491 100644
--- a/site/src/app/contracts.ts
+++ b/site/src/app/contracts.ts
@@ -7,14 +7,29 @@ export const EXPLORER = robinhood.blockExplorers.default.url;
/// reads the guardian view functions at the connected address — not at the impl address.
export const ADDR = {
/// GuardianModule implementation (the 7702 delegate target).
- guardianImpl: "0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77",
+ guardianImpl: "0x9953BB30cFef2ac842C74417eA6DC661b492E8dA",
/// Demo dUSD token used in the end-to-end scenario.
- token: "0xC32C2eB815F1413ee2c7A68d2EFf3760d828841E",
+ token: "0x31052145BBFB8aA9B4e3713a2fD34e57b3A942f3",
/// RulesEngineV1 — deploy on Robinhood (script/DeployRules.s.sol) and set its address as
/// VITE_RULES_ENGINE to enable the firewall controls. Empty = the Enable button stays off.
rulesEngine: ((import.meta.env.VITE_RULES_ENGINE as string | undefined) ?? "") as `0x${string}` | "",
+ /// SafeVaultFactory — deterministic per-user SafeVault, for gasless in-browser onboarding.
+ factory: ((import.meta.env.VITE_VAULT_FACTORY as string | undefined) ??
+ "0x1ef2B2539fa842A9c7e4EA07790aA6dBc47ec4A5") as `0x${string}`,
+ /// Canonical coincoin keeper the gasless onboarding policy authorizes.
+ keeper: "0x627872F35b724222413e7421C9e40A26B2762B9e" as `0x${string}`,
} as const;
+export const factoryAbi = [
+ {
+ type: "function",
+ name: "vaultOf",
+ stateMutability: "view",
+ inputs: [{ name: "owner", type: "address" }],
+ outputs: [{ type: "address" }],
+ },
+] as const;
+
export const guardianAbi = [
{ type: "function", name: "configured", stateMutability: "view", inputs: [], outputs: [{ type: "bool" }] },
{ type: "function", name: "safeVault", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] },
diff --git a/site/src/app/useGaslessOnboard.ts b/site/src/app/useGaslessOnboard.ts
new file mode 100644
index 0000000..28ea118
--- /dev/null
+++ b/site/src/app/useGaslessOnboard.ts
@@ -0,0 +1,98 @@
+import { useCallback, useState } from "react";
+import { useConnectorClient, usePublicClient } from "wagmi";
+import { readContract, signTypedData, signAuthorization, waitForTransactionReceipt, getTransactionCount } from "viem/actions";
+import { ADDR, factoryAbi } from "./contracts";
+import { robinhood } from "./chain";
+
+export type OnboardStatus = "idle" | "signing" | "relaying" | "done" | "error";
+
+/// Gasless in-browser onboarding: the wallet signs (off-chain, no gas) an EIP-712 Policy and an
+/// EIP-7702 authorization; the relayer (/api/onboard) deploys the user's deterministic SafeVault and
+/// submits the sponsored delegate+configure transaction. Falls back to the CLI if the wallet can't
+/// sign an EIP-7702 authorization yet.
+export function useGaslessOnboard(onDone?: () => void) {
+ const { data: walletClient } = useConnectorClient();
+ const publicClient = usePublicClient();
+ const [status, setStatus] = useState("idle");
+ const [error, setError] = useState(null);
+
+ const onboard = useCallback(async () => {
+ setError(null);
+ if (!walletClient?.account || !publicClient) {
+ setError("Connect a wallet on Robinhood Chain first.");
+ setStatus("error");
+ return;
+ }
+ const owner = walletClient.account.address;
+ try {
+ setStatus("signing");
+ const vault = await readContract(publicClient, {
+ address: ADDR.factory,
+ abi: factoryAbi,
+ functionName: "vaultOf",
+ args: [owner],
+ });
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
+
+ // 1) EIP-712 Policy (standard signTypedData)
+ const sig = await signTypedData(walletClient, {
+ account: walletClient.account,
+ domain: { name: "coincoin GuardianModule", version: "1", chainId: robinhood.id, verifyingContract: owner },
+ types: {
+ Policy: [
+ { name: "safeVault", type: "address" },
+ { name: "keepers", type: "address[]" },
+ { name: "nonce", type: "uint256" },
+ { name: "deadline", type: "uint256" },
+ ],
+ },
+ primaryType: "Policy",
+ message: { safeVault: vault, keepers: [ADDR.keeper], nonce: 0n, deadline },
+ });
+
+ // 2) EIP-7702 authorization (wallet-support dependent → CLI fallback)
+ let auth;
+ try {
+ // EIP-7702 authorization. nonce = the account's CURRENT tx nonce (relayer-sponsored, so no
+ // +1). Fetched explicitly at `latest` for determinism — a wrong nonce is silently skipped
+ // (the type-4 tx still succeeds but the delegation never applies), so this must be exact.
+ const txNonce = await getTransactionCount(publicClient, { address: owner, blockTag: "latest" });
+ auth = await signAuthorization(walletClient, {
+ account: walletClient.account,
+ contractAddress: ADDR.guardianImpl,
+ chainId: robinhood.id,
+ nonce: txNonce,
+ });
+ } catch {
+ setError("This wallet can't sign an EIP-7702 authorization yet. Use the CLI: `pnpm onboard`.");
+ setStatus("error");
+ return;
+ }
+
+ // 3) relayer submits (pays gas)
+ setStatus("relaying");
+ const res = await fetch("/api/onboard", {
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: JSON.stringify({
+ owner,
+ authorization: { address: auth.address, chainId: auth.chainId, nonce: auth.nonce, r: auth.r, s: auth.s, yParity: auth.yParity },
+ policy: { safeVault: vault, keepers: [ADDR.keeper] },
+ nonce: "0",
+ deadline: deadline.toString(),
+ sig,
+ }),
+ });
+ const json = (await res.json()) as { ok?: boolean; txHash?: `0x${string}`; error?: string };
+ if (!res.ok || !json.txHash) throw new Error(json.error ?? "relayer error");
+ await waitForTransactionReceipt(publicClient, { hash: json.txHash });
+ setStatus("done");
+ onDone?.();
+ } catch (e) {
+ setError((e as Error).message ?? "onboarding failed");
+ setStatus("error");
+ }
+ }, [walletClient, publicClient, onDone]);
+
+ return { onboard, status, error };
+}
diff --git a/site/src/data.ts b/site/src/data.ts
index f69a33b..a17be2c 100644
--- a/site/src/data.ts
+++ b/site/src/data.ts
@@ -11,8 +11,8 @@ export const LICENSE = "MIT";
// On-chain proof: the GuardianModule deployed on Robinhood Chain Testnet (chain 46630),
// on the chain's own block explorer.
export const DEPLOY_PROOF = {
- address: "0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77",
- explorer: "https://explorer.testnet.chain.robinhood.com/address/0xd0d301Aeaa7AA5Ced16C927030f131c9Cb083b77",
+ address: "0x9953BB30cFef2ac842C74417eA6DC661b492E8dA",
+ explorer: "https://explorer.testnet.chain.robinhood.com/address/0x9953BB30cFef2ac842C74417eA6DC661b492E8dA",
};
// Contacts (footer + contact page).
@@ -121,9 +121,9 @@ export const GUARDIAN_TERMINAL: ProcTerminal = {
{ text: "→ scanning on-chain threat logs in bounded windows (≤10 blocks, RPC cap)…", tone: "muted" },
{ text: "→ caught up to head · idle, polling every few seconds…", tone: "muted" },
{ text: "→ threat detected on-chain → CRITICAL drain alert decoded", tone: "warn" },
- { text: "[coincoin] 🦆 COIN COIN ! threat detected → evacuating 0xfa14…3368", tone: "danger" },
+ { text: "[coincoin] 🦆 COIN COIN ! threat detected → evacuating 0x46d1…80Cc", tone: "danger" },
{ text: "→ keeper signs evacuateERC20([dUSD]) → your EOA (EIP-7702 guardian context)", tone: "normal" },
- { text: "[coincoin] ✅ evacuated to your SafeVault 0x49be…f479 — confirmed on-chain", tone: "success" },
+ { text: "[coincoin] ✅ evacuated to your SafeVault 0x5309…4b91 — confirmed on-chain", tone: "success" },
{ text: "→ funds safe in your own vault · daemon still watching. (Ctrl-C to stop)", tone: "success" },
],
};
diff --git a/site/src/pages/Dashboard.tsx b/site/src/pages/Dashboard.tsx
index 6f681fa..7f71d0e 100644
--- a/site/src/pages/Dashboard.tsx
+++ b/site/src/pages/Dashboard.tsx
@@ -14,6 +14,7 @@ import { robinhood } from "../app/chain";
import { ADDR, EXPLORER, erc20Abi, guardianAbi, safeVaultAbi } from "../app/contracts";
import { fmtUnits, short } from "../app/format";
import { Pill, PageHeader } from "../components/ui";
+import { useGaslessOnboard } from "../app/useGaslessOnboard";
const REFRESH = { refetchInterval: 8000 } as const;
@@ -78,6 +79,7 @@ export function Dashboard() {
// ── Writes ──
const { writeContractAsync } = useWriteContract();
+ const { onboard, status: onbStatus, error: onbError } = useGaslessOnboard(() => state.refetch());
const [busy, setBusy] = useState(null);
const [tx, setTx] = useState(null);
const [err, setErr] = useState(null);
@@ -187,10 +189,37 @@ export function Dashboard() {
{!configured && (
-
- This account isn't delegated yet. Onboard once from the CLI:{" "}
- pnpm onboard — then it shows up here.
-
+
+ {isConnected && onRobinhood && isSelf ? (
+ <>
+
onboard()}
+ disabled={onbStatus === "signing" || onbStatus === "relaying"}
+ className="btn-comic"
+ >
+ {onbStatus === "signing"
+ ? "Sign in your wallet…"
+ : onbStatus === "relaying"
+ ? "Onboarding (gasless)…"
+ : "Protect my wallet — gasless"}
+
+
+ Two wallet signatures, zero gas — a relayer sponsors the delegation. Or onboard from the CLI:{" "}
+ pnpm onboard.
+
+ {onbError &&
{onbError}
}
+ >
+ ) : (
+
+ {isConnected
+ ? onRobinhood
+ ? "Connect the account you want to protect to onboard it gasless, "
+ : "Switch to Robinhood Chain to onboard, "
+ : "Connect your wallet to onboard gasless, "}
+ or use the CLI: pnpm onboard.
+
+ )}
+
)}
diff --git a/site/src/pages/Docs.tsx b/site/src/pages/Docs.tsx
index 366a398..909a9ee 100644
--- a/site/src/pages/Docs.tsx
+++ b/site/src/pages/Docs.tsx
@@ -138,6 +138,9 @@ forge script script/Deploy.s.sol:DeployGuardian \\
# local-firewall scorer (RulesEngineV1)
forge script script/DeployRules.s.sol:DeployRules \\
--rpc-url "$ROBINHOOD_TESTNET_RPC" --broadcast --skip-simulation
+# SafeVaultFactory (deterministic per-user vault — gasless onboarding)
+forge script script/DeployFactory.s.sol:DeployFactory \\
+ --rpc-url "$ROBINHOOD_TESTNET_RPC" --broadcast --skip-simulation
# demo set: token + SafeVault + MockAavePool (+ throwaway protocol for local testing)
forge script script/SetupDemo.s.sol:SetupDemo \\
--rpc-url "$ROBINHOOD_TESTNET_RPC" --broadcast --skip-simulation`}
@@ -145,11 +148,13 @@ forge script script/SetupDemo.s.sol:SetupDemo \\
Copy the printed addresses into .env:
GUARDIAN_IMPL,
RULES_ENGINE,
+ VAULT_FACTORY,
DEMO_TOKEN,
DEMO_PROTO,
DEMO_VAULT,
DEMO_AAVE_POOL (and
- VITE_RULES_ENGINE in site/.env).
+ VITE_RULES_ENGINE /
+ VITE_VAULT_FACTORY in site/.env).
3 · Connect and run
@@ -159,13 +164,18 @@ forge script script/SetupDemo.s.sol:SetupDemo \\
))}
- Run pnpm onboard once to connect, then
+ Prefer no CLI? The /app dashboard onboards your
+ connected wallet gasless in one click: you sign an EIP-712 policy and an EIP-7702
+ authorization, and a relayer sponsors the delegate+configure transaction.
+
+
+ Or run pnpm onboard once to connect, then
leave pnpm watch running. The guardian
catches a real on-chain Drained log itself
and evacuates to your vault — no human in the loop:
- {`🦆 COIN COIN ! threat detected → evacuating 0xfa14…3368
-→ evacuated to your SafeVault 0x49be…f479 (EIP-7702 guardian context)
+ {`🦆 COIN COIN ! threat detected → evacuating 0x46d1…80Cc
+→ evacuated to your SafeVault 0x5309…4b91 (EIP-7702 guardian context)
✓ funds safe in your own vault. Daemon still watching.`}
Want to see it react on your own machine? pnpm exploit{" "}
diff --git a/site/tsconfig.api.json b/site/tsconfig.api.json
new file mode 100644
index 0000000..a9842a1
--- /dev/null
+++ b/site/tsconfig.api.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2022"],
+ "types": ["node"],
+ "strict": true,
+ "noEmit": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["api"]
+}
diff --git a/video/src/content.ts b/video/src/content.ts
index 770c3a0..1688b6d 100644
--- a/video/src/content.ts
+++ b/video/src/content.ts
@@ -3,9 +3,10 @@
export const REPO_URL = "github.com/gamween/coincoin";
export const LIVE_URL = "coincoin-five.vercel.app";
-// Real on-chain values from the 2026-06-14 redeploy (used by the demo).
-export const VICTIM_SHORT = "0xfa14…3368";
+// Real on-chain values (used by the demo). Shorts reflect the 2026-06-16 redeploy;
+// TX_EXIT/TX_EVAC are from a representative end-to-end run.
+export const VICTIM_SHORT = "0x46d1…80Cc";
export const TX_EXIT = "0x4fcec0…cfb0";
export const TX_EVAC = "0xd7c0c5…79df";
-export const GUARDIAN_SHORT = "0xd0d3…3b77";
+export const GUARDIAN_SHORT = "0x9953…E8dA";
export const RULES_SHORT = "0xc20A…bc52";
diff --git a/watcher/package.json b/watcher/package.json
index 5c190e6..8e29316 100644
--- a/watcher/package.json
+++ b/watcher/package.json
@@ -6,6 +6,7 @@
"scripts": {
"test": "vitest run",
"onboard": "tsx scripts/onboard.ts",
+ "onboard:gasless": "tsx scripts/onboard-gasless.ts",
"watch": "tsx scripts/watch.ts",
"exploit": "tsx scripts/exploit.ts",
"revoke": "tsx scripts/revoke.ts"
diff --git a/watcher/scripts/onboard-gasless.ts b/watcher/scripts/onboard-gasless.ts
new file mode 100644
index 0000000..33c8e3d
--- /dev/null
+++ b/watcher/scripts/onboard-gasless.ts
@@ -0,0 +1,107 @@
+import { config } from "dotenv";
+config({ path: "../.env" });
+import { createWalletClient, createPublicClient, http, encodeFunctionData, parseAbi } from "viem";
+import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
+import { resolveChainConfig, ORBIT_TX_GAS } from "../src/config";
+
+/// End-to-end proof of the GASLESS onboarding path — the exact flow the browser does, but with a
+/// local key standing in for the wallet's two signatures:
+/// 1) a FRESH EOA (zero balance — it never sends a tx) signs the EIP-712 Policy via the STANDARD
+/// viem signTypedData (identical to a wallet's signTypedData), and signs the 7702 authorization;
+/// 2) the RELAYER pays gas: deploys the user's SafeVault via the factory, then submits ONE type-4
+/// tx (to: user, authorizationList: [auth], data: configureWithSig(...)) — delegating AND
+/// configuring in one sponsored transaction.
+/// If this succeeds, the contract's EIP-712 encoding is wallet-compatible and the relayer flow works.
+const FACTORY_ABI = parseAbi([
+ "function vaultOf(address owner) view returns (address)",
+ "function deploy(address owner) returns (address)",
+ "function isDeployed(address owner) view returns (bool)",
+]);
+const GUARDIAN_ABI = parseAbi([
+ "function configureWithSig((address safeVault, address[] keepers) p, uint256 nonce, uint256 deadline, bytes sig)",
+ "function configured() view returns (bool)",
+ "function safeVault() view returns (address)",
+ "function isKeeper(address) view returns (bool)",
+]);
+
+async function main() {
+ const cfg = resolveChainConfig();
+ const factory = process.env.VAULT_FACTORY as `0x${string}` | undefined;
+ const relayerKey = process.env.RELAYER_PRIVATE_KEY as `0x${string}` | undefined;
+ const keeperKey = process.env.KEEPER_PRIVATE_KEY as `0x${string}` | undefined;
+ if (!factory) throw new Error("gasless: VAULT_FACTORY missing");
+ if (!relayerKey) throw new Error("gasless: RELAYER_PRIVATE_KEY missing");
+ if (!keeperKey) throw new Error("gasless: KEEPER_PRIVATE_KEY missing");
+ const keeper = privateKeyToAccount(keeperKey).address;
+ const relayer = privateKeyToAccount(relayerKey);
+
+ const transport = http(cfg.rpcUrl);
+ const pub = createPublicClient({ chain: cfg.chain, transport });
+
+ // ── 1) the user (a fresh, unfunded EOA — only signs) ──
+ const user = privateKeyToAccount(generatePrivateKey());
+ const userWallet = createWalletClient({ account: user, chain: cfg.chain, transport });
+ const vault = await pub.readContract({ address: factory, abi: FACTORY_ABI, functionName: "vaultOf", args: [user.address] });
+ const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
+ console.log(`[gasless] user ${user.address} (0 gas) · vault ${vault} · keeper ${keeper}`);
+
+ // EIP-712 Policy signature (standard signTypedData — what the wallet will do in-browser)
+ const sig = await userWallet.signTypedData({
+ account: user,
+ domain: { name: "coincoin GuardianModule", version: "1", chainId: cfg.chain.id, verifyingContract: user.address },
+ types: {
+ Policy: [
+ { name: "safeVault", type: "address" },
+ { name: "keepers", type: "address[]" },
+ { name: "nonce", type: "uint256" },
+ { name: "deadline", type: "uint256" },
+ ],
+ },
+ primaryType: "Policy",
+ message: { safeVault: vault, keepers: [keeper], nonce: 0n, deadline },
+ });
+
+ // EIP-7702 authorization (relayer-sponsored → no +1). Explicit current nonce (latest) for
+ // determinism — a wrong nonce is silently skipped, so it must be exact.
+ const authNonce = await pub.getTransactionCount({ address: user.address, blockTag: "latest" });
+ const authorization = await userWallet.signAuthorization({
+ account: user,
+ contractAddress: cfg.guardianImpl,
+ chainId: cfg.chain.id,
+ nonce: authNonce,
+ });
+
+ // ── 2) the relayer pays gas ──
+ const relayerWallet = createWalletClient({ account: relayer, chain: cfg.chain, transport });
+ if (!(await pub.readContract({ address: factory, abi: FACTORY_ABI, functionName: "isDeployed", args: [user.address] }))) {
+ const dtx = await relayerWallet.writeContract({ address: factory, abi: FACTORY_ABI, functionName: "deploy", args: [user.address], gas: ORBIT_TX_GAS });
+ await pub.waitForTransactionReceipt({ hash: dtx });
+ console.log(`[gasless] relayer deployed vault (tx ${dtx})`);
+ }
+ const data = encodeFunctionData({
+ abi: GUARDIAN_ABI,
+ functionName: "configureWithSig",
+ args: [{ safeVault: vault, keepers: [keeper] }, 0n, deadline, sig],
+ });
+ const tx = await relayerWallet.sendTransaction({ to: user.address, data, authorizationList: [authorization], gas: ORBIT_TX_GAS });
+ const rcpt = await pub.waitForTransactionReceipt({ hash: tx });
+ if (rcpt.status !== "success") throw new Error(`[gasless] configure tx reverted: ${tx}`);
+ console.log(`[gasless] relayer submitted delegate+configure (tx ${tx})`);
+
+ // ── verify ──
+ const g = { address: user.address, abi: GUARDIAN_ABI } as const;
+ const [configured, sv, isK] = await Promise.all([
+ pub.readContract({ ...g, functionName: "configured" }),
+ pub.readContract({ ...g, functionName: "safeVault" }),
+ pub.readContract({ ...g, functionName: "isKeeper", args: [keeper] }),
+ ]);
+ const ok = configured === true && sv.toLowerCase() === vault.toLowerCase() && isK === true;
+ console.log(`[gasless] verify → configured=${configured} safeVault=${sv} isKeeper=${isK}`);
+ if (!ok) throw new Error("[gasless] verification failed");
+ console.log("[gasless] ✅ gasless onboarding works end-to-end (user paid 0 gas)");
+}
+
+main().catch((e) => {
+ console.error(e);
+ process.exit(1);
+});