@sherwoodagent/cli 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/{abis-OLIDCWDS.js → abis-ZQ6I3S6L.js} +2 -4
  2. package/dist/{addresses-EF5OQSKV.js → addresses-FGJ4I4IG.js} +3 -3
  3. package/dist/{agent-VLYILEUP.js → agent-QUINV725.js} +7 -7
  4. package/dist/{chat-56DDAGA2.js → chat-5TUHBSIA.js} +9 -9
  5. package/dist/{chunk-QK3FUTVI.js → chunk-27XY2HXV.js} +5 -5
  6. package/dist/{chunk-VJKZDHH3.js → chunk-3TLKNRZV.js} +10 -9
  7. package/dist/chunk-3TLKNRZV.js.map +1 -0
  8. package/dist/{chunk-RFJSV3EZ.js → chunk-4KJUX5SS.js} +5 -5
  9. package/dist/{chunk-K2NEFUEP.js → chunk-54CLRBPB.js} +5 -5
  10. package/dist/{chunk-QSYLZA4Z.js → chunk-6BVA4RIH.js} +2 -2
  11. package/dist/{chunk-MAG63U53.js → chunk-76DLC7CM.js} +4 -4
  12. package/dist/{chunk-BJ4G3TPP.js → chunk-EPQR6PJ4.js} +2 -2
  13. package/dist/{chunk-54WMDJTL.js → chunk-LZEV772I.js} +386 -5
  14. package/dist/chunk-LZEV772I.js.map +1 -0
  15. package/dist/{chunk-2LUTKS24.js → chunk-MC5QEPW3.js} +6 -6
  16. package/dist/{chunk-XW6NDXBA.js → chunk-PMRYC6SF.js} +12 -12
  17. package/dist/{chunk-XW6NDXBA.js.map → chunk-PMRYC6SF.js.map} +1 -1
  18. package/dist/{chunk-XDWYX5ZM.js → chunk-UHGWEJ24.js} +2 -2
  19. package/dist/{chunk-XDGCHURW.js → chunk-VR4VHX4D.js} +3 -3
  20. package/dist/chunk-VR4VHX4D.js.map +1 -0
  21. package/dist/{chunk-TU655ZTV.js → chunk-XSP64L7L.js} +1102 -480
  22. package/dist/chunk-XSP64L7L.js.map +1 -0
  23. package/dist/{chunk-UO4UITOY.js → chunk-Y6XOH23Y.js} +28 -7
  24. package/dist/chunk-Y6XOH23Y.js.map +1 -0
  25. package/dist/{client-25LF2O4P.js → client-VNSFNDF4.js} +3 -3
  26. package/dist/{eas-HHMRO37P.js → eas-PJ3Y4NDB.js} +6 -6
  27. package/dist/{governor-AL2XUGBE.js → governor-4ASVLOCI.js} +6 -6
  28. package/dist/index.js +4857 -4569
  29. package/dist/index.js.map +1 -1
  30. package/dist/{ipfs-RWSTXTZM.js → ipfs-EJ7IHXCU.js} +2 -2
  31. package/dist/{network-R45QDUYO.js → network-RI54LT2Y.js} +2 -2
  32. package/dist/{price-QO22NF5Q.js → price-JX2LFKFW.js} +6 -6
  33. package/dist/{providers-ALMXBRNW.js → providers-F6MNPMR4.js} +4 -4
  34. package/dist/{research-ICDDAAGV.js → research-5ZJTRILJ.js} +8 -8
  35. package/dist/{session-RSWSCMM5.js → session-AXLPYJHX.js} +11 -11
  36. package/dist/{trade-YNQVWUYZ.js → trade-BZM7K2F4.js} +15 -15
  37. package/dist/{vault-QKURBBSL.js → vault-AWQBFD4C.js} +9 -9
  38. package/dist/{xmtp-M2OBCCNR.js → xmtp-M74BRF3G.js} +6 -6
  39. package/package.json +1 -1
  40. package/dist/chunk-54WMDJTL.js.map +0 -1
  41. package/dist/chunk-TU655ZTV.js.map +0 -1
  42. package/dist/chunk-UO4UITOY.js.map +0 -1
  43. package/dist/chunk-VJKZDHH3.js.map +0 -1
  44. package/dist/chunk-XDGCHURW.js.map +0 -1
  45. /package/dist/{abis-OLIDCWDS.js.map → abis-ZQ6I3S6L.js.map} +0 -0
  46. /package/dist/{addresses-EF5OQSKV.js.map → addresses-FGJ4I4IG.js.map} +0 -0
  47. /package/dist/{agent-VLYILEUP.js.map → agent-QUINV725.js.map} +0 -0
  48. /package/dist/{chat-56DDAGA2.js.map → chat-5TUHBSIA.js.map} +0 -0
  49. /package/dist/{chunk-QK3FUTVI.js.map → chunk-27XY2HXV.js.map} +0 -0
  50. /package/dist/{chunk-RFJSV3EZ.js.map → chunk-4KJUX5SS.js.map} +0 -0
  51. /package/dist/{chunk-K2NEFUEP.js.map → chunk-54CLRBPB.js.map} +0 -0
  52. /package/dist/{chunk-QSYLZA4Z.js.map → chunk-6BVA4RIH.js.map} +0 -0
  53. /package/dist/{chunk-MAG63U53.js.map → chunk-76DLC7CM.js.map} +0 -0
  54. /package/dist/{chunk-BJ4G3TPP.js.map → chunk-EPQR6PJ4.js.map} +0 -0
  55. /package/dist/{chunk-2LUTKS24.js.map → chunk-MC5QEPW3.js.map} +0 -0
  56. /package/dist/{chunk-XDWYX5ZM.js.map → chunk-UHGWEJ24.js.map} +0 -0
  57. /package/dist/{client-25LF2O4P.js.map → client-VNSFNDF4.js.map} +0 -0
  58. /package/dist/{eas-HHMRO37P.js.map → eas-PJ3Y4NDB.js.map} +0 -0
  59. /package/dist/{governor-AL2XUGBE.js.map → governor-4ASVLOCI.js.map} +0 -0
  60. /package/dist/{ipfs-RWSTXTZM.js.map → ipfs-EJ7IHXCU.js.map} +0 -0
  61. /package/dist/{network-R45QDUYO.js.map → network-RI54LT2Y.js.map} +0 -0
  62. /package/dist/{price-QO22NF5Q.js.map → price-JX2LFKFW.js.map} +0 -0
  63. /package/dist/{providers-ALMXBRNW.js.map → providers-F6MNPMR4.js.map} +0 -0
  64. /package/dist/{research-ICDDAAGV.js.map → research-5ZJTRILJ.js.map} +0 -0
  65. /package/dist/{session-RSWSCMM5.js.map → session-AXLPYJHX.js.map} +0 -0
  66. /package/dist/{trade-YNQVWUYZ.js.map → trade-BZM7K2F4.js.map} +0 -0
  67. /package/dist/{vault-QKURBBSL.js.map → vault-AWQBFD4C.js.map} +0 -0
  68. /package/dist/{xmtp-M2OBCCNR.js.map → xmtp-M74BRF3G.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/governor.ts","../src/lib/generated/proposal-state.ts"],"sourcesContent":["/**\n * Governor contract wrapper — reads/writes to SyndicateGovernor.\n *\n * Per-vault governor design: the factory deploys ONE governor per vault\n * (BeaconProxy). Every helper takes the vault address and resolves its\n * governor via `factory.governorOf(vault)` (memoized per chain+vault —\n * the binding is set once at createSyndicate and never changes).\n *\n * Protocol-wide fee parameters (protocol/guardian fee bps + recipients)\n * live on the separate ProtocolConfig contract, resolved via\n * `factory.protocolConfig()`.\n */\n\nimport type { Address, Hex, Log } from \"viem\";\nimport { parseEventLogs } from \"viem\";\nimport { getPublicClient, getAccount, writeContractWithRetry, waitForReceipt } from \"./client.js\";\nimport { getChain } from \"./network.js\";\nimport { SHERWOOD } from \"./addresses.js\";\nimport { SYNDICATE_GOVERNOR_ABI, SYNDICATE_FACTORY_ABI, PROTOCOL_CONFIG_ABI } from \"./abis.js\";\nimport { PROPOSAL_STATE, PROPOSAL_STATES } from \"./generated/proposal-state.js\";\n\n// ── Types ──\n\nexport interface GovernorParams {\n votingPeriod: bigint;\n executionWindow: bigint;\n vetoThresholdBps: bigint;\n maxPerformanceFeeBps: bigint;\n cooldownPeriod: bigint;\n collaborationWindow: bigint;\n maxCoProposers: bigint;\n minStrategyDuration: bigint;\n maxStrategyDuration: bigint;\n}\n\nexport interface StrategyProposal {\n id: bigint;\n proposer: Address;\n vault: Address;\n /** Strategy contract address whose `positionValue()` the vault polls\n * for live NAV. Set at propose time, immutable thereafter. address(0)\n * for queue-only proposals (no live NAV). */\n strategy: Address;\n metadataURI: string;\n /** Agent fee (bps) snapshotted from the vault's `agentFeeBps` at propose. */\n performanceFeeBps: bigint;\n strategyDuration: bigint;\n votesFor: bigint;\n votesAgainst: bigint;\n votesAbstain: bigint;\n snapshotTimestamp: bigint;\n voteEnd: bigint;\n reviewEnd: bigint;\n executeBy: bigint;\n executedAt: bigint;\n state: number;\n vetoThresholdBps: bigint;\n /** Fee snapshot taken from ProtocolConfig at propose time (PR #421). These\n * four fields sit between `vetoThresholdBps` and `selfManagesFees` in the\n * on-chain `StrategyProposal` struct — omitting them corrupts tuple decode. */\n snapshotProtocolFeeBps: bigint;\n snapshotProtocolFeeRecipient: Address;\n snapshotGuardianFeeBps: bigint;\n snapshotGuardiansFeeRecipient: Address;\n /** `IStrategy.selfManagesFees()` snapshotted at propose (PR #388 #2); read\n * from storage at settle so it can't flip mid-flight or brick settlement. */\n selfManagesFees: boolean;\n}\n\nexport interface CoProposer {\n agent: Address;\n splitBps: bigint;\n}\n\nexport interface BatchCall {\n target: Address;\n data: Hex;\n value: bigint;\n}\n\n// ProposalState ordering is code-generated from the Solidity enum\n// (contracts/src/interfaces/ISyndicateGovernor.sol) — `npm run codegen`.\n// History: PR #229 inserted GuardianReview at index 2 and this hand-kept\n// mirror drifted for ~2 months, mislabeling Approved (3) as Rejected and\n// making `proposal execute` reject valid approved proposals. The generated\n// module (checked by codegen-drift.test.ts + CI) makes that impossible.\nexport { PROPOSAL_STATE, PROPOSAL_STATES };\n\nexport const VOTE_TYPE = {\n For: 0,\n Against: 1,\n Abstain: 2,\n} as const;\n\n// ── Client-side action gates (mirror SyndicateGovernor) ──\n//\n// The governor's mutating entrypoints resolve state INLINE and can act on a\n// state the read-only `getProposalState` view is still reporting as\n// GuardianReview. The client gates below reproduce that divergence so the CLI\n// stops refusing txs the contract would accept (issue #452).\n\n/** Result of the `proposal execute` client gate. */\nexport type ExecuteGate =\n | { kind: \"send\" } // state == Approved — straightforward execute.\n | { kind: \"send-self-resolve\" } // GuardianReview, window elapsed, still within\n // the execution window — executeProposal\n // self-resolves the (cold-start) review inline.\n | { kind: \"refuse-review-open\" } // GuardianReview, review window still open — the\n // tx WOULD revert (ProposalNotApproved).\n | { kind: \"refuse-expired\" } // GuardianReview past executeBy — the inline\n // self-resolve maps to Expired, tx WOULD revert.\n | { kind: \"refuse-state\" }; // any other non-executable state.\n\n/**\n * Decide whether `executeProposal(id)` should be broadcast.\n *\n * Mirrors `SyndicateGovernor.executeProposal` (contracts/src/SyndicateGovernor.sol:342)\n * vs. the `getProposalState` view:\n * - The view (`_resolveStateView` @824 / `_resolveAfterVote` @865) keeps\n * reporting `GuardianReview` once the review window elapses but the registry\n * has not cached a resolution — a zero-guardian / never-opened cold-start\n * review NEVER auto-opens, so the view is stuck there indefinitely.\n * - `executeProposal` runs the MUTATING `_resolveState` (@789) which, when the\n * window elapsed (`now > reviewEnd`), calls `registry.resolveReview` inline\n * (cold-start ⇒ blocked=false) and maps to Approved when `now <= executeBy`\n * (else Expired) — then executes in the same tx.\n * Invariant `voteEnd < reviewEnd < executeBy` (@528-530) holds for any past-vote\n * proposal, so `reviewEnd`/`executeBy` are always populated in the GuardianReview\n * branch.\n *\n * `now` MUST be chain time (latest block timestamp), not wall-clock — on a\n * time-warped vnet the two diverge and the contract compares against chain time.\n */\nexport function evaluateExecuteGate(args: {\n state: number;\n reviewEnd: bigint;\n executeBy: bigint;\n now: bigint;\n}): ExecuteGate {\n const { state, reviewEnd, executeBy, now } = args;\n if (state === PROPOSAL_STATE.Approved) return { kind: \"send\" };\n if (state === PROPOSAL_STATE.GuardianReview) {\n if (now <= reviewEnd) return { kind: \"refuse-review-open\" };\n if (now > executeBy) return { kind: \"refuse-expired\" };\n return { kind: \"send-self-resolve\" };\n }\n return { kind: \"refuse-state\" };\n}\n\n/**\n * Classification for the `resolve-reviews` keeper.\n * - `resolvable`: an OPENED, unresolved review past its window — needs a real\n * `resolveReview` tx (computes block quorum, may slash approvers).\n * - `cold-start`: an UNOPENED, unresolved review past its window — the\n * zero-guardian case. `resolveReview` would only mark it resolved/not-blocked\n * (GuardianRegistry.resolveReview @639-643), which `executeProposal`\n * already does inline, so the keeper should report it (pointing at\n * `proposal execute`) rather than spend a redundant tx.\n * - `none`: not past window yet, or already resolved.\n */\nexport type ReviewResolveClass = \"resolvable\" | \"cold-start\" | \"none\";\n\nexport function classifyReview(args: {\n opened: boolean;\n resolved: boolean;\n reviewEnd: bigint;\n now: bigint;\n}): ReviewResolveClass {\n const { opened, resolved, reviewEnd, now } = args;\n const pastWindow = reviewEnd > 0n && now >= reviewEnd;\n if (!pastWindow || resolved) return \"none\";\n return opened ? \"resolvable\" : \"cold-start\";\n}\n\n// ── Duration parser ──\n\nexport function parseDuration(input: string): bigint {\n const match = input.match(/^(\\d+)(d|h|m|s)?$/);\n if (!match) throw new Error(`Invalid duration: ${input}`);\n const value = BigInt(match[1]);\n switch (match[2]) {\n case \"d\":\n return value * 86400n;\n case \"h\":\n return value * 3600n;\n case \"m\":\n return value * 60n;\n case \"s\":\n case undefined:\n return value;\n default:\n throw new Error(`Unknown duration unit: ${match[2]}`);\n }\n}\n\n// ── Address helpers ──\n\n// governorOf(vault) is set once at createSyndicate and never rebinds, so\n// it is safe to memoize per chain+vault for the lifetime of the process.\nconst governorCache = new Map<string, Address>();\n\n/** Resolve the per-vault governor via `factory.governorOf(vault)`. */\nexport async function getGovernorOf(vault: Address): Promise<Address> {\n const key = `${getChain().id}:${vault.toLowerCase()}`;\n const cached = governorCache.get(key);\n if (cached) return cached;\n\n const client = getPublicClient();\n const governor = await client.readContract({\n address: SHERWOOD().FACTORY,\n abi: SYNDICATE_FACTORY_ABI,\n functionName: \"governorOf\",\n args: [vault],\n }) as Address;\n\n if (governor === \"0x0000000000000000000000000000000000000000\") {\n throw new Error(`No governor registered for vault ${vault} — is it a factory-created vault?`);\n }\n\n governorCache.set(key, governor);\n return governor;\n}\n\n// ProtocolConfig is a protocol-wide singleton behind the factory — same\n// set-once semantics as governorOf, memoized per chain.\nconst protocolConfigCache = new Map<number, Address>();\n\n/** Resolve the ProtocolConfig address via `factory.protocolConfig()`. */\nexport async function getProtocolConfigAddress(): Promise<Address> {\n const chainId = getChain().id;\n const cached = protocolConfigCache.get(chainId);\n if (cached) return cached;\n\n const client = getPublicClient();\n const config = await client.readContract({\n address: SHERWOOD().FACTORY,\n abi: SYNDICATE_FACTORY_ABI,\n functionName: \"protocolConfig\",\n }) as Address;\n\n protocolConfigCache.set(chainId, config);\n return config;\n}\n\n// ── Read helpers ──\n\nexport async function getGovernorParams(vault: Address): Promise<GovernorParams> {\n const client = getPublicClient();\n const result = await client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getGovernorParams\",\n }) as {\n votingPeriod: bigint;\n executionWindow: bigint;\n vetoThresholdBps: bigint;\n maxPerformanceFeeBps: bigint;\n cooldownPeriod: bigint;\n collaborationWindow: bigint;\n maxCoProposers: bigint;\n minStrategyDuration: bigint;\n maxStrategyDuration: bigint;\n };\n return result;\n}\n\nexport async function getProposal(vault: Address, id: bigint): Promise<StrategyProposal> {\n const client = getPublicClient();\n const result = await client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getProposal\",\n args: [id],\n }) as StrategyProposal;\n return result;\n}\n\nexport async function getProposalState(vault: Address, id: bigint): Promise<number> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getProposalState\",\n args: [id],\n }) as Promise<number>;\n}\n\nexport async function proposalCount(vault: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"proposalCount\",\n }) as Promise<bigint>;\n}\n\nexport async function getVoteWeight(vault: Address, proposalId: bigint, voter: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getVoteWeight\",\n args: [proposalId, voter],\n }) as Promise<bigint>;\n}\n\nexport async function hasVoted(vault: Address, proposalId: bigint, voter: Address): Promise<boolean> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"hasVoted\",\n args: [proposalId, voter],\n }) as Promise<boolean>;\n}\n\n// `getProposalCalls` (concat of execute + settle) was dropped in V1.5\n// to reclaim governor bytecode under EIP-170. Use `getExecuteCalls`\n// + `getSettlementCalls` instead — same data, no on-chain copy loop.\n\nexport async function getExecuteCalls(vault: Address, proposalId: bigint): Promise<BatchCall[]> {\n const client = getPublicClient();\n const result = await client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getExecuteCalls\",\n args: [proposalId],\n }) as { target: Address; data: Hex; value: bigint }[];\n return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));\n}\n\nexport async function getSettlementCalls(vault: Address, proposalId: bigint): Promise<BatchCall[]> {\n const client = getPublicClient();\n const result = await client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getSettlementCalls\",\n args: [proposalId],\n }) as { target: Address; data: Hex; value: bigint }[];\n return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));\n}\n\n// Per-vault governor: `getRegisteredVaults` / `isRegisteredVault` were\n// removed on-chain — a governor serves exactly one vault (`governor.vault()`).\n// Enumerate vaults via the factory (`getAllActiveSyndicates`) instead.\n\nexport async function getActiveProposal(vault: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getActiveProposal\",\n }) as Promise<bigint>;\n}\n\nexport async function getCooldownEnd(vault: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getCooldownEnd\",\n }) as Promise<bigint>;\n}\n\nexport async function openProposalCount(vault: Address): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"openProposalCount\",\n }) as Promise<bigint>;\n}\n\nexport async function getCapitalSnapshot(vault: Address, proposalId: bigint): Promise<bigint> {\n const client = getPublicClient();\n return client.readContract({\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"getCapitalSnapshot\",\n args: [proposalId],\n }) as Promise<bigint>;\n}\n\n/**\n * Extract proposalId from ProposalCreated event logs.\n * Returns undefined if the event is not found or parsing fails.\n */\nexport function parseProposalIdFromLogs(logs: Log[]): bigint | undefined {\n try {\n const parsed = parseEventLogs({\n abi: SYNDICATE_GOVERNOR_ABI,\n logs,\n eventName: \"ProposalCreated\",\n });\n return parsed[0]?.args.proposalId;\n } catch {\n return undefined;\n }\n}\n\n// ── Write helpers ──\n\nexport async function propose(\n vault: Address,\n strategy: Address,\n metadataURI: string,\n strategyDuration: bigint,\n executeCalls: BatchCall[],\n settlementCalls: BatchCall[],\n coProposers: CoProposer[] = [],\n): Promise<{ hash: Hex; proposalId: bigint }> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"propose\",\n args: [vault, strategy, metadataURI, strategyDuration, executeCalls, settlementCalls, coProposers],\n });\n\n const receipt = await waitForReceipt(hash);\n\n // Parse proposalId from the ProposalCreated event in the receipt logs.\n // Falls back to proposalCount() if event parsing fails.\n let proposalId = parseProposalIdFromLogs(receipt.logs);\n if (proposalId === undefined) {\n try {\n proposalId = await proposalCount(vault);\n } catch {\n proposalId = 0n;\n }\n }\n\n return { hash: receipt.transactionHash, proposalId };\n}\n\nexport async function vote(vault: Address, proposalId: bigint, support: number): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"vote\",\n args: [proposalId, support],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\nexport async function executeProposal(vault: Address, proposalId: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"executeProposal\",\n args: [proposalId],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\n/**\n * Walk a proposal's executeCalls and pick the strategy clone — the\n * target of the call whose selector matches `BaseStrategy.execute()`\n * (0x61461954). This is the canonical entry point that every Sherwood\n * strategy template inherits from BaseStrategy, so it's a reliable\n * marker even across template families (Moonwell / Aerodrome /\n * Hyperliquid / Mamo / Venice / Portfolio).\n *\n * Returns null when no execute() call is present (e.g. proposals that\n * only do approves and direct ERC-20 transfers without a strategy).\n */\nexport function detectStrategyAdapter(\n calls: { target: Address; data: Hex; value: bigint }[],\n): Address | null {\n const STRATEGY_EXECUTE_SELECTOR = \"0x61461954\";\n for (const call of calls) {\n if (call.data.slice(0, 10).toLowerCase() === STRATEGY_EXECUTE_SELECTOR) {\n return call.target;\n }\n }\n return null;\n}\n\nexport async function settleProposal(vault: Address, proposalId: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"settleProposal\",\n args: [proposalId],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\nexport async function emergencySettle(vault: Address, proposalId: bigint, calls: BatchCall[]): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"emergencySettleWithCalls\",\n args: [proposalId, calls],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\nexport async function cancelProposal(vault: Address, proposalId: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"cancelProposal\",\n args: [proposalId],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\nexport async function emergencyCancel(vault: Address, proposalId: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"emergencyCancel\",\n args: [proposalId],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\n// ── Parameter setters ──\n//\n// Per-vault governor: governance parameters are per-vault now, set by the\n// VAULT OWNER on that vault's governor. Setters revert with\n// `ParamsFrozenDuringProposal` while any proposal is open on the vault.\n\ntype GovernorParamSetter =\n | \"setVotingPeriod\"\n | \"setExecutionWindow\"\n | \"setVetoThresholdBps\"\n | \"setMaxPerformanceFeeBps\"\n | \"setMaxStrategyDuration\"\n | \"setCooldownPeriod\";\n\nasync function setGovernorParam(\n vault: Address,\n functionName: GovernorParamSetter,\n value: bigint,\n): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName,\n args: [value],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\nexport async function setVotingPeriod(vault: Address, seconds: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setVotingPeriod\", seconds);\n}\n\nexport async function setExecutionWindow(vault: Address, seconds: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setExecutionWindow\", seconds);\n}\n\nexport async function setVetoThresholdBps(vault: Address, bps: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setVetoThresholdBps\", bps);\n}\n\nexport async function setMaxPerformanceFeeBps(vault: Address, bps: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setMaxPerformanceFeeBps\", bps);\n}\n\nexport async function setMaxStrategyDuration(vault: Address, seconds: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setMaxStrategyDuration\", seconds);\n}\n\nexport async function setCooldownPeriod(vault: Address, seconds: bigint): Promise<Hex> {\n return setGovernorParam(vault, \"setCooldownPeriod\", seconds);\n}\n\nexport async function vetoProposal(vault: Address, proposalId: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getGovernorOf(vault),\n abi: SYNDICATE_GOVERNOR_ABI,\n functionName: \"vetoProposal\",\n args: [proposalId],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n\n// ── ProtocolConfig fee setters (protocol-owner only) ──\n//\n// Fee parameters moved off the governor onto the protocol-wide\n// ProtocolConfig contract (resolved via `factory.protocolConfig()`).\n\nexport async function setProtocolFeeBps(bps: bigint): Promise<Hex> {\n const hash = await writeContractWithRetry({\n account: getAccount(),\n chain: getChain(),\n address: await getProtocolConfigAddress(),\n abi: PROTOCOL_CONFIG_ABI,\n functionName: \"setProtocolFeeBps\",\n args: [bps],\n });\n const receipt = await waitForReceipt(hash);\n return receipt.transactionHash;\n}\n","/**\n * AUTO-GENERATED by cli/scripts/codegen-contracts.ts — do not edit.\n * Regenerate: npm run codegen (in cli/)\n *\n * Source of truth: sherwood-protocol src/interfaces/ISyndicateGovernor.sol (enum ProposalState)\n */\n\n/** On-chain `ProposalState` member names, index === Solidity enum value. */\nexport const PROPOSAL_STATES = [\n \"Draft\",\n \"Pending\",\n \"GuardianReview\",\n \"Approved\",\n \"Rejected\",\n \"Expired\",\n \"Executed\",\n \"Settled\",\n \"Cancelled\",\n] as const;\n\nexport type ProposalStateName = (typeof PROPOSAL_STATES)[number];\n\n/** Name → Solidity enum value. */\nexport const PROPOSAL_STATE = {\n Draft: 0,\n Pending: 1,\n GuardianReview: 2,\n Approved: 3,\n Rejected: 4,\n Expired: 5,\n Executed: 6,\n Settled: 7,\n Cancelled: 8,\n} as const;\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/network.ts"],"sourcesContent":["/**\n * Network state singleton.\n *\n * Called once at CLI startup via the --chain flag's preAction hook.\n * Every other module reads from here — never hardcodes a chain.\n *\n * Current deployment target: Robinhood L2 testnet (chain 46630) only. Sherwood\n * launches on Robinhood first; other chains may be added back later, at which\n * point they re-enter the Network union + CHAIN_REGISTRY below.\n */\n\nimport { type Chain, defineChain, parseEther } from \"viem\";\nimport { getConfigRpcUrl } from \"./config.js\";\n\n// ── Robinhood L2 Testnet (Arbitrum Orbit, chain ID 46630) ──\n\nexport const robinhoodTestnet = defineChain({\n id: 46630,\n name: \"Robinhood Chain Testnet\",\n nativeCurrency: { name: \"Ethereum\", symbol: \"ETH\", decimals: 18 },\n rpcUrls: {\n default: { http: [\"https://rpc.testnet.chain.robinhood.com\"] },\n },\n blockExplorers: {\n default: {\n name: \"Blockscout\",\n url: \"https://explorer.testnet.chain.robinhood.com\",\n },\n },\n testnet: true,\n});\n\n// ── Robinhood Chain mainnet fork (Tenderly Virtual TestNet, chain ID 9994663) ──\n//\n// A fork of Robinhood Chain *mainnet* (USDG stablecoin, official Uniswap v3+v4,\n// Chainlink push feeds, tokenized-stock liquidity). Used for mainnet-faithful\n// e2e + guardian-network simulations. Ephemeral: Tenderly vnets expire — the RPC\n// is overridable via ROBINHOOD_FORK_RPC_URL when a new vnet is minted.\n\nexport const robinhoodFork = defineChain({\n id: 9994663,\n name: \"Robinhood Chain (fork)\",\n nativeCurrency: { name: \"Ethereum\", symbol: \"ETH\", decimals: 18 },\n rpcUrls: {\n default: {\n http: [\n \"https://virtual.robinhood-chain.eu.rpc.tenderly.co/moonwell/wormhole-bridge/f4e186\",\n ],\n },\n },\n blockExplorers: {\n default: {\n name: \"Tenderly\",\n url: \"https://dashboard.tenderly.co\",\n },\n },\n testnet: true,\n});\n\n// ── Types ──\n\nexport type Network = \"robinhood-testnet\" | \"robinhood-fork\";\n\nexport interface ChainConfig {\n chain: Chain;\n /**\n * Ordered list of public RPC URLs tried in sequence when no user RPC is set.\n * First entry is the primary; the rest are fallbacks used transparently via\n * viem's `fallback` transport on rate-limit / network errors.\n */\n rpcFallbacks: string[];\n /** Legacy env var for backwards compat (optional per chain) */\n rpcEnvVar?: string;\n explorerHost: string;\n /** EAS GraphQL endpoint — null if EAS is not available on this chain */\n easGraphqlUrl: string | null;\n /** EAS scan host — null if unavailable */\n easScanHost: string | null;\n xmtpEnv: \"production\" | \"dev\";\n isTestnet: boolean;\n /**\n * ETH held back for gas in the native-ETH-wrap deposit path — the floor and\n * fallback for the live per-tx cushion estimate (`estimateGasCushion` in\n * client.ts), NOT a fixed requirement. High-fee chains (Base) want a\n * Base-scale reserve; near-zero-fee chains (Robinhood testnet, ~14-wei base\n * fee) want a tiny one so a valid deposit isn't rejected client-side. Unset →\n * `DEFAULT_MIN_GAS_RESERVE`.\n */\n minGasReserve?: bigint;\n}\n\n/**\n * Base-scale default reserve, used when a chain omits `minGasReserve`. Matches\n * the old hardcoded 0.0005-ETH cushion so higher-fee chains keep prior behavior.\n */\nexport const DEFAULT_MIN_GAS_RESERVE = parseEther(\"0.0005\");\n\n// ── Chain Registry ──\n\nexport const CHAIN_REGISTRY: Record<Network, ChainConfig> = {\n \"robinhood-testnet\": {\n chain: robinhoodTestnet,\n rpcFallbacks: [\"https://rpc.testnet.chain.robinhood.com\"],\n rpcEnvVar: \"ROBINHOOD_TESTNET_RPC_URL\",\n explorerHost: \"explorer.testnet.chain.robinhood.com\",\n easGraphqlUrl: null,\n easScanHost: null,\n xmtpEnv: \"production\",\n isTestnet: true,\n // ~14-wei base fee: a full wrap+approve+deposit sequence costs a few\n // million wei (~1e-11 ETH). 0.00002 ETH is a generous floor that still\n // leaves a 0.00058-ETH wallet room to wrap 0.0005 and deposit.\n minGasReserve: parseEther(\"0.00002\"),\n },\n \"robinhood-fork\": {\n chain: robinhoodFork,\n rpcFallbacks: [\n \"https://virtual.robinhood-chain.eu.rpc.tenderly.co/moonwell/wormhole-bridge/f4e186\",\n ],\n rpcEnvVar: \"ROBINHOOD_FORK_RPC_URL\",\n explorerHost: \"dashboard.tenderly.co\",\n easGraphqlUrl: null,\n easScanHost: null,\n xmtpEnv: \"production\",\n isTestnet: true,\n // Robinhood-mainnet-fork gas (~0.17 gwei) is higher than testnet's ~14 wei —\n // keep a modest reserve so the ETH-wrap deposit path isn't rejected.\n minGasReserve: parseEther(\"0.0005\"),\n },\n};\n\nexport const VALID_NETWORKS = Object.keys(CHAIN_REGISTRY) as Network[];\n\nexport const DEFAULT_NETWORK: Network = \"robinhood-testnet\";\n\n// ── Singleton state ──\n\nlet _network: Network = DEFAULT_NETWORK;\n\nexport function setNetwork(n: Network) {\n const config = CHAIN_REGISTRY[n];\n if (!config) {\n throw new Error(\n `Unknown network: ${n}. Valid: ${VALID_NETWORKS.join(\", \")}`,\n );\n }\n _network = n;\n}\n\nexport function getNetwork(): Network {\n return _network;\n}\n\n/**\n * Run `fn` with the network singleton temporarily switched to `network`,\n * restoring the previous network in `finally`.\n *\n * The viem clients (`getPublicClient`, `getWalletClient`) auto-invalidate on\n * chain change, so callers get fresh per-chain RPC + chain context inside the\n * block. Used for cross-chain fund resolution.\n */\nexport async function withNetwork<T>(\n network: Network,\n fn: () => Promise<T>,\n): Promise<T> {\n const previous = _network;\n _network = network;\n try {\n return await fn();\n } finally {\n _network = previous;\n }\n}\n\nexport function getChainConfig(): ChainConfig {\n return CHAIN_REGISTRY[_network];\n}\n\n/**\n * Per-chain ETH gas reserve floor/fallback for the ETH-wrap deposit path.\n * Falls back to the Base-scale default when the active chain doesn't set one.\n */\nexport function getMinGasReserve(): bigint {\n return CHAIN_REGISTRY[_network].minGasReserve ?? DEFAULT_MIN_GAS_RESERVE;\n}\n\n/**\n * Dev/CI-only chain-id override (`SIM_CHAIN_ID`) for custom-chainId vnet forks.\n * viem asserts the node's chainId matches the chain definition on every write,\n * so signing for such a fork requires the id to be swapped at the source.\n *\n * Interlock: honored ONLY while `SIM_ADDRESSES_FILE` is also set (the vnet\n * e2e harness's address override). A bare SIM_CHAIN_ID does nothing, so this\n * can never redirect signing on a real network run.\n */\nexport function applyChainIdOverride(chain: Chain): Chain {\n const raw = process.env.SIM_CHAIN_ID;\n if (!raw || !process.env.SIM_ADDRESSES_FILE) return chain;\n const id = Number(raw);\n if (!Number.isInteger(id) || id <= 0) return chain;\n return { ...chain, id };\n}\n\nexport function getChain(): Chain {\n return applyChainIdOverride(CHAIN_REGISTRY[_network].chain);\n}\n\n/**\n * Ordered list of RPC URLs to try, deduped:\n * 1. User config (~/.sherwood/config.json) — honored first\n * 2. Legacy env var (ROBINHOOD_TESTNET_RPC_URL)\n * 3. Public fallback list from CHAIN_REGISTRY\n *\n * Fed to viem's `fallback()` transport so rate-limited RPCs transparently\n * advance to the next entry. See issue #182.\n */\nexport function getRpcUrls(): string[] {\n const cfg = CHAIN_REGISTRY[_network];\n const urls: string[] = [];\n\n const fromConfig = getConfigRpcUrl(_network);\n if (fromConfig) urls.push(fromConfig);\n\n if (cfg.rpcEnvVar && process.env[cfg.rpcEnvVar]) {\n urls.push(process.env[cfg.rpcEnvVar]!);\n }\n\n // Vnet harness (SIM_CHAIN_ID + SIM_ADDRESSES_FILE interlock): the fork RPC\n // is the ONLY valid endpoint — the public fallbacks point at the real\n // network. viem's fallback() otherwise rotates there on a vnet hiccup and\n // silently estimates gas/fees against MAINNET state (live finding: an\n // approve estimated at 21.9k gas on mainnet — no code at that address —\n // reverted out-of-gas on the vnet where it needs ~46k). A vnet call must\n // fail loudly, never retarget the real chain.\n if (process.env.SIM_CHAIN_ID && process.env.SIM_ADDRESSES_FILE && urls.length > 0) {\n return Array.from(new Set(urls));\n }\n\n urls.push(...cfg.rpcFallbacks);\n\n return Array.from(new Set(urls));\n}\n\nexport function getRpcUrl(): string {\n return getRpcUrls()[0];\n}\n\nexport function getExplorerUrl(txHash: string): string {\n return `https://${CHAIN_REGISTRY[_network].explorerHost}/tx/${txHash}`;\n}\n\nexport function isTestnet(): boolean {\n return CHAIN_REGISTRY[_network].isTestnet;\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/ipfs.ts"],"sourcesContent":["/**\n * IPFS metadata upload/fetch via Sherwood API.\n *\n * Uploads go through the server-side API at sherwood.sh/api/ipfs/upload\n * which holds the Pinata JWT. CLI and app only need the gateway URL for reads.\n */\n\nexport interface FundMetadata {\n schema: string;\n name: string;\n description: string;\n logo?: string;\n chain: string;\n xmtpGroupId?: string;\n strategies: {\n id: string;\n name: string;\n description: string;\n protocols: string[];\n riskLevel: string;\n }[];\n terms: {\n minDeposit?: string;\n minDepositFormatted?: string;\n feeModel?: string;\n lockPeriod?: number;\n };\n links: {\n moltbook?: string;\n dashboard?: string;\n github?: string;\n };\n}\n\nconst DEFAULT_PINATA_GATEWAY = \"https://sherwood.mypinata.cloud\";\n// www is canonical — the apex 307-redirects to it (an extra hop that has been\n// observed slow/flaky on cold starts; vnet e2e finding).\nconst DEFAULT_UPLOAD_API = \"https://www.sherwood.sh/api/ipfs/upload\";\n\nfunction getUploadApiUrl(): string {\n return process.env.SHERWOOD_API_URL\n ? `${process.env.SHERWOOD_API_URL}/api/ipfs/upload`\n : DEFAULT_UPLOAD_API;\n}\n\nfunction getPinataGateway(): string {\n return process.env.PINATA_GATEWAY || DEFAULT_PINATA_GATEWAY;\n}\n\n/**\n * Upload JSON to IPFS via the Sherwood API (server-side Pinata).\n * Returns the IPFS URI (ipfs://Qm...).\n */\nasync function uploadToIPFS(content: Record<string, unknown>, name: string): Promise<string> {\n const url = getUploadApiUrl();\n\n // Transient \"fetch failed\" / cold-start 5xx from the pin API shouldn't kill\n // a propose flow — retry briefly before surfacing (vnet e2e finding).\n const attempts = 3;\n let lastErr: unknown;\n for (let i = 0; i < attempts; i++) {\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content, name }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n // 4xx won't improve on retry; 5xx might.\n if (response.status < 500) throw new Error(`IPFS upload failed (${response.status}): ${text}`);\n lastErr = new Error(`IPFS upload failed (${response.status}): ${text}`);\n } else {\n const result = (await response.json()) as { ipfsHash: string };\n return `ipfs://${result.ipfsHash}`;\n }\n } catch (err) {\n if (err instanceof Error && err.message.startsWith(\"IPFS upload failed (4\")) throw err;\n lastErr = err;\n }\n if (i < attempts - 1) await new Promise((r) => setTimeout(r, 2000 * (i + 1)));\n }\n throw lastErr instanceof Error ? lastErr : new Error(`IPFS upload failed: ${String(lastErr)}`);\n}\n\n/**\n * Pin arbitrary JSON to IPFS.\n * Used for research results and other generic JSON payloads.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function pinJSON(content: Record<string, unknown>, name: string): Promise<string> {\n return uploadToIPFS(content, name);\n}\n\n/**\n * Upload fund metadata to IPFS.\n * Returns the IPFS URI (ipfs://Qm...).\n */\nexport async function uploadMetadata(metadata: FundMetadata): Promise<string> {\n const name = `sherwood-fund-${metadata.name.toLowerCase().replace(/\\s+/g, \"-\")}`;\n return uploadToIPFS(metadata as unknown as Record<string, unknown>, name);\n}\n\n/**\n * Fetch and parse metadata from an IPFS URI.\n * Supports ipfs:// protocol URIs and raw CIDs.\n */\nexport async function fetchMetadata(ipfsURI: string): Promise<FundMetadata> {\n const gateway = getPinataGateway();\n let cid: string;\n\n if (ipfsURI.startsWith(\"ipfs://\")) {\n cid = ipfsURI.slice(7);\n } else if (ipfsURI.startsWith(\"Qm\") || ipfsURI.startsWith(\"bafy\")) {\n cid = ipfsURI;\n } else {\n throw new Error(`Invalid IPFS URI: ${ipfsURI}`);\n }\n\n const url = `${gateway}/ipfs/${cid}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch metadata from ${url} (${response.status})`);\n }\n\n return (await response.json()) as FundMetadata;\n}\n\n/**\n * Merge flag-driven overrides onto the fund's CURRENT metadata.\n *\n * `update-metadata` used to rebuild the JSON from scratch, silently dropping\n * any field not re-supplied by a flag — notably `xmtpGroupId`, which is the\n * ONLY group-discovery path for the dashboard on ENS-less chains (issue #458).\n *\n * Semantics: flags (`name`/`description`) win; everything else on `current`\n * (`xmtpGroupId`, `terms`, `links`, `strategies`, and any unknown keys) is\n * preserved. Unknown keys survive because the spread of `current` is a runtime\n * copy of every own-enumerable property, not just the ones in `FundMetadata`.\n * Pass `current = null` (e.g. when the fetch failed) to fall back to the old\n * build-from-scratch behavior.\n */\nexport function mergeFundMetadata(\n current: Partial<FundMetadata> | null | undefined,\n overrides: {\n name?: string;\n description?: string;\n subdomain: string;\n defaultChain: string;\n },\n): FundMetadata {\n const base = { ...(current ?? {}) } as Partial<FundMetadata> & Record<string, unknown>;\n const name = overrides.name || base.name || overrides.subdomain;\n const description =\n overrides.description ||\n base.description ||\n `${name} — a Sherwood fund on ${overrides.subdomain}.sherwoodagent.eth`;\n return {\n ...base,\n schema: base.schema || \"sherwood/fund/v1\",\n name,\n description,\n chain: base.chain || overrides.defaultChain,\n strategies: base.strategies ?? [],\n terms: base.terms ?? {},\n links: base.links ?? {},\n };\n}\n\n/**\n * Resolve a metadata URI (ipfs:// or data: base64) to name + description.\n * Returns null on any error — callers should treat enrichment as best-effort.\n */\nexport async function resolveMetadataSummary(\n uri: string,\n): Promise<{ name: string; description: string } | null> {\n try {\n if (uri.startsWith(\"data:application/json;base64,\")) {\n const b64 = uri.slice(\"data:application/json;base64,\".length);\n const json = JSON.parse(Buffer.from(b64, \"base64\").toString(\"utf-8\"));\n return { name: json.name || \"\", description: json.description || \"\" };\n }\n const meta = await fetchMetadata(uri);\n return { name: meta.name, description: meta.description };\n } catch {\n return null;\n }\n}\n"],"mappings":";AAkCA,IAAM,yBAAyB;AAG/B,IAAM,qBAAqB;AAE3B,SAAS,kBAA0B;AACjC,SAAO,QAAQ,IAAI,mBACf,GAAG,QAAQ,IAAI,gBAAgB,qBAC/B;AACN;AAEA,SAAS,mBAA2B;AAClC,SAAO,QAAQ,IAAI,kBAAkB;AACvC;AAMA,eAAe,aAAa,SAAkC,MAA+B;AAC3F,QAAM,MAAM,gBAAgB;AAI5B,QAAM,WAAW;AACjB,MAAI;AACJ,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAAA,MACxC,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,YAAI,SAAS,SAAS,IAAK,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,MAAM,IAAI,EAAE;AAC7F,kBAAU,IAAI,MAAM,uBAAuB,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,MACxE,OAAO;AACL,cAAM,SAAU,MAAM,SAAS,KAAK;AACpC,eAAO,UAAU,OAAO,QAAQ;AAAA,MAClC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,QAAQ,WAAW,uBAAuB,EAAG,OAAM;AACnF,gBAAU;AAAA,IACZ;AACA,QAAI,IAAI,WAAW,EAAG,OAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,OAAQ,IAAI,EAAE,CAAC;AAAA,EAC9E;AACA,QAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,uBAAuB,OAAO,OAAO,CAAC,EAAE;AAC/F;AAOA,eAAsB,QAAQ,SAAkC,MAA+B;AAC7F,SAAO,aAAa,SAAS,IAAI;AACnC;AAMA,eAAsB,eAAe,UAAyC;AAC5E,QAAM,OAAO,iBAAiB,SAAS,KAAK,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAC9E,SAAO,aAAa,UAAgD,IAAI;AAC1E;AAMA,eAAsB,cAAc,SAAwC;AAC1E,QAAM,UAAU,iBAAiB;AACjC,MAAI;AAEJ,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,QAAQ,MAAM,CAAC;AAAA,EACvB,WAAW,QAAQ,WAAW,IAAI,KAAK,QAAQ,WAAW,MAAM,GAAG;AACjE,UAAM;AAAA,EACR,OAAO;AACL,UAAM,IAAI,MAAM,qBAAqB,OAAO,EAAE;AAAA,EAChD;AAEA,QAAM,MAAM,GAAG,OAAO,SAAS,GAAG;AAClC,QAAM,WAAW,MAAM,MAAM,GAAG;AAEhC,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,iCAAiC,GAAG,KAAK,SAAS,MAAM,GAAG;AAAA,EAC7E;AAEA,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAgBO,SAAS,kBACd,SACA,WAMc;AACd,QAAM,OAAO,EAAE,GAAI,WAAW,CAAC,EAAG;AAClC,QAAM,OAAO,UAAU,QAAQ,KAAK,QAAQ,UAAU;AACtD,QAAM,cACJ,UAAU,eACV,KAAK,eACL,GAAG,IAAI,8BAAyB,UAAU,SAAS;AACrD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,KAAK,UAAU;AAAA,IACvB;AAAA,IACA;AAAA,IACA,OAAO,KAAK,SAAS,UAAU;AAAA,IAC/B,YAAY,KAAK,cAAc,CAAC;AAAA,IAChC,OAAO,KAAK,SAAS,CAAC;AAAA,IACtB,OAAO,KAAK,SAAS,CAAC;AAAA,EACxB;AACF;AAMA,eAAsB,uBACpB,KACuD;AACvD,MAAI;AACF,QAAI,IAAI,WAAW,+BAA+B,GAAG;AACnD,YAAM,MAAM,IAAI,MAAM,gCAAgC,MAAM;AAC5D,YAAM,OAAO,KAAK,MAAM,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO,CAAC;AACpE,aAAO,EAAE,MAAM,KAAK,QAAQ,IAAI,aAAa,KAAK,eAAe,GAAG;AAAA,IACtE;AACA,UAAM,OAAO,MAAM,cAAc,GAAG;AACpC,WAAO,EAAE,MAAM,KAAK,MAAM,aAAa,KAAK,YAAY;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}