@capxul/sdk 4.20.0-beta.26 → 4.20.0-beta.28
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/{access-BEMUjtyy.mjs → access-SP-ur4Ku.mjs} +757 -112
- package/dist/{index-JnWL305d.d.mts → index-V6HIBbMr.d.mts} +866 -544
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/node/index.d.mts +1 -1
- package/dist/node/index.mjs +1 -1
- package/dist/{production-D3saI6ME.mjs → production-CoxgepCr.mjs} +80 -435
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -2
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Clock, Context, Data, Deferred, Effect, Exit, Fiber, Layer, Option, Predicate, Queue, Ref, Result, Schema, SchemaGetter, SchemaParser } from "effect";
|
|
1
|
+
import { Cause, Clock, Context, Data, Deferred, Effect, Exit, Fiber, Layer, Option, Predicate, Queue, Ref, Result, Schema, SchemaGetter, SchemaIssue, SchemaParser } from "effect";
|
|
2
2
|
import { makeFunctionReference } from "convex/server";
|
|
3
3
|
import { bytesToHex, concat, decodeErrorResult, encodeAbiParameters, encodeFunctionData, encodePacked, formatUnits, getContractAddress, hashTypedData, isAddress, isHex, keccak256, pad, padHex, parseAbiParameters, recoverAddress, recoverTypedDataAddress, toBytes, toFunctionSignature, toHex } from "viem";
|
|
4
4
|
import { getUserOperationHash } from "viem/account-abstraction";
|
|
@@ -858,7 +858,7 @@ const FIELD_RULES = {
|
|
|
858
858
|
},
|
|
859
859
|
anonymousId: {
|
|
860
860
|
maxLength: 128,
|
|
861
|
-
pattern: /^anon_[A-Za-z0-9-]
|
|
861
|
+
pattern: /^(?:anon_[A-Za-z0-9-]+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/u
|
|
862
862
|
},
|
|
863
863
|
traceparent: {
|
|
864
864
|
maxLength: 55,
|
|
@@ -4823,10 +4823,19 @@ const ApprovalNetworkSchema = Schema.Struct({
|
|
|
4823
4823
|
name: Schema.String,
|
|
4824
4824
|
explorerUrl: Schema.String
|
|
4825
4825
|
});
|
|
4826
|
+
/**
|
|
4827
|
+
* Whose money the operation moves: the person's own, or an Organization's,
|
|
4828
|
+
* which a reader follows through that Organization's scope.
|
|
4829
|
+
*/
|
|
4830
|
+
const ApprovalActorSchema = Schema.Union([Schema.Struct({ kind: Schema.Literal("personal") }), Schema.Struct({
|
|
4831
|
+
kind: Schema.Literal("organization"),
|
|
4832
|
+
orgId: Schema.NonEmptyString
|
|
4833
|
+
})]);
|
|
4826
4834
|
/** What the person reads before they sign. */
|
|
4827
4835
|
const ApprovalSummarySchema = Schema.Struct({
|
|
4828
4836
|
title: Schema.String,
|
|
4829
4837
|
from: Schema.Struct({
|
|
4838
|
+
kind: Schema.Literals(["personal", "organization"]),
|
|
4830
4839
|
label: Schema.String,
|
|
4831
4840
|
address: AddressSchema$1
|
|
4832
4841
|
}),
|
|
@@ -4848,6 +4857,7 @@ const ApprovalSchema = Schema.Struct({
|
|
|
4848
4857
|
url: Schema.String,
|
|
4849
4858
|
kind: Schema.Literals(APPROVAL_KINDS),
|
|
4850
4859
|
state: Schema.Literals(APPROVAL_STATES),
|
|
4860
|
+
actor: ApprovalActorSchema,
|
|
4851
4861
|
summary: ApprovalSummarySchema,
|
|
4852
4862
|
operation: ApprovalOperationSchema,
|
|
4853
4863
|
/** Who must sign: the owner key of the paying Safe. */
|
|
@@ -5515,6 +5525,28 @@ const probes = {
|
|
|
5515
5525
|
})
|
|
5516
5526
|
};
|
|
5517
5527
|
//#endregion
|
|
5528
|
+
//#region ../protocol/src/evm/role-dsl.ts
|
|
5529
|
+
const ORG_BUDGET_ACTIONS = [
|
|
5530
|
+
"send",
|
|
5531
|
+
"sendBatch",
|
|
5532
|
+
"createCommitment"
|
|
5533
|
+
];
|
|
5534
|
+
/**
|
|
5535
|
+
* How a capped Budget's allowance refills (#2298). `monthly` refills the
|
|
5536
|
+
* allowance to its cap every 30 days. Roles v2 counts fixed periods from the
|
|
5537
|
+
* moment the allowance is set; it has no calendar month. Absent means `none`:
|
|
5538
|
+
* the cap is one pot that spending drains for good.
|
|
5539
|
+
*/
|
|
5540
|
+
const ORG_BUDGET_REFILLS = ["monthly", "none"];
|
|
5541
|
+
/** The fixed Roles v2 refill period of a `monthly` Budget: 30 days, in seconds. */
|
|
5542
|
+
const MONTHLY_REFILL_PERIOD_SECONDS = 30n * 24n * 60n * 60n;
|
|
5543
|
+
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
5544
|
+
function roleKeyForPermission(permissionId) {
|
|
5545
|
+
const normalized = permissionId.trim();
|
|
5546
|
+
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
5547
|
+
return keccak256(toBytes(`permission ${permissionId}`));
|
|
5548
|
+
}
|
|
5549
|
+
//#endregion
|
|
5518
5550
|
//#region ../protocol/src/permissions/permission-schema.ts
|
|
5519
5551
|
/**
|
|
5520
5552
|
* One stored `setAllowance` call inside a command's expected authority set.
|
|
@@ -5553,12 +5585,19 @@ const BudgetScopeSchema = Schema.Struct({
|
|
|
5553
5585
|
})]),
|
|
5554
5586
|
actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
|
|
5555
5587
|
}).pipe(Schema.refine((scope) => new Set(scope.actions).size === scope.actions.length && (scope.recipients.type === "anyone" || scope.recipients.accounts.length >= 1 && scope.recipients.accounts.length <= 32 && new Set(scope.recipients.accounts).size === scope.recipients.accounts.length), { message: "Budget scope requires unique actions and one to 32 unique allowed Accounts" }));
|
|
5588
|
+
/**
|
|
5589
|
+
* How a capped Budget refills (#2298): `monthly` tops the cap back up every 30
|
|
5590
|
+
* days (a fixed Roles v2 period, not a calendar month); `none` never refills.
|
|
5591
|
+
* Absent means `none`, and writers store only `monthly`.
|
|
5592
|
+
*/
|
|
5593
|
+
const BudgetRefillSchema = Schema.Literals(ORG_BUDGET_REFILLS);
|
|
5556
5594
|
/** The accepted product policy stored with one Permission revision. */
|
|
5557
5595
|
const CurrentPolicySchema = Schema.Union([Schema.Struct({
|
|
5558
5596
|
type: Schema.Literal("budget"),
|
|
5559
5597
|
asset: AssetIdSchema,
|
|
5560
5598
|
limit: Schema.NullOr(Schema.String),
|
|
5561
|
-
scope: BudgetScopeSchema
|
|
5599
|
+
scope: BudgetScopeSchema,
|
|
5600
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5562
5601
|
}), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5563
5602
|
const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
|
|
5564
5603
|
if (value.type !== "budget") return false;
|
|
@@ -5623,7 +5662,8 @@ const ResolvedPermissionBudgetPolicySchema = Schema.Struct({
|
|
|
5623
5662
|
"send",
|
|
5624
5663
|
"sendBatch",
|
|
5625
5664
|
"createCommitment"
|
|
5626
|
-
]))
|
|
5665
|
+
])),
|
|
5666
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5627
5667
|
});
|
|
5628
5668
|
const PermissionRecipientBindingSchema = Schema.Struct({
|
|
5629
5669
|
accountId: AccountIdSchema$1,
|
|
@@ -5635,16 +5675,22 @@ const StoredPermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
|
5635
5675
|
type: Schema.Literal("budget"),
|
|
5636
5676
|
asset: AssetIdSchema,
|
|
5637
5677
|
limit: Schema.NullOr(AssetAmountSchema),
|
|
5638
|
-
scope: Schema.optional(BudgetScopeSchema)
|
|
5678
|
+
scope: Schema.optional(BudgetScopeSchema),
|
|
5679
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5639
5680
|
}).pipe(Schema.refine((intent) => intent.limit === null || intent.limit.asset === intent.asset, { message: "Budget limit must use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5640
5681
|
/** Writes state every granted right and use a finite positive AssetAmount cap. */
|
|
5641
5682
|
const PermissionIntentSchema = Schema.Union([Schema.Struct({
|
|
5642
5683
|
type: Schema.Literal("budget"),
|
|
5643
5684
|
asset: AssetIdSchema,
|
|
5644
5685
|
limit: AssetAmountSchema,
|
|
5645
|
-
scope: BudgetScopeSchema
|
|
5686
|
+
scope: BudgetScopeSchema,
|
|
5687
|
+
refill: Schema.optional(BudgetRefillSchema)
|
|
5646
5688
|
}).pipe(Schema.refine((intent) => intent.limit.asset === intent.asset && /[1-9]/u.test(intent.limit.value), { message: "Budget cap must be positive and use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
|
|
5647
|
-
/**
|
|
5689
|
+
/**
|
|
5690
|
+
* Decode once and give set-like scope fields one stable command representation.
|
|
5691
|
+
* An explicit `refill: "none"` is kept: on a change it turns a refill off,
|
|
5692
|
+
* where a missing `refill` keeps the Budget's current one (`keepRefillOnChange`).
|
|
5693
|
+
*/
|
|
5648
5694
|
function decodePermissionIntent(input) {
|
|
5649
5695
|
const intent = Schema.decodeUnknownSync(PermissionIntentSchema, { onExcessProperty: "error" })(input);
|
|
5650
5696
|
if (intent.type === "managePeople") return intent;
|
|
@@ -5659,6 +5705,19 @@ function decodePermissionIntent(input) {
|
|
|
5659
5705
|
}
|
|
5660
5706
|
});
|
|
5661
5707
|
}
|
|
5708
|
+
/**
|
|
5709
|
+
* A Budget change that leaves `refill` out keeps the Budget's current refill:
|
|
5710
|
+
* a change never turns a refill off by omission. An explicit `"none"` turns it
|
|
5711
|
+
* off. The backend plans, the SDK verifies and the CLI confirms the same
|
|
5712
|
+
* result. A create has no current refill, so a missing one means `none`.
|
|
5713
|
+
*/
|
|
5714
|
+
function keepRefillOnChange(intent, before) {
|
|
5715
|
+
if (intent.type !== "budget" || intent.refill !== void 0 || before?.type !== "budget" || before.refill !== "monthly") return intent;
|
|
5716
|
+
return {
|
|
5717
|
+
...intent,
|
|
5718
|
+
refill: before.refill
|
|
5719
|
+
};
|
|
5720
|
+
}
|
|
5662
5721
|
//#endregion
|
|
5663
5722
|
//#region ../protocol/src/permissions/replacement-schema.ts
|
|
5664
5723
|
const InvitationPhaseSchema = Schema.Literals([
|
|
@@ -5863,7 +5922,7 @@ const InviteGrantsSchema = Schema.Array(Schema.Union([
|
|
|
5863
5922
|
Schema.Struct({
|
|
5864
5923
|
type: Schema.Literal("new_budget"),
|
|
5865
5924
|
label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
|
|
5866
|
-
intent: BudgetIntentSchema
|
|
5925
|
+
intent: BudgetIntentSchema.pipe(Schema.refine((intent) => intent.refill !== "monthly", { message: "An invitation's new Budget cannot refill; change it once granted" }))
|
|
5867
5926
|
})
|
|
5868
5927
|
])).pipe(Schema.refine((grants) => {
|
|
5869
5928
|
if (grants.length < 1 || grants.length > 16) return false;
|
|
@@ -8954,7 +9013,9 @@ const PEOPLE_OPERATIONS = {
|
|
|
8954
9013
|
emitSafeConfirmed: "accounts.safes.emitSafeConfirmed",
|
|
8955
9014
|
readSafe: "accounts.safes.readSafe",
|
|
8956
9015
|
recordDeployment: "accounts.safes.recordDeployment"
|
|
8957
|
-
}
|
|
9016
|
+
},
|
|
9017
|
+
/** The home's requests to pay and Payments to retry, read in one query. */
|
|
9018
|
+
summaries: { loadNeedsYou: "accounts.summaries.loadNeedsYou" }
|
|
8958
9019
|
},
|
|
8959
9020
|
/** The host observer's names for `client.approvals.*`. */
|
|
8960
9021
|
approvals: {
|
|
@@ -9135,7 +9196,11 @@ const ORGANIZATION_OPERATIONS = {
|
|
|
9135
9196
|
seedNativeReadiness: "operator.fixtures.seedNativeReadiness",
|
|
9136
9197
|
seedPrerequisites: "operator.fixtures.seedPrerequisites"
|
|
9137
9198
|
},
|
|
9138
|
-
identities: {
|
|
9199
|
+
identities: {
|
|
9200
|
+
purgeByEmail: "operator.identities.purgeByEmail",
|
|
9201
|
+
/** One Account and every row that refers to it, unless it has Payments or an Organization tie. */
|
|
9202
|
+
purgeAccount: "operator.identities.purgeAccount"
|
|
9203
|
+
}
|
|
9139
9204
|
},
|
|
9140
9205
|
/** Organizations' internal functions; its public rows join from `OPERATIONS`. */
|
|
9141
9206
|
organizations: {
|
|
@@ -9209,7 +9274,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
9209
9274
|
pay: "organizationPayments.pay",
|
|
9210
9275
|
payBatch: "organizationPayments.payBatch",
|
|
9211
9276
|
prepareExecution: "organizationPayments.prepareExecution",
|
|
9212
|
-
retry: "organizationPayments.retry"
|
|
9277
|
+
retry: "organizationPayments.retry",
|
|
9278
|
+
cancel: "organizationPayments.cancel",
|
|
9279
|
+
redirect: "organizationPayments.redirect"
|
|
9213
9280
|
},
|
|
9214
9281
|
/**
|
|
9215
9282
|
* Payments' internal functions and the client's own method names; its public
|
|
@@ -9231,7 +9298,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
9231
9298
|
loadContext: "payments.commands.loadContext",
|
|
9232
9299
|
loadExecutionContext: "payments.commands.loadExecutionContext",
|
|
9233
9300
|
/** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
|
|
9234
|
-
loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
|
|
9301
|
+
loadBudgetAllowance: "payments.commands.loadBudgetAllowance",
|
|
9302
|
+
/** What a new Payment command would spend and from which Safe, read before it is born. */
|
|
9303
|
+
loadBirthFunding: "payments.commands.loadBirthFunding"
|
|
9235
9304
|
},
|
|
9236
9305
|
lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
|
|
9237
9306
|
/** The stored UserOperation of every money command: Payment, Permission and configuration. */
|
|
@@ -9385,7 +9454,11 @@ const PLATFORM_OPERATIONS = {
|
|
|
9385
9454
|
recordUse: "applications.bootstraps.recordUse",
|
|
9386
9455
|
resolve: "applications.bootstraps.resolve"
|
|
9387
9456
|
},
|
|
9388
|
-
keys: {
|
|
9457
|
+
keys: {
|
|
9458
|
+
issue: "applications.keys.issue",
|
|
9459
|
+
/** Operator-only: revoke one key by its id, whoever owns it. */
|
|
9460
|
+
revokeById: "applications.keys.revokeById"
|
|
9461
|
+
},
|
|
9389
9462
|
quickstarts: {
|
|
9390
9463
|
claim: "applications.quickstarts.claim",
|
|
9391
9464
|
mint: "applications.quickstarts.mint"
|
|
@@ -9586,6 +9659,13 @@ const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
|
|
|
9586
9659
|
* no catalog schema validates it.
|
|
9587
9660
|
*/
|
|
9588
9661
|
const EXCEPTION_EVENT_NAME = "$exception";
|
|
9662
|
+
/**
|
|
9663
|
+
* The SDK's twin of `capxul_backend_expected_outcome`: an expected outcome the
|
|
9664
|
+
* SDK's failure projection captures through the host's PostHog client. Like
|
|
9665
|
+
* the backend's, its properties are the error evidence, so no catalog schema
|
|
9666
|
+
* validates it.
|
|
9667
|
+
*/
|
|
9668
|
+
const SDK_EXPECTED_OUTCOME_EVENT_NAME = "capxul_sdk_expected_outcome";
|
|
9589
9669
|
const PII = Schema.String.pipe(Schema.brand("PII"));
|
|
9590
9670
|
/**
|
|
9591
9671
|
* The canonical envelope (ADR-0020 A4): snake_case ONLY. `capxul_env` and
|
|
@@ -9685,6 +9765,42 @@ const UiStepProps = {
|
|
|
9685
9765
|
organization_id: OptionalOrgId,
|
|
9686
9766
|
reason_code: OptionalReasonCode
|
|
9687
9767
|
};
|
|
9768
|
+
/**
|
|
9769
|
+
* A journey's variants (`JOURNEYS`): the way a person came into a journey, or
|
|
9770
|
+
* the way they took through it. A variant is a property of the journey's step
|
|
9771
|
+
* events, never a journey of its own, so each journey funnel breaks down by
|
|
9772
|
+
* it. A step event takes only the variants that a journey contract names on
|
|
9773
|
+
* it, picked one by one; the analytics plan test checks both directions.
|
|
9774
|
+
* `pay_path` and `funding_source` have no carrier until their Phase 2 events.
|
|
9775
|
+
* `payment_*` events already carry `recipient_is_capxul_account`, and
|
|
9776
|
+
* `invoice_issued` carries `issuer_scope_kind`; neither is repeated here.
|
|
9777
|
+
*/
|
|
9778
|
+
const JourneyVariantProps = {
|
|
9779
|
+
/** How a person came to Get started: by themselves, an invite, money that waited, or an invoice. */
|
|
9780
|
+
entry_path: Schema.optional(Schema.Literals([
|
|
9781
|
+
"signup",
|
|
9782
|
+
"invite",
|
|
9783
|
+
"claim",
|
|
9784
|
+
"invoice"
|
|
9785
|
+
])),
|
|
9786
|
+
/** Where the person acted. `agent` is an AI agent driving the CLI. */
|
|
9787
|
+
surface: Schema.optional(Schema.Literals([
|
|
9788
|
+
"web",
|
|
9789
|
+
"cli",
|
|
9790
|
+
"agent"
|
|
9791
|
+
])),
|
|
9792
|
+
/** Who pays: a person from their own account, or an Organization from a Budget. */
|
|
9793
|
+
payer_kind: Schema.optional(Schema.Literals(["person", "organization"])),
|
|
9794
|
+
/** How a request was paid: from the payer's inbox, or by its link at checkout. */
|
|
9795
|
+
pay_path: Schema.optional(Schema.Literals(["inbox", "link"])),
|
|
9796
|
+
/** Where an account's first money came from. `bank` is the planned on-ramp. */
|
|
9797
|
+
funding_source: Schema.optional(Schema.Literals([
|
|
9798
|
+
"deposit",
|
|
9799
|
+
"payment",
|
|
9800
|
+
"claim",
|
|
9801
|
+
"bank"
|
|
9802
|
+
]))
|
|
9803
|
+
};
|
|
9688
9804
|
//#endregion
|
|
9689
9805
|
//#region ../observability/src/events/money.ts
|
|
9690
9806
|
/**
|
|
@@ -9774,6 +9890,8 @@ const PaymentInitiatedProps = Schema.Struct({
|
|
|
9774
9890
|
execution_id: OptionalString,
|
|
9775
9891
|
payment_lifecycle_id: OptionalString,
|
|
9776
9892
|
...TelemetryEnvelopeProps,
|
|
9893
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
9894
|
+
surface: JourneyVariantProps.surface,
|
|
9777
9895
|
...RecipientTelemetryProps,
|
|
9778
9896
|
kind: Schema.String,
|
|
9779
9897
|
payment_id: Schema.String
|
|
@@ -9789,6 +9907,8 @@ const PaymentLifecycleProps = {
|
|
|
9789
9907
|
const PaymentSettledL2Props = Schema.Struct({
|
|
9790
9908
|
...PaymentLifecycleProps,
|
|
9791
9909
|
...TelemetryEnvelopeProps,
|
|
9910
|
+
payer_kind: JourneyVariantProps.payer_kind,
|
|
9911
|
+
surface: JourneyVariantProps.surface,
|
|
9792
9912
|
...RecipientTelemetryProps,
|
|
9793
9913
|
kind: Schema.String,
|
|
9794
9914
|
chain_id: Schema.Finite,
|
|
@@ -9996,6 +10116,7 @@ const OrganizationCreationStartedProps = Schema.Struct({
|
|
|
9996
10116
|
});
|
|
9997
10117
|
const OrganizationCreationReadyProps = Schema.Struct({
|
|
9998
10118
|
...TelemetryEnvelopeProps,
|
|
10119
|
+
surface: JourneyVariantProps.surface,
|
|
9999
10120
|
$insert_id: Schema.String,
|
|
10000
10121
|
organization_id: OrgIdTelemetrySchema,
|
|
10001
10122
|
attempt_number: AttemptNumberSchema,
|
|
@@ -10020,45 +10141,6 @@ const OrganizationCreationFailedProps = Schema.Struct({
|
|
|
10020
10141
|
...ORGANIZATION_SETUP_REASONS
|
|
10021
10142
|
]))
|
|
10022
10143
|
});
|
|
10023
|
-
const OrgCreateStartedProps = Schema.Struct({
|
|
10024
|
-
...TelemetryEnvelopeProps,
|
|
10025
|
-
org_id: OptionalOrgId,
|
|
10026
|
-
email_domain: OptionalString,
|
|
10027
|
-
template: OptionalString
|
|
10028
|
-
});
|
|
10029
|
-
const OrgSafeCreatedProps = Schema.Struct({
|
|
10030
|
-
...TelemetryEnvelopeProps,
|
|
10031
|
-
org_id: OptionalOrgId,
|
|
10032
|
-
chainId: OptionalChainId,
|
|
10033
|
-
safe_address: OptionalAddress
|
|
10034
|
-
});
|
|
10035
|
-
const OrgSafeConfirmedProps = Schema.Struct({
|
|
10036
|
-
...TelemetryEnvelopeProps,
|
|
10037
|
-
org_id: OptionalOrgId,
|
|
10038
|
-
chainId: OptionalChainId,
|
|
10039
|
-
safe_address: OptionalAddress,
|
|
10040
|
-
durationMs: OptionalDurationMs
|
|
10041
|
-
});
|
|
10042
|
-
const OrgRolesSeededProps = Schema.Struct({
|
|
10043
|
-
...TelemetryEnvelopeProps,
|
|
10044
|
-
org_id: OptionalOrgId,
|
|
10045
|
-
chainId: OptionalChainId,
|
|
10046
|
-
role: OptionalString,
|
|
10047
|
-
txHash: OptionalTxHash
|
|
10048
|
-
});
|
|
10049
|
-
const OrgCreatedProps = Schema.Struct({
|
|
10050
|
-
...TelemetryEnvelopeProps,
|
|
10051
|
-
org_id: OptionalOrgId,
|
|
10052
|
-
chainId: OptionalChainId,
|
|
10053
|
-
safe_address: OptionalAddress,
|
|
10054
|
-
durationMs: OptionalDurationMs
|
|
10055
|
-
});
|
|
10056
|
-
const OrgCreateFailedProps = Schema.Struct({
|
|
10057
|
-
...TelemetryEnvelopeProps,
|
|
10058
|
-
...ChainCauseTelemetryProps,
|
|
10059
|
-
org_id: OptionalOrgId,
|
|
10060
|
-
reason: OptionalString
|
|
10061
|
-
});
|
|
10062
10144
|
const OrgInviteSentProps = Schema.Struct({
|
|
10063
10145
|
...TelemetryEnvelopeProps,
|
|
10064
10146
|
org_id: OptionalOrgId,
|
|
@@ -10103,30 +10185,6 @@ Schema.Struct({
|
|
|
10103
10185
|
name: Schema.Literal("organization_creation_failed"),
|
|
10104
10186
|
props: OrganizationCreationFailedProps
|
|
10105
10187
|
});
|
|
10106
|
-
Schema.Struct({
|
|
10107
|
-
name: Schema.Literal("org_create_started"),
|
|
10108
|
-
props: OrgCreateStartedProps
|
|
10109
|
-
});
|
|
10110
|
-
Schema.Struct({
|
|
10111
|
-
name: Schema.Literal("org_safe_created"),
|
|
10112
|
-
props: OrgSafeCreatedProps
|
|
10113
|
-
});
|
|
10114
|
-
Schema.Struct({
|
|
10115
|
-
name: Schema.Literal("org_safe_confirmed"),
|
|
10116
|
-
props: OrgSafeConfirmedProps
|
|
10117
|
-
});
|
|
10118
|
-
Schema.Struct({
|
|
10119
|
-
name: Schema.Literal("org_roles_seeded"),
|
|
10120
|
-
props: OrgRolesSeededProps
|
|
10121
|
-
});
|
|
10122
|
-
Schema.Struct({
|
|
10123
|
-
name: Schema.Literal("org_created"),
|
|
10124
|
-
props: OrgCreatedProps
|
|
10125
|
-
});
|
|
10126
|
-
Schema.Struct({
|
|
10127
|
-
name: Schema.Literal("org_create_failed"),
|
|
10128
|
-
props: OrgCreateFailedProps
|
|
10129
|
-
});
|
|
10130
10188
|
Schema.Struct({
|
|
10131
10189
|
name: Schema.Literal("org_invite_sent"),
|
|
10132
10190
|
props: OrgInviteSentProps
|
|
@@ -11000,10 +11058,14 @@ const BootstrapFailedProps = Schema.Struct({
|
|
|
11000
11058
|
});
|
|
11001
11059
|
const MemberActivationStartedProps = Schema.Struct({
|
|
11002
11060
|
...TelemetryEnvelopeProps,
|
|
11061
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
11062
|
+
surface: JourneyVariantProps.surface,
|
|
11003
11063
|
$insert_id: Schema.String
|
|
11004
11064
|
});
|
|
11005
11065
|
const MemberActivationReadyProps = Schema.Struct({
|
|
11006
11066
|
...TelemetryEnvelopeProps,
|
|
11067
|
+
entry_path: JourneyVariantProps.entry_path,
|
|
11068
|
+
surface: JourneyVariantProps.surface,
|
|
11007
11069
|
$insert_id: Schema.String,
|
|
11008
11070
|
duration_ms: DurationMsSchema
|
|
11009
11071
|
});
|
|
@@ -12549,7 +12611,7 @@ function mapAccountLifecycle(input) {
|
|
|
12549
12611
|
}
|
|
12550
12612
|
//#endregion
|
|
12551
12613
|
//#region package.json
|
|
12552
|
-
var version = "4.20.0-beta.
|
|
12614
|
+
var version = "4.20.0-beta.28";
|
|
12553
12615
|
//#endregion
|
|
12554
12616
|
//#region src/observability/telemetry/exception-projection.ts
|
|
12555
12617
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -12987,7 +13049,7 @@ const REVERT_SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
|
|
|
12987
13049
|
//#region src/observability/posthog-failure.ts
|
|
12988
13050
|
/** The expected-outcome codes, read as plain strings so a free-form kind can be looked up. */
|
|
12989
13051
|
/** Stable PostHog event used for typed failures that are expected product outcomes. */
|
|
12990
|
-
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT =
|
|
13052
|
+
const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = SDK_EXPECTED_OUTCOME_EVENT_NAME;
|
|
12991
13053
|
/**
|
|
12992
13054
|
* Adapt the host's already-initialized PostHog-like client. This function does
|
|
12993
13055
|
* not import, initialize, configure, or own PostHog.
|
|
@@ -15670,6 +15732,12 @@ function paymentsV2Signature(name) {
|
|
|
15670
15732
|
const SEND_SIGNATURE = paymentsV2Signature("send");
|
|
15671
15733
|
const SEND_BATCH_SIGNATURE = paymentsV2Signature("sendBatch");
|
|
15672
15734
|
const CREATE_COMMITMENT_SIGNATURE = paymentsV2Signature("createCommitment");
|
|
15735
|
+
/**
|
|
15736
|
+
* #2299 ruling C: only an Owner cancels or redirects an Organization-funded
|
|
15737
|
+
* commitment, so these two sit in the Owner role's scope, never a Budget's.
|
|
15738
|
+
* The contract admits only the commitment's creator, the Organization Safe.
|
|
15739
|
+
*/
|
|
15740
|
+
const OWNER_COMMITMENT_SIGNATURES = [paymentsV2Signature("cancel"), paymentsV2Signature("redirect")];
|
|
15673
15741
|
//#endregion
|
|
15674
15742
|
//#region ../protocol/src/evm/organization-payments.ts
|
|
15675
15743
|
/** Zodiac ModuleProxyFactory on Base Sepolia. */
|
|
@@ -15906,24 +15974,18 @@ function encodePreparedPaymentCallData(command) {
|
|
|
15906
15974
|
}));
|
|
15907
15975
|
}
|
|
15908
15976
|
const [item] = command.items;
|
|
15909
|
-
|
|
15910
|
-
|
|
15977
|
+
const route = command.executionRoute;
|
|
15978
|
+
if (command.items.length !== 1 || item === void 0 || isBirth(item) || route.kind !== "direct" && item.operation === "claim_commitment") throw Errors.invalidInput("items", "Commitment lifecycle requires one lone execution");
|
|
15979
|
+
const call = {
|
|
15911
15980
|
to: command.contractAddress,
|
|
15981
|
+
value: 0n,
|
|
15912
15982
|
data: lifecycleData(item)
|
|
15913
|
-
}
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
"sendBatch",
|
|
15920
|
-
"createCommitment"
|
|
15921
|
-
];
|
|
15922
|
-
/** Derive the Zodiac substrate key for one durable Permission identity. */
|
|
15923
|
-
function roleKeyForPermission(permissionId) {
|
|
15924
|
-
const normalized = permissionId.trim();
|
|
15925
|
-
if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
|
|
15926
|
-
return keccak256(toBytes(`permission ${permissionId}`));
|
|
15983
|
+
};
|
|
15984
|
+
return route.kind === "direct" ? safe4337ExecuteUserOpCallData(call) : buildOrgRoleSpendPersonalCallData(buildOrgRoleMultiCall({
|
|
15985
|
+
rolesModuleAddress: route.rolesModule,
|
|
15986
|
+
roleKey: route.roleKey,
|
|
15987
|
+
calls: [call]
|
|
15988
|
+
}));
|
|
15927
15989
|
}
|
|
15928
15990
|
//#endregion
|
|
15929
15991
|
//#region src/payments/operation-verification.ts
|
|
@@ -15952,6 +16014,16 @@ function moneySafeOpDigest(operation, chainId) {
|
|
|
15952
16014
|
userOp: safe4337UserOperation(operation)
|
|
15953
16015
|
});
|
|
15954
16016
|
}
|
|
16017
|
+
/**
|
|
16018
|
+
* An Organization-funded cancel or redirect runs through the Owner role
|
|
16019
|
+
* (#2299), which no Budget names. Its role key cannot widen what the signer
|
|
16020
|
+
* may do: the Roles module admits only a role the signer's Safe holds, and the
|
|
16021
|
+
* contract admits only the commitment's creator.
|
|
16022
|
+
*/
|
|
16023
|
+
function isOwnerLifecycle(command) {
|
|
16024
|
+
const [item] = command.items;
|
|
16025
|
+
return command.items.length === 1 && (item?.operation === "cancel_commitment" || item?.operation === "redirect_commitment");
|
|
16026
|
+
}
|
|
15955
16027
|
/** Verify every actual Money signing target against configured trust anchors. */
|
|
15956
16028
|
function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
15957
16029
|
try {
|
|
@@ -15962,7 +16034,7 @@ function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
|
|
|
15962
16034
|
const route = command.executionRoute;
|
|
15963
16035
|
if (route.kind === "direct") {
|
|
15964
16036
|
if (permissionId !== void 0 || command.senderSafe.toLowerCase() !== expectedSafe.toLowerCase()) return false;
|
|
15965
|
-
} else if (permissionId === void 0
|
|
16037
|
+
} else if ((permissionId === void 0 ? !isOwnerLifecycle(command) : route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase()) || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
|
|
15966
16038
|
if (operation.callData.toLowerCase() !== encodePreparedPaymentCallData(command).toLowerCase()) return false;
|
|
15967
16039
|
const digest = moneySafeOpDigest(operation, chainId);
|
|
15968
16040
|
return evidence.digest.toLowerCase() === digest.toLowerCase() && evidence.executionId === `money_execution_${digest.slice(2).toLowerCase()}`;
|
|
@@ -16750,6 +16822,15 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
|
|
|
16750
16822
|
}
|
|
16751
16823
|
//#endregion
|
|
16752
16824
|
//#region src/payments/lifecycle-execution.ts
|
|
16825
|
+
/**
|
|
16826
|
+
* The prepared command is this lifecycle intent; a retry also names the
|
|
16827
|
+
* original birth command of the scope it was asked for. A claim, and an
|
|
16828
|
+
* Account's own cancel or redirect, runs directly from the signer's Safe; an
|
|
16829
|
+
* Organization's cancel or redirect runs through its Owner role (#2299).
|
|
16830
|
+
*/
|
|
16831
|
+
const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind === "retry" ? expectedOrganizationId === void 0 ? evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "account" : evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "organization" && evidence.sourceCommand.actor.orgId === expectedOrganizationId : evidence.preparedCommand?.executionRoute.kind === (intent.kind === "claim" || expectedOrganizationId === void 0 ? "direct" : "zodiac_role"));
|
|
16832
|
+
/** The prepared command pays exactly the request the payer confirmed. */
|
|
16833
|
+
const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
|
|
16753
16834
|
function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
|
|
16754
16835
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, lifecycles.prepareScoped, copyInvocationObservation(controls, {
|
|
16755
16836
|
signerAddress,
|
|
@@ -16758,13 +16839,13 @@ function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganiz
|
|
|
16758
16839
|
kind: "organization",
|
|
16759
16840
|
orgId: expectedOrganizationId
|
|
16760
16841
|
}
|
|
16761
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
16842
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), lifecycleCommandCheck(intent, expectedOrganizationId), false, void 0, signal, controls, onPrepared);
|
|
16762
16843
|
}
|
|
16763
16844
|
function executeRequestPayment(deps, confirmation, controls, onPrepared) {
|
|
16764
16845
|
return executePrepared(deps, (signerAddress) => runIfActive(controls?.signal, commands$1.prepareRequest.name, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareRequest, copyInvocationObservation(controls, {
|
|
16765
16846
|
confirmation,
|
|
16766
16847
|
signerAddress
|
|
16767
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
16848
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), requestPaymentCheck(confirmation), false, void 0, controls?.signal, controls, onPrepared);
|
|
16768
16849
|
}
|
|
16769
16850
|
function executeOrganizationPayment(deps, input, signal, onPreparedClosed, controls, onPrepared) {
|
|
16770
16851
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.organizationPayments.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareOrganization, copyInvocationObservation(controls, {
|
|
@@ -17668,17 +17749,21 @@ function paymentExecutionIntent(input) {
|
|
|
17668
17749
|
const to = normalizeTargetForBackend(input.to, "to");
|
|
17669
17750
|
if (!to.ok) return to;
|
|
17670
17751
|
try {
|
|
17671
|
-
|
|
17752
|
+
const decoded = Schema.decodeResult(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
|
|
17753
|
+
to: to.value,
|
|
17754
|
+
amount: input.amount,
|
|
17755
|
+
...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
|
|
17756
|
+
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
17757
|
+
...input.document === void 0 ? {} : { document: input.document },
|
|
17758
|
+
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
17759
|
+
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
17760
|
+
});
|
|
17761
|
+
return Result.isSuccess(decoded) ? {
|
|
17672
17762
|
ok: true,
|
|
17673
|
-
value:
|
|
17674
|
-
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
|
|
17678
|
-
...input.document === void 0 ? {} : { document: input.document },
|
|
17679
|
-
...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
|
|
17680
|
-
...input.timing === void 0 ? {} : { timing: input.timing }
|
|
17681
|
-
})
|
|
17763
|
+
value: decoded.success
|
|
17764
|
+
} : {
|
|
17765
|
+
ok: false,
|
|
17766
|
+
error: invalidPaymentField(decoded.failure.issue)
|
|
17682
17767
|
};
|
|
17683
17768
|
} catch {
|
|
17684
17769
|
return {
|
|
@@ -17687,6 +17772,13 @@ function paymentExecutionIntent(input) {
|
|
|
17687
17772
|
};
|
|
17688
17773
|
}
|
|
17689
17774
|
}
|
|
17775
|
+
const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
|
|
17776
|
+
/** The first failing field and why, e.g. `amount.value`, as the backend's row decode names it (#2381). */
|
|
17777
|
+
function invalidPaymentField(issue) {
|
|
17778
|
+
const first = formatIssue(issue).issues[0];
|
|
17779
|
+
const field = first?.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
17780
|
+
return Errors.invalidInput(field === void 0 || field.length === 0 ? "payment" : field, first?.message ?? "invalid personal payment input");
|
|
17781
|
+
}
|
|
17690
17782
|
function normalizePaymentTiming(payment) {
|
|
17691
17783
|
const { release: _release, ...rest } = payment;
|
|
17692
17784
|
return {
|
|
@@ -18366,7 +18458,7 @@ function makeOrganizationPaymentReadMethods(deps, orgId) {
|
|
|
18366
18458
|
}
|
|
18367
18459
|
//#endregion
|
|
18368
18460
|
//#region src/payments/organization-flights.ts
|
|
18369
|
-
function executionDependencies(input) {
|
|
18461
|
+
function executionDependencies$1(input) {
|
|
18370
18462
|
return input.actor === void 0 || input.chainId === void 0 || input.signer === void 0 ? null : {
|
|
18371
18463
|
actor: input.actor,
|
|
18372
18464
|
chainId: input.chainId,
|
|
@@ -18387,7 +18479,7 @@ async function executeOrganizationBatch(scope, input, signal, operation, control
|
|
|
18387
18479
|
};
|
|
18388
18480
|
const intent = organizationPaymentIntent(scope.orgId, input);
|
|
18389
18481
|
if (!intent.ok) return intent;
|
|
18390
|
-
const execution = executionDependencies(scope.deps);
|
|
18482
|
+
const execution = executionDependencies$1(scope.deps);
|
|
18391
18483
|
if (execution === null) return {
|
|
18392
18484
|
ok: false,
|
|
18393
18485
|
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
@@ -18503,6 +18595,27 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
18503
18595
|
flights: /* @__PURE__ */ new Map()
|
|
18504
18596
|
};
|
|
18505
18597
|
const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared);
|
|
18598
|
+
/** One Owner-signed cancel or redirect, answering the one Payment it acted on (#2299). */
|
|
18599
|
+
const ownerLifecycle = (intent, controls, operation) => runCapxulEffect(Effect.gen(function* () {
|
|
18600
|
+
const captured = carryInvocationParentSpan(controls, yield* Effect.currentSpan.pipe(Effect.orDie));
|
|
18601
|
+
return yield* awaitCapxulResult(async () => {
|
|
18602
|
+
const execution = executionDependencies$1(deps);
|
|
18603
|
+
if (execution === null) return {
|
|
18604
|
+
ok: false,
|
|
18605
|
+
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
18606
|
+
};
|
|
18607
|
+
const result = await executePaymentLifecycle(execution, intent, controls?.signal, captured, orgId);
|
|
18608
|
+
if (!result.ok) return result;
|
|
18609
|
+
const [payment, ...rest] = result.value.payments;
|
|
18610
|
+
return payment === void 0 || rest.length !== 0 ? {
|
|
18611
|
+
ok: false,
|
|
18612
|
+
error: Errors.unknown()
|
|
18613
|
+
} : {
|
|
18614
|
+
ok: true,
|
|
18615
|
+
value: normalizePaymentTiming(payment)
|
|
18616
|
+
};
|
|
18617
|
+
});
|
|
18618
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
18506
18619
|
return {
|
|
18507
18620
|
...makeOrganizationPaymentReadMethods(deps, orgId),
|
|
18508
18621
|
retry: (paymentId, options) => {
|
|
@@ -18511,7 +18624,7 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
18511
18624
|
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
18512
18625
|
const captured = carryInvocationParentSpan(controls, parent);
|
|
18513
18626
|
return yield* awaitCapxulResult(async () => {
|
|
18514
|
-
const execution = executionDependencies(deps);
|
|
18627
|
+
const execution = executionDependencies$1(deps);
|
|
18515
18628
|
if (execution === null) return {
|
|
18516
18629
|
ok: false,
|
|
18517
18630
|
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
@@ -18561,6 +18674,19 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
18561
18674
|
});
|
|
18562
18675
|
}), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.pay, deps.runPromise);
|
|
18563
18676
|
},
|
|
18677
|
+
cancel: (paymentId, options) => ownerLifecycle({
|
|
18678
|
+
kind: "cancel",
|
|
18679
|
+
paymentId
|
|
18680
|
+
}, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.organizationPayments.cancel),
|
|
18681
|
+
redirect: (input, options) => {
|
|
18682
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
18683
|
+
const recipient = normalizeTargetForBackend(input.to, "to");
|
|
18684
|
+
return recipient.ok ? ownerLifecycle({
|
|
18685
|
+
kind: "redirect",
|
|
18686
|
+
paymentId: input.paymentId,
|
|
18687
|
+
recipient: recipient.value
|
|
18688
|
+
}, controls, CAPXUL_OPERATIONS.organizationPayments.redirect) : runCapxulEffect(Effect.fail(recipient.error), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.redirect, deps.runPromise);
|
|
18689
|
+
},
|
|
18564
18690
|
payBatch: (input, options) => {
|
|
18565
18691
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
18566
18692
|
return runCapxulEffect(Effect.gen(function* () {
|
|
@@ -19275,6 +19401,524 @@ function sleep(ms, signal) {
|
|
|
19275
19401
|
});
|
|
19276
19402
|
}
|
|
19277
19403
|
//#endregion
|
|
19404
|
+
//#region ../protocol/src/payroll/model.ts
|
|
19405
|
+
function toPayrollRunId(raw) {
|
|
19406
|
+
return toNonEmptyStringBrand(raw, "payrollRunId", (value) => isNonEmptyString(value));
|
|
19407
|
+
}
|
|
19408
|
+
function toPayrollGroupId(raw) {
|
|
19409
|
+
return toNonEmptyStringBrand(raw, "payrollGroupId", (value) => isNonEmptyString(value));
|
|
19410
|
+
}
|
|
19411
|
+
//#endregion
|
|
19412
|
+
//#region src/payroll/run-checks.ts
|
|
19413
|
+
/** Match the caller's compensation to the committed V3 Payslip. */
|
|
19414
|
+
function payslipIdentityMatches(message, item, index, runId, period) {
|
|
19415
|
+
return message.runId === runId && message.itemIndex === index && message.employeeRef === item.partyId && message.periodStart === period.start && message.periodEnd === period.end && message.adjustmentsHash === hashAdjustmentsV3(item.adjustments);
|
|
19416
|
+
}
|
|
19417
|
+
function payslipAmountMatches(message, item) {
|
|
19418
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19419
|
+
return message.gross.asset === exact.asset && message.gross.minorUnits === item.gross && message.gross.decimals === exact.decimals && message.net.asset === exact.asset && message.net.minorUnits === item.net && message.net.decimals === exact.decimals;
|
|
19420
|
+
}
|
|
19421
|
+
function isRunItem(prepared, item, evidence, index, runId, period) {
|
|
19422
|
+
if (prepared === void 0) return false;
|
|
19423
|
+
const bundle = evidence.preparedCommand?.documentBundles?.[index];
|
|
19424
|
+
const preparedItem = evidence.preparedCommand?.items[index];
|
|
19425
|
+
const payslip = bundle?.envelope;
|
|
19426
|
+
if (bundle === void 0 || payslip?.primaryType !== "Payslip") return false;
|
|
19427
|
+
return canonicalJson(prepared.to) === canonicalJson(item.to) && prepared.paymentType === "payroll" && canonicalJson(prepared.amount) === canonicalJson(item.amount) && bundle.adjustments !== void 0 && canonicalJson(bundle.adjustments) === canonicalJson(item.adjustments) && payslipIdentityMatches(payslip.message, item, index, runId, period) && payslipAmountMatches(payslip.message, item) && (preparedItem?.operation === "send" || preparedItem?.operation === "create_commitment") && preparedItem.documentHash === evidence.preparedCommand?.instructionDocuments?.[index]?.documentHash;
|
|
19428
|
+
}
|
|
19429
|
+
/**
|
|
19430
|
+
* The batch the backend derived from a payroll intent must be the batch this
|
|
19431
|
+
* caller asked for, checked before the digest is signed.
|
|
19432
|
+
*
|
|
19433
|
+
* The payslip envelope on each item is server-built and carries the run row's
|
|
19434
|
+
* own `issuedAt`, so the batch cannot be fingerprinted against a local copy
|
|
19435
|
+
* (`matchesIntent` is unavailable here). What CAN be verified is every field
|
|
19436
|
+
* the caller supplied — the org, the Budget, the request key, and per item the
|
|
19437
|
+
* recipient, the amount and the payslip figures — plus the lineage binding the
|
|
19438
|
+
* batch to the run just written. A prepared command that pays a different
|
|
19439
|
+
* person, a different amount, from a different Budget, or under a different
|
|
19440
|
+
* run never reaches the signer.
|
|
19441
|
+
*/
|
|
19442
|
+
function isRunBatch(input) {
|
|
19443
|
+
return (batch, evidence) => Promise.resolve(!("kind" in batch) && batch.orgId === input.orgId && batch.permissionId === input.permissionId && batch.requestKey === input.requestKey && batch.lineage?.kind === "payroll" && batch.lineage.runId === input.runId && batch.items.length === input.items.length && batch.items.every((item) => item.amount.asset === input.asset) && input.items.every((item, index) => isRunItem(batch.items[index], item, evidence, index, input.runId, input.period)));
|
|
19444
|
+
}
|
|
19445
|
+
/**
|
|
19446
|
+
* The batch is `isRunBatch` for the run the backend just wrote, and that run
|
|
19447
|
+
* is the period the caller asked for: the payslip's `period` is a
|
|
19448
|
+
* server-formatted label, so the stored run is where the caller's own two
|
|
19449
|
+
* numbers are checkable.
|
|
19450
|
+
*/
|
|
19451
|
+
function isAuthorizedRunBatch(input, run) {
|
|
19452
|
+
return (request, evidence) => run === void 0 || run.periodStart !== input.period.start || run.periodEnd !== input.period.end ? Promise.resolve(false) : isRunBatch({
|
|
19453
|
+
...input,
|
|
19454
|
+
runId: run.id
|
|
19455
|
+
})(request, evidence);
|
|
19456
|
+
}
|
|
19457
|
+
/** A decoded detail is the run asked for, whole: its scope, membership and quantities agree. */
|
|
19458
|
+
function checkPayrollRunDetail(value, orgId, runId) {
|
|
19459
|
+
if (value === null) return {
|
|
19460
|
+
ok: true,
|
|
19461
|
+
value: null
|
|
19462
|
+
};
|
|
19463
|
+
if (value.orgId !== orgId || value.run.id !== runId || value.items.length === 0 || value.items.length !== value.run.recipientCount || new Set(value.items.map((item) => item.payment.id)).size !== value.items.length || value.items.some((item, index) => item.itemIndex !== index || item.amount.asset !== value.run.total.asset || item.payment.amount.asset !== item.amount.asset || item.payment.paymentType !== "payroll")) return {
|
|
19464
|
+
ok: false,
|
|
19465
|
+
error: Errors.invalidInput("run", "Payroll run scope or membership does not match")
|
|
19466
|
+
};
|
|
19467
|
+
try {
|
|
19468
|
+
const total = toExactAssetAmount(value.run.total, CONFIGURED_MONEY_ASSETS);
|
|
19469
|
+
let rawTotal = 0n;
|
|
19470
|
+
for (const item of value.items) {
|
|
19471
|
+
const amount = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19472
|
+
const payment = toExactAssetAmount(item.payment.amount, CONFIGURED_MONEY_ASSETS);
|
|
19473
|
+
if (amount.minorUnits !== payment.minorUnits) return {
|
|
19474
|
+
ok: false,
|
|
19475
|
+
error: Errors.invalidInput("run", "Payroll item quantity does not match its Payment")
|
|
19476
|
+
};
|
|
19477
|
+
rawTotal += BigInt(amount.minorUnits);
|
|
19478
|
+
}
|
|
19479
|
+
if (rawTotal !== BigInt(total.minorUnits)) return {
|
|
19480
|
+
ok: false,
|
|
19481
|
+
error: Errors.invalidInput("run", "Payroll run total does not match its items")
|
|
19482
|
+
};
|
|
19483
|
+
} catch {
|
|
19484
|
+
return {
|
|
19485
|
+
ok: false,
|
|
19486
|
+
error: Errors.invalidInput("run", "Payroll run quantities are invalid")
|
|
19487
|
+
};
|
|
19488
|
+
}
|
|
19489
|
+
return {
|
|
19490
|
+
ok: true,
|
|
19491
|
+
value
|
|
19492
|
+
};
|
|
19493
|
+
}
|
|
19494
|
+
//#endregion
|
|
19495
|
+
//#region src/payroll/run-decoding.ts
|
|
19496
|
+
function toPayrollRun(wire) {
|
|
19497
|
+
return {
|
|
19498
|
+
id: toPayrollRunId(wire.id),
|
|
19499
|
+
status: wire.status,
|
|
19500
|
+
periodStart: wire.periodStart,
|
|
19501
|
+
periodEnd: wire.periodEnd,
|
|
19502
|
+
total: wire.total,
|
|
19503
|
+
recipientCount: wire.recipientCount
|
|
19504
|
+
};
|
|
19505
|
+
}
|
|
19506
|
+
function toPayrollRuns(wire) {
|
|
19507
|
+
return {
|
|
19508
|
+
...mapPage(wire, toPayrollRun),
|
|
19509
|
+
settledThisMonth: wire.settledThisMonth
|
|
19510
|
+
};
|
|
19511
|
+
}
|
|
19512
|
+
function toPayrollGroup(wire) {
|
|
19513
|
+
return {
|
|
19514
|
+
id: toPayrollGroupId(wire.id),
|
|
19515
|
+
name: wire.name,
|
|
19516
|
+
tone: wire.tone,
|
|
19517
|
+
members: wire.members.map((member) => ({
|
|
19518
|
+
partyId: member.partyId,
|
|
19519
|
+
amount: member.amount,
|
|
19520
|
+
currency: member.currency
|
|
19521
|
+
}))
|
|
19522
|
+
};
|
|
19523
|
+
}
|
|
19524
|
+
function validateRunItems(input) {
|
|
19525
|
+
for (const [index, item] of input.items.entries()) {
|
|
19526
|
+
if (Schema.decodeResult(PayrollRunItemInputSchema, { onExcessProperty: "error" })(item)._tag === "Failure") return Errors.invalidInput(`items.${index}`, "invalid Payroll item");
|
|
19527
|
+
try {
|
|
19528
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19529
|
+
const net = BigInt(item.net);
|
|
19530
|
+
const gross = BigInt(item.gross);
|
|
19531
|
+
const adjustments = item.adjustments.reduce((sum, row) => sum + BigInt(row.amount), 0n);
|
|
19532
|
+
if (item.amount.asset !== input.asset || item.net !== exact.minorUnits || gross + adjustments !== net) return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
19533
|
+
} catch {
|
|
19534
|
+
return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
19535
|
+
}
|
|
19536
|
+
}
|
|
19537
|
+
return null;
|
|
19538
|
+
}
|
|
19539
|
+
const RunInputSchema = Schema.Struct({
|
|
19540
|
+
permissionId: AuthorizePayrollRunExecutionInputSchema.fields.permissionId,
|
|
19541
|
+
asset: AuthorizePayrollRunExecutionInputSchema.fields.asset,
|
|
19542
|
+
period: AuthorizePayrollRunExecutionInputSchema.fields.period,
|
|
19543
|
+
items: AuthorizePayrollRunExecutionInputSchema.fields.items,
|
|
19544
|
+
requestKey: Schema.optional(AuthorizePayrollRunExecutionInputSchema.fields.requestKey)
|
|
19545
|
+
});
|
|
19546
|
+
/** Decode caller input before session or signer work. Keep routing outside the input. */
|
|
19547
|
+
function decodePayrollRunInput(value) {
|
|
19548
|
+
try {
|
|
19549
|
+
const decoded = Schema.decodeUnknownSync(RunInputSchema, { onExcessProperty: "error" })(value);
|
|
19550
|
+
const input = {
|
|
19551
|
+
permissionId: decoded.permissionId,
|
|
19552
|
+
asset: decoded.asset,
|
|
19553
|
+
period: decoded.period,
|
|
19554
|
+
...decoded.requestKey === void 0 ? {} : { requestKey: decoded.requestKey },
|
|
19555
|
+
items: decoded.items.map((item) => ({
|
|
19556
|
+
amount: item.amount,
|
|
19557
|
+
gross: item.gross,
|
|
19558
|
+
net: item.net,
|
|
19559
|
+
adjustments: item.adjustments,
|
|
19560
|
+
partyId: toPartyId(item.partyId),
|
|
19561
|
+
to: item.to.kind === "party" ? {
|
|
19562
|
+
kind: "party",
|
|
19563
|
+
partyId: toPartyId(item.to.partyId)
|
|
19564
|
+
} : item.to.kind === "address" ? {
|
|
19565
|
+
kind: "address",
|
|
19566
|
+
address: toAddress(item.to.address)
|
|
19567
|
+
} : item.to
|
|
19568
|
+
}))
|
|
19569
|
+
};
|
|
19570
|
+
if (input.items.length === 0 || input.period.start > input.period.end) throw Errors.invalidInput("input", "Payroll requires items and an ordered period");
|
|
19571
|
+
const error = validateRunItems(input);
|
|
19572
|
+
if (error !== null) throw error;
|
|
19573
|
+
return input;
|
|
19574
|
+
} catch (cause) {
|
|
19575
|
+
throw cause instanceof CapxulError ? cause : Errors.invalidInput("input", "Invalid Payroll input");
|
|
19576
|
+
}
|
|
19577
|
+
}
|
|
19578
|
+
/** Read only the exact backend prepare receipt, never an arbitrary signer failure. */
|
|
19579
|
+
function payrollResolution(error, requestKey) {
|
|
19580
|
+
const details = error.details;
|
|
19581
|
+
if (error.code !== "WRONG_STATE" || details?.method !== runs.authorize.name || details.requestKey !== requestKey || typeof details.commandId !== "string" || typeof details.runId !== "string") return void 0;
|
|
19582
|
+
return details.payrollResolution === "failed" || details.payrollResolution === "settled" ? details.payrollResolution : void 0;
|
|
19583
|
+
}
|
|
19584
|
+
function validatePayrollRunLookup(orgId, runId) {
|
|
19585
|
+
return !Schema.is(PayrollRunIdSchema)(runId) || orgId.length === 0 ? {
|
|
19586
|
+
ok: false,
|
|
19587
|
+
error: Errors.invalidInput("runId", "Organization and run ID must be valid")
|
|
19588
|
+
} : {
|
|
19589
|
+
ok: true,
|
|
19590
|
+
value: void 0
|
|
19591
|
+
};
|
|
19592
|
+
}
|
|
19593
|
+
/** Strict saved detail decoder shared by the read and fresh stream. */
|
|
19594
|
+
function decodePayrollRunDetail(raw, orgId, runId) {
|
|
19595
|
+
const decoded = Schema.decodeUnknownResult(Schema.NullOr(PayrollRunDetailSchema), { onExcessProperty: "error" })(raw);
|
|
19596
|
+
if (decoded._tag === "Failure") return {
|
|
19597
|
+
ok: false,
|
|
19598
|
+
error: Errors.invalidInput("run", "Invalid Payroll run detail")
|
|
19599
|
+
};
|
|
19600
|
+
return checkPayrollRunDetail(decoded.success, orgId, runId);
|
|
19601
|
+
}
|
|
19602
|
+
//#endregion
|
|
19603
|
+
//#region src/payroll/run-subscription.ts
|
|
19604
|
+
function detailValue(result) {
|
|
19605
|
+
if (!result.ok) throw result.error;
|
|
19606
|
+
return result.value;
|
|
19607
|
+
}
|
|
19608
|
+
function subscribePayrollRun(deps, orgId, runId, listener, options) {
|
|
19609
|
+
const operation = CAPXUL_OPERATIONS.org.payroll.subscribeRun;
|
|
19610
|
+
const valid = deps.validate(orgId, runId);
|
|
19611
|
+
if (!valid.ok) return runCapxulEffect(Effect.fail(valid.error), observationOnlyControls(deps.invocationControls?.(options) ?? options), operation, deps.runPromise);
|
|
19612
|
+
const decode = (raw) => detailValue(deps.decode(raw, orgId, runId));
|
|
19613
|
+
return subscribeFreshQuery(deps, {
|
|
19614
|
+
orgId,
|
|
19615
|
+
runId
|
|
19616
|
+
}, listener, operation, {
|
|
19617
|
+
decode,
|
|
19618
|
+
validateCached: (raw) => {
|
|
19619
|
+
decode(raw);
|
|
19620
|
+
},
|
|
19621
|
+
invalidValue: (cause) => isCapxulError(cause) ? cause : Errors.invalidInput("run", "Invalid Payroll run detail"),
|
|
19622
|
+
invalidScope: () => Errors.invalidInput("run", "Payroll run subscription scope does not match"),
|
|
19623
|
+
lostValue: () => Errors.invalidInput("run", "Payroll run is no longer readable"),
|
|
19624
|
+
unavailable: () => Errors.notImplemented("payroll", "subscribeRun")
|
|
19625
|
+
}, options);
|
|
19626
|
+
}
|
|
19627
|
+
//#endregion
|
|
19628
|
+
//#region src/payroll/run-authorization.ts
|
|
19629
|
+
/**
|
|
19630
|
+
* Authorize one payroll run: a stable request key, the prepared batch checked
|
|
19631
|
+
* against the run the caller asked for, the signature, and the submission.
|
|
19632
|
+
*/
|
|
19633
|
+
async function authorizePayrollRun(scope, input, controls, onRequestKey, onResolved) {
|
|
19634
|
+
const { deps, orgId, requestKeyScope } = scope;
|
|
19635
|
+
const signal = controls?.signal;
|
|
19636
|
+
const execution = executionDependencies(deps);
|
|
19637
|
+
if (execution === null) return {
|
|
19638
|
+
ok: false,
|
|
19639
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
19640
|
+
};
|
|
19641
|
+
try {
|
|
19642
|
+
input = decodePayrollRunInput(input);
|
|
19643
|
+
} catch (cause) {
|
|
19644
|
+
return {
|
|
19645
|
+
ok: false,
|
|
19646
|
+
error: asCapxulError(cause)
|
|
19647
|
+
};
|
|
19648
|
+
}
|
|
19649
|
+
const { requestKey: provided, ...canonicalInput } = input;
|
|
19650
|
+
const actorId = onRequestKey === void 0 ? void 0 : execution.actor.authSession()?.authUserId;
|
|
19651
|
+
if (onRequestKey !== void 0 && actorId === void 0) return {
|
|
19652
|
+
ok: false,
|
|
19653
|
+
error: Errors.notAuthenticated()
|
|
19654
|
+
};
|
|
19655
|
+
const requestKey = await paymentRequestKeyLifecycle(`${requestKeyScope}:${AUTHORIZE_RUN}`, onRequestKey === void 0 ? {
|
|
19656
|
+
orgId,
|
|
19657
|
+
input
|
|
19658
|
+
} : {
|
|
19659
|
+
actorId,
|
|
19660
|
+
orgId,
|
|
19661
|
+
input: canonicalInput
|
|
19662
|
+
}, provided, onRequestKey === void 0 ? void 0 : { manageProvidedKey: true });
|
|
19663
|
+
try {
|
|
19664
|
+
await onRequestKey?.(requestKey.key);
|
|
19665
|
+
if (onRequestKey !== void 0 && execution.actor.authSession()?.authUserId !== actorId) {
|
|
19666
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
19667
|
+
return {
|
|
19668
|
+
ok: false,
|
|
19669
|
+
error: Errors.notAuthenticated()
|
|
19670
|
+
};
|
|
19671
|
+
}
|
|
19672
|
+
} catch (cause) {
|
|
19673
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
19674
|
+
return {
|
|
19675
|
+
ok: false,
|
|
19676
|
+
error: asCapxulError(cause)
|
|
19677
|
+
};
|
|
19678
|
+
}
|
|
19679
|
+
let authorized;
|
|
19680
|
+
let acceptedReplay;
|
|
19681
|
+
let preparedClosed = false;
|
|
19682
|
+
let terminalReplay;
|
|
19683
|
+
const submitted = await executePrepared(execution, async (signerAddress) => {
|
|
19684
|
+
const prepared = await runIfActive(signal, AUTHORIZE_RUN, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.authorize, copyInvocationObservation(controls, {
|
|
19685
|
+
orgId,
|
|
19686
|
+
permissionId: input.permissionId,
|
|
19687
|
+
asset: input.asset,
|
|
19688
|
+
requestKey: requestKey.key,
|
|
19689
|
+
signerAddress,
|
|
19690
|
+
period: input.period,
|
|
19691
|
+
items: input.items
|
|
19692
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), controls), deps.runPromise);
|
|
19693
|
+
if (!prepared.ok) {
|
|
19694
|
+
terminalReplay = payrollResolution(prepared.error, requestKey.key);
|
|
19695
|
+
return prepared;
|
|
19696
|
+
}
|
|
19697
|
+
authorized = prepared.value.run;
|
|
19698
|
+
acceptedReplay = prepared.value.replay.kind === "accepted" ? prepared.value.replay.submission : void 0;
|
|
19699
|
+
return {
|
|
19700
|
+
ok: true,
|
|
19701
|
+
value: prepared.value.execution
|
|
19702
|
+
};
|
|
19703
|
+
}, (request, evidence) => isAuthorizedRunBatch({
|
|
19704
|
+
orgId,
|
|
19705
|
+
permissionId: input.permissionId,
|
|
19706
|
+
requestKey: requestKey.key,
|
|
19707
|
+
asset: input.asset,
|
|
19708
|
+
period: input.period,
|
|
19709
|
+
items: input.items
|
|
19710
|
+
}, authorized)(request, evidence), true, () => preparedClosed = true, signal, controls, void 0, () => acceptedReplay);
|
|
19711
|
+
const resolution = preparedClosed ? "failed" : terminalReplay;
|
|
19712
|
+
if (resolution !== void 0) try {
|
|
19713
|
+
await onResolved?.(resolution);
|
|
19714
|
+
} catch {}
|
|
19715
|
+
try {
|
|
19716
|
+
await requestKey.finish(submitted.ok || resolution !== void 0);
|
|
19717
|
+
} catch {}
|
|
19718
|
+
if (!submitted.ok) return submitted;
|
|
19719
|
+
return authorized === void 0 ? {
|
|
19720
|
+
ok: false,
|
|
19721
|
+
error: Errors.unknown()
|
|
19722
|
+
} : {
|
|
19723
|
+
ok: true,
|
|
19724
|
+
value: toPayrollRun(authorized)
|
|
19725
|
+
};
|
|
19726
|
+
}
|
|
19727
|
+
const AUTHORIZE_RUN = runs.authorize.name;
|
|
19728
|
+
function executionDependencies(deps) {
|
|
19729
|
+
return deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0 ? null : {
|
|
19730
|
+
actor: deps.actor,
|
|
19731
|
+
chainId: deps.chainId,
|
|
19732
|
+
convexCall: deps.convexCall,
|
|
19733
|
+
runPromise: deps.runPromise,
|
|
19734
|
+
signer: deps.signer,
|
|
19735
|
+
...deps.telemetry === void 0 ? {} : { telemetry: deps.telemetry }
|
|
19736
|
+
};
|
|
19737
|
+
}
|
|
19738
|
+
//#endregion
|
|
19739
|
+
//#region src/payroll/runs.ts
|
|
19740
|
+
function makePayrollMethods(deps, orgId) {
|
|
19741
|
+
const requestKeyScope = deps.requestKeyScope ?? `client-${crypto.randomUUID()}`;
|
|
19742
|
+
/** One observed call of one payroll row, its reply projected inside the call, under the row's name. */
|
|
19743
|
+
const call = (row, args, options, project) => {
|
|
19744
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19745
|
+
return runPortEffect(callOperation(deps.convexCall, row, copyInvocationObservation(controls, args)).pipe(Effect.map(project), Effect.mapError((publicError) => ({ publicError }))), controls, row.name, deps.runPromise);
|
|
19746
|
+
};
|
|
19747
|
+
return {
|
|
19748
|
+
subscribeRun: (runId, listener, options) => subscribePayrollRun({
|
|
19749
|
+
actor: deps.actor,
|
|
19750
|
+
convexCall: deps.convexCall,
|
|
19751
|
+
query: reference(runs.get),
|
|
19752
|
+
runPromise: deps.runPromise,
|
|
19753
|
+
invocationControls: deps.invocationControls,
|
|
19754
|
+
decode: decodePayrollRunDetail,
|
|
19755
|
+
validate: validatePayrollRunLookup
|
|
19756
|
+
}, orgId, runId, listener, options),
|
|
19757
|
+
getRun: (runId, options) => {
|
|
19758
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19759
|
+
const lifetime = deps.actor?.authLifetime();
|
|
19760
|
+
const operation = runs.get.name;
|
|
19761
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
19762
|
+
const lookup = validatePayrollRunLookup(orgId, runId);
|
|
19763
|
+
if (!lookup.ok) return yield* Effect.fail(lookup.error);
|
|
19764
|
+
if (lifetime === void 0 || lifetime.authUserId === null || deps.actor === void 0) return yield* Effect.fail(Errors.notAuthenticated());
|
|
19765
|
+
const current = () => deps.actor?.matchesAuthLifetime(lifetime, lifetime.authUserId) === true;
|
|
19766
|
+
if (!current()) return yield* Effect.fail(Errors.notAuthenticated());
|
|
19767
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
19768
|
+
const captured = carryInvocationParentSpan(controls, parent);
|
|
19769
|
+
const run = yield* awaitCapxulResult(() => runIfActive(controls?.signal, operation, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.get, copyInvocationObservation(captured, {
|
|
19770
|
+
orgId,
|
|
19771
|
+
runId
|
|
19772
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), captured), deps.runPromise));
|
|
19773
|
+
if (!current()) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
19774
|
+
const checked = checkPayrollRunDetail(run, orgId, runId);
|
|
19775
|
+
return yield* checked.ok ? Effect.succeed(checked.value) : Effect.fail(checked.error);
|
|
19776
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
19777
|
+
},
|
|
19778
|
+
runs: (options) => call(runs.list, { orgId }, options, toPayrollRuns),
|
|
19779
|
+
authorizeRun: (input, options) => {
|
|
19780
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19781
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
19782
|
+
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: AUTHORIZE_RUN }));
|
|
19783
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
19784
|
+
return yield* awaitCapxulResult(() => authorizePayrollRun({
|
|
19785
|
+
deps,
|
|
19786
|
+
orgId,
|
|
19787
|
+
requestKeyScope
|
|
19788
|
+
}, input, carryInvocationParentSpan(controls, parent), options?.onRequestKey, options?.onResolved));
|
|
19789
|
+
}), observationOnlyControls(controls), AUTHORIZE_RUN, deps.runPromise);
|
|
19790
|
+
},
|
|
19791
|
+
terms: (options) => call(terms.list, { orgId }, options, (rates) => rates),
|
|
19792
|
+
groups: {
|
|
19793
|
+
list: (options) => call(groups.list, { orgId }, options, (saved) => mapPage(saved, toPayrollGroup)),
|
|
19794
|
+
save: (input, options) => call(groups.save, {
|
|
19795
|
+
orgId,
|
|
19796
|
+
...input.id === void 0 ? {} : { id: String(input.id) },
|
|
19797
|
+
name: input.name,
|
|
19798
|
+
tone: input.tone,
|
|
19799
|
+
members: input.members
|
|
19800
|
+
}, options, toPayrollGroup),
|
|
19801
|
+
remove: (id, options) => call(groups.remove, {
|
|
19802
|
+
orgId,
|
|
19803
|
+
id
|
|
19804
|
+
}, options, () => void 0)
|
|
19805
|
+
}
|
|
19806
|
+
};
|
|
19807
|
+
}
|
|
19808
|
+
const payrollUnavailable = () => Promise.resolve({
|
|
19809
|
+
ok: false,
|
|
19810
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
19811
|
+
});
|
|
19812
|
+
/** The bundle an unconfigured client exposes — every verb refuses, none throws. */
|
|
19813
|
+
function makeUnavailablePayrollMethods() {
|
|
19814
|
+
return {
|
|
19815
|
+
getRun: payrollUnavailable,
|
|
19816
|
+
subscribeRun: payrollUnavailable,
|
|
19817
|
+
runs: payrollUnavailable,
|
|
19818
|
+
authorizeRun: payrollUnavailable,
|
|
19819
|
+
terms: payrollUnavailable,
|
|
19820
|
+
groups: {
|
|
19821
|
+
list: payrollUnavailable,
|
|
19822
|
+
save: payrollUnavailable,
|
|
19823
|
+
remove: payrollUnavailable
|
|
19824
|
+
}
|
|
19825
|
+
};
|
|
19826
|
+
}
|
|
19827
|
+
//#endregion
|
|
19828
|
+
//#region src/accounts/command-approvals.ts
|
|
19829
|
+
/**
|
|
19830
|
+
* Keep a prepared command only when it is the one asked for and its operation
|
|
19831
|
+
* is the person's own. A mismatch is closed unsent when `close`, as the signed
|
|
19832
|
+
* path closes a new Payment; a retry or a request keeps its Payment open.
|
|
19833
|
+
*/
|
|
19834
|
+
async function checked(deps, signerAddress, prepared, expected, close) {
|
|
19835
|
+
if (!prepared.ok) return prepared;
|
|
19836
|
+
const session = deps.actor?.authSession() ?? null;
|
|
19837
|
+
if (session === null || deps.chainId === void 0) return {
|
|
19838
|
+
ok: false,
|
|
19839
|
+
error: Errors.notAuthenticated()
|
|
19840
|
+
};
|
|
19841
|
+
const evidence = prepared.value;
|
|
19842
|
+
if (await expected(evidence.request, evidence) && await verifiedPreparedCommand(evidence, session.email, signerAddress, deps.chainId)) return prepared;
|
|
19843
|
+
const refused = {
|
|
19844
|
+
ok: false,
|
|
19845
|
+
error: Errors.invalidInput("execution", "prepared command mismatch")
|
|
19846
|
+
};
|
|
19847
|
+
return close ? abandonPrepared(deps, evidence.executionId, "prepared_mismatch", refused) : refused;
|
|
19848
|
+
}
|
|
19849
|
+
/** A retry, a request payment or a payroll run, rather than a new Payment. */
|
|
19850
|
+
const isCommandApproval = (input) => "retry" in input || "request" in input || "payrollRun" in input;
|
|
19851
|
+
/** Prepare a retry, a request payment or a payroll run for `signerAddress`, checked. */
|
|
19852
|
+
function prepareVerifiedCommand(deps, input, signerAddress, signal) {
|
|
19853
|
+
if ("retry" in input) return prepareVerifiedRetry(deps, input, signerAddress, signal);
|
|
19854
|
+
if ("request" in input) return prepareVerifiedRequestPayment(deps, input, signerAddress, signal);
|
|
19855
|
+
return prepareVerifiedPayrollRun(deps, input, signerAddress, signal);
|
|
19856
|
+
}
|
|
19857
|
+
/** The retry of one Payment, prepared in the scope that paid it. */
|
|
19858
|
+
async function prepareVerifiedRetry(deps, input, signerAddress, signal) {
|
|
19859
|
+
const intent = {
|
|
19860
|
+
kind: "retry",
|
|
19861
|
+
paymentId: input.retry
|
|
19862
|
+
};
|
|
19863
|
+
return checked(deps, signerAddress, await runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => callOperation(deps.convexCall, lifecycles.prepareScoped, {
|
|
19864
|
+
signerAddress,
|
|
19865
|
+
intent,
|
|
19866
|
+
expectedScope: input.organizationId === void 0 ? { kind: "account" } : {
|
|
19867
|
+
kind: "organization",
|
|
19868
|
+
orgId: input.organizationId
|
|
19869
|
+
}
|
|
19870
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), lifecycleCommandCheck(intent, input.organizationId), false);
|
|
19871
|
+
}
|
|
19872
|
+
/** The Payment of one request, exactly as the request froze it. */
|
|
19873
|
+
async function prepareVerifiedRequestPayment(deps, input, signerAddress, signal) {
|
|
19874
|
+
const decoded = Schema.decodeResult(RequestPaymentConfirmationSchema, { onExcessProperty: "error" })(input.request);
|
|
19875
|
+
if (decoded._tag === "Failure") return {
|
|
19876
|
+
ok: false,
|
|
19877
|
+
error: Errors.invalidInput("confirmation", "invalid request confirmation")
|
|
19878
|
+
};
|
|
19879
|
+
const confirmation = decoded.success;
|
|
19880
|
+
return checked(deps, signerAddress, await runIfActive(signal, commands$1.prepareRequest.name, () => callOperation(deps.convexCall, commands$1.prepareRequest, {
|
|
19881
|
+
confirmation,
|
|
19882
|
+
signerAddress
|
|
19883
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), requestPaymentCheck(confirmation), false);
|
|
19884
|
+
}
|
|
19885
|
+
/** One payroll run: the run the backend writes and the batch that pays it, checked together. */
|
|
19886
|
+
async function prepareVerifiedPayrollRun(deps, input, signerAddress, signal) {
|
|
19887
|
+
let run;
|
|
19888
|
+
try {
|
|
19889
|
+
run = decodePayrollRunInput(input.payrollRun);
|
|
19890
|
+
} catch (cause) {
|
|
19891
|
+
return {
|
|
19892
|
+
ok: false,
|
|
19893
|
+
error: asCapxulError(cause)
|
|
19894
|
+
};
|
|
19895
|
+
}
|
|
19896
|
+
const orgId = input.organizationId;
|
|
19897
|
+
const requestKey = run.requestKey ?? `payroll_${crypto.randomUUID()}`;
|
|
19898
|
+
const { permissionId, asset, period, items } = run;
|
|
19899
|
+
const authorized = await runIfActive(signal, runs.authorize.name, () => callOperation(deps.convexCall, runs.authorize, {
|
|
19900
|
+
orgId,
|
|
19901
|
+
permissionId,
|
|
19902
|
+
asset,
|
|
19903
|
+
requestKey,
|
|
19904
|
+
signerAddress,
|
|
19905
|
+
period,
|
|
19906
|
+
items
|
|
19907
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
19908
|
+
if (!authorized.ok) return authorized;
|
|
19909
|
+
return checked(deps, signerAddress, {
|
|
19910
|
+
ok: true,
|
|
19911
|
+
value: authorized.value.execution
|
|
19912
|
+
}, isAuthorizedRunBatch({
|
|
19913
|
+
orgId,
|
|
19914
|
+
permissionId,
|
|
19915
|
+
requestKey,
|
|
19916
|
+
asset,
|
|
19917
|
+
period,
|
|
19918
|
+
items
|
|
19919
|
+
}, authorized.value.run), authorized.value.replay.kind !== "accepted");
|
|
19920
|
+
}
|
|
19921
|
+
//#endregion
|
|
19278
19922
|
//#region src/accounts/organization-approvals.ts
|
|
19279
19923
|
/** The Organization Payment prepared for `signerAddress` and checked, before any approval. */
|
|
19280
19924
|
async function prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal) {
|
|
@@ -19381,6 +20025,7 @@ function makeApprovalsMethods(deps) {
|
|
|
19381
20025
|
};
|
|
19382
20026
|
const prepare = async (input, options) => {
|
|
19383
20027
|
const signal = options?.signal;
|
|
20028
|
+
if (isCommandApproval(input)) return openApproval(signal, (signerAddress) => prepareVerifiedCommand(deps, input, signerAddress, signal));
|
|
19384
20029
|
if ("organizationId" in input) return openApproval(signal, (signerAddress) => prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal));
|
|
19385
20030
|
const intent = paymentExecutionIntent(input);
|
|
19386
20031
|
if (!intent.ok) return intent;
|
|
@@ -19473,7 +20118,7 @@ function makeApprovalsMethods(deps) {
|
|
|
19473
20118
|
ok: true,
|
|
19474
20119
|
value: null
|
|
19475
20120
|
};
|
|
19476
|
-
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId, signal);
|
|
20121
|
+
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId ?? (approval.actor.kind === "organization" ? approval.actor.orgId : void 0), signal);
|
|
19477
20122
|
if (!payments.ok) return payments;
|
|
19478
20123
|
if (unsent(approval, payments.value)) {
|
|
19479
20124
|
const sent = await submit(approvalId);
|
|
@@ -22402,4 +23047,4 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
22402
23047
|
return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
|
|
22403
23048
|
}
|
|
22404
23049
|
//#endregion
|
|
22405
|
-
export {
|
|
23050
|
+
export { parseAssetAmount as $, hashPermissionReplacementPreview as $n, carryInvocationParentSpan as $r, isSettingUpLifecycle as $t, registerProductTelemetryInvocation as A, toEpochSeconds as Ai, onboardings as An, isEvmAddress as Ar, safe4337MultiSendUserOpCallData as At, makeOrganizationPaymentsMethods as B, isRecord as Bi, invitations as Bn, runPortEffect as Br, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as Bt, AccountReadPortTag as C, globalLocalStorage as Ci, EXCEPTION_EVENT_NAME as Cn, toRoleKey as Cr, SEND_SIGNATURE as Ct, convexCallErrorFromCapxul as D, parseCachedJwt as Di, normalizeCapxulOperation as Dn, toCurrencyCode as Dr, deriveCapxulSafeAddress as Dt, ConvexCallPortTag as E, parseAuthSession as Ei, isCapxulOperation as En, toAssetId as Er, TOKENS as Et, decodePayrollRunInput as F, Errors as Fi, PreparedPermissionExecutionSchema as Fn, reference as Fr, callOperation as Ft, makeActorRelationshipMethods as G, workspaces as Gn, awaitCapxulResult as Gr, withCapturedAttemptContext as Gt, makePaymentsMethods as H, AuthCacheError as Hi, roles as Hn, observationOnlyControls as Hr, observeFailedResult as Ht, toPayrollGroupId as I, CapxulError as Ii, PermissionCommandViewSchema as In, makeAuthMethods as Ir, asCapxulError as It, makeTargetsMethods as J, ORGANIZATION_SETUP_STATUSES as Jn, toCapxulResult as Jr, normalizeExceptionErrorKind as Jt, makeCheckoutMethods as K, OrganizationSetupSchema as Kn, defectAsCapxulError as Kr, EXCEPTION_MESSAGE as Kt, makeAccountMethods as L, isCapxulError as Li, PayrollGroupIdSchema as Ln, askIdentity as Lr, toPublicError$1 as Lt, makeApprovalsMethods as M, toEmail as Mi, commands as Mn, toChainId as Mr, BASE_SEPOLIA_CHAIN_ID as Mt, makePayrollMethods as N, toJwtToken as Ni, configurations as Nn, toWeiAmount as Nr, MULTI_SEND_CALL_ONLY as Nt, retryIdempotentRead as O, toDurationMs as Oi, OPERATIONS as On, toExactAssetAmount as Or, normalizeSafeSaltEmail as Ot, makeUnavailablePayrollMethods as P, toSessionToken as Pi, replacements as Pn, absurd as Pr, SAFE_SENTINEL_OWNERS as Pt, formatAssetAmount as Q, InvitationViewSchema as Qn, ActorFailure as Qr, version as Qt, emitDepositInitiated as R, FAILURE_MODES$1 as Ri, PayrollGroupInputSchema as Rn, publicIdentityFailure as Rr, failureDetail as Rt, valueIn as S, detectAuthCacheAdapter as Si, CLI_INVOCATION_KINDS as Sn, toPermissionId as Sr, SEND_BATCH_SIGNATURE as St, TelemetryPortTag as T, BrowserAuthCacheAdapter as Ti, CAPXUL_OPERATIONS as Tn, currencySymbolFor as Tr, CONFIGURED_MONEY_ASSETS as Tt, waitForPaymentSubmission as U, AuthCachePortTag as Ui, setups as Un, runCapxulEffect as Ur, observeSdkClient as Ut, makeOfframpMethods as V, readField as Vi, members as Vn, sessionFromActor as Vr, postHogFailureObservation as Vt, makeDestinationsMethods as W, treasuries as Wn, withVerifiedOrganizationScope as Wr, observationContextProps as Wt, assetAmountFromRaw as X, decodeChainCause as Xn, CAPXUL_ERROR_CODES as Xr, projectSdkException as Xt, subscribeFreshQuery as Y, CHAIN_UPSTREAMS as Yn, formatTraceparent as Yr, normalizeExceptionOperation as Yt, assetSymbolFor as Z, toOrgId as Zn, EXPECTED_OPERATION_OUTCOMES as Zr, safeExceptionLabel as Zt, accountFromAccountPositions as _, toAppId as _i, validateHandle as _n, PAYMENT_STATUSES as _r, ROLES_SET_ALLOWANCE_ABI as _t, paymentPhase as a, readClockNow as ai, openfortSignerNotReadyCode as an, roleKeyForPermission as ar, fingerprintPaymentIntent as at, MONEY_READ_RATES as b, AuthClientError as bi, CLI_CAPABILITIES as bn, toBudgetId as br, ROLE_MANAGEMENT_SIGNATURES as bt, makePaymentDocumentsMethods as c, observationRequestHeaders as ci, resolveFailureMode as cn, images as cr, resolveOrgPermissionIdentity as ct, SmartAccountPortTag as d, readInvocationObservation as di, toCountryCode as dn, isBytes32 as dr, MULTI_SEND_SELECTOR as dt, withInvocationParentSpan as ei, UNOBSERVABLE_SIGNER_STATUS as en, decodePermissionIntent as er, requireAsset as et, smartAccountErrorFromCapxul as f, sanitizeObservationContext as fi, toKycTier as fn, toExactHex as fr, OPERATOR as ft, ConvexAccountLayer as g, toAllowedOrigin as gi, toPartyId as gn, PAYMENT_DIRECTIONS as gr, ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI as gt, makeAccountsMethods as h, isCredentialField as hi, toHandle as hn, toLowerHex as hr, ROLES_ASSIGN_ROLES_ABI as ht, inboxPhase as i, ClockPortTag as ii, isSignerNotReadySignal as in, ORG_BUDGET_ACTIONS as ir, canonicalJson as it, wireChainId as j, toAuthUserId as ji, authorities as jn, toAddress as jr, buildSafeMultiSendExecTransactionCall as jt, bindProductTelemetryInvocation as k, toEpochMs as ki, details as kn, EVM_ADDRESS_RE$1 as kr, safe4337ExecuteUserOpCallData as kt, makeSmartAccountMethods as l, attachInvocationObservation as li, makeCurrentUserMethods as ln, toAccountId as lr, CREATE_COMMITMENT_SIGNATURE as lt, PonderIndexerAdapter as m, redactUrlSecrets as mi, HANDLE_RE as mn, toLowerBytes32 as mr, PARAMETER_TYPE as mt, bootIdentityFlow as n, SystemClockLayer as ni, injectedWalletSigner as nn, CurrentPolicySchema as nr, isMoneyParseError as nt, requestPhase as o, BetterAuthNodeLayer as oi, recoverRawDigestSigner as on, diagnostics as or, moneySafeOpDigest as ot, makeHoldingsMethods as p, redactSecrets as pi, toTesterKind as pn, toLowerAddress as pr, OWNER_COMMITMENT_SIGNATURES as pt, isCheckoutSettlementConsumed as q, ORGANIZATION_SETUP_REASONS as qn, runIfActive as qr, SDK_VERSION as qt, makeMeMethods as r, ClockError as ri, isEcdsaSignatureHex as rn, MONTHLY_REFILL_PERIOD_SECONDS as rr, parseMoney as rt, makeActivityMethods as s, BetterAuthBrowserLayer as si, signerFailure as sn, AppIdSchema$1 as sr, ZODIAC_MODULE_PROXY_FACTORY as st, executeIdentityProductObservation as t, oauthBearerAuthClient as ti, causeChain as tn, keepRefillOnChange as tr, toRaw as tt, ConvexSmartAccountLayer as u, copyInvocationObservation as ui, makeClientRequestMethods as un, PaymentDocumentOptionsSchema as ur, MODULE_PROXY_FACTORY_ABI as ut, accountPositionsFromWire as v, toPublishableKey as vi, redactTelemetryEvent as vn, DurationMsSchema$1 as vr, ROLES_SET_TRANSACTION_UNWRAPPER_ABI as vt, accountReadErrorFromCapxul as w, InMemoryAuthCacheAdapter as wi, stampTelemetryEnvelope as wn, ChainIdSchema$1 as wr, CAPXUL_PAYMENTS_V2_ADDRESS as wt, valueActivitySummary as x, AuthClientPortTag as xi, CLI_COMMAND_NAMES as xn, toPermissionAssignmentId as xr, SAFE_ENABLE_MODULE_ABI as xt, positionPeg as y, authClientPortFromPromiseAdapter as yi, redactTelemetryProps as yn, EpochMsSchema$1 as yr, ROLES_SET_UP_ABI as yt, logDroppedEvent as z, isFailureMode as zi, FixedOfferDraftSchema as zn, runIdentityFacade as zr, failureEvidenceProps as zt };
|