@usesigil/kit 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/advanced-analytics.d.ts +3 -1
- package/dist/advanced-analytics.d.ts.map +1 -1
- package/dist/advanced-analytics.js +31 -15
- package/dist/advanced-analytics.js.map +1 -1
- package/dist/agent-analytics.d.ts +6 -6
- package/dist/agent-analytics.d.ts.map +1 -1
- package/dist/agent-analytics.js +16 -14
- package/dist/agent-analytics.js.map +1 -1
- package/dist/agent-errors.d.ts +4 -3
- package/dist/agent-errors.d.ts.map +1 -1
- package/dist/agent-errors.js +83 -7
- package/dist/agent-errors.js.map +1 -1
- package/dist/balance-tracker.d.ts +20 -0
- package/dist/balance-tracker.d.ts.map +1 -1
- package/dist/balance-tracker.js +18 -5
- package/dist/balance-tracker.js.map +1 -1
- package/dist/create-vault.js +1 -1
- package/dist/create-vault.js.map +1 -1
- package/dist/dashboard/constraint-reads.d.ts +50 -0
- package/dist/dashboard/constraint-reads.d.ts.map +1 -0
- package/dist/dashboard/constraint-reads.js +119 -0
- package/dist/dashboard/constraint-reads.js.map +1 -0
- package/dist/dashboard/errors.d.ts +14 -0
- package/dist/dashboard/errors.d.ts.map +1 -0
- package/dist/dashboard/errors.js +71 -0
- package/dist/dashboard/errors.js.map +1 -0
- package/dist/dashboard/index.d.ts +29 -2
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +45 -0
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard/mutations.d.ts +18 -0
- package/dist/dashboard/mutations.d.ts.map +1 -1
- package/dist/dashboard/mutations.js +36 -34
- package/dist/dashboard/mutations.js.map +1 -1
- package/dist/dashboard/reads.d.ts +86 -1
- package/dist/dashboard/reads.d.ts.map +1 -1
- package/dist/dashboard/reads.js +469 -166
- package/dist/dashboard/reads.js.map +1 -1
- package/dist/dashboard/types.d.ts +118 -6
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/event-analytics.d.ts +5 -0
- package/dist/event-analytics.d.ts.map +1 -1
- package/dist/event-analytics.js +40 -3
- package/dist/event-analytics.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -1
- package/dist/events.js.map +1 -1
- package/dist/formatting.d.ts +40 -1
- package/dist/formatting.d.ts.map +1 -1
- package/dist/formatting.js +53 -5
- package/dist/formatting.js.map +1 -1
- package/dist/generated/accounts/index.d.ts +1 -0
- package/dist/generated/accounts/index.d.ts.map +1 -1
- package/dist/generated/accounts/index.js +1 -0
- package/dist/generated/accounts/index.js.map +1 -1
- package/dist/generated/accounts/instructionConstraints.d.ts +20 -11
- package/dist/generated/accounts/instructionConstraints.d.ts.map +1 -1
- package/dist/generated/accounts/instructionConstraints.js +17 -8
- package/dist/generated/accounts/instructionConstraints.js.map +1 -1
- package/dist/generated/accounts/pendingAgentPermissionsUpdate.d.ts +4 -2
- package/dist/generated/accounts/pendingAgentPermissionsUpdate.d.ts.map +1 -1
- package/dist/generated/accounts/pendingAgentPermissionsUpdate.js +4 -2
- package/dist/generated/accounts/pendingAgentPermissionsUpdate.js.map +1 -1
- package/dist/generated/accounts/pendingConstraintsUpdate.d.ts +30 -21
- package/dist/generated/accounts/pendingConstraintsUpdate.d.ts.map +1 -1
- package/dist/generated/accounts/pendingConstraintsUpdate.js +17 -10
- package/dist/generated/accounts/pendingConstraintsUpdate.js.map +1 -1
- package/dist/generated/accounts/policyConfig.d.ts +14 -8
- package/dist/generated/accounts/policyConfig.d.ts.map +1 -1
- package/dist/generated/accounts/policyConfig.js +2 -0
- package/dist/generated/accounts/policyConfig.js.map +1 -1
- package/dist/generated/accounts/postExecutionAssertions.d.ts +50 -0
- package/dist/generated/accounts/postExecutionAssertions.d.ts.map +1 -0
- package/dist/generated/accounts/postExecutionAssertions.js +72 -0
- package/dist/generated/accounts/postExecutionAssertions.js.map +1 -0
- package/dist/generated/accounts/sessionAuthority.d.ts +14 -5
- package/dist/generated/accounts/sessionAuthority.d.ts.map +1 -1
- package/dist/generated/accounts/sessionAuthority.js +5 -4
- package/dist/generated/accounts/sessionAuthority.js.map +1 -1
- package/dist/generated/errors/sigil.d.ts +16 -2
- package/dist/generated/errors/sigil.d.ts.map +1 -1
- package/dist/generated/errors/sigil.js +23 -2
- package/dist/generated/errors/sigil.js.map +1 -1
- package/dist/generated/event-discriminators.d.ts.map +1 -1
- package/dist/generated/event-discriminators.js +6 -1
- package/dist/generated/event-discriminators.js.map +1 -1
- package/dist/generated/instructions/allocateConstraintsPda.d.ts +62 -0
- package/dist/generated/instructions/allocateConstraintsPda.d.ts.map +1 -0
- package/dist/generated/instructions/allocateConstraintsPda.js +134 -0
- package/dist/generated/instructions/allocateConstraintsPda.js.map +1 -0
- package/dist/generated/instructions/allocatePendingConstraintsPda.d.ts +66 -0
- package/dist/generated/instructions/allocatePendingConstraintsPda.d.ts.map +1 -0
- package/dist/generated/instructions/allocatePendingConstraintsPda.js +157 -0
- package/dist/generated/instructions/allocatePendingConstraintsPda.js.map +1 -0
- package/dist/generated/instructions/applyConstraintsUpdate.d.ts +3 -12
- package/dist/generated/instructions/applyConstraintsUpdate.d.ts.map +1 -1
- package/dist/generated/instructions/applyConstraintsUpdate.js.map +1 -1
- package/dist/generated/instructions/closePostAssertions.d.ts +59 -0
- package/dist/generated/instructions/closePostAssertions.d.ts.map +1 -0
- package/dist/generated/instructions/closePostAssertions.js +137 -0
- package/dist/generated/instructions/closePostAssertions.js.map +1 -0
- package/dist/generated/instructions/createInstructionConstraints.d.ts +10 -11
- package/dist/generated/instructions/createInstructionConstraints.d.ts.map +1 -1
- package/dist/generated/instructions/createInstructionConstraints.js +2 -16
- package/dist/generated/instructions/createInstructionConstraints.js.map +1 -1
- package/dist/generated/instructions/createPostAssertions.d.ts +65 -0
- package/dist/generated/instructions/createPostAssertions.d.ts.map +1 -0
- package/dist/generated/instructions/createPostAssertions.js +146 -0
- package/dist/generated/instructions/createPostAssertions.js.map +1 -0
- package/dist/generated/instructions/extendPda.d.ts +52 -0
- package/dist/generated/instructions/extendPda.d.ts.map +1 -0
- package/dist/generated/instructions/extendPda.js +86 -0
- package/dist/generated/instructions/extendPda.js.map +1 -0
- package/dist/generated/instructions/index.d.ts +5 -0
- package/dist/generated/instructions/index.d.ts.map +1 -1
- package/dist/generated/instructions/index.js +5 -0
- package/dist/generated/instructions/index.js.map +1 -1
- package/dist/generated/instructions/queueAgentPermissionsUpdate.d.ts +4 -4
- package/dist/generated/instructions/queueAgentPermissionsUpdate.d.ts.map +1 -1
- package/dist/generated/instructions/queueAgentPermissionsUpdate.js +3 -3
- package/dist/generated/instructions/queueAgentPermissionsUpdate.js.map +1 -1
- package/dist/generated/instructions/queueConstraintsUpdate.d.ts +13 -11
- package/dist/generated/instructions/queueConstraintsUpdate.d.ts.map +1 -1
- package/dist/generated/instructions/queueConstraintsUpdate.js +2 -16
- package/dist/generated/instructions/queueConstraintsUpdate.js.map +1 -1
- package/dist/generated/instructions/reactivateVault.d.ts +3 -3
- package/dist/generated/instructions/reactivateVault.d.ts.map +1 -1
- package/dist/generated/instructions/reactivateVault.js +3 -3
- package/dist/generated/instructions/reactivateVault.js.map +1 -1
- package/dist/generated/instructions/registerAgent.d.ts +3 -3
- package/dist/generated/instructions/registerAgent.d.ts.map +1 -1
- package/dist/generated/instructions/registerAgent.js +3 -3
- package/dist/generated/instructions/registerAgent.js.map +1 -1
- package/dist/generated/instructions/validateAndAuthorize.d.ts +4 -13
- package/dist/generated/instructions/validateAndAuthorize.d.ts.map +1 -1
- package/dist/generated/instructions/validateAndAuthorize.js +1 -6
- package/dist/generated/instructions/validateAndAuthorize.js.map +1 -1
- package/dist/generated/programs/sigil.d.ts +56 -34
- package/dist/generated/programs/sigil.d.ts.map +1 -1
- package/dist/generated/programs/sigil.js +99 -34
- package/dist/generated/programs/sigil.js.map +1 -1
- package/dist/generated/types/accountConstraintZC.d.ts +18 -0
- package/dist/generated/types/accountConstraintZC.d.ts.map +1 -0
- package/dist/generated/types/accountConstraintZC.js +26 -0
- package/dist/generated/types/accountConstraintZC.js.map +1 -0
- package/dist/generated/types/actionAuthorized.d.ts +2 -13
- package/dist/generated/types/actionAuthorized.d.ts.map +1 -1
- package/dist/generated/types/actionAuthorized.js +2 -3
- package/dist/generated/types/actionAuthorized.js.map +1 -1
- package/dist/generated/types/agentEntry.d.ts +13 -3
- package/dist/generated/types/agentEntry.d.ts.map +1 -1
- package/dist/generated/types/agentEntry.js +5 -3
- package/dist/generated/types/agentEntry.js.map +1 -1
- package/dist/generated/types/agentRegistered.d.ts +2 -2
- package/dist/generated/types/agentRegistered.d.ts.map +1 -1
- package/dist/generated/types/agentRegistered.js +3 -3
- package/dist/generated/types/agentRegistered.js.map +1 -1
- package/dist/generated/types/constraintEntry.d.ts +11 -1
- package/dist/generated/types/constraintEntry.d.ts.map +1 -1
- package/dist/generated/types/constraintEntry.js +8 -2
- package/dist/generated/types/constraintEntry.js.map +1 -1
- package/dist/generated/types/constraintEntryZC.d.ts +51 -0
- package/dist/generated/types/constraintEntryZC.d.ts.map +1 -0
- package/dist/generated/types/constraintEntryZC.js +49 -0
- package/dist/generated/types/constraintEntryZC.js.map +1 -0
- package/dist/generated/types/constraintsChangeApplied.d.ts +6 -4
- package/dist/generated/types/constraintsChangeApplied.d.ts.map +1 -1
- package/dist/generated/types/constraintsChangeApplied.js +3 -1
- package/dist/generated/types/constraintsChangeApplied.js.map +1 -1
- package/dist/generated/types/constraintsChangeQueued.d.ts +6 -4
- package/dist/generated/types/constraintsChangeQueued.d.ts.map +1 -1
- package/dist/generated/types/constraintsChangeQueued.js +3 -1
- package/dist/generated/types/constraintsChangeQueued.js.map +1 -1
- package/dist/generated/types/dataConstraintZC.d.ts +20 -0
- package/dist/generated/types/dataConstraintZC.d.ts.map +1 -0
- package/dist/generated/types/dataConstraintZC.js +30 -0
- package/dist/generated/types/dataConstraintZC.js.map +1 -0
- package/dist/generated/types/discriminatorFormat.d.ts +17 -0
- package/dist/generated/types/discriminatorFormat.d.ts.map +1 -0
- package/dist/generated/types/discriminatorFormat.js +23 -0
- package/dist/generated/types/discriminatorFormat.js.map +1 -0
- package/dist/generated/types/index.d.ts +11 -1
- package/dist/generated/types/index.d.ts.map +1 -1
- package/dist/generated/types/index.js +11 -1
- package/dist/generated/types/index.js.map +1 -1
- package/dist/generated/types/instructionConstraintsCreated.d.ts +6 -4
- package/dist/generated/types/instructionConstraintsCreated.d.ts.map +1 -1
- package/dist/generated/types/instructionConstraintsCreated.js +3 -1
- package/dist/generated/types/instructionConstraintsCreated.js.map +1 -1
- package/dist/generated/types/pdaAllocated.d.ts +24 -0
- package/dist/generated/types/pdaAllocated.d.ts.map +1 -0
- package/dist/generated/types/pdaAllocated.js +28 -0
- package/dist/generated/types/pdaAllocated.js.map +1 -0
- package/dist/generated/types/pdaExtended.d.ts +24 -0
- package/dist/generated/types/pdaExtended.d.ts.map +1 -0
- package/dist/generated/types/pdaExtended.js +28 -0
- package/dist/generated/types/pdaExtended.js.map +1 -0
- package/dist/generated/types/postAssertionChecked.d.ts +24 -0
- package/dist/generated/types/postAssertionChecked.d.ts.map +1 -0
- package/dist/generated/types/postAssertionChecked.js +28 -0
- package/dist/generated/types/postAssertionChecked.js.map +1 -0
- package/dist/generated/types/postAssertionEntry.d.ts +22 -0
- package/dist/generated/types/postAssertionEntry.d.ts.map +1 -0
- package/dist/generated/types/postAssertionEntry.js +32 -0
- package/dist/generated/types/postAssertionEntry.js.map +1 -0
- package/dist/generated/types/postAssertionEntryZC.d.ts +54 -0
- package/dist/generated/types/postAssertionEntryZC.d.ts.map +1 -0
- package/dist/generated/types/postAssertionEntryZC.js +34 -0
- package/dist/generated/types/postAssertionEntryZC.js.map +1 -0
- package/dist/generated/types/postAssertionsClosed.d.ts +20 -0
- package/dist/generated/types/postAssertionsClosed.d.ts.map +1 -0
- package/dist/generated/types/postAssertionsClosed.js +24 -0
- package/dist/generated/types/postAssertionsClosed.js.map +1 -0
- package/dist/generated/types/postAssertionsCreated.d.ts +22 -0
- package/dist/generated/types/postAssertionsCreated.d.ts.map +1 -0
- package/dist/generated/types/postAssertionsCreated.js +26 -0
- package/dist/generated/types/postAssertionsCreated.js.map +1 -0
- package/dist/generated/types/sessionFinalized.d.ts +10 -12
- package/dist/generated/types/sessionFinalized.d.ts.map +1 -1
- package/dist/generated/types/sessionFinalized.js +4 -2
- package/dist/generated/types/sessionFinalized.js.map +1 -1
- package/dist/generated/types/vaultReactivated.d.ts +2 -2
- package/dist/generated/types/vaultReactivated.d.ts.map +1 -1
- package/dist/generated/types/vaultReactivated.js +3 -3
- package/dist/generated/types/vaultReactivated.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/inscribe.d.ts +1 -1
- package/dist/inscribe.d.ts.map +1 -1
- package/dist/integrations/compose-errors.d.ts +64 -0
- package/dist/integrations/compose-errors.d.ts.map +1 -0
- package/dist/integrations/compose-errors.js +105 -0
- package/dist/integrations/compose-errors.js.map +1 -0
- package/dist/integrations/protocol-handler.d.ts +59 -0
- package/dist/integrations/protocol-handler.d.ts.map +1 -0
- package/dist/integrations/protocol-handler.js +9 -0
- package/dist/integrations/protocol-handler.js.map +1 -0
- package/dist/presets.d.ts +10 -4
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +8 -4
- package/dist/presets.js.map +1 -1
- package/dist/seal.d.ts +2 -17
- package/dist/seal.d.ts.map +1 -1
- package/dist/seal.js +18 -62
- package/dist/seal.js.map +1 -1
- package/dist/security-analytics.d.ts +1 -1
- package/dist/security-analytics.d.ts.map +1 -1
- package/dist/security-analytics.js +48 -20
- package/dist/security-analytics.js.map +1 -1
- package/dist/testing/devnet.js +3 -3
- package/dist/testing/devnet.js.map +1 -1
- package/dist/testing/mock-state.d.ts +2 -0
- package/dist/testing/mock-state.d.ts.map +1 -1
- package/dist/testing/mock-state.js +6 -2
- package/dist/testing/mock-state.js.map +1 -1
- package/dist/types.d.ts +16 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -4
- package/dist/types.js.map +1 -1
- package/dist/vault-analytics.d.ts.map +1 -1
- package/dist/vault-analytics.js +3 -3
- package/dist/vault-analytics.js.map +1 -1
- package/package.json +5 -5
- package/dist/generated/types/actionType.d.ts +0 -37
- package/dist/generated/types/actionType.d.ts.map +0 -1
- package/dist/generated/types/actionType.js +0 -43
- package/dist/generated/types/actionType.js.map +0 -1
package/dist/dashboard/reads.js
CHANGED
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Each function is stateless (fetches fresh from RPC), composes existing
|
|
5
5
|
* SDK functions, and returns raw values with toJSON() for MCP serialization.
|
|
6
|
+
*
|
|
7
|
+
* S14: the five view types are composed by pure `build*` helpers that take a
|
|
8
|
+
* shared {@link OverviewContext}. The existing reads each assemble their own
|
|
9
|
+
* context with minimal fetches; `getOverview` fetches once and shares the
|
|
10
|
+
* context across all helpers so derived values (security posture etc.) are
|
|
11
|
+
* computed exactly once.
|
|
6
12
|
*/
|
|
13
|
+
import { isSome } from "@solana/kit";
|
|
14
|
+
import { toDxError } from "./errors.js";
|
|
7
15
|
import { resolveVaultStateForOwner, getSpendingHistory, getPendingPolicyForVault, } from "../state-resolver.js";
|
|
8
|
-
import { getVaultPnL } from "../balance-tracker.js";
|
|
16
|
+
import { getVaultPnL, getVaultPnLFromState } from "../balance-tracker.js";
|
|
9
17
|
import { getSecurityPosture } from "../security-analytics.js";
|
|
10
18
|
import { evaluateAlertConditions } from "../security-analytics.js";
|
|
11
19
|
import { getAgentProfile } from "../agent-analytics.js";
|
|
@@ -40,15 +48,61 @@ function serializeBigints(obj) {
|
|
|
40
48
|
}
|
|
41
49
|
return obj;
|
|
42
50
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Default size of the activity window included in `getOverview`. Consumers
|
|
53
|
+
* may override via `GetOverviewOptions.activityLimit`.
|
|
54
|
+
*
|
|
55
|
+
* The value matches `getAgents`' existing per-agent enrichment window so one
|
|
56
|
+
* fetch serves both the overview's activity feed and the agents' last-action
|
|
57
|
+
* fields without inflating RPC cost.
|
|
58
|
+
*/
|
|
59
|
+
export const DEFAULT_OVERVIEW_ACTIVITY_LIMIT = 100;
|
|
60
|
+
/**
|
|
61
|
+
* Shared "is this an account-not-found error?" predicate.
|
|
62
|
+
*
|
|
63
|
+
* Both `getPolicy` and `getOverview` treat a missing `PendingPolicyUpdate`
|
|
64
|
+
* account as "no pending update" (not an error). The current Kit doesn't
|
|
65
|
+
* expose a typed `AccountNotFound` SolanaError at this call site, so both
|
|
66
|
+
* paths fall back to substring matching. Extracting it here means the
|
|
67
|
+
* fragility lives in one place and only one site needs to update if Kit
|
|
68
|
+
* ever surfaces a typed variant.
|
|
69
|
+
*/
|
|
70
|
+
function isAccountNotFoundError(err) {
|
|
71
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
return (message.includes("could not find") ||
|
|
73
|
+
message.includes("Account does not exist"));
|
|
74
|
+
}
|
|
75
|
+
// ─── Build helpers (pure composition — no RPC) ───────────────────────────────
|
|
76
|
+
// Each helper accepts an OverviewContext and returns one view type. `getOverview`
|
|
77
|
+
// pre-populates memoized derivations (posture/breakdown/alerts) so repeat calls
|
|
78
|
+
// share one computation; existing reads pass a minimal ctx and the helper
|
|
79
|
+
// derives what it needs from `ctx.state`.
|
|
80
|
+
/**
|
|
81
|
+
* Guard for state fields that `resolveVaultStateForOwner` normally guarantees.
|
|
82
|
+
*
|
|
83
|
+
* `state.vault` and `state.policy` are non-null on any success path from the
|
|
84
|
+
* resolver, but consumers that hand-construct an {@link OverviewContext} for
|
|
85
|
+
* testing or custom composition could pass a partial shape. Fail fast with a
|
|
86
|
+
* labeled error instead of a cryptic "cannot read properties of null".
|
|
87
|
+
*/
|
|
88
|
+
function requireCtxField(value, field) {
|
|
89
|
+
if (value === null || value === undefined) {
|
|
90
|
+
throw new Error(`[dashboard/reads] OverviewContext.state.${field} is required but missing`);
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Compose {@link VaultState} from a pre-fetched {@link OverviewContext}.
|
|
96
|
+
*
|
|
97
|
+
* Requires `ctx.state`. Uses `ctx.pnl` when present; otherwise defaults to
|
|
98
|
+
* zero P&L. Uses `ctx.posture` when memoized; otherwise computes from state.
|
|
99
|
+
*/
|
|
100
|
+
export function buildVaultState(ctx) {
|
|
101
|
+
const v = requireCtxField(ctx.state.vault, "vault");
|
|
102
|
+
const posture = ctx.posture ?? getSecurityPosture(asVaultState(ctx.state));
|
|
103
|
+
const pnlPercent = ctx.pnl && Number.isFinite(ctx.pnl.pnlPercent) ? ctx.pnl.pnlPercent : 0;
|
|
104
|
+
const pnlAbsolute = ctx.pnl ? ctx.pnl.pnl : 0n;
|
|
105
|
+
const bal = ctx.state.stablecoinBalances;
|
|
52
106
|
const total = bal.usdc + bal.usdt;
|
|
53
107
|
const tokens = [
|
|
54
108
|
...(bal.usdc > 0n ? [{ mint: "USDC", amount: bal.usdc, decimals: 6 }] : []),
|
|
@@ -63,10 +117,12 @@ export async function getVaultState(rpc, vault, network) {
|
|
|
63
117
|
: posture.failCount > 0
|
|
64
118
|
? "elevated"
|
|
65
119
|
: "healthy";
|
|
120
|
+
const vaultAddr = ctx.vault;
|
|
121
|
+
const status = (v.status === 0 ? "active" : v.status === 1 ? "frozen" : "closed");
|
|
66
122
|
return {
|
|
67
123
|
vault: {
|
|
68
|
-
address:
|
|
69
|
-
status
|
|
124
|
+
address: vaultAddr,
|
|
125
|
+
status,
|
|
70
126
|
owner: v.owner,
|
|
71
127
|
agentCount: v.agents?.length ?? 0,
|
|
72
128
|
openPositions: v.openPositions,
|
|
@@ -74,19 +130,12 @@ export async function getVaultState(rpc, vault, network) {
|
|
|
74
130
|
totalFees: v.totalFeesCollected,
|
|
75
131
|
},
|
|
76
132
|
balance: { total, tokens },
|
|
77
|
-
pnl: {
|
|
78
|
-
percent: Number.isFinite(pnl.pnlPercent) ? pnl.pnlPercent : 0,
|
|
79
|
-
absolute: pnl.pnl,
|
|
80
|
-
},
|
|
133
|
+
pnl: { percent: pnlPercent, absolute: pnlAbsolute },
|
|
81
134
|
health: { level, alertCount: posture.failCount, checks },
|
|
82
135
|
toJSON: () => ({
|
|
83
136
|
vault: {
|
|
84
|
-
address:
|
|
85
|
-
status
|
|
86
|
-
? "active"
|
|
87
|
-
: v.status === 1
|
|
88
|
-
? "frozen"
|
|
89
|
-
: "closed"),
|
|
137
|
+
address: vaultAddr,
|
|
138
|
+
status,
|
|
90
139
|
owner: v.owner,
|
|
91
140
|
agentCount: v.agents?.length ?? 0,
|
|
92
141
|
openPositions: v.openPositions,
|
|
@@ -97,20 +146,25 @@ export async function getVaultState(rpc, vault, network) {
|
|
|
97
146
|
total: bs(total),
|
|
98
147
|
tokens: tokens.map((t) => ({ ...t, amount: bs(t.amount) })),
|
|
99
148
|
},
|
|
100
|
-
pnl: {
|
|
101
|
-
percent: Number.isFinite(pnl.pnlPercent) ? pnl.pnlPercent : 0,
|
|
102
|
-
absolute: bs(pnl.pnl),
|
|
103
|
-
},
|
|
149
|
+
pnl: { percent: pnlPercent, absolute: bs(pnlAbsolute) },
|
|
104
150
|
health: { level, alertCount: posture.failCount, checks },
|
|
105
151
|
}),
|
|
106
152
|
};
|
|
107
153
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Compose {@link AgentData}[] from a pre-fetched {@link OverviewContext}.
|
|
156
|
+
*
|
|
157
|
+
* Requires `ctx.state`. Uses `ctx.activity` to populate per-agent last-action
|
|
158
|
+
* and blocked-count fields; when absent, those fields default to empty/zero.
|
|
159
|
+
*/
|
|
160
|
+
export function buildAgents(ctx) {
|
|
161
|
+
const state = ctx.state;
|
|
162
|
+
const v = requireCtxField(state.vault, "vault");
|
|
163
|
+
const vaultAgents = v.agents;
|
|
112
164
|
if (!vaultAgents || vaultAgents.length === 0)
|
|
113
165
|
return [];
|
|
166
|
+
const activity = ctx.activity ?? [];
|
|
167
|
+
const blockedCutoffMs = Date.now() - 24 * 3600 * 1000;
|
|
114
168
|
return vaultAgents.map((entry) => {
|
|
115
169
|
const addr = entry.pubkey;
|
|
116
170
|
const profile = getAgentProfile(asVaultState(state), addr);
|
|
@@ -118,39 +172,52 @@ export async function getAgents(rpc, vault, network) {
|
|
|
118
172
|
const spentAmt = budget?.spent24h ?? 0n;
|
|
119
173
|
const capAmt = budget?.cap ?? 0n;
|
|
120
174
|
const pct = capAmt > 0n ? Number((spentAmt * 10000n) / capAmt) / 100 : 0;
|
|
175
|
+
// Items are newest-first (getSignaturesForAddress ordering).
|
|
176
|
+
const agentActivity = activity.filter((item) => item.agent !== null && item.agent === addr);
|
|
177
|
+
const last = agentActivity[0];
|
|
178
|
+
const lastActionType = last
|
|
179
|
+
? mapCategory(last.category ?? "unknown", last.eventType ?? "", last.actionType ?? undefined)
|
|
180
|
+
: "";
|
|
181
|
+
const lastActionProtocol = last?.protocolName ?? "";
|
|
182
|
+
const lastActionTimestamp = last ? last.timestamp * 1000 : 0;
|
|
183
|
+
const blockedCount24h = agentActivity.filter((item) => !item.success && item.timestamp * 1000 >= blockedCutoffMs).length;
|
|
121
184
|
return {
|
|
122
185
|
address: addr,
|
|
123
186
|
status: (profile?.paused ? "paused" : "active"),
|
|
124
|
-
|
|
125
|
-
|
|
187
|
+
capabilityLabel: profile?.capabilityLabel ?? "Disabled",
|
|
188
|
+
capability: profile?.capability ?? 0,
|
|
126
189
|
spending: { amount: spentAmt, limit: capAmt, percent: pct },
|
|
127
|
-
lastActionType
|
|
128
|
-
lastActionProtocol
|
|
129
|
-
lastActionTimestamp
|
|
130
|
-
blockedCount24h
|
|
190
|
+
lastActionType,
|
|
191
|
+
lastActionProtocol,
|
|
192
|
+
lastActionTimestamp,
|
|
193
|
+
blockedCount24h,
|
|
131
194
|
toJSON: () => ({
|
|
132
195
|
address: addr,
|
|
133
196
|
status: profile?.paused ? "paused" : "active",
|
|
134
|
-
|
|
135
|
-
|
|
197
|
+
capabilityLabel: profile?.capabilityLabel ?? "Disabled",
|
|
198
|
+
capability: profile?.capability ?? 0,
|
|
136
199
|
spending: { amount: bs(spentAmt), limit: bs(capAmt), percent: pct },
|
|
137
|
-
lastActionType
|
|
138
|
-
lastActionProtocol
|
|
139
|
-
lastActionTimestamp
|
|
140
|
-
blockedCount24h
|
|
200
|
+
lastActionType,
|
|
201
|
+
lastActionProtocol,
|
|
202
|
+
lastActionTimestamp,
|
|
203
|
+
blockedCount24h,
|
|
141
204
|
}),
|
|
142
205
|
};
|
|
143
206
|
});
|
|
144
207
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Compose {@link SpendingData} from a pre-fetched {@link OverviewContext}.
|
|
210
|
+
*
|
|
211
|
+
* Requires `ctx.state`. Uses `ctx.breakdown` when memoized.
|
|
212
|
+
*/
|
|
213
|
+
export function buildSpending(ctx) {
|
|
214
|
+
const state = ctx.state;
|
|
215
|
+
const breakdown = ctx.breakdown ?? getSpendingBreakdown(asVaultState(state));
|
|
149
216
|
const nowUnix = BigInt(Math.floor(Date.now() / 1000));
|
|
150
217
|
const epochs = getSpendingHistory(state.tracker, nowUnix);
|
|
151
218
|
const chart = epochs.map((e) => ({
|
|
152
219
|
time: new Date(e.timestamp * 1000).toISOString(),
|
|
153
|
-
amount: Number(e.usdAmount),
|
|
220
|
+
amount: Number(e.usdAmount) / 1_000_000,
|
|
154
221
|
}));
|
|
155
222
|
const { spent24h: spent, cap, remaining } = state.globalBudget;
|
|
156
223
|
const percent = cap > 0n ? Number((spent * 10000n) / cap) / 100 : 0;
|
|
@@ -184,109 +251,15 @@ export async function getSpending(rpc, vault, network) {
|
|
|
184
251
|
}),
|
|
185
252
|
};
|
|
186
253
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const amt = item.amount ?? 0n;
|
|
197
|
-
const sig = item.txSignature || `evt-${item.timestamp}-${item.eventType}`;
|
|
198
|
-
return {
|
|
199
|
-
id: sig,
|
|
200
|
-
timestamp: item.timestamp * 1000,
|
|
201
|
-
type,
|
|
202
|
-
protocol: item.protocolName || "",
|
|
203
|
-
protocolId: item.protocol || "",
|
|
204
|
-
agent: item.agent || "",
|
|
205
|
-
amount: amt,
|
|
206
|
-
status: item.success ? "approved" : "blocked",
|
|
207
|
-
reason: item.success ? undefined : item.description,
|
|
208
|
-
txSignature: item.txSignature,
|
|
209
|
-
toJSON: () => ({
|
|
210
|
-
id: sig,
|
|
211
|
-
timestamp: item.timestamp * 1000,
|
|
212
|
-
type,
|
|
213
|
-
protocol: item.protocolName || "",
|
|
214
|
-
protocolId: item.protocol || "",
|
|
215
|
-
agent: item.agent || "",
|
|
216
|
-
amount: bs(amt),
|
|
217
|
-
status: item.success ? "approved" : "blocked",
|
|
218
|
-
reason: item.success ? undefined : item.description,
|
|
219
|
-
txSignature: item.txSignature,
|
|
220
|
-
}),
|
|
221
|
-
};
|
|
222
|
-
});
|
|
223
|
-
if (filters?.agent)
|
|
224
|
-
rows = rows.filter((r) => r.agent === filters.agent);
|
|
225
|
-
if (filters?.protocol)
|
|
226
|
-
rows = rows.filter((r) => r.protocolId === filters.protocol || r.protocol === filters.protocol);
|
|
227
|
-
if (filters?.status)
|
|
228
|
-
rows = rows.filter((r) => r.status === filters.status);
|
|
229
|
-
if (filters?.timeRange) {
|
|
230
|
-
const cutoff = Date.now() - rangeToMs(filters.timeRange);
|
|
231
|
-
rows = rows.filter((r) => r.timestamp >= cutoff);
|
|
232
|
-
}
|
|
233
|
-
const approved = rows.filter((r) => r.status === "approved").length;
|
|
234
|
-
const blocked = rows.length - approved;
|
|
235
|
-
const volume = rows.reduce((s, r) => s + r.amount, 0n);
|
|
236
|
-
return {
|
|
237
|
-
rows,
|
|
238
|
-
summary: { total: rows.length, approved, blocked, volume },
|
|
239
|
-
toJSON: () => ({
|
|
240
|
-
rows: rows.map((r) => r.toJSON()),
|
|
241
|
-
summary: { total: rows.length, approved, blocked, volume: bs(volume) },
|
|
242
|
-
}),
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function mapCategory(cat, evt, actionType) {
|
|
246
|
-
if (cat === "trade") {
|
|
247
|
-
// ActionAuthorized carries actionType to distinguish swap vs lend vs perps
|
|
248
|
-
if (actionType) {
|
|
249
|
-
const at = actionType.toLowerCase();
|
|
250
|
-
if (at.includes("lend") ||
|
|
251
|
-
at.includes("deposit") ||
|
|
252
|
-
at.includes("withdraw"))
|
|
253
|
-
return "lend";
|
|
254
|
-
if (at.includes("open") ||
|
|
255
|
-
at === "openposition" ||
|
|
256
|
-
at === "swapandopenposition")
|
|
257
|
-
return "open_position";
|
|
258
|
-
if (at.includes("close") || at === "closeposition")
|
|
259
|
-
return "close_position";
|
|
260
|
-
}
|
|
261
|
-
if (evt.includes("Open"))
|
|
262
|
-
return "open_position";
|
|
263
|
-
if (evt.includes("Close"))
|
|
264
|
-
return "close_position";
|
|
265
|
-
return "swap";
|
|
266
|
-
}
|
|
267
|
-
if (cat === "deposit")
|
|
268
|
-
return "deposit";
|
|
269
|
-
if (cat === "withdrawal")
|
|
270
|
-
return "withdraw";
|
|
271
|
-
if (evt === "AgentTransferExecuted")
|
|
272
|
-
return "transfer";
|
|
273
|
-
return "swap";
|
|
274
|
-
}
|
|
275
|
-
function rangeToMs(r) {
|
|
276
|
-
const map = {
|
|
277
|
-
"1h": 3600000,
|
|
278
|
-
"6h": 21600000,
|
|
279
|
-
"24h": 86400000,
|
|
280
|
-
"7d": 604800000,
|
|
281
|
-
"30d": 2592000000,
|
|
282
|
-
};
|
|
283
|
-
return map[r] ?? 86400000;
|
|
284
|
-
}
|
|
285
|
-
// ─── getHealth ───────────────────────────────────────────────────────────────
|
|
286
|
-
export async function getHealth(rpc, vault, network) {
|
|
287
|
-
const state = await resolveVaultStateForOwner(rpc, vault, undefined, toNet(network));
|
|
288
|
-
const posture = getSecurityPosture(asVaultState(state));
|
|
289
|
-
const alerts = evaluateAlertConditions(state, vault);
|
|
254
|
+
/**
|
|
255
|
+
* Compose {@link HealthData} from a pre-fetched {@link OverviewContext}.
|
|
256
|
+
*
|
|
257
|
+
* Requires `ctx.state` + `ctx.vault` (for alert evaluation). Uses `ctx.posture`
|
|
258
|
+
* and `ctx.alerts` when memoized.
|
|
259
|
+
*/
|
|
260
|
+
export function buildHealth(ctx) {
|
|
261
|
+
const posture = ctx.posture ?? getSecurityPosture(asVaultState(ctx.state));
|
|
262
|
+
const alerts = ctx.alerts ?? evaluateAlertConditions(ctx.state, ctx.vault);
|
|
290
263
|
const level = posture.criticalFailures.length > 0
|
|
291
264
|
? "critical"
|
|
292
265
|
: posture.failCount > 0
|
|
@@ -320,21 +293,16 @@ export async function getHealth(rpc, vault, network) {
|
|
|
320
293
|
}),
|
|
321
294
|
};
|
|
322
295
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
throw err;
|
|
335
|
-
}),
|
|
336
|
-
]);
|
|
337
|
-
const p = state.policy;
|
|
296
|
+
/**
|
|
297
|
+
* Compose {@link PolicyData} from a pre-fetched {@link OverviewContext}.
|
|
298
|
+
*
|
|
299
|
+
* Requires `ctx.state`. Uses `ctx.pendingPolicy` (which may be `null` to mean
|
|
300
|
+
* "confirmed no pending update"); when `undefined` treats as no pending update.
|
|
301
|
+
*/
|
|
302
|
+
export function buildPolicy(ctx) {
|
|
303
|
+
const state = ctx.state;
|
|
304
|
+
const pendingPolicy = ctx.pendingPolicy ?? null;
|
|
305
|
+
const p = requireCtxField(state.policy, "policy");
|
|
338
306
|
const protocols = (p.protocols || []);
|
|
339
307
|
const approvedApps = protocols.map((addr) => ({
|
|
340
308
|
name: resolveProtocolName(addr),
|
|
@@ -357,8 +325,40 @@ export async function getPolicy(rpc, vault, network) {
|
|
|
357
325
|
const executesAtSec = Number(pp.executesAt ?? 0);
|
|
358
326
|
const appliesAt = Number.isFinite(executesAtSec) ? executesAtSec * 1000 : 0;
|
|
359
327
|
const nowSec = Math.floor(Date.now() / 1000);
|
|
328
|
+
// Decode each Option<T> field from PendingPolicyUpdate. Only Some fields
|
|
329
|
+
// land in `changes`. 14 fields total — every timelockable PolicyConfig field.
|
|
330
|
+
// Source: programs/sigil/src/state/pending_policy.rs
|
|
331
|
+
const changes = {};
|
|
332
|
+
if (isSome(pp.dailySpendingCapUsd))
|
|
333
|
+
changes.dailyCap = pp.dailySpendingCapUsd.value;
|
|
334
|
+
if (isSome(pp.maxTransactionAmountUsd))
|
|
335
|
+
changes.maxPerTrade = pp.maxTransactionAmountUsd.value;
|
|
336
|
+
if (isSome(pp.protocols))
|
|
337
|
+
changes.approvedApps = pp.protocols.value;
|
|
338
|
+
if (isSome(pp.protocolMode))
|
|
339
|
+
changes.protocolMode = modeMap[pp.protocolMode.value] || "unrestricted";
|
|
340
|
+
if (isSome(pp.hasProtocolCaps))
|
|
341
|
+
changes.hasProtocolCaps = pp.hasProtocolCaps.value;
|
|
342
|
+
if (isSome(pp.protocolCaps))
|
|
343
|
+
changes.protocolCaps = pp.protocolCaps.value;
|
|
344
|
+
if (isSome(pp.canOpenPositions))
|
|
345
|
+
changes.canOpenPositions = pp.canOpenPositions.value;
|
|
346
|
+
if (isSome(pp.maxConcurrentPositions))
|
|
347
|
+
changes.maxConcurrentPositions = pp.maxConcurrentPositions.value;
|
|
348
|
+
if (isSome(pp.maxSlippageBps))
|
|
349
|
+
changes.maxSlippageBps = pp.maxSlippageBps.value;
|
|
350
|
+
if (isSome(pp.maxLeverageBps))
|
|
351
|
+
changes.leverageLimit = pp.maxLeverageBps.value;
|
|
352
|
+
if (isSome(pp.allowedDestinations))
|
|
353
|
+
changes.allowedDestinations = pp.allowedDestinations.value;
|
|
354
|
+
if (isSome(pp.developerFeeRate))
|
|
355
|
+
changes.developerFeeRate = pp.developerFeeRate.value;
|
|
356
|
+
if (isSome(pp.sessionExpirySlots))
|
|
357
|
+
changes.sessionExpirySlots = pp.sessionExpirySlots.value;
|
|
358
|
+
if (isSome(pp.timelockDuration))
|
|
359
|
+
changes.timelock = Number(pp.timelockDuration.value);
|
|
360
360
|
pendingUpdate = {
|
|
361
|
-
changes
|
|
361
|
+
changes,
|
|
362
362
|
appliesAt,
|
|
363
363
|
canApply: executesAtSec > 0 && executesAtSec <= nowSec,
|
|
364
364
|
canCancel: true,
|
|
@@ -408,4 +408,307 @@ export async function getPolicy(rpc, vault, network) {
|
|
|
408
408
|
}),
|
|
409
409
|
};
|
|
410
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Map raw {@link VaultActivityItem}[] to {@link ActivityRow}[] with stable
|
|
413
|
+
* derived IDs and toJSON serializers. Pure — no filtering applied.
|
|
414
|
+
*
|
|
415
|
+
* Both `getActivity` (which then filters) and `getOverview` (which returns
|
|
416
|
+
* unfiltered) consume the output.
|
|
417
|
+
*/
|
|
418
|
+
export function buildActivityRows(items) {
|
|
419
|
+
return items.map((item) => {
|
|
420
|
+
const cat = item.category ?? "unknown";
|
|
421
|
+
const evt = item.eventType ?? "";
|
|
422
|
+
const act = item.actionType ?? undefined;
|
|
423
|
+
const posEffect = item.positionEffect ?? undefined;
|
|
424
|
+
const type = mapCategory(cat, evt, act, posEffect);
|
|
425
|
+
const amt = item.amount ?? 0n;
|
|
426
|
+
const sig = item.txSignature || `evt-${item.timestamp}-${item.eventType}`;
|
|
427
|
+
return {
|
|
428
|
+
id: sig,
|
|
429
|
+
timestamp: item.timestamp * 1000,
|
|
430
|
+
type,
|
|
431
|
+
protocol: item.protocolName || "",
|
|
432
|
+
protocolId: item.protocol || "",
|
|
433
|
+
agent: item.agent || "",
|
|
434
|
+
amount: amt,
|
|
435
|
+
status: item.success ? "approved" : "blocked",
|
|
436
|
+
reason: item.success ? undefined : item.description,
|
|
437
|
+
txSignature: item.txSignature,
|
|
438
|
+
toJSON: () => ({
|
|
439
|
+
id: sig,
|
|
440
|
+
timestamp: item.timestamp * 1000,
|
|
441
|
+
type,
|
|
442
|
+
protocol: item.protocolName || "",
|
|
443
|
+
protocolId: item.protocol || "",
|
|
444
|
+
agent: item.agent || "",
|
|
445
|
+
amount: bs(amt),
|
|
446
|
+
status: item.success ? "approved" : "blocked",
|
|
447
|
+
reason: item.success ? undefined : item.description,
|
|
448
|
+
txSignature: item.txSignature,
|
|
449
|
+
}),
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
// ─── getVaultState ───────────────────────────────────────────────────────────
|
|
454
|
+
export async function getVaultState(rpc, vault, network) {
|
|
455
|
+
try {
|
|
456
|
+
const [state, pnl] = await Promise.all([
|
|
457
|
+
resolveVaultStateForOwner(rpc, vault, undefined, toNet(network)),
|
|
458
|
+
getVaultPnL(rpc, vault, toNet(network)),
|
|
459
|
+
]);
|
|
460
|
+
return buildVaultState({ vault, state, pnl });
|
|
461
|
+
}
|
|
462
|
+
catch (err) {
|
|
463
|
+
throw toDxError(err, "OwnerClient.getVaultState");
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// ─── getAgents ───────────────────────────────────────────────────────────────
|
|
467
|
+
export async function getAgents(rpc, vault, network) {
|
|
468
|
+
try {
|
|
469
|
+
// Single getVaultActivity call is shared across all agents (N+1 prevention).
|
|
470
|
+
// Activity is enrichment, so a fetch failure degrades gracefully to empty
|
|
471
|
+
// last-action fields. Window: 100 most recent signatures — large enough to
|
|
472
|
+
// surface last action for low-volume agents without inflating RPC cost.
|
|
473
|
+
//
|
|
474
|
+
// Fix for docs/SECURITY-FINDINGS-2026-04-07.md Finding 5: the previous
|
|
475
|
+
// `.catch(() => [])` swallowed activity-fetch failures silently. If Helius
|
|
476
|
+
// started rate-limiting getSignaturesForAddress, every dashboard call would
|
|
477
|
+
// show "last action: never" for every agent forever and nobody would
|
|
478
|
+
// notice. Graceful degradation is still the right behavior (activity is
|
|
479
|
+
// enrichment, not core), but it must be observable — console.warn is the
|
|
480
|
+
// minimum bar.
|
|
481
|
+
const [state, activity] = await Promise.all([
|
|
482
|
+
resolveVaultStateForOwner(rpc, vault, undefined, toNet(network)),
|
|
483
|
+
getVaultActivity(rpc, vault, 100, toNet(network)).catch((err) => {
|
|
484
|
+
// eslint-disable-next-line no-console
|
|
485
|
+
console.warn("[OwnerClient.getAgents] activity enrichment failed — falling back to empty last-action fields:", err instanceof Error ? err.message : String(err));
|
|
486
|
+
return [];
|
|
487
|
+
}),
|
|
488
|
+
]);
|
|
489
|
+
return buildAgents({ vault, state, activity });
|
|
490
|
+
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
throw toDxError(err, "OwnerClient.getAgents");
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// ─── getSpending ─────────────────────────────────────────────────────────────
|
|
496
|
+
export async function getSpending(rpc, vault, network) {
|
|
497
|
+
try {
|
|
498
|
+
const state = await resolveVaultStateForOwner(rpc, vault, undefined, toNet(network));
|
|
499
|
+
return buildSpending({ vault, state });
|
|
500
|
+
}
|
|
501
|
+
catch (err) {
|
|
502
|
+
throw toDxError(err, "OwnerClient.getSpending");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// ─── getActivity ─────────────────────────────────────────────────────────────
|
|
506
|
+
export async function getActivity(rpc, vault, network, filters) {
|
|
507
|
+
try {
|
|
508
|
+
const limit = filters?.limit ?? 50;
|
|
509
|
+
const items = await getVaultActivity(rpc, vault, limit, toNet(network));
|
|
510
|
+
let rows = buildActivityRows(items);
|
|
511
|
+
if (filters?.agent)
|
|
512
|
+
rows = rows.filter((r) => r.agent === filters.agent);
|
|
513
|
+
if (filters?.protocol)
|
|
514
|
+
rows = rows.filter((r) => r.protocolId === filters.protocol || r.protocol === filters.protocol);
|
|
515
|
+
if (filters?.status)
|
|
516
|
+
rows = rows.filter((r) => r.status === filters.status);
|
|
517
|
+
if (filters?.type)
|
|
518
|
+
rows = rows.filter((r) => r.type === filters.type);
|
|
519
|
+
if (filters?.timeRange) {
|
|
520
|
+
const cutoff = Date.now() - rangeToMs(filters.timeRange);
|
|
521
|
+
rows = rows.filter((r) => r.timestamp >= cutoff);
|
|
522
|
+
}
|
|
523
|
+
const approved = rows.filter((r) => r.status === "approved").length;
|
|
524
|
+
const blocked = rows.length - approved;
|
|
525
|
+
const volume = rows.reduce((s, r) => s + r.amount, 0n);
|
|
526
|
+
return {
|
|
527
|
+
rows,
|
|
528
|
+
summary: { total: rows.length, approved, blocked, volume },
|
|
529
|
+
toJSON: () => ({
|
|
530
|
+
rows: rows.map((r) => r.toJSON()),
|
|
531
|
+
summary: { total: rows.length, approved, blocked, volume: bs(volume) },
|
|
532
|
+
}),
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
throw toDxError(err, "OwnerClient.getActivity");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function mapCategory(cat, evt, actionType, positionEffect) {
|
|
540
|
+
if (cat === "trade") {
|
|
541
|
+
// v6 events carry positionEffect ("increment"/"decrement"/"none") instead
|
|
542
|
+
// of actionType. Prefer it when present — actionType is null for post-
|
|
543
|
+
// ActionType-elimination events.
|
|
544
|
+
if (positionEffect === "increment")
|
|
545
|
+
return "open_position";
|
|
546
|
+
if (positionEffect === "decrement")
|
|
547
|
+
return "close_position";
|
|
548
|
+
// Legacy v5 event path: actionType carries swap vs lend vs perps detail.
|
|
549
|
+
if (actionType) {
|
|
550
|
+
const at = actionType.toLowerCase();
|
|
551
|
+
if (at.includes("lend") ||
|
|
552
|
+
at.includes("deposit") ||
|
|
553
|
+
at.includes("withdraw"))
|
|
554
|
+
return "lend";
|
|
555
|
+
if (at.includes("open") ||
|
|
556
|
+
at === "openposition" ||
|
|
557
|
+
at === "swapandopenposition")
|
|
558
|
+
return "open_position";
|
|
559
|
+
if (at.includes("close") || at === "closeposition")
|
|
560
|
+
return "close_position";
|
|
561
|
+
}
|
|
562
|
+
if (evt.includes("Open"))
|
|
563
|
+
return "open_position";
|
|
564
|
+
if (evt.includes("Close"))
|
|
565
|
+
return "close_position";
|
|
566
|
+
return "swap";
|
|
567
|
+
}
|
|
568
|
+
if (cat === "deposit")
|
|
569
|
+
return "deposit";
|
|
570
|
+
if (cat === "withdrawal")
|
|
571
|
+
return "withdraw";
|
|
572
|
+
if (evt === "AgentTransferExecuted")
|
|
573
|
+
return "transfer";
|
|
574
|
+
return "swap";
|
|
575
|
+
}
|
|
576
|
+
function rangeToMs(r) {
|
|
577
|
+
const map = {
|
|
578
|
+
"1h": 3600000,
|
|
579
|
+
"6h": 21600000,
|
|
580
|
+
"24h": 86400000,
|
|
581
|
+
"7d": 604800000,
|
|
582
|
+
"30d": 2592000000,
|
|
583
|
+
};
|
|
584
|
+
return map[r] ?? 86400000;
|
|
585
|
+
}
|
|
586
|
+
// ─── getHealth ───────────────────────────────────────────────────────────────
|
|
587
|
+
export async function getHealth(rpc, vault, network) {
|
|
588
|
+
try {
|
|
589
|
+
const state = await resolveVaultStateForOwner(rpc, vault, undefined, toNet(network));
|
|
590
|
+
return buildHealth({ vault, state });
|
|
591
|
+
}
|
|
592
|
+
catch (err) {
|
|
593
|
+
throw toDxError(err, "OwnerClient.getHealth");
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// ─── getPolicy ───────────────────────────────────────────────────────────────
|
|
597
|
+
export async function getPolicy(rpc, vault, network) {
|
|
598
|
+
try {
|
|
599
|
+
const [state, pendingPolicy] = await Promise.all([
|
|
600
|
+
resolveVaultStateForOwner(rpc, vault, undefined, toNet(network)),
|
|
601
|
+
getPendingPolicyForVault(rpc, vault).catch((err) => {
|
|
602
|
+
// Account-not-found is expected (no pending update) — return null.
|
|
603
|
+
// Re-throw RPC errors so they're not silently swallowed.
|
|
604
|
+
if (isAccountNotFoundError(err))
|
|
605
|
+
return null;
|
|
606
|
+
throw err;
|
|
607
|
+
}),
|
|
608
|
+
]);
|
|
609
|
+
return buildPolicy({ vault, state, pendingPolicy });
|
|
610
|
+
}
|
|
611
|
+
catch (err) {
|
|
612
|
+
throw toDxError(err, "OwnerClient.getPolicy");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// ─── getOverview (S14) ───────────────────────────────────────────────────────
|
|
616
|
+
/**
|
|
617
|
+
* Single-call overview bundle — resolves vault state once, composes all five
|
|
618
|
+
* view types (vault, agents, spending, health, policy) plus a raw activity
|
|
619
|
+
* list, with PnL derived from the resolved state (no duplicate resolve).
|
|
620
|
+
*
|
|
621
|
+
* **Actual RPC shape.** Calling the five individual reads duplicates the
|
|
622
|
+
* vault-state resolution up to five times. `getOverview` resolves state
|
|
623
|
+
* exactly once and computes PnL from it via {@link getVaultPnLFromState}.
|
|
624
|
+
* The activity fetch is independent: `getVaultActivity(limit)` issues one
|
|
625
|
+
* `getSignaturesForAddress` followed by up to `limit` sequential
|
|
626
|
+
* `getTransaction` calls, so the wall-time cost of the activity feed
|
|
627
|
+
* dominates regardless of this method. Net savings vs. five separate reads:
|
|
628
|
+
* state resolution count drops from ~5 → 1.
|
|
629
|
+
*
|
|
630
|
+
* Activity is **unfiltered**. For filtered activity, call {@link getActivity}
|
|
631
|
+
* with `ActivityFilters`.
|
|
632
|
+
*
|
|
633
|
+
* Graceful degradation: activity fetch failure degrades to empty activity
|
|
634
|
+
* (same observable pattern as `getAgents`, documented in
|
|
635
|
+
* `docs/SECURITY-FINDINGS-2026-04-07.md` Finding 5); pending-policy
|
|
636
|
+
* account-not-found is treated as "no pending update" (same as `getPolicy`).
|
|
637
|
+
* **PnL and state-resolution errors are NOT degraded** and propagate via
|
|
638
|
+
* `toDxError`. A pending-policy error that is NOT account-not-found (e.g.
|
|
639
|
+
* network failure) also propagates — it is NOT treated as "no pending
|
|
640
|
+
* update", even on the `includeActivity: false` lightweight path.
|
|
641
|
+
*/
|
|
642
|
+
export async function getOverview(rpc, vault, network, options) {
|
|
643
|
+
try {
|
|
644
|
+
const includeActivity = options?.includeActivity ?? true;
|
|
645
|
+
const activityLimit = options?.activityLimit ?? DEFAULT_OVERVIEW_ACTIVITY_LIMIT;
|
|
646
|
+
const net = toNet(network);
|
|
647
|
+
// Fan out every independent fetch in one Promise.all. State resolution,
|
|
648
|
+
// activity, and pending-policy have no cross-dependency, so wall time
|
|
649
|
+
// collapses to the slowest of the three. PnL is derived from state
|
|
650
|
+
// synchronously after — one state resolve, zero duplication.
|
|
651
|
+
const [state, activity, pendingPolicy] = await Promise.all([
|
|
652
|
+
resolveVaultStateForOwner(rpc, vault, undefined, net),
|
|
653
|
+
includeActivity
|
|
654
|
+
? getVaultActivity(rpc, vault, activityLimit, net).catch((err) => {
|
|
655
|
+
// Same graceful-degradation pattern as getAgents
|
|
656
|
+
// (docs/SECURITY-FINDINGS-2026-04-07.md Finding 5): activity is
|
|
657
|
+
// enrichment, not core, but the failure must be observable.
|
|
658
|
+
// eslint-disable-next-line no-console
|
|
659
|
+
console.warn("[OwnerClient.getOverview] activity fetch failed — falling back to empty:", err instanceof Error ? err.message : String(err));
|
|
660
|
+
return [];
|
|
661
|
+
})
|
|
662
|
+
: Promise.resolve(undefined),
|
|
663
|
+
getPendingPolicyForVault(rpc, vault).catch((err) => {
|
|
664
|
+
if (isAccountNotFoundError(err))
|
|
665
|
+
return null;
|
|
666
|
+
throw err;
|
|
667
|
+
}),
|
|
668
|
+
]);
|
|
669
|
+
// PnL is pure from resolved state — no extra RPC.
|
|
670
|
+
const pnl = getVaultPnLFromState(state);
|
|
671
|
+
// Compute the three state-derived values exactly once and memoize on ctx.
|
|
672
|
+
// Every build* helper reads these via the `ctx.field ?? derive()` fallback
|
|
673
|
+
// so the memoized value short-circuits re-derivation.
|
|
674
|
+
const posture = getSecurityPosture(asVaultState(state));
|
|
675
|
+
const breakdown = getSpendingBreakdown(asVaultState(state));
|
|
676
|
+
const alerts = evaluateAlertConditions(state, vault);
|
|
677
|
+
const ctx = {
|
|
678
|
+
vault,
|
|
679
|
+
state,
|
|
680
|
+
pnl,
|
|
681
|
+
activity,
|
|
682
|
+
pendingPolicy,
|
|
683
|
+
posture,
|
|
684
|
+
breakdown,
|
|
685
|
+
alerts,
|
|
686
|
+
};
|
|
687
|
+
const vaultView = buildVaultState(ctx);
|
|
688
|
+
const agentsView = buildAgents(ctx);
|
|
689
|
+
const spendingView = buildSpending(ctx);
|
|
690
|
+
const healthView = buildHealth(ctx);
|
|
691
|
+
const policyView = buildPolicy(ctx);
|
|
692
|
+
const activityRows = buildActivityRows(activity ?? []);
|
|
693
|
+
return {
|
|
694
|
+
vault: vaultView,
|
|
695
|
+
agents: agentsView,
|
|
696
|
+
spending: spendingView,
|
|
697
|
+
health: healthView,
|
|
698
|
+
policy: policyView,
|
|
699
|
+
activity: activityRows,
|
|
700
|
+
toJSON: () => ({
|
|
701
|
+
vault: vaultView.toJSON(),
|
|
702
|
+
agents: agentsView.map((a) => a.toJSON()),
|
|
703
|
+
spending: spendingView.toJSON(),
|
|
704
|
+
health: healthView.toJSON(),
|
|
705
|
+
policy: policyView.toJSON(),
|
|
706
|
+
activity: activityRows.map((r) => r.toJSON()),
|
|
707
|
+
}),
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
catch (err) {
|
|
711
|
+
throw toDxError(err, "OwnerClient.getOverview");
|
|
712
|
+
}
|
|
713
|
+
}
|
|
411
714
|
//# sourceMappingURL=reads.js.map
|