@capxul/sdk 4.2.0-rc.10 → 4.2.0-rc.11
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/dist/{OAuthBearerAuthClient-IB2W1Fzj.mjs → OAuthBearerAuthClient-CbU_W9Sp.mjs} +5 -1
- package/dist/capxul-source.json +3 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/node/index.mjs +1 -1
- package/dist/{production-BndbwuVx.mjs → production-7jr-KUvX.mjs} +630 -72
- package/dist/{production-Du4Vc7YD.d.mts → production-A1zW1JPt.d.mts} +11 -0
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -2
- package/package.json +6 -6
|
@@ -985,6 +985,10 @@ function toChainId(raw) {
|
|
|
985
985
|
if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw <= 0) throw Errors.invalidInput("chainId", invalidValueReason("must be a positive safe integer", raw));
|
|
986
986
|
return raw;
|
|
987
987
|
}
|
|
988
|
+
function toWeiAmount(raw) {
|
|
989
|
+
if (typeof raw !== "string" || !WEI_RE.test(raw)) throw Errors.invalidInput("weiAmount", invalidValueReason("must be a non-negative integer string", raw));
|
|
990
|
+
return raw;
|
|
991
|
+
}
|
|
988
992
|
function toCountryCode(raw) {
|
|
989
993
|
if (typeof raw !== "string") throw Errors.invalidInput("countryCode", "must be a string");
|
|
990
994
|
const upper = raw.toUpperCase();
|
|
@@ -2468,4 +2472,4 @@ function readExchange(body) {
|
|
|
2468
2472
|
};
|
|
2469
2473
|
}
|
|
2470
2474
|
//#endregion
|
|
2471
|
-
export { toRoleKey as $, currencySymbolFor as A, toCurrencyCode as B, ASSET_ID_RE as C,
|
|
2475
|
+
export { toRoleKey as $, currencySymbolFor as A, toCurrencyCode as B, ASSET_ID_RE as C, isCredentialField as Ct, WEI_RE as D, SUPPORTED_CURRENCY_CODES as E, toAssetId as F, toHandle as G, toEmail as H, toAuthUserId as I, toOrgId as J, toJwtToken as K, toBudgetId as L, toAddress as M, toAllowedOrigin as N, ZERO_BYTES32 as O, toAppId as P, toPublishableKey as Q, toChainId as R, APP_ID_RE as S, containsSensitiveMaterial as St, EVM_ADDRESS_RE as T, redactUrlSecrets as Tt, toEpochMs as U, toDurationMs as V, toEpochSeconds as W, toPayrollGroupId as X, toPartyId as Y, toPayrollRunId as Z, normalizeBindingEmail as _, decodeChainCause as _t, parseAuthSession as a, HANDLE_RE as at, configuredMoneyAssetById as b, isFailureMode as bt, AuthCachePortTag as c, CapxulError as ct, authClientPortFromPromiseAdapter as d, isCapxulError as dt, toSessionToken as et, AuthClientError as f, CHAIN_UPSTREAMS as ft, CAPXUL_PAYMENTS_V2_ADDRESS as g, chainEvidenceLabel as gt, orgRoleKeyForLabel as h, chainCauseProperties as ht, BrowserAuthCacheAdapter as i, validateHandle as it, toAccountId as j, assetIdFor as k, SystemClockLayer as l, EXPECTED_OPERATION_OUTCOMES as lt, ADMIN_ROLE_LABEL as m, boundedResponseHeaders as mt, readClockNow as n, toTxHash as nt, parseCachedJwt as o, decodeConvexError as ot, AuthClientPortTag as p, FAILURE_MODES as pt, toKycTier as q, InMemoryAuthCacheAdapter as r, toWeiAmount as rt, AuthCacheError as s, CAPXUL_ERROR_CODES as st, oauthBearerAuthClient as t, toTesterKind as tt, ClockPortTag as u, Errors as ut, BASE_SEPOLIA_CHAIN_ID as v, failureFingerprint as vt, BYTES32_RE as w, redactSecrets as wt, ACCOUNT_ID_RE as x, revertSummaryText as xt, deriveCapxulSafeAddress as y, isChainUpstream as yt, toCountryCode as z };
|
package/dist/capxul-source.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": {
|
|
3
|
-
"commit": "
|
|
4
|
-
"tree": "
|
|
3
|
+
"commit": "12a590f10914e8b056ce54732e6f484f36e060f3",
|
|
4
|
+
"tree": "90adc6656be3c1fb0a6e18170877d46081fa2538",
|
|
5
5
|
"branch": "codex/programme-rc",
|
|
6
6
|
"repository": "https://github.com/Xelmar-tech/infrastructure",
|
|
7
7
|
"lockfileSha256": "5870f53b72706d103220c1bcf733cb4fb71196aea32a9cfbdc277d73cc959264"
|
|
8
8
|
},
|
|
9
9
|
"name": "@capxul/sdk",
|
|
10
|
-
"version": "4.2.0-rc.
|
|
10
|
+
"version": "4.2.0-rc.11"
|
|
11
11
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $ as PayrollGroupId, B as ChainId, Ct as ChainCause, D as Account, Dt as RevertSummary, E as CAPXUL_PAYMENTS_V2_ADDRESS, Et as FailureMode, F as AssetId, G as EVM_ADDRESS_RE, H as CurrencyCode, I as AuthSession, J as Handle, L as AuthUserId, O as AccountId, P as AssetAmount, St as CHAIN_UPSTREAMS, Tt as FAILURE_MODES, V as CountryCode, X as OrgId, Y as Money, Z as PartyId, _ as Session, _t as CapxulErrorCode, a as Eip1193RequestProvider, at as RoleKey, c as injectedWalletSigner, ct as TesterKind, d as AccountRequirement, dt as toAddress, et as PayrollRunId, f as Eip1193Provider, ft as toCountryCode, g as Profile, gt as CapxulError, h as CapxulResult, ht as CAPXUL_ERROR_CODES, i as CapxulSigner, k as Address, l as AccountProvider, m as localPrivateKeyAccountProvider, mt as toPartyId, o as SignerStatus, p as eip1193AccountProvider, pt as toHandle, r as CapxulDigestSigner, s as SignerStatusStore, u as AccountProviderSource, v as SmartAccount, vt as CapxulErrorDetails, wt as ChainUpstream, xt as isCapxulError, yt as Errors, z as BudgetId } from "./OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
|
-
import { $ as OrganizationPaymentBatchInput, $n as RequestStatus, $t as Destination, A as OrgScopedMethods, An as Ref, At as AddressBookAddInput, B as AuthorizeRunOptions, Bn as SmartAccountMethods, Bt as ActivityFilter, C as CurrentUserMethods, Cn as PaymentMoney, Ct as AccountMethods, D as MemberStatus, Dn as PaymentsMethods, Dt as ActorRequest, E as InviteMemberInput, En as PaymentType, Et as ActorRelationshipMethods, F as ResendInviteTokenInput, Fn as AccountLifecycle, Ft as InboxItem, G as PayrollGroupsMethods, Gn as ActorRef, Gt as ActivityPage, H as PayrollGroup, Hn as OrgLifecycle, Ht as ActivityKind, I as RoleDefinition, In as AccountSetupStep, It as InboxMethods, J as PayrollRun, Jn as SubmittedPermissionExecution, Jt as ActivitySummary, K as PayrollMethods, Kn as CurrentHoldings, Kt as ActivityRange, L as RoleSpendCap, Ln as isSettingUpLifecycle, Lt as ActivityAnnotation, M as OrgView, Mn as TargetReference, Mt as AddressBookLabelInput, N as OrganizationAccount, Nn as TargetsMethods, Nt as AddressBookMethods, O as MemberView, On as PaymentsPayInput, Ot as ActorRequestIssueInput, P as OrganizationAuditLogItem, Pn as fingerprintPaymentIntent, Pt as InboxApproveInput, Q as PayrollTermsUnit, Qn as PaymentStatus$1, Qt as DepositInstructions, R as RoleView, Rn as CompleteProfileInput, Rt as ActivityAnnotationInput, S as CurrentUserContext, Sn as PaymentDocumentsMethods, St as ReadyAccountLifecycle, T as DetectPendingOrgInvitationsResult, Tn as PaymentTiming, Tt as ActorProfileMethods, U as PayrollGroupInput, Un as OrgSetupStep, Ut as ActivityListParams, V as PayrollEngagementTerms, Vn as AuthMethods, Vt as ActivityItem, W as PayrollGroupMember, Wn as PayoutAddress, Wt as ActivityMethods, X as PayrollRunStatus, Xn as PAYMENT_DIRECTIONS, Xt as ActivitySummaryTotal, Y as PayrollRunItemInput, Yn as InboxStatus, Yt as ActivitySummaryParams, Z as PayrollRuns, Zn as PAYMENT_STATUSES, Zt as ActorReference, _ as HoldingsMethods, _n as PaymentDirection, _t as OnboardingMethods, a as ObservationContext, an as DestinationRemoveInput, ar as IdentityEvent, at as PermissionCreateInput, b as SystemHealth, bn as PaymentDocumentRender, bt as PersonOnboarding, c as HostObservability, cn as MeMethods, cr as Readiness, ct as PermissionReplaceInput, d as postHogObservability, dn as OfframpMethods, dr as isClaimed, dt as Budget, en as DestinationAddInput, et as OrganizationPaymentInput, f as CapxulClient, fn as OfframpQuote, fr as isRestoring, ft as OrgMe, g as IdentityRuntimeSendResult, gn as PaymentActivityEvidence, gt as CompletedPersonProfile, h as IdentityRuntime, hn as Payment, ht as AccountsMethods, i as ObservationAdapter, in as DestinationRail, ir as Destination$1, it as PermissionChangeInput, j as OrgTemplate, jn as ResolvedTarget, jt as AddressBookEntry, k as OrgMethods, kn as RecipientResolution, kt as ActorRequestsMethods, l as PostHogObservabilityClient, ln as MeProfile, lr as StateLabel, lt as PermissionRevokeInput, m as IdentityProfileDetails, mn as OfframpStatus, mr as InvocationControls, mt as OrgMeOptions, nn as DestinationListInput, nt as OrganizationPaymentsMethods, o as ObservationDelivery, on as DestinationsMethods, or as IdentityState, ot as PermissionMethods, p as CreateCapxulClientInput, pn as OfframpQuoteInput, pr as IdentityTransition, pt as OrgMeMethod, q as PayrollOptions, qn as Permission, qt as ActivityReference, r as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, rn as DestinationPayload, rr as TelemetryPort, rt as PermissionAssignInput, s as SdkFailureObservation, sn as FinancialOpsMethods, sr as OrgLane, st as PermissionOptions, t as CapxulClientInput, tn as DestinationKind, tt as OrganizationPaymentItemInput, u as PostHogObservabilityOptions, un as MovementActivityEvidence, ur as destination, ut as PermissionReadResult, v as Holding, vn as PaymentDocumentKind, vt as OrganizationOnboarding, w as CreateOrgInput, wn as PaymentStatus, wt as ActorProfile, x as MediaMethods, xn as PaymentDocumentVerification, xt as PersonOnboardingInput, y as SystemMethods, yn as PaymentDocumentRef, yt as OrganizationOnboardingInput, z as AuthorizeRunInput, zn as IdentityMethods, zt as ActivityDetail } from "./production-
|
|
2
|
+
import { $ as OrganizationPaymentBatchInput, $n as RequestStatus, $t as Destination, A as OrgScopedMethods, An as Ref, At as AddressBookAddInput, B as AuthorizeRunOptions, Bn as SmartAccountMethods, Bt as ActivityFilter, C as CurrentUserMethods, Cn as PaymentMoney, Ct as AccountMethods, D as MemberStatus, Dn as PaymentsMethods, Dt as ActorRequest, E as InviteMemberInput, En as PaymentType, Et as ActorRelationshipMethods, F as ResendInviteTokenInput, Fn as AccountLifecycle, Ft as InboxItem, G as PayrollGroupsMethods, Gn as ActorRef, Gt as ActivityPage, H as PayrollGroup, Hn as OrgLifecycle, Ht as ActivityKind, I as RoleDefinition, In as AccountSetupStep, It as InboxMethods, J as PayrollRun, Jn as SubmittedPermissionExecution, Jt as ActivitySummary, K as PayrollMethods, Kn as CurrentHoldings, Kt as ActivityRange, L as RoleSpendCap, Ln as isSettingUpLifecycle, Lt as ActivityAnnotation, M as OrgView, Mn as TargetReference, Mt as AddressBookLabelInput, N as OrganizationAccount, Nn as TargetsMethods, Nt as AddressBookMethods, O as MemberView, On as PaymentsPayInput, Ot as ActorRequestIssueInput, P as OrganizationAuditLogItem, Pn as fingerprintPaymentIntent, Pt as InboxApproveInput, Q as PayrollTermsUnit, Qn as PaymentStatus$1, Qt as DepositInstructions, R as RoleView, Rn as CompleteProfileInput, Rt as ActivityAnnotationInput, S as CurrentUserContext, Sn as PaymentDocumentsMethods, St as ReadyAccountLifecycle, T as DetectPendingOrgInvitationsResult, Tn as PaymentTiming, Tt as ActorProfileMethods, U as PayrollGroupInput, Un as OrgSetupStep, Ut as ActivityListParams, V as PayrollEngagementTerms, Vn as AuthMethods, Vt as ActivityItem, W as PayrollGroupMember, Wn as PayoutAddress, Wt as ActivityMethods, X as PayrollRunStatus, Xn as PAYMENT_DIRECTIONS, Xt as ActivitySummaryTotal, Y as PayrollRunItemInput, Yn as InboxStatus, Yt as ActivitySummaryParams, Z as PayrollRuns, Zn as PAYMENT_STATUSES, Zt as ActorReference, _ as HoldingsMethods, _n as PaymentDirection, _t as OnboardingMethods, a as ObservationContext, an as DestinationRemoveInput, ar as IdentityEvent, at as PermissionCreateInput, b as SystemHealth, bn as PaymentDocumentRender, bt as PersonOnboarding, c as HostObservability, cn as MeMethods, cr as Readiness, ct as PermissionReplaceInput, d as postHogObservability, dn as OfframpMethods, dr as isClaimed, dt as Budget, en as DestinationAddInput, et as OrganizationPaymentInput, f as CapxulClient, fn as OfframpQuote, fr as isRestoring, ft as OrgMe, g as IdentityRuntimeSendResult, gn as PaymentActivityEvidence, gt as CompletedPersonProfile, h as IdentityRuntime, hn as Payment, ht as AccountsMethods, i as ObservationAdapter, in as DestinationRail, ir as Destination$1, it as PermissionChangeInput, j as OrgTemplate, jn as ResolvedTarget, jt as AddressBookEntry, k as OrgMethods, kn as RecipientResolution, kt as ActorRequestsMethods, l as PostHogObservabilityClient, ln as MeProfile, lr as StateLabel, lt as PermissionRevokeInput, m as IdentityProfileDetails, mn as OfframpStatus, mr as InvocationControls, mt as OrgMeOptions, nn as DestinationListInput, nt as OrganizationPaymentsMethods, o as ObservationDelivery, on as DestinationsMethods, or as IdentityState, ot as PermissionMethods, p as CreateCapxulClientInput, pn as OfframpQuoteInput, pr as IdentityTransition, pt as OrgMeMethod, q as PayrollOptions, qn as Permission, qt as ActivityReference, r as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, rn as DestinationPayload, rr as TelemetryPort, rt as PermissionAssignInput, s as SdkFailureObservation, sn as FinancialOpsMethods, sr as OrgLane, st as PermissionOptions, t as CapxulClientInput, tn as DestinationKind, tt as OrganizationPaymentItemInput, u as PostHogObservabilityOptions, un as MovementActivityEvidence, ur as destination, ut as PermissionReadResult, v as Holding, vn as PaymentDocumentKind, vt as OrganizationOnboarding, w as CreateOrgInput, wn as PaymentStatus, wt as ActorProfile, x as MediaMethods, xn as PaymentDocumentVerification, xt as PersonOnboardingInput, y as SystemMethods, yn as PaymentDocumentRef, yt as OrganizationOnboardingInput, z as AuthorizeRunInput, zn as IdentityMethods, zt as ActivityDetail } from "./production-A1zW1JPt.mjs";
|
|
3
3
|
import { Hex } from "viem";
|
|
4
4
|
import { Effect, Layer } from "effect";
|
|
5
5
|
//#region ../errors/src/index.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as signerFailure, B as isRestoring, C as normalizeExceptionErrorKind, E as safeExceptionLabel, F as CAPXUL_OPERATIONS, I as isCapxulOperation, L as normalizeCapxulOperation, M as PAYMENT_DIRECTIONS, N as PAYMENT_STATUSES, O as isSettingUpLifecycle, R as destination, S as SDK_VERSION, T as projectSdkException, _ as fingerprintPaymentIntent, b as failureDetail, d as embeddedSigner, f as openfortEmbeddedSigner, g as devPrivateKeySigner, h as deriveDevPrivateKey, j as resolveFailureMode, k as injectedWalletSigner, m as openfortEmbeddedWalletPort, p as openfortEmbeddedSignerFromWallet, r as postHogObservability, t as createCapxulClient$1, w as normalizeExceptionOperation, x as EXCEPTION_MESSAGE, y as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, z as isClaimed } from "./production-
|
|
2
|
-
import { A as currencySymbolFor, B as toCurrencyCode, G as toHandle, M as toAddress, T as EVM_ADDRESS_RE, Y as toPartyId,
|
|
1
|
+
import { A as signerFailure, B as isRestoring, C as normalizeExceptionErrorKind, E as safeExceptionLabel, F as CAPXUL_OPERATIONS, I as isCapxulOperation, L as normalizeCapxulOperation, M as PAYMENT_DIRECTIONS, N as PAYMENT_STATUSES, O as isSettingUpLifecycle, R as destination, S as SDK_VERSION, T as projectSdkException, _ as fingerprintPaymentIntent, b as failureDetail, d as embeddedSigner, f as openfortEmbeddedSigner, g as devPrivateKeySigner, h as deriveDevPrivateKey, j as resolveFailureMode, k as injectedWalletSigner, m as openfortEmbeddedWalletPort, p as openfortEmbeddedSignerFromWallet, r as postHogObservability, t as createCapxulClient$1, w as normalizeExceptionOperation, x as EXCEPTION_MESSAGE, y as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, z as isClaimed } from "./production-7jr-KUvX.mjs";
|
|
2
|
+
import { A as currencySymbolFor, B as toCurrencyCode, G as toHandle, M as toAddress, T as EVM_ADDRESS_RE, Y as toPartyId, at as HANDLE_RE, b as configuredMoneyAssetById, ct as CapxulError, dt as isCapxulError, ft as CHAIN_UPSTREAMS, g as CAPXUL_PAYMENTS_V2_ADDRESS, k as assetIdFor, pt as FAILURE_MODES, st as CAPXUL_ERROR_CODES, ut as Errors, z as toCountryCode } from "./OAuthBearerAuthClient-CbU_W9Sp.mjs";
|
|
3
3
|
import { formatUnits } from "viem";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import { privateKeyToAccount } from "viem/accounts";
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as toAddress, a as parseAuthSession, c as AuthCachePortTag, i as BrowserAuthCacheAdapter, o as parseCachedJwt, r as InMemoryAuthCacheAdapter, s as AuthCacheError, t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-
|
|
1
|
+
import { M as toAddress, a as parseAuthSession, c as AuthCachePortTag, i as BrowserAuthCacheAdapter, o as parseCachedJwt, r as InMemoryAuthCacheAdapter, s as AuthCacheError, t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-CbU_W9Sp.mjs";
|
|
2
2
|
import { Effect, FileSystem, Layer, Path } from "effect";
|
|
3
3
|
import { privateKeyToAccount } from "viem/accounts";
|
|
4
4
|
import * as os from "node:os";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as toRoleKey, B as toCurrencyCode, C as ASSET_ID_RE, Ct as
|
|
1
|
+
import { $ as toRoleKey, B as toCurrencyCode, C as ASSET_ID_RE, Ct as isCredentialField, D as WEI_RE, E as SUPPORTED_CURRENCY_CODES, F as toAssetId, G as toHandle, H as toEmail, I as toAuthUserId, J as toOrgId, K as toJwtToken, L as toBudgetId, M as toAddress, N as toAllowedOrigin, O as ZERO_BYTES32, Q as toPublishableKey, R as toChainId, S as APP_ID_RE, St as containsSensitiveMaterial, T as EVM_ADDRESS_RE$1, Tt as redactUrlSecrets, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, X as toPayrollGroupId, Y as toPartyId, Z as toPayrollRunId, _ as normalizeBindingEmail, _t as decodeChainCause, b as configuredMoneyAssetById, bt as isFailureMode, c as AuthCachePortTag, ct as CapxulError, d as authClientPortFromPromiseAdapter, dt as isCapxulError, et as toSessionToken, f as AuthClientError, ft as CHAIN_UPSTREAMS, gt as chainEvidenceLabel, h as orgRoleKeyForLabel, ht as chainCauseProperties, i as BrowserAuthCacheAdapter, it as validateHandle, j as toAccountId, l as SystemClockLayer, lt as EXPECTED_OPERATION_OUTCOMES, m as ADMIN_ROLE_LABEL, mt as boundedResponseHeaders, nt as toTxHash, ot as decodeConvexError, p as AuthClientPortTag, pt as FAILURE_MODES$1, q as toKycTier, r as InMemoryAuthCacheAdapter, rt as toWeiAmount, st as CAPXUL_ERROR_CODES, tt as toTesterKind, u as ClockPortTag, ut as Errors, v as BASE_SEPOLIA_CHAIN_ID, vt as failureFingerprint, w as BYTES32_RE, wt as redactSecrets, x as ACCOUNT_ID_RE, xt as revertSummaryText, y as deriveCapxulSafeAddress, yt as isChainUpstream, z as toCountryCode } from "./OAuthBearerAuthClient-CbU_W9Sp.mjs";
|
|
2
2
|
import { formatUnits, keccak256, parseUnits, recoverAddress, stringToHex, toBytes } from "viem";
|
|
3
3
|
import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
|
|
4
4
|
import { getFunctionName, makeFunctionReference } from "convex/server";
|
|
@@ -1363,7 +1363,27 @@ const PermissionMirrorVerificationProps = Schema.Struct({
|
|
|
1363
1363
|
"conflicting_replay",
|
|
1364
1364
|
"revision_mismatch"
|
|
1365
1365
|
])),
|
|
1366
|
-
command_id: OptionalString
|
|
1366
|
+
command_id: OptionalString,
|
|
1367
|
+
/**
|
|
1368
|
+
* #1961 R03. The authorized stored row names the Permission it changed; the
|
|
1369
|
+
* verified receipt names the chain facts that prove it. Every field is
|
|
1370
|
+
* optional because a pending or provider-failed verification has neither an
|
|
1371
|
+
* application result nor a receipt to report.
|
|
1372
|
+
*/
|
|
1373
|
+
permission_id: OptionalString,
|
|
1374
|
+
permission_assignment_id: OptionalString,
|
|
1375
|
+
permission_operation: Schema.optional(Schema.Literals([
|
|
1376
|
+
"create",
|
|
1377
|
+
"change",
|
|
1378
|
+
"assign",
|
|
1379
|
+
"revoke",
|
|
1380
|
+
"replace"
|
|
1381
|
+
])),
|
|
1382
|
+
revision: Schema.optional(Schema.Number),
|
|
1383
|
+
user_op_hash: OptionalString,
|
|
1384
|
+
transaction_hash: OptionalString,
|
|
1385
|
+
block_number: Schema.optional(Schema.Number),
|
|
1386
|
+
log_index: Schema.optional(Schema.Number)
|
|
1367
1387
|
});
|
|
1368
1388
|
const MovementScanWindowProps = Schema.Struct({
|
|
1369
1389
|
...TelemetryEnvelopeProps,
|
|
@@ -2295,6 +2315,7 @@ const CAPXUL_FUNCTIONS = {
|
|
|
2295
2315
|
list: "movement/activity:list",
|
|
2296
2316
|
summary: "movement/activity:summary"
|
|
2297
2317
|
},
|
|
2318
|
+
"movement/annotations": { overlay: "movement/annotations:overlay" },
|
|
2298
2319
|
"moneyExecution/actions": {
|
|
2299
2320
|
abandonPaymentExecution: "moneyExecution/actions:abandonPaymentExecution",
|
|
2300
2321
|
preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
|
|
@@ -2893,7 +2914,19 @@ const StoredPermissionCommandSchema = Schema.Struct({
|
|
|
2893
2914
|
roleKey: RoleKeySchema,
|
|
2894
2915
|
allowanceKey: Schema.NullOr(AllowanceKeySchema),
|
|
2895
2916
|
memberSafe: Schema.NullOr(SafeAddressSchema),
|
|
2896
|
-
expectedAuthorityEvents: Schema.Array(Bytes32Schema)
|
|
2917
|
+
expectedAuthorityEvents: Schema.Array(Bytes32Schema),
|
|
2918
|
+
/**
|
|
2919
|
+
* #1961 R03. The authenticated user who created the command, and the browser
|
|
2920
|
+
* linkage captured with it. Delayed verification and bounded retries read
|
|
2921
|
+
* this snapshot instead of the current viewer, so one command keeps one
|
|
2922
|
+
* origin. Both are absent on commands created before this contract.
|
|
2923
|
+
*
|
|
2924
|
+
* The context stays `unknown` on purpose: `sanitizeObservationContext` owns
|
|
2925
|
+
* the field allowlist, and every reader passes the stored value through it
|
|
2926
|
+
* rather than repeating those rules here.
|
|
2927
|
+
*/
|
|
2928
|
+
originatingAuthUserId: Schema.optional(Schema.String),
|
|
2929
|
+
originatingObservationContext: Schema.optional(Schema.Unknown)
|
|
2897
2930
|
});
|
|
2898
2931
|
Schema.Struct({
|
|
2899
2932
|
command: StoredPermissionCommandSchema,
|
|
@@ -5666,6 +5699,7 @@ const financialOpsContract = {
|
|
|
5666
5699
|
activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
|
|
5667
5700
|
activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
|
|
5668
5701
|
activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
|
|
5702
|
+
activityOverlay: makeFunctionReference(CAPXUL_FUNCTIONS["movement/annotations"].overlay),
|
|
5669
5703
|
verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
|
|
5670
5704
|
renderStoredDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].renderStoredDocument)
|
|
5671
5705
|
};
|
|
@@ -5682,8 +5716,357 @@ const moneyExecutionContract = {
|
|
|
5682
5716
|
submitPaymentCommandExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].submitPaymentCommandExecution)
|
|
5683
5717
|
};
|
|
5684
5718
|
//#endregion
|
|
5719
|
+
//#region src/ports/indexer-read.ts
|
|
5720
|
+
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
5721
|
+
const INDEXER_MAX_LIMIT = 1e3;
|
|
5722
|
+
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
5723
|
+
/**
|
|
5724
|
+
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
5725
|
+
* round trip on it. Every implementation calls this, so a consumer test written
|
|
5726
|
+
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
5727
|
+
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
5728
|
+
*/
|
|
5729
|
+
function assertIndexerPage(input) {
|
|
5730
|
+
const safe = String(input.safe).toLowerCase();
|
|
5731
|
+
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
5732
|
+
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
|
|
5733
|
+
const offset = input.offset ?? 0;
|
|
5734
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
5735
|
+
return safe;
|
|
5736
|
+
}
|
|
5737
|
+
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
5738
|
+
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
5739
|
+
return new IndexerReadError({
|
|
5740
|
+
operation,
|
|
5741
|
+
publicCode: error.code,
|
|
5742
|
+
publicError: error,
|
|
5743
|
+
cause,
|
|
5744
|
+
...error.details === void 0 ? {} : { details: error.details }
|
|
5745
|
+
});
|
|
5746
|
+
}
|
|
5747
|
+
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
5748
|
+
//#endregion
|
|
5749
|
+
//#region src/surface/activity-stitch.ts
|
|
5750
|
+
/** Epoch milliseconds of the block that carried the row. */
|
|
5751
|
+
function blockMillis(row) {
|
|
5752
|
+
return Number(row.timestamp) * 1e3;
|
|
5753
|
+
}
|
|
5754
|
+
/** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
|
|
5755
|
+
function shortAddressLabel(address) {
|
|
5756
|
+
return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
5757
|
+
}
|
|
5758
|
+
function directionFor(safe, transfer) {
|
|
5759
|
+
const from = transfer.from.toLowerCase();
|
|
5760
|
+
const to = transfer.to.toLowerCase();
|
|
5761
|
+
if (from === safe && to === safe) return "self";
|
|
5762
|
+
return to === safe ? "in" : "out";
|
|
5763
|
+
}
|
|
5764
|
+
/**
|
|
5765
|
+
* The Transfer a protocol Payment moved is the same money as its `payment`
|
|
5766
|
+
* row. One actor sees one item for it, so the Transfer is claimed and does not
|
|
5767
|
+
* become a second row. Another transfer in the same transaction that this
|
|
5768
|
+
* payment did not move stays unclaimed and keeps its own row.
|
|
5769
|
+
*/
|
|
5770
|
+
function claimedTransferIds(transfers, payments) {
|
|
5771
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
5772
|
+
for (const payment of payments) {
|
|
5773
|
+
const match = transfers.find((transfer) => !claimed.has(transfer.id) && transfer.txHash === payment.txHash && transfer.token.toLowerCase() === payment.token.toLowerCase() && transfer.amount === payment.amount && transfer.to.toLowerCase() === payment.recipient.toLowerCase());
|
|
5774
|
+
if (match !== void 0) claimed.add(match.id);
|
|
5775
|
+
}
|
|
5776
|
+
return claimed;
|
|
5777
|
+
}
|
|
5778
|
+
function withAnnotation(item, annotation) {
|
|
5779
|
+
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
5780
|
+
...item,
|
|
5781
|
+
counterpartyLabel: annotation.counterpartyLabel
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5784
|
+
/**
|
|
5785
|
+
* Confirmation comes from the chain, never from Convex.
|
|
5786
|
+
*
|
|
5787
|
+
* A Ponder row for this Payment's settlement means settled, and its receipt is
|
|
5788
|
+
* the indexed transaction. No Ponder row and a Convex row that still says
|
|
5789
|
+
* settled means the block that carried it was dropped: the row goes back to
|
|
5790
|
+
* `settling` and its annotation, which keys on the Payment id, is untouched.
|
|
5791
|
+
*/
|
|
5792
|
+
function confirmPayment(intent, chain, hasSettlement) {
|
|
5793
|
+
if (chain !== void 0) return {
|
|
5794
|
+
...intent,
|
|
5795
|
+
status: "settled",
|
|
5796
|
+
receipt: {
|
|
5797
|
+
chainId: chain.chainId,
|
|
5798
|
+
transactionHash: chain.txHash
|
|
5799
|
+
},
|
|
5800
|
+
updatedAt: blockMillis(chain)
|
|
5801
|
+
};
|
|
5802
|
+
if (hasSettlement && intent.status === "settled") return {
|
|
5803
|
+
...intent,
|
|
5804
|
+
status: "settling",
|
|
5805
|
+
receipt: null
|
|
5806
|
+
};
|
|
5807
|
+
return intent;
|
|
5808
|
+
}
|
|
5809
|
+
/**
|
|
5810
|
+
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
5811
|
+
* denominate its asset. The indexer serves only configured assets, so a row
|
|
5812
|
+
* the registry does not know means the SDK and that deployment disagree; it is
|
|
5813
|
+
* dropped rather than shown with a guessed currency.
|
|
5814
|
+
*/
|
|
5815
|
+
function transferItem(safe, transfer, annotation) {
|
|
5816
|
+
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
5817
|
+
if (asset === null) return null;
|
|
5818
|
+
const direction = directionFor(safe, transfer);
|
|
5819
|
+
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
5820
|
+
const at = blockMillis(transfer);
|
|
5821
|
+
return withAnnotation({
|
|
5822
|
+
kind: "movement",
|
|
5823
|
+
id: transfer.id,
|
|
5824
|
+
classification: "movement_only_outflow",
|
|
5825
|
+
amount: {
|
|
5826
|
+
currency: asset.peg ?? asset.symbol,
|
|
5827
|
+
value: formatUnits(transfer.amount, asset.decimals),
|
|
5828
|
+
decimals: asset.decimals
|
|
5829
|
+
},
|
|
5830
|
+
direction,
|
|
5831
|
+
counterpartyLabel: shortAddressLabel(counterparty),
|
|
5832
|
+
receipt: {
|
|
5833
|
+
chainId: transfer.chainId,
|
|
5834
|
+
transactionHash: transfer.txHash
|
|
5835
|
+
},
|
|
5836
|
+
createdAt: at,
|
|
5837
|
+
updatedAt: at
|
|
5838
|
+
}, annotation);
|
|
5839
|
+
}
|
|
5840
|
+
/** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
|
|
5841
|
+
function compareItems(left, right) {
|
|
5842
|
+
if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
|
|
5843
|
+
if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
|
|
5844
|
+
if (left.id === right.id) return 0;
|
|
5845
|
+
return left.id < right.id ? 1 : -1;
|
|
5846
|
+
}
|
|
5847
|
+
function stitchActivity(input) {
|
|
5848
|
+
const safe = input.safe.toLowerCase();
|
|
5849
|
+
const chainBySettlement = /* @__PURE__ */ new Map();
|
|
5850
|
+
for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
|
|
5851
|
+
const chainByPaymentId = /* @__PURE__ */ new Map();
|
|
5852
|
+
const settledPaymentIds = /* @__PURE__ */ new Set();
|
|
5853
|
+
for (const link of input.links) {
|
|
5854
|
+
settledPaymentIds.add(link.paymentId);
|
|
5855
|
+
const row = chainBySettlement.get(link.settlementId.toLowerCase());
|
|
5856
|
+
if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
|
|
5857
|
+
}
|
|
5858
|
+
const annotations = /* @__PURE__ */ new Map();
|
|
5859
|
+
for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
|
|
5860
|
+
const items = [];
|
|
5861
|
+
const claiming = [];
|
|
5862
|
+
for (const intent of input.intents) {
|
|
5863
|
+
if (intent.kind !== "payment") continue;
|
|
5864
|
+
const chain = chainByPaymentId.get(intent.id);
|
|
5865
|
+
if (chain !== void 0) claiming.push(chain);
|
|
5866
|
+
items.push(withAnnotation(confirmPayment(intent, chain, settledPaymentIds.has(intent.id)), annotations.get(`payment:${intent.id}`)));
|
|
5867
|
+
}
|
|
5868
|
+
const claimed = claimedTransferIds(input.transfers, claiming);
|
|
5869
|
+
for (const transfer of input.transfers) {
|
|
5870
|
+
if (claimed.has(transfer.id)) continue;
|
|
5871
|
+
const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
|
|
5872
|
+
if (item !== null) items.push(item);
|
|
5873
|
+
}
|
|
5874
|
+
return items.sort(compareItems);
|
|
5875
|
+
}
|
|
5876
|
+
/** The reference ids one stitched page needs its Convex overlay for. */
|
|
5877
|
+
function overlayReferences(items) {
|
|
5878
|
+
return items.map((item) => ({
|
|
5879
|
+
kind: item.kind,
|
|
5880
|
+
id: item.id
|
|
5881
|
+
}));
|
|
5882
|
+
}
|
|
5883
|
+
/**
|
|
5884
|
+
* Money in, money out and the pending share, one bucket per currency and
|
|
5885
|
+
* precision. Two assets that peg to the same currency at different precision
|
|
5886
|
+
* stay distinct buckets: their raw units are not interchangeable.
|
|
5887
|
+
*/
|
|
5888
|
+
function summarizeItems(items) {
|
|
5889
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
5890
|
+
for (const item of items) {
|
|
5891
|
+
const key = `${item.amount.currency}:${String(item.amount.decimals)}`;
|
|
5892
|
+
const bucket = buckets.get(key) ?? {
|
|
5893
|
+
currency: item.amount.currency,
|
|
5894
|
+
decimals: item.amount.decimals,
|
|
5895
|
+
in: 0n,
|
|
5896
|
+
out: 0n,
|
|
5897
|
+
pendingIn: 0n
|
|
5898
|
+
};
|
|
5899
|
+
const raw = rawUnits(item.amount.value, item.amount.decimals);
|
|
5900
|
+
if (item.direction === "in") {
|
|
5901
|
+
bucket.in += raw;
|
|
5902
|
+
if (item.kind === "payment" && item.status !== "settled") bucket.pendingIn += raw;
|
|
5903
|
+
} else if (item.direction === "out") bucket.out += raw;
|
|
5904
|
+
buckets.set(key, bucket);
|
|
5905
|
+
}
|
|
5906
|
+
return [...buckets.values()].map((bucket) => ({
|
|
5907
|
+
currency: bucket.currency,
|
|
5908
|
+
decimals: bucket.decimals,
|
|
5909
|
+
in: bucket.in.toString(),
|
|
5910
|
+
out: bucket.out.toString(),
|
|
5911
|
+
net: (bucket.in - bucket.out).toString(),
|
|
5912
|
+
pendingIn: bucket.pendingIn.toString()
|
|
5913
|
+
}));
|
|
5914
|
+
}
|
|
5915
|
+
/** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
|
|
5916
|
+
function rawUnits(value, decimals) {
|
|
5917
|
+
const negative = value.startsWith("-");
|
|
5918
|
+
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
5919
|
+
const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
|
|
5920
|
+
return negative ? -raw : raw;
|
|
5921
|
+
}
|
|
5922
|
+
//#endregion
|
|
5923
|
+
//#region src/surface/activity-read.ts
|
|
5924
|
+
/** How many Convex intent rows one page reads for its labels and pending rows. */
|
|
5925
|
+
const INTENT_WINDOW = 100;
|
|
5926
|
+
/** The window a summary aggregates over. The indexer's own page bound. */
|
|
5927
|
+
const SUMMARY_WINDOW = INDEXER_MAX_LIMIT;
|
|
5928
|
+
function parseCursor(value) {
|
|
5929
|
+
if (value === void 0) return 0;
|
|
5930
|
+
try {
|
|
5931
|
+
const parsed = JSON.parse(value);
|
|
5932
|
+
return Number.isSafeInteger(parsed.offset) && parsed.offset >= 0 ? parsed.offset : 0;
|
|
5933
|
+
} catch {
|
|
5934
|
+
return 0;
|
|
5935
|
+
}
|
|
5936
|
+
}
|
|
5937
|
+
function inRange(item, range) {
|
|
5938
|
+
if (range?.from !== void 0 && item.createdAt < range.from) return false;
|
|
5939
|
+
return range?.to === void 0 || item.createdAt <= range.to;
|
|
5940
|
+
}
|
|
5941
|
+
/** The caller's filter applied to the merged page. The join owns the order. */
|
|
5942
|
+
function matchesFilter(item, filter) {
|
|
5943
|
+
if (filter === void 0) return true;
|
|
5944
|
+
if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
|
|
5945
|
+
if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
|
|
5946
|
+
if (filter.status === void 0) return true;
|
|
5947
|
+
return item.kind === "payment" && filter.status.includes(item.status);
|
|
5948
|
+
}
|
|
5949
|
+
function overlay(deps, actor, controls, input) {
|
|
5950
|
+
return deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
|
|
5951
|
+
...actor === void 0 ? {} : { actor },
|
|
5952
|
+
...input.references === void 0 ? {} : { references: input.references },
|
|
5953
|
+
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds }
|
|
5954
|
+
} }));
|
|
5955
|
+
}
|
|
5956
|
+
function convexPage(deps, actor, controls, input) {
|
|
5957
|
+
return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
|
|
5958
|
+
...actor === void 0 ? {} : { actor },
|
|
5959
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor },
|
|
5960
|
+
...input.limit === void 0 ? {} : { limit: input.limit },
|
|
5961
|
+
...input.range === void 0 ? {} : { range: input.range }
|
|
5962
|
+
} }));
|
|
5963
|
+
}
|
|
5964
|
+
/** Both indexed tables for one Safe, or `null` when the indexer cannot answer. */
|
|
5965
|
+
function chainRows(deps, safe, limit) {
|
|
5966
|
+
const page = {
|
|
5967
|
+
safe,
|
|
5968
|
+
limit: Math.min(limit, INDEXER_MAX_LIMIT)
|
|
5969
|
+
};
|
|
5970
|
+
return Effect.all([deps.indexer.read("transfer", page), deps.indexer.read("payment", page)], { concurrency: 2 }).pipe(Effect.map(([transfers, payments]) => ({
|
|
5971
|
+
transfers,
|
|
5972
|
+
payments
|
|
5973
|
+
})), Effect.catch(() => Effect.succeed(null)));
|
|
5974
|
+
}
|
|
5975
|
+
/** The stitched, filtered rows of one window, newest first. */
|
|
5976
|
+
function stitchedWindow(deps, actor, controls, window, params) {
|
|
5977
|
+
return Effect.gen(function* () {
|
|
5978
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
5979
|
+
const convex = yield* convexPage(deps, actor, controls, {
|
|
5980
|
+
limit: INTENT_WINDOW,
|
|
5981
|
+
...params.range === void 0 ? {} : { range: params.range }
|
|
5982
|
+
});
|
|
5983
|
+
const rows = scope.safe === null ? null : yield* chainRows(deps, scope.safe, window);
|
|
5984
|
+
if (rows === null || scope.safe === null) return {
|
|
5985
|
+
items: convex.items.filter((item) => matchesFilter(item, params.filter)),
|
|
5986
|
+
stitched: false
|
|
5987
|
+
};
|
|
5988
|
+
const decoration = yield* overlay(deps, actor, controls, {
|
|
5989
|
+
references: [...overlayReferences(convex.items), ...rows.transfers.map((row) => ({
|
|
5990
|
+
kind: "movement",
|
|
5991
|
+
id: row.id
|
|
5992
|
+
}))],
|
|
5993
|
+
settlementIds: rows.payments.map((row) => row.settlementId)
|
|
5994
|
+
});
|
|
5995
|
+
return {
|
|
5996
|
+
items: stitchActivity({
|
|
5997
|
+
safe: scope.safe,
|
|
5998
|
+
transfers: rows.transfers,
|
|
5999
|
+
payments: rows.payments,
|
|
6000
|
+
intents: convex.items,
|
|
6001
|
+
links: decoration.links,
|
|
6002
|
+
annotations: decoration.annotations
|
|
6003
|
+
}).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
|
|
6004
|
+
stitched: true
|
|
6005
|
+
};
|
|
6006
|
+
});
|
|
6007
|
+
}
|
|
6008
|
+
function readActivityPage(deps, actor, controls, params) {
|
|
6009
|
+
const limit = params?.limit ?? 25;
|
|
6010
|
+
const offset = parseCursor(params?.cursor);
|
|
6011
|
+
return stitchedWindow(deps, actor, controls, offset + limit + 1, {
|
|
6012
|
+
...params?.range === void 0 ? {} : { range: params.range },
|
|
6013
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
6014
|
+
}).pipe(Effect.map(({ items, stitched }) => {
|
|
6015
|
+
const observedAt = Date.now();
|
|
6016
|
+
if (!stitched) return {
|
|
6017
|
+
items: items.slice(0, limit),
|
|
6018
|
+
cursor: null,
|
|
6019
|
+
checkpoint: observedAt,
|
|
6020
|
+
observedAt
|
|
6021
|
+
};
|
|
6022
|
+
return {
|
|
6023
|
+
items: items.slice(offset, offset + limit),
|
|
6024
|
+
cursor: items.length > offset + limit ? JSON.stringify({ offset: offset + limit }) : null,
|
|
6025
|
+
checkpoint: observedAt,
|
|
6026
|
+
observedAt
|
|
6027
|
+
};
|
|
6028
|
+
}));
|
|
6029
|
+
}
|
|
6030
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
6031
|
+
return stitchedWindow(deps, actor, controls, SUMMARY_WINDOW, params?.window === void 0 ? {} : { range: params.window }).pipe(Effect.map(({ items }) => ({
|
|
6032
|
+
window: {
|
|
6033
|
+
from: params?.window?.from ?? null,
|
|
6034
|
+
to: params?.window?.to ?? null
|
|
6035
|
+
},
|
|
6036
|
+
totals: summarizeItems(items),
|
|
6037
|
+
count: items.length,
|
|
6038
|
+
observedAt: Date.now()
|
|
6039
|
+
})));
|
|
6040
|
+
}
|
|
6041
|
+
/**
|
|
6042
|
+
* Tell the caller when the indexed page for this Safe changed.
|
|
6043
|
+
*
|
|
6044
|
+
* The transport is the indexer's one SSE connection, wired in the Ponder
|
|
6045
|
+
* adapter under `activity.subscribe`. A reactive snapshot is not a product
|
|
6046
|
+
* event, so nothing here emits telemetry: the adapter reports a teardown, and
|
|
6047
|
+
* the caller re-reads.
|
|
6048
|
+
*/
|
|
6049
|
+
function subscribeActivity(deps, actor, controls, onChange) {
|
|
6050
|
+
return Effect.gen(function* () {
|
|
6051
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
6052
|
+
if (scope.safe === null) return () => {};
|
|
6053
|
+
const page = {
|
|
6054
|
+
safe: scope.safe,
|
|
6055
|
+
limit: INTENT_WINDOW
|
|
6056
|
+
};
|
|
6057
|
+
let delivered = false;
|
|
6058
|
+
const notify = () => {
|
|
6059
|
+
if (delivered) onChange();
|
|
6060
|
+
delivered = true;
|
|
6061
|
+
};
|
|
6062
|
+
return yield* deps.indexer.subscribe("transfer", page, (snapshot) => {
|
|
6063
|
+
if (snapshot.status === "ok") notify();
|
|
6064
|
+
});
|
|
6065
|
+
});
|
|
6066
|
+
}
|
|
6067
|
+
//#endregion
|
|
5685
6068
|
//#region package.json
|
|
5686
|
-
var version = "4.2.0-rc.
|
|
6069
|
+
var version = "4.2.0-rc.11";
|
|
5687
6070
|
//#endregion
|
|
5688
6071
|
//#region src/telemetry/exception-projection.ts
|
|
5689
6072
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -5881,10 +6264,15 @@ function observeFailedResult(result, adapter, operation, origin) {
|
|
|
5881
6264
|
* A reactive stream is torn down after its call already returned, so nothing
|
|
5882
6265
|
* downstream observes it. `captureException` is the right kind: the caller did
|
|
5883
6266
|
* not ask for the end, so it is not an expected product outcome.
|
|
6267
|
+
*
|
|
6268
|
+
* A caller that DID ask for the end — the session it authenticated with
|
|
6269
|
+
* expired, and the service closed the stream on purpose — passes
|
|
6270
|
+
* `kind: "operation"` instead, so the refusal lands in the expected-outcome
|
|
6271
|
+
* stream its code already declares.
|
|
5884
6272
|
*/
|
|
5885
|
-
function observeStreamFailure(adapter, operation, cause) {
|
|
6273
|
+
function observeStreamFailure(adapter, operation, cause, kind = "exception") {
|
|
5886
6274
|
if (adapter === void 0) return;
|
|
5887
|
-
report(adapter,
|
|
6275
|
+
report(adapter, kind, operation, cause, resolveAdapterSnapshot(adapter));
|
|
5888
6276
|
}
|
|
5889
6277
|
/**
|
|
5890
6278
|
* One failure, one event. The identity machine reports a failed transition
|
|
@@ -6989,6 +7377,11 @@ function paymentExecutionIntent(input) {
|
|
|
6989
7377
|
}
|
|
6990
7378
|
function makeFinancialOpsMethods(deps) {
|
|
6991
7379
|
const fns = deps.functions ?? financialOpsContract;
|
|
7380
|
+
const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
|
|
7381
|
+
indexer: deps.indexerRead,
|
|
7382
|
+
convexCall: deps.convexCall,
|
|
7383
|
+
functions: fns
|
|
7384
|
+
};
|
|
6992
7385
|
const executionFns = deps.moneyExecutionFunctions ?? moneyExecutionContract;
|
|
6993
7386
|
const requestKeyScope = deps.requestKeyScope ?? randomPaymentRequestKey();
|
|
6994
7387
|
const inFlightPersonalPayments = /* @__PURE__ */ new Map();
|
|
@@ -7219,22 +7612,26 @@ function makeFinancialOpsMethods(deps) {
|
|
|
7219
7612
|
}
|
|
7220
7613
|
},
|
|
7221
7614
|
activity: {
|
|
7615
|
+
subscribe: (onChange, params, options) => {
|
|
7616
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
7617
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => indexerDeps === void 0 ? Effect.succeed(() => {}) : Effect.suspend(() => subscribeActivity(indexerDeps, actor, controls, onChange)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.subscribe, deps.runPromise);
|
|
7618
|
+
},
|
|
7222
7619
|
list: (params, options) => {
|
|
7223
7620
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
7224
|
-
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
7621
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
|
|
7225
7622
|
...actor === void 0 ? {} : { actor },
|
|
7226
7623
|
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
7227
7624
|
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
7228
7625
|
...params?.range === void 0 ? {} : { range: params.range },
|
|
7229
7626
|
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
7230
|
-
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
7627
|
+
} })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
|
|
7231
7628
|
},
|
|
7232
7629
|
summary: (params, options) => {
|
|
7233
7630
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
7234
|
-
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
7631
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
|
|
7235
7632
|
...actor === void 0 ? {} : { actor },
|
|
7236
7633
|
...params?.window === void 0 ? {} : { window: params.window }
|
|
7237
|
-
} }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
7634
|
+
} })) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
|
|
7238
7635
|
},
|
|
7239
7636
|
get: (reference, options) => {
|
|
7240
7637
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
@@ -9041,6 +9438,9 @@ const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
9041
9438
|
"org/actions:resumeBootstrapSubmission",
|
|
9042
9439
|
"org/actions:confirmBootstrap",
|
|
9043
9440
|
"org/actions:readTreasury",
|
|
9441
|
+
"moneyExecution/actions:preparePermissionExecution",
|
|
9442
|
+
"moneyExecution/actions:submitPermissionExecution",
|
|
9443
|
+
"permission/actions:verify",
|
|
9044
9444
|
"moneyExecution/actions:preparePaymentExecution",
|
|
9045
9445
|
"moneyExecution/actions:abandonPaymentExecution",
|
|
9046
9446
|
"moneyExecution/actions:submitPaymentExecution",
|
|
@@ -9065,6 +9465,9 @@ const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
|
|
|
9065
9465
|
"payroll/queries:groups",
|
|
9066
9466
|
"payroll/queries:terms",
|
|
9067
9467
|
"financialOps/queries:resolveRecipient",
|
|
9468
|
+
"org/queries:me",
|
|
9469
|
+
"permission/queries:read",
|
|
9470
|
+
"permission/queries:authorize",
|
|
9068
9471
|
"identity/queries:loadByAuthUserId",
|
|
9069
9472
|
"smartAccount/queries:loadByAuthUserId",
|
|
9070
9473
|
"org/lifecycle:load"
|
|
@@ -9083,6 +9486,7 @@ const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
|
|
|
9083
9486
|
"financialOps/requestsInbox:decline",
|
|
9084
9487
|
"payroll/mutations:saveGroup",
|
|
9085
9488
|
"payroll/mutations:removeGroup",
|
|
9489
|
+
"permission/mutations:command",
|
|
9086
9490
|
"identity/mutations:create",
|
|
9087
9491
|
"identity/mutations:update",
|
|
9088
9492
|
"identity/mutations:completeOnboarding",
|
|
@@ -12139,8 +12543,33 @@ function toOrgMe(wire) {
|
|
|
12139
12543
|
observedAt: wire.observedAt
|
|
12140
12544
|
};
|
|
12141
12545
|
}
|
|
12546
|
+
/**
|
|
12547
|
+
* The read runs through the canonical observed runner, so `org.me` owns one
|
|
12548
|
+
* `capxul_sdk_operation` record and one span like every other public method.
|
|
12549
|
+
*
|
|
12550
|
+
* The call-start invocation snapshot travels on the query arguments: the
|
|
12551
|
+
* Convex adapter reads it there and sends the browser linkage with the call,
|
|
12552
|
+
* so an account switch mid-flight cannot re-resolve the actor. A successful
|
|
12553
|
+
* read proves participation in this Organization, so the owning span carries
|
|
12554
|
+
* the verified Organization and the two authority booleans the backend
|
|
12555
|
+
* actually enforces. Labels and Budget caps stay off the record.
|
|
12556
|
+
*/
|
|
12142
12557
|
function makeOrgMeMethod(deps, orgId) {
|
|
12143
|
-
return (options) =>
|
|
12558
|
+
return (options) => {
|
|
12559
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
12560
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
12561
|
+
const wire = yield* deps.convexCall.query(orgReadContract.me, copyInvocationObservation(controls, { orgId })).pipe(Effect.mapError((failure) => failure.publicError));
|
|
12562
|
+
yield* Effect.annotateCurrentSpan({
|
|
12563
|
+
verified_organization_id: orgId,
|
|
12564
|
+
scope_kind: "organization",
|
|
12565
|
+
can_manage_people: wire.capabilities.canManagePeople,
|
|
12566
|
+
can_spend: wire.capabilities.canSpend,
|
|
12567
|
+
budget_count: wire.budgets.length,
|
|
12568
|
+
observed_at: wire.observedAt
|
|
12569
|
+
}).pipe(Effect.catchCause(() => Effect.void));
|
|
12570
|
+
return toOrgMe(wire);
|
|
12571
|
+
}), controls, CAPXUL_OPERATIONS.org.me, deps.runPromise);
|
|
12572
|
+
};
|
|
12144
12573
|
}
|
|
12145
12574
|
//#endregion
|
|
12146
12575
|
//#region src/contract/permission.ts
|
|
@@ -12160,16 +12589,24 @@ function isAborted(signal) {
|
|
|
12160
12589
|
*/
|
|
12161
12590
|
function observed(deps, operation, options, run) {
|
|
12162
12591
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
12592
|
+
let verifiedOrganizationId;
|
|
12163
12593
|
return runCapxulEffect(Effect.gen(function* () {
|
|
12164
12594
|
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
12595
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
12596
|
+
const captured = carryInvocationParentSpan(controls, parent);
|
|
12165
12597
|
const result = yield* Effect.tryPromise({
|
|
12166
|
-
try: () => run(controls?.signal)
|
|
12598
|
+
try: () => run(controls?.signal, captured, (orgId) => {
|
|
12599
|
+
verifiedOrganizationId = orgId;
|
|
12600
|
+
}),
|
|
12167
12601
|
catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
|
|
12168
12602
|
});
|
|
12169
12603
|
return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
|
|
12170
|
-
})
|
|
12604
|
+
}).pipe(Effect.ensuring(Effect.suspend(() => verifiedOrganizationId === void 0 ? Effect.void : Effect.annotateCurrentSpan({
|
|
12605
|
+
verified_organization_id: verifiedOrganizationId,
|
|
12606
|
+
scope_kind: "organization"
|
|
12607
|
+
})).pipe(Effect.catchCause(() => Effect.void)))), observationOnlyControls(controls), operation, deps.runPromise);
|
|
12171
12608
|
}
|
|
12172
|
-
async function execute(deps, orgId, command, signal) {
|
|
12609
|
+
async function execute(deps, orgId, command, signal, controls, verifiedOrganization) {
|
|
12173
12610
|
if (isAborted(signal)) return {
|
|
12174
12611
|
ok: false,
|
|
12175
12612
|
error: Errors.cancelled({ operation: "permissions" })
|
|
@@ -12193,17 +12630,18 @@ async function execute(deps, orgId, command, signal) {
|
|
|
12193
12630
|
};
|
|
12194
12631
|
}
|
|
12195
12632
|
const executionFns = deps.executionFunctions ?? moneyExecutionContract;
|
|
12196
|
-
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => deps.convexCall.action(executionFns.preparePermissionExecution, { input: {
|
|
12633
|
+
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.preparePermissionExecution, copyInvocationObservation(controls, { input: {
|
|
12197
12634
|
orgId,
|
|
12198
12635
|
signerAddress,
|
|
12199
12636
|
command
|
|
12200
|
-
} }));
|
|
12637
|
+
} }))), controls), deps.runPromise);
|
|
12201
12638
|
if (!prepared.ok) return prepared;
|
|
12202
12639
|
const expectedSafe = deriveCapxulSafeAddress({ email: session.email });
|
|
12203
12640
|
if (prepared.value.chainId !== deps.chainId || prepared.value.signerAddress.toLowerCase() !== signerAddress.toLowerCase() || prepared.value.userOpSenderSafe.toLowerCase() !== expectedSafe.toLowerCase() || prepared.value.operation !== command.operation || prepared.value.orgId !== orgId || canonicalJson(prepared.value.command) !== canonicalJson(command)) return {
|
|
12204
12641
|
ok: false,
|
|
12205
12642
|
error: Errors.invalidInput("permission", "prepared authority mismatch")
|
|
12206
12643
|
};
|
|
12644
|
+
verifiedOrganization(prepared.value.orgId);
|
|
12207
12645
|
if (isAborted(signal)) return {
|
|
12208
12646
|
ok: false,
|
|
12209
12647
|
error: Errors.cancelled({ operation: "permissions" })
|
|
@@ -12221,38 +12659,40 @@ async function execute(deps, orgId, command, signal) {
|
|
|
12221
12659
|
ok: false,
|
|
12222
12660
|
error: Errors.cancelled({ operation: "permissions" })
|
|
12223
12661
|
};
|
|
12224
|
-
return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => deps.convexCall.action(executionFns.submitPermissionExecution, { input: {
|
|
12662
|
+
return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.submitPermissionExecution, copyInvocationObservation(controls, { input: {
|
|
12225
12663
|
executionId: prepared.value.executionId,
|
|
12226
12664
|
signature
|
|
12227
|
-
} }));
|
|
12665
|
+
} }))), controls), deps.runPromise);
|
|
12228
12666
|
}
|
|
12229
12667
|
function makePermissionMethods(deps, orgId) {
|
|
12230
12668
|
const reads = deps.permissionFunctions ?? permissionContract;
|
|
12231
12669
|
const ops = CAPXUL_OPERATIONS.org.permissions;
|
|
12670
|
+
const read = (signal, operation, controls) => runIfActive(signal, operation, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.query(reads.read, copyInvocationObservation(controls, { orgId }))), controls), deps.runPromise);
|
|
12671
|
+
const command = (operation, build, options) => observed(deps, operation, options, (signal, captured, verifiedOrganization) => execute(deps, orgId, build(), signal, captured, verifiedOrganization));
|
|
12232
12672
|
return {
|
|
12233
|
-
create: (input, options) =>
|
|
12673
|
+
create: (input, options) => command(ops.create, () => ({
|
|
12234
12674
|
operation: "create",
|
|
12235
12675
|
...input
|
|
12236
|
-
},
|
|
12237
|
-
change: (input, options) =>
|
|
12676
|
+
}), options),
|
|
12677
|
+
change: (input, options) => command(ops.change, () => ({
|
|
12238
12678
|
operation: "change",
|
|
12239
12679
|
...input
|
|
12240
|
-
},
|
|
12241
|
-
assign: (input, options) =>
|
|
12680
|
+
}), options),
|
|
12681
|
+
assign: (input, options) => command(ops.assign, () => ({
|
|
12242
12682
|
operation: "assign",
|
|
12243
12683
|
...input
|
|
12244
|
-
},
|
|
12245
|
-
revoke: (input, options) =>
|
|
12684
|
+
}), options),
|
|
12685
|
+
revoke: (input, options) => command(ops.revoke, () => ({
|
|
12246
12686
|
operation: "revoke",
|
|
12247
12687
|
...input
|
|
12248
|
-
},
|
|
12249
|
-
replace: (input, options) =>
|
|
12688
|
+
}), options),
|
|
12689
|
+
replace: (input, options) => command(ops.replace, () => ({
|
|
12250
12690
|
operation: "replace",
|
|
12251
12691
|
...input
|
|
12252
|
-
},
|
|
12253
|
-
list: (options) => observed(deps, ops.list, options, (signal) =>
|
|
12254
|
-
get: (permissionId, options) => observed(deps, ops.get, options, async (signal) => {
|
|
12255
|
-
const listed = await
|
|
12692
|
+
}), options),
|
|
12693
|
+
list: (options) => observed(deps, ops.list, options, (signal, captured) => read(signal, CAPXUL_OPERATIONS.permissions.list, captured)),
|
|
12694
|
+
get: (permissionId, options) => observed(deps, ops.get, options, async (signal, captured) => {
|
|
12695
|
+
const listed = await read(signal, CAPXUL_OPERATIONS.permissions.get, captured);
|
|
12256
12696
|
if (!listed.ok) return listed;
|
|
12257
12697
|
return {
|
|
12258
12698
|
ok: true,
|
|
@@ -13123,7 +13563,10 @@ function makeOrgMethods(deps) {
|
|
|
13123
13563
|
},
|
|
13124
13564
|
...convexCall === void 0 ? {} : { convexCall }
|
|
13125
13565
|
}),
|
|
13126
|
-
me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
|
|
13566
|
+
me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
|
|
13567
|
+
convexCall,
|
|
13568
|
+
...observedRunnerDeps(deps)
|
|
13569
|
+
}, String(orgId)),
|
|
13127
13570
|
async getLifecycle(options) {
|
|
13128
13571
|
if (options?.signal?.aborted) return {
|
|
13129
13572
|
ok: false,
|
|
@@ -13360,14 +13803,65 @@ function compareDecimal(first, second) {
|
|
|
13360
13803
|
}
|
|
13361
13804
|
//#endregion
|
|
13362
13805
|
//#region src/surface/holdings.ts
|
|
13806
|
+
/** SPEC Freshness: past this block age the read is labelled stale. Nothing refuses. */
|
|
13807
|
+
const FRESHNESS_LIMIT_SECONDS = 60;
|
|
13808
|
+
/**
|
|
13809
|
+
* The Ponder balance view for one Safe as `CurrentHoldings`.
|
|
13810
|
+
*
|
|
13811
|
+
* The view is the signed sum of every indexed transfer, so the balance IS what
|
|
13812
|
+
* the chain said — there is no snapshot to go stale behind it. `observedAt` is
|
|
13813
|
+
* the indexed block's own timestamp, which is what the last-good label reads,
|
|
13814
|
+
* and `stale` is that block's age. A stopped indexer therefore shows an old
|
|
13815
|
+
* "as of" and the stale flag; it never turns the read into a refusal.
|
|
13816
|
+
*/
|
|
13817
|
+
function indexerHoldings(indexer, convexCall, overlay, actor, controls) {
|
|
13818
|
+
return Effect.gen(function* () {
|
|
13819
|
+
const scope = yield* convexCall.query(overlay, copyInvocationObservation(controls, actor?.kind === "organization" ? { input: { actor: {
|
|
13820
|
+
kind: "organization",
|
|
13821
|
+
orgId: actor.organizationId
|
|
13822
|
+
} } } : { input: {} }));
|
|
13823
|
+
if (scope.safe === null) return null;
|
|
13824
|
+
const balances = yield* indexer.read("balance", {
|
|
13825
|
+
safe: toAddress(scope.safe),
|
|
13826
|
+
limit: INDEXER_MAX_LIMIT
|
|
13827
|
+
});
|
|
13828
|
+
const status = yield* indexer.status.pipe(Effect.catch(() => Effect.succeed([])));
|
|
13829
|
+
const rows = [];
|
|
13830
|
+
for (const balance of balances) {
|
|
13831
|
+
const asset = configuredMoneyAssetById(balance.assetId);
|
|
13832
|
+
if (asset === null || balance.amount < 0n) continue;
|
|
13833
|
+
rows.push({
|
|
13834
|
+
assetKind: "erc20",
|
|
13835
|
+
tokenAddress: toAddress(asset.tokenAddress),
|
|
13836
|
+
symbol: asset.symbol,
|
|
13837
|
+
decimals: asset.decimals,
|
|
13838
|
+
rawBalance: toWeiAmount(balance.amount.toString())
|
|
13839
|
+
});
|
|
13840
|
+
}
|
|
13841
|
+
const chainId = balances[0]?.chainId ?? status[0]?.chainId;
|
|
13842
|
+
if (chainId === void 0) return null;
|
|
13843
|
+
const head = status.find((entry) => entry.chainId === chainId);
|
|
13844
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
13845
|
+
const headSeconds = head === void 0 ? void 0 : Number(head.blockTimestamp);
|
|
13846
|
+
return {
|
|
13847
|
+
actor: scope.actor,
|
|
13848
|
+
chainId: toChainId(chainId),
|
|
13849
|
+
observedAt: headSeconds === void 0 ? Date.now() : headSeconds * 1e3,
|
|
13850
|
+
stale: headSeconds === void 0 || nowSeconds - headSeconds > FRESHNESS_LIMIT_SECONDS,
|
|
13851
|
+
rows
|
|
13852
|
+
};
|
|
13853
|
+
}).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
13854
|
+
}
|
|
13363
13855
|
function makeHoldingsMethods(deps) {
|
|
13364
13856
|
const functions = deps.functions ?? holdingsContract;
|
|
13857
|
+
const overlay = (deps.financialOpsFunctions ?? financialOpsContract).activityOverlay;
|
|
13365
13858
|
const read = (operation, actor, options, pick) => {
|
|
13366
13859
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
13367
|
-
|
|
13860
|
+
const convexSnapshot = retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
|
|
13368
13861
|
kind: "organization",
|
|
13369
13862
|
orgId: actor.organizationId
|
|
13370
|
-
} } : {})), operation, deps.telemetry, controls).pipe(Effect.
|
|
13863
|
+
} } : {})), operation, deps.telemetry, controls).pipe(Effect.mapError((error) => error.publicError));
|
|
13864
|
+
return runCapxulEffect((deps.indexerRead === void 0 ? convexSnapshot : indexerHoldings(deps.indexerRead, deps.convexCall, overlay, actor, controls).pipe(Effect.flatMap((current) => current === null ? convexSnapshot : Effect.succeed(current)))).pipe(Effect.map(pick)), controls, operation, deps.runPromise);
|
|
13371
13865
|
};
|
|
13372
13866
|
return {
|
|
13373
13867
|
current: (input, options) => read(CAPXUL_OPERATIONS.holdings.current, input?.actor, options, (snapshot) => snapshot),
|
|
@@ -13393,8 +13887,8 @@ const APPLIED_ACTION_BY_EVENT = {
|
|
|
13393
13887
|
RestoreSession: "none",
|
|
13394
13888
|
ResumeOtpEntry: "none",
|
|
13395
13889
|
Reset: "none",
|
|
13396
|
-
SessionRestored: "
|
|
13397
|
-
SessionAbsent: "
|
|
13890
|
+
SessionRestored: "restoration",
|
|
13891
|
+
SessionAbsent: "restoration",
|
|
13398
13892
|
EnsureAccount: "none",
|
|
13399
13893
|
ClaimAccount: "none",
|
|
13400
13894
|
RetryAccount: "none",
|
|
@@ -13406,8 +13900,8 @@ const APPLIED_ACTION_BY_EVENT = {
|
|
|
13406
13900
|
OtpFailed: "failed",
|
|
13407
13901
|
Verified: "verified",
|
|
13408
13902
|
VerifyFailed: "failed",
|
|
13409
|
-
SessionRead: "
|
|
13410
|
-
SessionReadFailed: "
|
|
13903
|
+
SessionRead: "restoration",
|
|
13904
|
+
SessionReadFailed: "restoration",
|
|
13411
13905
|
SignedOut: "signed-out",
|
|
13412
13906
|
SignOutFailed: "failed",
|
|
13413
13907
|
AccountAt: "none",
|
|
@@ -13433,6 +13927,49 @@ const authFailure = (record, reason, mode) => ({
|
|
|
13433
13927
|
...linkage(record)
|
|
13434
13928
|
}
|
|
13435
13929
|
});
|
|
13930
|
+
/**
|
|
13931
|
+
* The label of the restore window every client boots into. A settled session
|
|
13932
|
+
* fact that left this label is the restoration result (#1962 R04); the same
|
|
13933
|
+
* facts from a signed-out or authenticated state are the routine session probe
|
|
13934
|
+
* that `getSession()` runs, and a probe is not a restoration attempt.
|
|
13935
|
+
*/
|
|
13936
|
+
const RESTORE_WINDOW = "restoring";
|
|
13937
|
+
/**
|
|
13938
|
+
* Which result the window settled on. `SessionRestored` and `SessionAbsent` are
|
|
13939
|
+
* the cache door and say it themselves. `ReadSession` is also valid inside the
|
|
13940
|
+
* window — `getSession()` drives it on a cache miss and its `SessionRead`
|
|
13941
|
+
* completion settles the window — so that one event reports both halves and the
|
|
13942
|
+
* state it produced is what separates them.
|
|
13943
|
+
*/
|
|
13944
|
+
const restorationOutcome = (event, state) => {
|
|
13945
|
+
switch (event) {
|
|
13946
|
+
case "SessionRestored": return "restored";
|
|
13947
|
+
case "SessionAbsent": return "absent";
|
|
13948
|
+
case "SessionReadFailed": return "failed";
|
|
13949
|
+
case "SessionRead":
|
|
13950
|
+
if (state.phase === "authenticated") return "restored";
|
|
13951
|
+
return state.phase === "signed_out" ? "absent" : void 0;
|
|
13952
|
+
default: return;
|
|
13953
|
+
}
|
|
13954
|
+
};
|
|
13955
|
+
/**
|
|
13956
|
+
* The restore window's one result, or nothing when this settled session fact
|
|
13957
|
+
* belongs to a probe rather than to the window.
|
|
13958
|
+
*/
|
|
13959
|
+
const restorationObservation = (record, state) => {
|
|
13960
|
+
if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return NONE;
|
|
13961
|
+
const outcome = restorationOutcome(record.event, state);
|
|
13962
|
+
if (outcome === void 0) return NONE;
|
|
13963
|
+
return {
|
|
13964
|
+
name: "access_recovery_completed",
|
|
13965
|
+
props: {
|
|
13966
|
+
recovery: "session_restoration",
|
|
13967
|
+
outcome,
|
|
13968
|
+
...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
|
|
13969
|
+
...linkage(record)
|
|
13970
|
+
}
|
|
13971
|
+
};
|
|
13972
|
+
};
|
|
13436
13973
|
const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
|
|
13437
13974
|
"ClaimAccount",
|
|
13438
13975
|
"RetryAccount",
|
|
@@ -13477,6 +14014,7 @@ function mapIdentityProductObservation(record, state) {
|
|
|
13477
14014
|
name: "auth_signed_out",
|
|
13478
14015
|
props: linkage(record)
|
|
13479
14016
|
};
|
|
14017
|
+
case "restoration": return restorationObservation(record, state);
|
|
13480
14018
|
}
|
|
13481
14019
|
}
|
|
13482
14020
|
const ignoreTransportFailure = (operation, operationName, eventName) => Effect.suspend(operation).pipe(Effect.catchCause(() => Effect.logWarning("identity.product.telemetry.dropped").pipe(Effect.annotateLogs({
|
|
@@ -13488,7 +14026,7 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
13488
14026
|
if (mapped === null) return Effect.void;
|
|
13489
14027
|
copyInvocationObservation(record, mapped);
|
|
13490
14028
|
const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
|
|
13491
|
-
const after = () => mapped.name === "auth_verified"
|
|
14029
|
+
const after = () => state.phase === "authenticated" && (mapped.name === "auth_verified" || mapped.name === "access_recovery_completed") ? invocationTelemetry.identify({
|
|
13492
14030
|
distinctId: state.session.authUserId,
|
|
13493
14031
|
traits: { email_domain: emailDomain(state.session.email) }
|
|
13494
14032
|
}) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
|
|
@@ -13533,6 +14071,51 @@ function startSignerReadinessOnClaim(actor, signer) {
|
|
|
13533
14071
|
browserSigner.ensureWalletReady?.().catch(() => {});
|
|
13534
14072
|
});
|
|
13535
14073
|
}
|
|
14074
|
+
/**
|
|
14075
|
+
* Report the result of each signer readiness cycle (#1962 R04).
|
|
14076
|
+
*
|
|
14077
|
+
* The readiness store already carries the cycle boundary this needs, so no
|
|
14078
|
+
* public signer hook, second identity subscriber, or render counter is added.
|
|
14079
|
+
* `ensureOpenfortWalletReady` writes `recovering` synchronously when it starts
|
|
14080
|
+
* a cycle, and only the cycle that is still the current one writes `ready` or
|
|
14081
|
+
* `unavailable` (`create-openfort-browser-signer.ts:501-521`). Callers that
|
|
14082
|
+
* join a live cycle share that single write, and a `resetSession` writes
|
|
14083
|
+
* `unknown` first, so a cycle that a reset or an account replacement outran
|
|
14084
|
+
* reports nothing.
|
|
14085
|
+
*
|
|
14086
|
+
* The host context is bound when the cycle starts, so a person who signs out
|
|
14087
|
+
* and back in while a cycle runs cannot relabel the result that began under
|
|
14088
|
+
* the previous person. A signer with no readiness cycle — a node or dev key,
|
|
14089
|
+
* and a headless client whose requirement never opens the account lane —
|
|
14090
|
+
* writes no status and reports nothing.
|
|
14091
|
+
*/
|
|
14092
|
+
function observeSignerReadinessCycles(signer, telemetry, hostObservationSnapshot, runPromise) {
|
|
14093
|
+
const statusStore = signer?.statusStore;
|
|
14094
|
+
if (statusStore === void 0 || telemetry === void 0) return () => {};
|
|
14095
|
+
let running = false;
|
|
14096
|
+
let startContext;
|
|
14097
|
+
return statusStore.subscribe((status) => {
|
|
14098
|
+
if (status === "recovering") {
|
|
14099
|
+
running = true;
|
|
14100
|
+
startContext = hostObservationSnapshot?.();
|
|
14101
|
+
return;
|
|
14102
|
+
}
|
|
14103
|
+
const cycleContext = startContext;
|
|
14104
|
+
const completesCycle = running;
|
|
14105
|
+
running = false;
|
|
14106
|
+
startContext = void 0;
|
|
14107
|
+
if (!completesCycle || status !== "ready" && status !== "unavailable") return;
|
|
14108
|
+
const event = {
|
|
14109
|
+
name: "access_recovery_completed",
|
|
14110
|
+
props: {
|
|
14111
|
+
recovery: "signer_readiness",
|
|
14112
|
+
outcome: status
|
|
14113
|
+
}
|
|
14114
|
+
};
|
|
14115
|
+
if (cycleContext !== void 0) attachInvocationObservation(event, cycleContext);
|
|
14116
|
+
runPromise(Effect.suspend(() => bindProductTelemetryInvocation(telemetry, event).emit(event)).pipe(Effect.catchCause(() => Effect.void))).catch(() => {});
|
|
14117
|
+
});
|
|
14118
|
+
}
|
|
13536
14119
|
function assembleCapxulClient(input) {
|
|
13537
14120
|
const observation = input;
|
|
13538
14121
|
const authCache = input.authCache ?? detectAuthCacheAdapter();
|
|
@@ -13562,12 +14145,14 @@ function assembleCapxulClient(input) {
|
|
|
13562
14145
|
}, failureObservation, normalizeCapxulOperation(`identity.${record.event}`), "machine");
|
|
13563
14146
|
});
|
|
13564
14147
|
const unsubscribeSignerReadiness = startSignerReadinessOnClaim(actor, input.signer);
|
|
14148
|
+
const unsubscribeReadinessObservation = observeSignerReadinessCycles(input.signer, input.ports.telemetry, observation.hostObservationSnapshot, effectRunner.runPromise);
|
|
13565
14149
|
let stopPromise = null;
|
|
13566
14150
|
const stopActor = () => {
|
|
13567
14151
|
if (stopPromise === null) {
|
|
13568
14152
|
unsubscribeProductTelemetry();
|
|
13569
14153
|
unsubscribeFailureObservation();
|
|
13570
14154
|
unsubscribeSignerReadiness();
|
|
14155
|
+
unsubscribeReadinessObservation();
|
|
13571
14156
|
stopPromise = effectRunner.runPromise(Scope.close(scope, Exit.void));
|
|
13572
14157
|
}
|
|
13573
14158
|
return stopPromise;
|
|
@@ -13584,7 +14169,8 @@ function assembleCapxulClient(input) {
|
|
|
13584
14169
|
convexCall: input.ports.convexCall,
|
|
13585
14170
|
...input.signer === void 0 ? {} : { signer: input.signer },
|
|
13586
14171
|
telemetry: input.ports.telemetry,
|
|
13587
|
-
requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}
|
|
14172
|
+
requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`,
|
|
14173
|
+
...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
|
|
13588
14174
|
});
|
|
13589
14175
|
const accountBundle = makeAccountMethods({
|
|
13590
14176
|
actor,
|
|
@@ -13655,7 +14241,8 @@ function assembleCapxulClient(input) {
|
|
|
13655
14241
|
convexCall: input.ports.convexCall,
|
|
13656
14242
|
telemetry: input.ports.telemetry,
|
|
13657
14243
|
runPromise: effectRunner.runPromise,
|
|
13658
|
-
invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId)
|
|
14244
|
+
invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId),
|
|
14245
|
+
...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
|
|
13659
14246
|
});
|
|
13660
14247
|
const accounts = makeAccountsMethods({
|
|
13661
14248
|
accountReadPort: input.ports.accountRead,
|
|
@@ -13983,36 +14570,6 @@ function isPromiseLike(value) {
|
|
|
13983
14570
|
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value;
|
|
13984
14571
|
}
|
|
13985
14572
|
//#endregion
|
|
13986
|
-
//#region src/ports/indexer-read.ts
|
|
13987
|
-
/** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
|
|
13988
|
-
const INDEXER_MAX_LIMIT = 1e3;
|
|
13989
|
-
const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
|
|
13990
|
-
/**
|
|
13991
|
-
* Refuse a page the indexer would refuse, before any implementation spends a
|
|
13992
|
-
* round trip on it. Every implementation calls this, so a consumer test written
|
|
13993
|
-
* against the hermetic adapter cannot pass on input production rejects. Throws
|
|
13994
|
-
* a `CapxulError`; callers convert it to an `IndexerReadError`.
|
|
13995
|
-
*/
|
|
13996
|
-
function assertIndexerPage(input) {
|
|
13997
|
-
const safe = String(input.safe).toLowerCase();
|
|
13998
|
-
if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
|
|
13999
|
-
if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
|
|
14000
|
-
const offset = input.offset ?? 0;
|
|
14001
|
-
if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
|
|
14002
|
-
return safe;
|
|
14003
|
-
}
|
|
14004
|
-
var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
|
|
14005
|
-
function indexerReadErrorFromCapxul(operation, error, cause = error) {
|
|
14006
|
-
return new IndexerReadError({
|
|
14007
|
-
operation,
|
|
14008
|
-
publicCode: error.code,
|
|
14009
|
-
publicError: error,
|
|
14010
|
-
cause,
|
|
14011
|
-
...error.details === void 0 ? {} : { details: error.details }
|
|
14012
|
-
});
|
|
14013
|
-
}
|
|
14014
|
-
Context.Service()("@capxul/sdk/ports/IndexerReadPort");
|
|
14015
|
-
//#endregion
|
|
14016
14573
|
//#region src/adapters/ponder/decode.ts
|
|
14017
14574
|
/**
|
|
14018
14575
|
* Decode the indexer's rows at the trust boundary.
|
|
@@ -14318,7 +14875,7 @@ var PonderIndexerAdapter = class {
|
|
|
14318
14875
|
}
|
|
14319
14876
|
if (stopped()) return;
|
|
14320
14877
|
const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
|
|
14321
|
-
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
|
|
14878
|
+
observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error, error.code === "NOT_AUTHENTICATED" ? "operation" : "exception");
|
|
14322
14879
|
callback({
|
|
14323
14880
|
status: "error",
|
|
14324
14881
|
error
|
|
@@ -14955,6 +15512,7 @@ function idempotentClose(close) {
|
|
|
14955
15512
|
* and Convex see one session with one scope.
|
|
14956
15513
|
*/
|
|
14957
15514
|
function productionIndexerReadPort(input, ports, observation) {
|
|
15515
|
+
if (input.activityReadSource === "convex") return void 0;
|
|
14958
15516
|
if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
|
|
14959
15517
|
return new PonderIndexerAdapter({
|
|
14960
15518
|
baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
|
|
@@ -1679,6 +1679,17 @@ interface ActivityAnnotationInput {
|
|
|
1679
1679
|
* are unaffected.
|
|
1680
1680
|
*/
|
|
1681
1681
|
interface ActivityMethods {
|
|
1682
|
+
/**
|
|
1683
|
+
* Tell the caller when the indexed page for this actor's Safe changed, over
|
|
1684
|
+
* the indexer's one live connection. The callback carries no rows: it is the
|
|
1685
|
+
* signal to re-read `list` or `summary`. Without an indexer this resolves a
|
|
1686
|
+
* no-op teardown, so a caller needs no branch.
|
|
1687
|
+
*
|
|
1688
|
+
* @internal-use — substrate reactive transport, like `ConvexCallPort.subscribe`.
|
|
1689
|
+
*/
|
|
1690
|
+
subscribe(onChange: () => void, params?: {
|
|
1691
|
+
readonly actor?: ActorReference;
|
|
1692
|
+
}, options?: InvocationControls): Promise<CapxulResult<() => void>>;
|
|
1682
1693
|
list(params?: ActivityListParams, options?: InvocationControls): Promise<CapxulResult<ActivityPage>>;
|
|
1683
1694
|
summary(params?: ActivitySummaryParams, options?: InvocationControls): Promise<CapxulResult<ActivitySummary>>;
|
|
1684
1695
|
get(reference: ActivityReference, options?: InvocationControls & {
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { h as CapxulResult } from "../OAuthBearerAuthClient-C-ip-z8M.mjs";
|
|
2
|
-
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-
|
|
2
|
+
import { er as TelemetryEvent, f as CapxulClient, i as ObservationAdapter, n as ProductionSignerControls, nr as TelemetryIdentifyInput, pr as IdentityTransition, t as CapxulClientInput, tr as TelemetryGroupInput } from "../production-A1zW1JPt.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
4
|
//#region src/testing/production-client.d.ts
|
|
5
5
|
/** Create the one production client with per-client controls at its signing boundaries. */
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-
|
|
2
|
-
import { G as toHandle, H as toEmail, I as toAuthUserId, K as toJwtToken, M as toAddress, N as toAllowedOrigin, P as toAppId, Q as toPublishableKey, R as toChainId, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, d as authClientPortFromPromiseAdapter, et as toSessionToken,
|
|
1
|
+
import { D as fromWei, P as redactTelemetryEvent, a as smartAccountErrorFromCapxul, c as identityErrorFromCapxul, i as assembleCapxulClient, l as convexCallErrorFromCapxul, n as createCapxulClientWithSignerControls, o as accountReadErrorFromCapxul, s as wireChainId, u as bootstrapErrorFromCapxul, v as toWei } from "../production-7jr-KUvX.mjs";
|
|
2
|
+
import { G as toHandle, H as toEmail, I as toAuthUserId, K as toJwtToken, M as toAddress, N as toAllowedOrigin, P as toAppId, Q as toPublishableKey, R as toChainId, U as toEpochMs, V as toDurationMs, W as toEpochSeconds, ct as CapxulError, d as authClientPortFromPromiseAdapter, et as toSessionToken, it as validateHandle, j as toAccountId, n as readClockNow, q as toKycTier, r as InMemoryAuthCacheAdapter, ut as Errors, y as deriveCapxulSafeAddress, z as toCountryCode } from "../OAuthBearerAuthClient-CbU_W9Sp.mjs";
|
|
3
3
|
import { keccak256 } from "viem";
|
|
4
4
|
import { Effect, Result, Semaphore } from "effect";
|
|
5
5
|
import { getFunctionName } from "convex/server";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capxul/sdk",
|
|
3
|
-
"version": "4.2.0-rc.
|
|
3
|
+
"version": "4.2.0-rc.11",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Xelmar-tech/infrastructure.git",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
|
|
48
48
|
"vite-plus": "0.3.0",
|
|
49
49
|
"vitest": "4.1.11",
|
|
50
|
-
"@capxul/
|
|
51
|
-
"@capxul/
|
|
52
|
-
"@capxul/observability": "4.2.0-rc.10",
|
|
53
|
-
"@capxul/wire": "0.7.0",
|
|
50
|
+
"@capxul/config": "0.3.0",
|
|
51
|
+
"@capxul/observability": "4.2.0-rc.11",
|
|
54
52
|
"@capxul/types": "0.3.0",
|
|
55
|
-
"@capxul/config": "0.
|
|
53
|
+
"@capxul/typescript-config": "0.0.0",
|
|
54
|
+
"@capxul/errors": "0.3.0",
|
|
55
|
+
"@capxul/wire": "0.7.0"
|
|
56
56
|
},
|
|
57
57
|
"_permissionlessPinReason": "permissionless.toSafeSmartAccount is pinned to 0.3.4 for live Safe deployment E2E. Counterfactual address fixtures captured 2026-05-17 in packages/backend/convex/_shared/__tests__/counterfactual.test.ts and packages/config/tests/safe.test.ts must be re-verified before upgrading.",
|
|
58
58
|
"scripts": {
|