@capxul/sdk 4.20.0-beta.24 → 4.20.0-beta.27
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-CCRHHGfU.mjs → access-BnlgphFT.mjs} +1647 -221
- package/dist/{index-Q7zfSqaq.d.mts → index-BjDZXc7n.d.mts} +1045 -836
- 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-B6MQBe5R.mjs → production-D5uC-i8N.mjs} +24 -1199
- package/dist/testing/index.d.mts +1 -1
- package/dist/testing/index.mjs +2 -2
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Clock, Context, Data, Deferred, Effect, Exit, Fiber, Layer, 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, 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";
|
|
@@ -218,12 +218,17 @@ const Errors = {
|
|
|
218
218
|
* a PERMISSION denial — explicitly NOT an `INSUFFICIENT_BALANCE` (the treasury
|
|
219
219
|
* held the funds; the role's authority is what bound). `reason` discriminates
|
|
220
220
|
* the violated condition (`over_cap` / `daily_cap` / `not_member` /
|
|
221
|
-
* `disallowed_recipient` / `condition_violation`);
|
|
222
|
-
*
|
|
221
|
+
* `disallowed_recipient` / `condition_violation` / `budget_exhausted`);
|
|
222
|
+
* no Safe, Roles module or role key enters the details.
|
|
223
|
+
* `budget_exhausted` carries what the Budget's allowance still holds and
|
|
224
|
+
* what the spend needs, as asset amounts in major units (#2297); the asset
|
|
225
|
+
* id names the public token contract.
|
|
223
226
|
*/
|
|
224
|
-
rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details:
|
|
227
|
+
rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: {
|
|
225
228
|
reason: details.reason,
|
|
226
|
-
operation: details.operation
|
|
229
|
+
...details.operation === void 0 ? {} : { operation: details.operation },
|
|
230
|
+
...details.remaining === void 0 ? {} : { remaining: details.remaining },
|
|
231
|
+
...details.required === void 0 ? {} : { required: details.required }
|
|
227
232
|
} }),
|
|
228
233
|
/**
|
|
229
234
|
* A transaction (or sponsored UserOp) failed. `details.reason` discriminates
|
|
@@ -3552,7 +3557,7 @@ const ActivityItemSchema = Schema.Union([Schema.Struct({
|
|
|
3552
3557
|
}), Schema.Struct({
|
|
3553
3558
|
kind: Schema.Literal("movement"),
|
|
3554
3559
|
id: Schema.String,
|
|
3555
|
-
classification: Schema.
|
|
3560
|
+
classification: Schema.Literals(["deposit", "movement_only_outflow"]),
|
|
3556
3561
|
amount: Amount,
|
|
3557
3562
|
direction: Direction,
|
|
3558
3563
|
counterpartyLabel: Schema.String,
|
|
@@ -4818,10 +4823,19 @@ const ApprovalNetworkSchema = Schema.Struct({
|
|
|
4818
4823
|
name: Schema.String,
|
|
4819
4824
|
explorerUrl: Schema.String
|
|
4820
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
|
+
})]);
|
|
4821
4834
|
/** What the person reads before they sign. */
|
|
4822
4835
|
const ApprovalSummarySchema = Schema.Struct({
|
|
4823
4836
|
title: Schema.String,
|
|
4824
4837
|
from: Schema.Struct({
|
|
4838
|
+
kind: Schema.Literals(["personal", "organization"]),
|
|
4825
4839
|
label: Schema.String,
|
|
4826
4840
|
address: AddressSchema$1
|
|
4827
4841
|
}),
|
|
@@ -4843,6 +4857,7 @@ const ApprovalSchema = Schema.Struct({
|
|
|
4843
4857
|
url: Schema.String,
|
|
4844
4858
|
kind: Schema.Literals(APPROVAL_KINDS),
|
|
4845
4859
|
state: Schema.Literals(APPROVAL_STATES),
|
|
4860
|
+
actor: ApprovalActorSchema,
|
|
4846
4861
|
summary: ApprovalSummarySchema,
|
|
4847
4862
|
operation: ApprovalOperationSchema,
|
|
4848
4863
|
/** Who must sign: the owner key of the paying Safe. */
|
|
@@ -5075,7 +5090,44 @@ const approvals = {
|
|
|
5075
5090
|
]
|
|
5076
5091
|
})
|
|
5077
5092
|
};
|
|
5093
|
+
/**
|
|
5094
|
+
* The home screen in one read. A section whose read failed is null, so the
|
|
5095
|
+
* caller shows it as unavailable, never as empty.
|
|
5096
|
+
*/
|
|
5097
|
+
const HomeSchema = Schema.Struct({
|
|
5098
|
+
email: Schema.String,
|
|
5099
|
+
actingAs: Schema.Union([Schema.Struct({ kind: Schema.Literal("personal") }), Schema.Struct({
|
|
5100
|
+
kind: Schema.Literal("organization"),
|
|
5101
|
+
orgId: Schema.String
|
|
5102
|
+
})]),
|
|
5103
|
+
/** The acting Safe's position in each settlement asset of its chain. */
|
|
5104
|
+
balances: Schema.NullOr(Schema.Array(AssetPositionSchema)),
|
|
5105
|
+
/** The ids of what waits on the caller, newest first. */
|
|
5106
|
+
needsYou: Schema.NullOr(Schema.Struct({
|
|
5107
|
+
requestsToPay: Schema.Array(Schema.String),
|
|
5108
|
+
payrollToApprove: Schema.Array(Schema.String),
|
|
5109
|
+
paymentsToRetry: Schema.Array(Schema.String)
|
|
5110
|
+
})),
|
|
5111
|
+
/** The newest rows of the actor's activity. */
|
|
5112
|
+
recentActivity: Schema.NullOr(Schema.Array(ActivityItemSchema))
|
|
5113
|
+
});
|
|
5078
5114
|
const summaries = {
|
|
5115
|
+
/**
|
|
5116
|
+
* Where the caller acts, its balances, what needs it and its newest activity,
|
|
5117
|
+
* for the person or for an Organization they take part in. One round trip.
|
|
5118
|
+
*/
|
|
5119
|
+
home: Operation.action("accounts.summaries.home", {
|
|
5120
|
+
input: Schema.Struct({
|
|
5121
|
+
chainId: ChainIdSchema$1,
|
|
5122
|
+
orgId: Schema.optionalKey(Schema.NonEmptyString)
|
|
5123
|
+
}),
|
|
5124
|
+
output: HomeSchema,
|
|
5125
|
+
errors: [
|
|
5126
|
+
...CALL$10,
|
|
5127
|
+
...DEFECTS,
|
|
5128
|
+
"ROLE_PERMISSION_DENIED"
|
|
5129
|
+
]
|
|
5130
|
+
}),
|
|
5079
5131
|
/** The signed-in person: id, verified email and Profile display name. */
|
|
5080
5132
|
me: Operation.query("accounts.summaries.me", {
|
|
5081
5133
|
input: Schema.Struct({}),
|
|
@@ -7167,7 +7219,11 @@ const OrgId$2 = Schema.NonEmptyString;
|
|
|
7167
7219
|
const CALL$4 = ["NOT_AUTHENTICATED", "INVALID_INPUT"];
|
|
7168
7220
|
/** The caller's own Account and Safe, which a Payment is signed from. */
|
|
7169
7221
|
const ACCOUNT$1 = ["SMART_ACCOUNT_MISSING", "ACCOUNT_NOT_FOUND"];
|
|
7170
|
-
/**
|
|
7222
|
+
/**
|
|
7223
|
+
* Not a participant of the Organization, no Budget in it that may pay for
|
|
7224
|
+
* this, or a Budget whose on-chain allowance holds less than the batch spends
|
|
7225
|
+
* (`budget_exhausted`, naming what is left).
|
|
7226
|
+
*/
|
|
7171
7227
|
const DENIED$1 = ["ROLE_PERMISSION_DENIED"];
|
|
7172
7228
|
/** The command, its Payment or its execution is not in a state this applies to. */
|
|
7173
7229
|
const STATE$3 = ["WRONG_STATE"];
|
|
@@ -7478,7 +7534,9 @@ const OneOrganization = Schema.Struct({ orgId: OrgId$1 });
|
|
|
7478
7534
|
const CALL$3 = ["NOT_AUTHENTICATED", "INVALID_INPUT"];
|
|
7479
7535
|
/**
|
|
7480
7536
|
* The caller does not take part in the Organization, or holds no Budget in it
|
|
7481
|
-
* that may pay for this
|
|
7537
|
+
* that may pay for this, or the Budget's on-chain allowance holds less than the
|
|
7538
|
+
* run pays (`budget_exhausted`, naming what is left). A caller outside the
|
|
7539
|
+
* Organization learns nothing more.
|
|
7482
7540
|
*/
|
|
7483
7541
|
const DENIED = ["ROLE_PERMISSION_DENIED"];
|
|
7484
7542
|
/**
|
|
@@ -9087,7 +9145,11 @@ const ORGANIZATION_OPERATIONS = {
|
|
|
9087
9145
|
seedNativeReadiness: "operator.fixtures.seedNativeReadiness",
|
|
9088
9146
|
seedPrerequisites: "operator.fixtures.seedPrerequisites"
|
|
9089
9147
|
},
|
|
9090
|
-
identities: {
|
|
9148
|
+
identities: {
|
|
9149
|
+
purgeByEmail: "operator.identities.purgeByEmail",
|
|
9150
|
+
/** One Account and every row that refers to it, unless it has Payments or an Organization tie. */
|
|
9151
|
+
purgeAccount: "operator.identities.purgeAccount"
|
|
9152
|
+
}
|
|
9091
9153
|
},
|
|
9092
9154
|
/** Organizations' internal functions; its public rows join from `OPERATIONS`. */
|
|
9093
9155
|
organizations: {
|
|
@@ -9181,7 +9243,9 @@ emitSaved: "activity.annotations.emitSaved" },
|
|
|
9181
9243
|
linkObservation: "payments.commands.linkObservation",
|
|
9182
9244
|
failPrepared: "payments.commands.failPrepared",
|
|
9183
9245
|
loadContext: "payments.commands.loadContext",
|
|
9184
|
-
loadExecutionContext: "payments.commands.loadExecutionContext"
|
|
9246
|
+
loadExecutionContext: "payments.commands.loadExecutionContext",
|
|
9247
|
+
/** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
|
|
9248
|
+
loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
|
|
9185
9249
|
},
|
|
9186
9250
|
lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
|
|
9187
9251
|
/** The stored UserOperation of every money command: Payment, Permission and configuration. */
|
|
@@ -9335,7 +9399,11 @@ const PLATFORM_OPERATIONS = {
|
|
|
9335
9399
|
recordUse: "applications.bootstraps.recordUse",
|
|
9336
9400
|
resolve: "applications.bootstraps.resolve"
|
|
9337
9401
|
},
|
|
9338
|
-
keys: {
|
|
9402
|
+
keys: {
|
|
9403
|
+
issue: "applications.keys.issue",
|
|
9404
|
+
/** Operator-only: revoke one key by its id, whoever owns it. */
|
|
9405
|
+
revokeById: "applications.keys.revokeById"
|
|
9406
|
+
},
|
|
9339
9407
|
quickstarts: {
|
|
9340
9408
|
claim: "applications.quickstarts.claim",
|
|
9341
9409
|
mint: "applications.quickstarts.mint"
|
|
@@ -10692,95 +10760,83 @@ Schema.Struct({
|
|
|
10692
10760
|
});
|
|
10693
10761
|
//#endregion
|
|
10694
10762
|
//#region ../observability/src/catalog/cli.ts
|
|
10695
|
-
/**
|
|
10763
|
+
/** Every command path of the `capxul` tree, dot-separated, plus the root and `unknown`. */
|
|
10696
10764
|
const CLI_COMMAND_NAMES = [
|
|
10697
|
-
"status",
|
|
10698
|
-
"request",
|
|
10699
|
-
"request.list",
|
|
10700
|
-
"request.get",
|
|
10701
|
-
"request.cancel",
|
|
10702
|
-
"offer",
|
|
10703
|
-
"offer.create",
|
|
10704
|
-
"offer.list",
|
|
10705
|
-
"offer.get",
|
|
10706
|
-
"offer.revise",
|
|
10707
|
-
"offer.deactivate",
|
|
10708
|
-
"org.offer",
|
|
10709
|
-
"org.offer.create",
|
|
10710
|
-
"org.offer.list",
|
|
10711
|
-
"org.offer.get",
|
|
10712
|
-
"org.offer.revise",
|
|
10713
|
-
"org.offer.deactivate",
|
|
10714
|
-
"request.issue",
|
|
10715
|
-
"inbox",
|
|
10716
|
-
CAPXUL_OPERATIONS.inbox.list,
|
|
10717
|
-
"inbox.get",
|
|
10718
|
-
"inbox.pay",
|
|
10719
|
-
CAPXUL_OPERATIONS.inbox.decline,
|
|
10720
|
-
"org.request",
|
|
10721
|
-
"org.request.list",
|
|
10722
|
-
"org.request.get",
|
|
10723
|
-
"org.request.cancel",
|
|
10724
|
-
"org.request.issue",
|
|
10725
|
-
"org.inbox",
|
|
10726
|
-
CAPXUL_OPERATIONS.org.inbox.list,
|
|
10727
|
-
"org.inbox.get",
|
|
10728
|
-
"org.inbox.pay",
|
|
10729
|
-
CAPXUL_OPERATIONS.org.inbox.decline,
|
|
10730
10765
|
"capxul",
|
|
10766
|
+
"home",
|
|
10731
10767
|
"whoami",
|
|
10732
10768
|
"use",
|
|
10733
10769
|
"use.personal",
|
|
10734
10770
|
"use.org",
|
|
10735
10771
|
"use.env",
|
|
10736
|
-
"doctor",
|
|
10737
|
-
"telemetry",
|
|
10738
|
-
"telemetry.status",
|
|
10739
|
-
"telemetry.enable",
|
|
10740
|
-
"telemetry.disable",
|
|
10741
10772
|
"auth",
|
|
10742
10773
|
"auth.login",
|
|
10743
|
-
"auth.signup",
|
|
10744
|
-
"auth.send",
|
|
10745
10774
|
"auth.verify",
|
|
10746
10775
|
"auth.switch",
|
|
10747
|
-
"auth.profile",
|
|
10748
|
-
"auth.status",
|
|
10749
10776
|
"auth.logout",
|
|
10750
10777
|
"account",
|
|
10751
|
-
|
|
10752
|
-
CAPXUL_OPERATIONS.account.balance,
|
|
10753
|
-
"account.deposit",
|
|
10778
|
+
"account.show",
|
|
10754
10779
|
"account.retry",
|
|
10755
|
-
"
|
|
10756
|
-
"
|
|
10757
|
-
"document.verify",
|
|
10758
|
-
"document.render",
|
|
10759
|
-
"document.export",
|
|
10780
|
+
"balance",
|
|
10781
|
+
"address",
|
|
10760
10782
|
"payment",
|
|
10783
|
+
"payment.send",
|
|
10761
10784
|
"payment.list",
|
|
10762
10785
|
"payment.get",
|
|
10763
10786
|
"payment.wait",
|
|
10764
|
-
"payment.send",
|
|
10765
10787
|
"payment.retry",
|
|
10766
|
-
"
|
|
10767
|
-
|
|
10768
|
-
"
|
|
10769
|
-
"
|
|
10770
|
-
|
|
10788
|
+
"inbox",
|
|
10789
|
+
CAPXUL_OPERATIONS.inbox.list,
|
|
10790
|
+
"inbox.get",
|
|
10791
|
+
"inbox.pay",
|
|
10792
|
+
CAPXUL_OPERATIONS.inbox.decline,
|
|
10793
|
+
"request",
|
|
10794
|
+
"request.issue",
|
|
10795
|
+
"request.list",
|
|
10796
|
+
"request.get",
|
|
10797
|
+
"request.cancel",
|
|
10798
|
+
"offer",
|
|
10799
|
+
"offer.create",
|
|
10800
|
+
"offer.list",
|
|
10801
|
+
"offer.get",
|
|
10802
|
+
"offer.revise",
|
|
10803
|
+
"offer.deactivate",
|
|
10771
10804
|
"contact",
|
|
10805
|
+
"contact.add",
|
|
10772
10806
|
"contact.list",
|
|
10773
10807
|
"contact.get",
|
|
10774
|
-
"contact.add",
|
|
10775
10808
|
"contact.label",
|
|
10776
10809
|
"contact.hide",
|
|
10777
10810
|
"contact.unhide",
|
|
10811
|
+
"document",
|
|
10812
|
+
"document.get",
|
|
10813
|
+
"document.render",
|
|
10814
|
+
"document.export",
|
|
10815
|
+
"document.verify",
|
|
10816
|
+
"activity",
|
|
10817
|
+
"activity.list",
|
|
10818
|
+
"activity.get",
|
|
10819
|
+
"activity.summary",
|
|
10820
|
+
"activity.annotate",
|
|
10821
|
+
"invite",
|
|
10822
|
+
"invite.list",
|
|
10823
|
+
"invite.accept",
|
|
10824
|
+
"invite.decline",
|
|
10778
10825
|
"org",
|
|
10779
10826
|
CAPXUL_OPERATIONS.org.list,
|
|
10780
|
-
|
|
10827
|
+
CAPXUL_OPERATIONS.org.create,
|
|
10828
|
+
"org.show",
|
|
10781
10829
|
CAPXUL_OPERATIONS.org.me,
|
|
10782
|
-
"org.
|
|
10783
|
-
|
|
10830
|
+
"org.wait",
|
|
10831
|
+
CAPXUL_OPERATIONS.org.retry,
|
|
10832
|
+
"org.member",
|
|
10833
|
+
"org.member.list",
|
|
10834
|
+
"org.member.get",
|
|
10835
|
+
"org.member.invite",
|
|
10836
|
+
CAPXUL_OPERATIONS.org.invite,
|
|
10837
|
+
"org.invite.list",
|
|
10838
|
+
"org.invite.cancel",
|
|
10839
|
+
"org.invite.resend",
|
|
10784
10840
|
"org.permission",
|
|
10785
10841
|
"org.permission.list",
|
|
10786
10842
|
"org.permission.get",
|
|
@@ -10792,46 +10848,32 @@ const CLI_COMMAND_NAMES = [
|
|
|
10792
10848
|
"org.permission.command",
|
|
10793
10849
|
"org.permission.command.get",
|
|
10794
10850
|
"org.permission.command.wait",
|
|
10795
|
-
CAPXUL_OPERATIONS.org.payment,
|
|
10796
|
-
"org.payment.list",
|
|
10797
|
-
"org.payment.get",
|
|
10798
|
-
"org.payment.wait",
|
|
10799
|
-
"org.payment.retry",
|
|
10800
|
-
"org.payment.send",
|
|
10801
10851
|
"org.payroll",
|
|
10852
|
+
"org.payroll.run",
|
|
10853
|
+
"org.payroll.list",
|
|
10854
|
+
"org.payroll.get",
|
|
10855
|
+
"org.payroll.wait",
|
|
10856
|
+
CAPXUL_OPERATIONS.org.payroll.terms,
|
|
10802
10857
|
"org.payroll.groups",
|
|
10803
10858
|
CAPXUL_OPERATIONS.org.payroll.groups.list,
|
|
10804
10859
|
CAPXUL_OPERATIONS.org.payroll.groups.save,
|
|
10805
10860
|
CAPXUL_OPERATIONS.org.payroll.groups.remove,
|
|
10806
|
-
|
|
10807
|
-
"
|
|
10808
|
-
"
|
|
10809
|
-
"
|
|
10810
|
-
"
|
|
10811
|
-
|
|
10812
|
-
"
|
|
10813
|
-
"
|
|
10814
|
-
"org.member.invite",
|
|
10815
|
-
"org.contact",
|
|
10816
|
-
"org.contact.list",
|
|
10817
|
-
"org.contact.get",
|
|
10818
|
-
"org.contact.add",
|
|
10819
|
-
"org.contact.label",
|
|
10820
|
-
"org.contact.hide",
|
|
10821
|
-
"org.contact.unhide",
|
|
10822
|
-
CAPXUL_OPERATIONS.org.invite,
|
|
10823
|
-
"org.invite.list",
|
|
10861
|
+
"config",
|
|
10862
|
+
"config.telemetry",
|
|
10863
|
+
"config.telemetry.on",
|
|
10864
|
+
"config.telemetry.off",
|
|
10865
|
+
"config.telemetry.status",
|
|
10866
|
+
"invite.get",
|
|
10867
|
+
"invite.review",
|
|
10868
|
+
"invite.wait",
|
|
10824
10869
|
"org.invite.get",
|
|
10825
10870
|
"org.invite.wait",
|
|
10826
|
-
"org.invite.review",
|
|
10827
|
-
"org.invite.accept",
|
|
10828
|
-
"org.invite.decline",
|
|
10829
|
-
"org.invite.cancel",
|
|
10830
|
-
"org.invite.resend",
|
|
10831
10871
|
"org.invite.retry",
|
|
10832
|
-
"
|
|
10833
|
-
|
|
10834
|
-
|
|
10872
|
+
"schema",
|
|
10873
|
+
"doctor",
|
|
10874
|
+
"dev",
|
|
10875
|
+
"dev.key",
|
|
10876
|
+
"dev.fund",
|
|
10835
10877
|
"unknown"
|
|
10836
10878
|
];
|
|
10837
10879
|
const CLI_CAPABILITIES = [
|
|
@@ -12525,7 +12567,7 @@ function mapAccountLifecycle(input) {
|
|
|
12525
12567
|
}
|
|
12526
12568
|
//#endregion
|
|
12527
12569
|
//#region package.json
|
|
12528
|
-
var version = "4.20.0-beta.
|
|
12570
|
+
var version = "4.20.0-beta.27";
|
|
12529
12571
|
//#endregion
|
|
12530
12572
|
//#region src/observability/telemetry/exception-projection.ts
|
|
12531
12573
|
/** Fixed fallback for failures that have no safe message. */
|
|
@@ -16726,6 +16768,13 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
|
|
|
16726
16768
|
}
|
|
16727
16769
|
//#endregion
|
|
16728
16770
|
//#region src/payments/lifecycle-execution.ts
|
|
16771
|
+
/**
|
|
16772
|
+
* The prepared command is this lifecycle intent; a retry also names the
|
|
16773
|
+
* original birth command of the scope it was asked for.
|
|
16774
|
+
*/
|
|
16775
|
+
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));
|
|
16776
|
+
/** The prepared command pays exactly the request the payer confirmed. */
|
|
16777
|
+
const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
|
|
16729
16778
|
function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
|
|
16730
16779
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, lifecycles.prepareScoped, copyInvocationObservation(controls, {
|
|
16731
16780
|
signerAddress,
|
|
@@ -16734,13 +16783,13 @@ function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganiz
|
|
|
16734
16783
|
kind: "organization",
|
|
16735
16784
|
orgId: expectedOrganizationId
|
|
16736
16785
|
}
|
|
16737
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
16786
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), lifecycleCommandCheck(intent, expectedOrganizationId), false, void 0, signal, controls, onPrepared);
|
|
16738
16787
|
}
|
|
16739
16788
|
function executeRequestPayment(deps, confirmation, controls, onPrepared) {
|
|
16740
16789
|
return executePrepared(deps, (signerAddress) => runIfActive(controls?.signal, commands$1.prepareRequest.name, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareRequest, copyInvocationObservation(controls, {
|
|
16741
16790
|
confirmation,
|
|
16742
16791
|
signerAddress
|
|
16743
|
-
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise),
|
|
16792
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))), controls), deps.runPromise), requestPaymentCheck(confirmation), false, void 0, controls?.signal, controls, onPrepared);
|
|
16744
16793
|
}
|
|
16745
16794
|
function executeOrganizationPayment(deps, input, signal, onPreparedClosed, controls, onPrepared) {
|
|
16746
16795
|
return executePrepared(deps, (signerAddress) => runIfActive(signal, CAPXUL_OPERATIONS.organizationPayments.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, commands$1.prepareOrganization, copyInvocationObservation(controls, {
|
|
@@ -18224,63 +18273,6 @@ function makeOfframpMethods() {
|
|
|
18224
18273
|
};
|
|
18225
18274
|
}
|
|
18226
18275
|
//#endregion
|
|
18227
|
-
//#region src/payments/organization-payment-reads.ts
|
|
18228
|
-
const exactPaymentId = (value) => Schema.decodeEffect(PaymentIdSchema)(value).pipe(Effect.mapError(() => Errors.invalidInput("paymentId", "Provide one exact Payment ID")));
|
|
18229
|
-
/** Read the backend-authorized Organization wrapper before projecting any Payment. */
|
|
18230
|
-
function makeOrganizationPaymentReadMethods(deps, orgId) {
|
|
18231
|
-
return {
|
|
18232
|
-
previewRetry(paymentId, options) {
|
|
18233
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
18234
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
18235
|
-
const id = yield* exactPaymentId(paymentId);
|
|
18236
|
-
const preview = yield* callOperation(deps.convexCall, lifecycles.previewRetry, copyInvocationObservation(controls, {
|
|
18237
|
-
orgId,
|
|
18238
|
-
paymentId: id
|
|
18239
|
-
}));
|
|
18240
|
-
if (preview.orgId !== orgId || !preview.paymentIds.includes(id) || new Set(preview.paymentIds).size !== preview.paymentIds.length || preview.payments.length !== preview.paymentIds.length || preview.payments.some((payment, index) => payment.id !== preview.paymentIds[index] || payment.direction !== "out")) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization retry preview"));
|
|
18241
|
-
return {
|
|
18242
|
-
...preview,
|
|
18243
|
-
paymentIds: [...preview.paymentIds],
|
|
18244
|
-
payments: preview.payments.map((payment) => normalizePaymentTiming(payment))
|
|
18245
|
-
};
|
|
18246
|
-
}), controls, lifecycles.previewRetry.name, deps.runPromise);
|
|
18247
|
-
},
|
|
18248
|
-
list(options) {
|
|
18249
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
18250
|
-
return runCapxulEffect(callOperation(deps.convexCall, records.listForOrganization, copyInvocationObservation(controls, { orgId })).pipe(Effect.filterOrFail((result) => result.orgId === orgId, () => Errors.invalidInput("orgId", "Invalid Organization Payment list")), Effect.map((result) => mapPage(result, (payment) => normalizePaymentTiming(payment)))), controls, records.listForOrganization.name, deps.runPromise);
|
|
18251
|
-
},
|
|
18252
|
-
get(paymentId, options) {
|
|
18253
|
-
const controls = deps.invocationControls?.(options) ?? options;
|
|
18254
|
-
return runCapxulEffect(Effect.gen(function* () {
|
|
18255
|
-
const id = yield* exactPaymentId(paymentId);
|
|
18256
|
-
const response = yield* callOperation(deps.convexCall, records.getForOrganization, copyInvocationObservation(controls, {
|
|
18257
|
-
orgId,
|
|
18258
|
-
paymentId: id
|
|
18259
|
-
}));
|
|
18260
|
-
if (response.orgId !== orgId) return yield* Effect.fail(Errors.invalidInput("orgId", "Payment read scope does not match"));
|
|
18261
|
-
const decoded = response.payment;
|
|
18262
|
-
if (decoded === null) return null;
|
|
18263
|
-
if (decoded.id !== id) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization Payment response"));
|
|
18264
|
-
return normalizePaymentTiming(decoded);
|
|
18265
|
-
}), controls, records.getForOrganization.name, deps.runPromise);
|
|
18266
|
-
},
|
|
18267
|
-
subscribe(paymentId, listener, options) {
|
|
18268
|
-
const parsed = Schema.decodeResult(PaymentIdSchema)(paymentId);
|
|
18269
|
-
if (parsed._tag === "Failure") return Promise.resolve({
|
|
18270
|
-
ok: false,
|
|
18271
|
-
error: Errors.invalidInput("paymentId", "Provide one exact Payment ID")
|
|
18272
|
-
});
|
|
18273
|
-
return subscribeOrganizationPayment({
|
|
18274
|
-
actor: deps.actor,
|
|
18275
|
-
convexCall: deps.convexCall,
|
|
18276
|
-
getPayment: reference(records.getForOrganization),
|
|
18277
|
-
runPromise: deps.runPromise,
|
|
18278
|
-
invocationControls: deps.invocationControls
|
|
18279
|
-
}, orgId, parsed.success, listener, options);
|
|
18280
|
-
}
|
|
18281
|
-
};
|
|
18282
|
-
}
|
|
18283
|
-
//#endregion
|
|
18284
18276
|
//#region src/payments/organization-payment-requests.ts
|
|
18285
18277
|
const organizationPaymentIntentSchema = Schema.Struct({
|
|
18286
18278
|
orgId: PrepareOrganizationPaymentExecutionInputSchema.fields.orgId,
|
|
@@ -18341,8 +18333,65 @@ async function organizationPaymentRequest(scope, actor, intent, providedKey) {
|
|
|
18341
18333
|
}
|
|
18342
18334
|
}
|
|
18343
18335
|
//#endregion
|
|
18336
|
+
//#region src/payments/organization-payment-reads.ts
|
|
18337
|
+
const exactPaymentId = (value) => Schema.decodeEffect(PaymentIdSchema)(value).pipe(Effect.mapError(() => Errors.invalidInput("paymentId", "Provide one exact Payment ID")));
|
|
18338
|
+
/** Read the backend-authorized Organization wrapper before projecting any Payment. */
|
|
18339
|
+
function makeOrganizationPaymentReadMethods(deps, orgId) {
|
|
18340
|
+
return {
|
|
18341
|
+
previewRetry(paymentId, options) {
|
|
18342
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
18343
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
18344
|
+
const id = yield* exactPaymentId(paymentId);
|
|
18345
|
+
const preview = yield* callOperation(deps.convexCall, lifecycles.previewRetry, copyInvocationObservation(controls, {
|
|
18346
|
+
orgId,
|
|
18347
|
+
paymentId: id
|
|
18348
|
+
}));
|
|
18349
|
+
if (preview.orgId !== orgId || !preview.paymentIds.includes(id) || new Set(preview.paymentIds).size !== preview.paymentIds.length || preview.payments.length !== preview.paymentIds.length || preview.payments.some((payment, index) => payment.id !== preview.paymentIds[index] || payment.direction !== "out")) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization retry preview"));
|
|
18350
|
+
return {
|
|
18351
|
+
...preview,
|
|
18352
|
+
paymentIds: [...preview.paymentIds],
|
|
18353
|
+
payments: preview.payments.map((payment) => normalizePaymentTiming(payment))
|
|
18354
|
+
};
|
|
18355
|
+
}), controls, lifecycles.previewRetry.name, deps.runPromise);
|
|
18356
|
+
},
|
|
18357
|
+
list(options) {
|
|
18358
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
18359
|
+
return runCapxulEffect(callOperation(deps.convexCall, records.listForOrganization, copyInvocationObservation(controls, { orgId })).pipe(Effect.filterOrFail((result) => result.orgId === orgId, () => Errors.invalidInput("orgId", "Invalid Organization Payment list")), Effect.map((result) => mapPage(result, (payment) => normalizePaymentTiming(payment)))), controls, records.listForOrganization.name, deps.runPromise);
|
|
18360
|
+
},
|
|
18361
|
+
get(paymentId, options) {
|
|
18362
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
18363
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
18364
|
+
const id = yield* exactPaymentId(paymentId);
|
|
18365
|
+
const response = yield* callOperation(deps.convexCall, records.getForOrganization, copyInvocationObservation(controls, {
|
|
18366
|
+
orgId,
|
|
18367
|
+
paymentId: id
|
|
18368
|
+
}));
|
|
18369
|
+
if (response.orgId !== orgId) return yield* Effect.fail(Errors.invalidInput("orgId", "Payment read scope does not match"));
|
|
18370
|
+
const decoded = response.payment;
|
|
18371
|
+
if (decoded === null) return null;
|
|
18372
|
+
if (decoded.id !== id) return yield* Effect.fail(Errors.invalidInput("paymentId", "Invalid Organization Payment response"));
|
|
18373
|
+
return normalizePaymentTiming(decoded);
|
|
18374
|
+
}), controls, records.getForOrganization.name, deps.runPromise);
|
|
18375
|
+
},
|
|
18376
|
+
subscribe(paymentId, listener, options) {
|
|
18377
|
+
const parsed = Schema.decodeResult(PaymentIdSchema)(paymentId);
|
|
18378
|
+
if (parsed._tag === "Failure") return Promise.resolve({
|
|
18379
|
+
ok: false,
|
|
18380
|
+
error: Errors.invalidInput("paymentId", "Provide one exact Payment ID")
|
|
18381
|
+
});
|
|
18382
|
+
return subscribeOrganizationPayment({
|
|
18383
|
+
actor: deps.actor,
|
|
18384
|
+
convexCall: deps.convexCall,
|
|
18385
|
+
getPayment: reference(records.getForOrganization),
|
|
18386
|
+
runPromise: deps.runPromise,
|
|
18387
|
+
invocationControls: deps.invocationControls
|
|
18388
|
+
}, orgId, parsed.success, listener, options);
|
|
18389
|
+
}
|
|
18390
|
+
};
|
|
18391
|
+
}
|
|
18392
|
+
//#endregion
|
|
18344
18393
|
//#region src/payments/organization-flights.ts
|
|
18345
|
-
function executionDependencies(input) {
|
|
18394
|
+
function executionDependencies$1(input) {
|
|
18346
18395
|
return input.actor === void 0 || input.chainId === void 0 || input.signer === void 0 ? null : {
|
|
18347
18396
|
actor: input.actor,
|
|
18348
18397
|
chainId: input.chainId,
|
|
@@ -18363,7 +18412,7 @@ async function executeOrganizationBatch(scope, input, signal, operation, control
|
|
|
18363
18412
|
};
|
|
18364
18413
|
const intent = organizationPaymentIntent(scope.orgId, input);
|
|
18365
18414
|
if (!intent.ok) return intent;
|
|
18366
|
-
const execution = executionDependencies(scope.deps);
|
|
18415
|
+
const execution = executionDependencies$1(scope.deps);
|
|
18367
18416
|
if (execution === null) return {
|
|
18368
18417
|
ok: false,
|
|
18369
18418
|
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
@@ -18487,7 +18536,7 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
|
|
|
18487
18536
|
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
18488
18537
|
const captured = carryInvocationParentSpan(controls, parent);
|
|
18489
18538
|
return yield* awaitCapxulResult(async () => {
|
|
18490
|
-
const execution = executionDependencies(deps);
|
|
18539
|
+
const execution = executionDependencies$1(deps);
|
|
18491
18540
|
if (execution === null) return {
|
|
18492
18541
|
ok: false,
|
|
18493
18542
|
error: Errors.notImplemented("organizationPayments", "executionComposition")
|
|
@@ -19251,50 +19300,650 @@ function sleep(ms, signal) {
|
|
|
19251
19300
|
});
|
|
19252
19301
|
}
|
|
19253
19302
|
//#endregion
|
|
19254
|
-
//#region src/
|
|
19255
|
-
function
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19276
|
-
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19303
|
+
//#region ../protocol/src/payroll/model.ts
|
|
19304
|
+
function toPayrollRunId(raw) {
|
|
19305
|
+
return toNonEmptyStringBrand(raw, "payrollRunId", (value) => isNonEmptyString(value));
|
|
19306
|
+
}
|
|
19307
|
+
function toPayrollGroupId(raw) {
|
|
19308
|
+
return toNonEmptyStringBrand(raw, "payrollGroupId", (value) => isNonEmptyString(value));
|
|
19309
|
+
}
|
|
19310
|
+
//#endregion
|
|
19311
|
+
//#region src/payroll/run-checks.ts
|
|
19312
|
+
/** Match the caller's compensation to the committed V3 Payslip. */
|
|
19313
|
+
function payslipIdentityMatches(message, item, index, runId, period) {
|
|
19314
|
+
return message.runId === runId && message.itemIndex === index && message.employeeRef === item.partyId && message.periodStart === period.start && message.periodEnd === period.end && message.adjustmentsHash === hashAdjustmentsV3(item.adjustments);
|
|
19315
|
+
}
|
|
19316
|
+
function payslipAmountMatches(message, item) {
|
|
19317
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19318
|
+
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;
|
|
19319
|
+
}
|
|
19320
|
+
function isRunItem(prepared, item, evidence, index, runId, period) {
|
|
19321
|
+
if (prepared === void 0) return false;
|
|
19322
|
+
const bundle = evidence.preparedCommand?.documentBundles?.[index];
|
|
19323
|
+
const preparedItem = evidence.preparedCommand?.items[index];
|
|
19324
|
+
const payslip = bundle?.envelope;
|
|
19325
|
+
if (bundle === void 0 || payslip?.primaryType !== "Payslip") return false;
|
|
19326
|
+
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;
|
|
19327
|
+
}
|
|
19328
|
+
/**
|
|
19329
|
+
* The batch the backend derived from a payroll intent must be the batch this
|
|
19330
|
+
* caller asked for, checked before the digest is signed.
|
|
19331
|
+
*
|
|
19332
|
+
* The payslip envelope on each item is server-built and carries the run row's
|
|
19333
|
+
* own `issuedAt`, so the batch cannot be fingerprinted against a local copy
|
|
19334
|
+
* (`matchesIntent` is unavailable here). What CAN be verified is every field
|
|
19335
|
+
* the caller supplied — the org, the Budget, the request key, and per item the
|
|
19336
|
+
* recipient, the amount and the payslip figures — plus the lineage binding the
|
|
19337
|
+
* batch to the run just written. A prepared command that pays a different
|
|
19338
|
+
* person, a different amount, from a different Budget, or under a different
|
|
19339
|
+
* run never reaches the signer.
|
|
19340
|
+
*/
|
|
19341
|
+
function isRunBatch(input) {
|
|
19342
|
+
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)));
|
|
19343
|
+
}
|
|
19344
|
+
/**
|
|
19345
|
+
* The batch is `isRunBatch` for the run the backend just wrote, and that run
|
|
19346
|
+
* is the period the caller asked for: the payslip's `period` is a
|
|
19347
|
+
* server-formatted label, so the stored run is where the caller's own two
|
|
19348
|
+
* numbers are checkable.
|
|
19349
|
+
*/
|
|
19350
|
+
function isAuthorizedRunBatch(input, run) {
|
|
19351
|
+
return (request, evidence) => run === void 0 || run.periodStart !== input.period.start || run.periodEnd !== input.period.end ? Promise.resolve(false) : isRunBatch({
|
|
19352
|
+
...input,
|
|
19353
|
+
runId: run.id
|
|
19354
|
+
})(request, evidence);
|
|
19355
|
+
}
|
|
19356
|
+
/** A decoded detail is the run asked for, whole: its scope, membership and quantities agree. */
|
|
19357
|
+
function checkPayrollRunDetail(value, orgId, runId) {
|
|
19358
|
+
if (value === null) return {
|
|
19359
|
+
ok: true,
|
|
19360
|
+
value: null
|
|
19361
|
+
};
|
|
19362
|
+
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 {
|
|
19363
|
+
ok: false,
|
|
19364
|
+
error: Errors.invalidInput("run", "Payroll run scope or membership does not match")
|
|
19365
|
+
};
|
|
19366
|
+
try {
|
|
19367
|
+
const total = toExactAssetAmount(value.run.total, CONFIGURED_MONEY_ASSETS);
|
|
19368
|
+
let rawTotal = 0n;
|
|
19369
|
+
for (const item of value.items) {
|
|
19370
|
+
const amount = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19371
|
+
const payment = toExactAssetAmount(item.payment.amount, CONFIGURED_MONEY_ASSETS);
|
|
19372
|
+
if (amount.minorUnits !== payment.minorUnits) return {
|
|
19373
|
+
ok: false,
|
|
19374
|
+
error: Errors.invalidInput("run", "Payroll item quantity does not match its Payment")
|
|
19375
|
+
};
|
|
19376
|
+
rawTotal += BigInt(amount.minorUnits);
|
|
19377
|
+
}
|
|
19378
|
+
if (rawTotal !== BigInt(total.minorUnits)) return {
|
|
19379
|
+
ok: false,
|
|
19380
|
+
error: Errors.invalidInput("run", "Payroll run total does not match its items")
|
|
19381
|
+
};
|
|
19382
|
+
} catch {
|
|
19383
|
+
return {
|
|
19384
|
+
ok: false,
|
|
19385
|
+
error: Errors.invalidInput("run", "Payroll run quantities are invalid")
|
|
19386
|
+
};
|
|
19387
|
+
}
|
|
19388
|
+
return {
|
|
19389
|
+
ok: true,
|
|
19390
|
+
value
|
|
19391
|
+
};
|
|
19392
|
+
}
|
|
19393
|
+
//#endregion
|
|
19394
|
+
//#region src/payroll/run-decoding.ts
|
|
19395
|
+
function toPayrollRun(wire) {
|
|
19396
|
+
return {
|
|
19397
|
+
id: toPayrollRunId(wire.id),
|
|
19398
|
+
status: wire.status,
|
|
19399
|
+
periodStart: wire.periodStart,
|
|
19400
|
+
periodEnd: wire.periodEnd,
|
|
19401
|
+
total: wire.total,
|
|
19402
|
+
recipientCount: wire.recipientCount
|
|
19403
|
+
};
|
|
19404
|
+
}
|
|
19405
|
+
function toPayrollRuns(wire) {
|
|
19406
|
+
return {
|
|
19407
|
+
...mapPage(wire, toPayrollRun),
|
|
19408
|
+
settledThisMonth: wire.settledThisMonth
|
|
19409
|
+
};
|
|
19410
|
+
}
|
|
19411
|
+
function toPayrollGroup(wire) {
|
|
19412
|
+
return {
|
|
19413
|
+
id: toPayrollGroupId(wire.id),
|
|
19414
|
+
name: wire.name,
|
|
19415
|
+
tone: wire.tone,
|
|
19416
|
+
members: wire.members.map((member) => ({
|
|
19417
|
+
partyId: member.partyId,
|
|
19418
|
+
amount: member.amount,
|
|
19419
|
+
currency: member.currency
|
|
19420
|
+
}))
|
|
19421
|
+
};
|
|
19422
|
+
}
|
|
19423
|
+
function validateRunItems(input) {
|
|
19424
|
+
for (const [index, item] of input.items.entries()) {
|
|
19425
|
+
if (Schema.decodeResult(PayrollRunItemInputSchema, { onExcessProperty: "error" })(item)._tag === "Failure") return Errors.invalidInput(`items.${index}`, "invalid Payroll item");
|
|
19426
|
+
try {
|
|
19427
|
+
const exact = toExactAssetAmount(item.amount, CONFIGURED_MONEY_ASSETS);
|
|
19428
|
+
const net = BigInt(item.net);
|
|
19429
|
+
const gross = BigInt(item.gross);
|
|
19430
|
+
const adjustments = item.adjustments.reduce((sum, row) => sum + BigInt(row.amount), 0n);
|
|
19431
|
+
if (item.amount.asset !== input.asset || item.net !== exact.minorUnits || gross + adjustments !== net) return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
19432
|
+
} catch {
|
|
19433
|
+
return Errors.invalidInput(`items.${index}`, "invalid Payroll units");
|
|
19434
|
+
}
|
|
19435
|
+
}
|
|
19436
|
+
return null;
|
|
19437
|
+
}
|
|
19438
|
+
const RunInputSchema = Schema.Struct({
|
|
19439
|
+
permissionId: AuthorizePayrollRunExecutionInputSchema.fields.permissionId,
|
|
19440
|
+
asset: AuthorizePayrollRunExecutionInputSchema.fields.asset,
|
|
19441
|
+
period: AuthorizePayrollRunExecutionInputSchema.fields.period,
|
|
19442
|
+
items: AuthorizePayrollRunExecutionInputSchema.fields.items,
|
|
19443
|
+
requestKey: Schema.optional(AuthorizePayrollRunExecutionInputSchema.fields.requestKey)
|
|
19444
|
+
});
|
|
19445
|
+
/** Decode caller input before session or signer work. Keep routing outside the input. */
|
|
19446
|
+
function decodePayrollRunInput(value) {
|
|
19447
|
+
try {
|
|
19448
|
+
const decoded = Schema.decodeUnknownSync(RunInputSchema, { onExcessProperty: "error" })(value);
|
|
19449
|
+
const input = {
|
|
19450
|
+
permissionId: decoded.permissionId,
|
|
19451
|
+
asset: decoded.asset,
|
|
19452
|
+
period: decoded.period,
|
|
19453
|
+
...decoded.requestKey === void 0 ? {} : { requestKey: decoded.requestKey },
|
|
19454
|
+
items: decoded.items.map((item) => ({
|
|
19455
|
+
amount: item.amount,
|
|
19456
|
+
gross: item.gross,
|
|
19457
|
+
net: item.net,
|
|
19458
|
+
adjustments: item.adjustments,
|
|
19459
|
+
partyId: toPartyId(item.partyId),
|
|
19460
|
+
to: item.to.kind === "party" ? {
|
|
19461
|
+
kind: "party",
|
|
19462
|
+
partyId: toPartyId(item.to.partyId)
|
|
19463
|
+
} : item.to.kind === "address" ? {
|
|
19464
|
+
kind: "address",
|
|
19465
|
+
address: toAddress(item.to.address)
|
|
19466
|
+
} : item.to
|
|
19467
|
+
}))
|
|
19468
|
+
};
|
|
19469
|
+
if (input.items.length === 0 || input.period.start > input.period.end) throw Errors.invalidInput("input", "Payroll requires items and an ordered period");
|
|
19470
|
+
const error = validateRunItems(input);
|
|
19471
|
+
if (error !== null) throw error;
|
|
19472
|
+
return input;
|
|
19473
|
+
} catch (cause) {
|
|
19474
|
+
throw cause instanceof CapxulError ? cause : Errors.invalidInput("input", "Invalid Payroll input");
|
|
19475
|
+
}
|
|
19476
|
+
}
|
|
19477
|
+
/** Read only the exact backend prepare receipt, never an arbitrary signer failure. */
|
|
19478
|
+
function payrollResolution(error, requestKey) {
|
|
19479
|
+
const details = error.details;
|
|
19480
|
+
if (error.code !== "WRONG_STATE" || details?.method !== runs.authorize.name || details.requestKey !== requestKey || typeof details.commandId !== "string" || typeof details.runId !== "string") return void 0;
|
|
19481
|
+
return details.payrollResolution === "failed" || details.payrollResolution === "settled" ? details.payrollResolution : void 0;
|
|
19482
|
+
}
|
|
19483
|
+
function validatePayrollRunLookup(orgId, runId) {
|
|
19484
|
+
return !Schema.is(PayrollRunIdSchema)(runId) || orgId.length === 0 ? {
|
|
19485
|
+
ok: false,
|
|
19486
|
+
error: Errors.invalidInput("runId", "Organization and run ID must be valid")
|
|
19487
|
+
} : {
|
|
19488
|
+
ok: true,
|
|
19489
|
+
value: void 0
|
|
19490
|
+
};
|
|
19491
|
+
}
|
|
19492
|
+
/** Strict saved detail decoder shared by the read and fresh stream. */
|
|
19493
|
+
function decodePayrollRunDetail(raw, orgId, runId) {
|
|
19494
|
+
const decoded = Schema.decodeUnknownResult(Schema.NullOr(PayrollRunDetailSchema), { onExcessProperty: "error" })(raw);
|
|
19495
|
+
if (decoded._tag === "Failure") return {
|
|
19496
|
+
ok: false,
|
|
19497
|
+
error: Errors.invalidInput("run", "Invalid Payroll run detail")
|
|
19498
|
+
};
|
|
19499
|
+
return checkPayrollRunDetail(decoded.success, orgId, runId);
|
|
19500
|
+
}
|
|
19501
|
+
//#endregion
|
|
19502
|
+
//#region src/payroll/run-subscription.ts
|
|
19503
|
+
function detailValue(result) {
|
|
19504
|
+
if (!result.ok) throw result.error;
|
|
19505
|
+
return result.value;
|
|
19506
|
+
}
|
|
19507
|
+
function subscribePayrollRun(deps, orgId, runId, listener, options) {
|
|
19508
|
+
const operation = CAPXUL_OPERATIONS.org.payroll.subscribeRun;
|
|
19509
|
+
const valid = deps.validate(orgId, runId);
|
|
19510
|
+
if (!valid.ok) return runCapxulEffect(Effect.fail(valid.error), observationOnlyControls(deps.invocationControls?.(options) ?? options), operation, deps.runPromise);
|
|
19511
|
+
const decode = (raw) => detailValue(deps.decode(raw, orgId, runId));
|
|
19512
|
+
return subscribeFreshQuery(deps, {
|
|
19513
|
+
orgId,
|
|
19514
|
+
runId
|
|
19515
|
+
}, listener, operation, {
|
|
19516
|
+
decode,
|
|
19517
|
+
validateCached: (raw) => {
|
|
19518
|
+
decode(raw);
|
|
19519
|
+
},
|
|
19520
|
+
invalidValue: (cause) => isCapxulError(cause) ? cause : Errors.invalidInput("run", "Invalid Payroll run detail"),
|
|
19521
|
+
invalidScope: () => Errors.invalidInput("run", "Payroll run subscription scope does not match"),
|
|
19522
|
+
lostValue: () => Errors.invalidInput("run", "Payroll run is no longer readable"),
|
|
19523
|
+
unavailable: () => Errors.notImplemented("payroll", "subscribeRun")
|
|
19524
|
+
}, options);
|
|
19525
|
+
}
|
|
19526
|
+
//#endregion
|
|
19527
|
+
//#region src/payroll/run-authorization.ts
|
|
19528
|
+
/**
|
|
19529
|
+
* Authorize one payroll run: a stable request key, the prepared batch checked
|
|
19530
|
+
* against the run the caller asked for, the signature, and the submission.
|
|
19531
|
+
*/
|
|
19532
|
+
async function authorizePayrollRun(scope, input, controls, onRequestKey, onResolved) {
|
|
19533
|
+
const { deps, orgId, requestKeyScope } = scope;
|
|
19534
|
+
const signal = controls?.signal;
|
|
19535
|
+
const execution = executionDependencies(deps);
|
|
19536
|
+
if (execution === null) return {
|
|
19537
|
+
ok: false,
|
|
19538
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
19539
|
+
};
|
|
19540
|
+
try {
|
|
19541
|
+
input = decodePayrollRunInput(input);
|
|
19542
|
+
} catch (cause) {
|
|
19543
|
+
return {
|
|
19544
|
+
ok: false,
|
|
19545
|
+
error: asCapxulError(cause)
|
|
19546
|
+
};
|
|
19547
|
+
}
|
|
19548
|
+
const { requestKey: provided, ...canonicalInput } = input;
|
|
19549
|
+
const actorId = onRequestKey === void 0 ? void 0 : execution.actor.authSession()?.authUserId;
|
|
19550
|
+
if (onRequestKey !== void 0 && actorId === void 0) return {
|
|
19551
|
+
ok: false,
|
|
19552
|
+
error: Errors.notAuthenticated()
|
|
19553
|
+
};
|
|
19554
|
+
const requestKey = await paymentRequestKeyLifecycle(`${requestKeyScope}:${AUTHORIZE_RUN}`, onRequestKey === void 0 ? {
|
|
19555
|
+
orgId,
|
|
19556
|
+
input
|
|
19557
|
+
} : {
|
|
19558
|
+
actorId,
|
|
19559
|
+
orgId,
|
|
19560
|
+
input: canonicalInput
|
|
19561
|
+
}, provided, onRequestKey === void 0 ? void 0 : { manageProvidedKey: true });
|
|
19562
|
+
try {
|
|
19563
|
+
await onRequestKey?.(requestKey.key);
|
|
19564
|
+
if (onRequestKey !== void 0 && execution.actor.authSession()?.authUserId !== actorId) {
|
|
19565
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
19566
|
+
return {
|
|
19567
|
+
ok: false,
|
|
19568
|
+
error: Errors.notAuthenticated()
|
|
19569
|
+
};
|
|
19570
|
+
}
|
|
19571
|
+
} catch (cause) {
|
|
19572
|
+
await requestKey.finish(false).catch(() => void 0);
|
|
19573
|
+
return {
|
|
19574
|
+
ok: false,
|
|
19575
|
+
error: asCapxulError(cause)
|
|
19576
|
+
};
|
|
19577
|
+
}
|
|
19578
|
+
let authorized;
|
|
19579
|
+
let acceptedReplay;
|
|
19580
|
+
let preparedClosed = false;
|
|
19581
|
+
let terminalReplay;
|
|
19582
|
+
const submitted = await executePrepared(execution, async (signerAddress) => {
|
|
19583
|
+
const prepared = await runIfActive(signal, AUTHORIZE_RUN, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.authorize, copyInvocationObservation(controls, {
|
|
19584
|
+
orgId,
|
|
19585
|
+
permissionId: input.permissionId,
|
|
19586
|
+
asset: input.asset,
|
|
19587
|
+
requestKey: requestKey.key,
|
|
19588
|
+
signerAddress,
|
|
19589
|
+
period: input.period,
|
|
19590
|
+
items: input.items
|
|
19591
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), controls), deps.runPromise);
|
|
19592
|
+
if (!prepared.ok) {
|
|
19593
|
+
terminalReplay = payrollResolution(prepared.error, requestKey.key);
|
|
19594
|
+
return prepared;
|
|
19595
|
+
}
|
|
19596
|
+
authorized = prepared.value.run;
|
|
19597
|
+
acceptedReplay = prepared.value.replay.kind === "accepted" ? prepared.value.replay.submission : void 0;
|
|
19598
|
+
return {
|
|
19599
|
+
ok: true,
|
|
19600
|
+
value: prepared.value.execution
|
|
19601
|
+
};
|
|
19602
|
+
}, (request, evidence) => isAuthorizedRunBatch({
|
|
19603
|
+
orgId,
|
|
19604
|
+
permissionId: input.permissionId,
|
|
19605
|
+
requestKey: requestKey.key,
|
|
19606
|
+
asset: input.asset,
|
|
19607
|
+
period: input.period,
|
|
19608
|
+
items: input.items
|
|
19609
|
+
}, authorized)(request, evidence), true, () => preparedClosed = true, signal, controls, void 0, () => acceptedReplay);
|
|
19610
|
+
const resolution = preparedClosed ? "failed" : terminalReplay;
|
|
19611
|
+
if (resolution !== void 0) try {
|
|
19612
|
+
await onResolved?.(resolution);
|
|
19613
|
+
} catch {}
|
|
19614
|
+
try {
|
|
19615
|
+
await requestKey.finish(submitted.ok || resolution !== void 0);
|
|
19616
|
+
} catch {}
|
|
19617
|
+
if (!submitted.ok) return submitted;
|
|
19618
|
+
return authorized === void 0 ? {
|
|
19619
|
+
ok: false,
|
|
19620
|
+
error: Errors.unknown()
|
|
19621
|
+
} : {
|
|
19622
|
+
ok: true,
|
|
19623
|
+
value: toPayrollRun(authorized)
|
|
19624
|
+
};
|
|
19625
|
+
}
|
|
19626
|
+
const AUTHORIZE_RUN = runs.authorize.name;
|
|
19627
|
+
function executionDependencies(deps) {
|
|
19628
|
+
return deps.actor === void 0 || deps.chainId === void 0 || deps.signer === void 0 ? null : {
|
|
19629
|
+
actor: deps.actor,
|
|
19630
|
+
chainId: deps.chainId,
|
|
19631
|
+
convexCall: deps.convexCall,
|
|
19632
|
+
runPromise: deps.runPromise,
|
|
19633
|
+
signer: deps.signer,
|
|
19634
|
+
...deps.telemetry === void 0 ? {} : { telemetry: deps.telemetry }
|
|
19635
|
+
};
|
|
19636
|
+
}
|
|
19637
|
+
//#endregion
|
|
19638
|
+
//#region src/payroll/runs.ts
|
|
19639
|
+
function makePayrollMethods(deps, orgId) {
|
|
19640
|
+
const requestKeyScope = deps.requestKeyScope ?? `client-${crypto.randomUUID()}`;
|
|
19641
|
+
/** One observed call of one payroll row, its reply projected inside the call, under the row's name. */
|
|
19642
|
+
const call = (row, args, options, project) => {
|
|
19643
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19644
|
+
return runPortEffect(callOperation(deps.convexCall, row, copyInvocationObservation(controls, args)).pipe(Effect.map(project), Effect.mapError((publicError) => ({ publicError }))), controls, row.name, deps.runPromise);
|
|
19645
|
+
};
|
|
19646
|
+
return {
|
|
19647
|
+
subscribeRun: (runId, listener, options) => subscribePayrollRun({
|
|
19648
|
+
actor: deps.actor,
|
|
19649
|
+
convexCall: deps.convexCall,
|
|
19650
|
+
query: reference(runs.get),
|
|
19651
|
+
runPromise: deps.runPromise,
|
|
19652
|
+
invocationControls: deps.invocationControls,
|
|
19653
|
+
decode: decodePayrollRunDetail,
|
|
19654
|
+
validate: validatePayrollRunLookup
|
|
19655
|
+
}, orgId, runId, listener, options),
|
|
19656
|
+
getRun: (runId, options) => {
|
|
19657
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19658
|
+
const lifetime = deps.actor?.authLifetime();
|
|
19659
|
+
const operation = runs.get.name;
|
|
19660
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
19661
|
+
const lookup = validatePayrollRunLookup(orgId, runId);
|
|
19662
|
+
if (!lookup.ok) return yield* Effect.fail(lookup.error);
|
|
19663
|
+
if (lifetime === void 0 || lifetime.authUserId === null || deps.actor === void 0) return yield* Effect.fail(Errors.notAuthenticated());
|
|
19664
|
+
const current = () => deps.actor?.matchesAuthLifetime(lifetime, lifetime.authUserId) === true;
|
|
19665
|
+
if (!current()) return yield* Effect.fail(Errors.notAuthenticated());
|
|
19666
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
19667
|
+
const captured = carryInvocationParentSpan(controls, parent);
|
|
19668
|
+
const run = yield* awaitCapxulResult(() => runIfActive(controls?.signal, operation, () => withInvocationParentSpan(Effect.suspend(() => callOperation(deps.convexCall, runs.get, copyInvocationObservation(captured, {
|
|
19669
|
+
orgId,
|
|
19670
|
+
runId
|
|
19671
|
+
}))).pipe(Effect.mapError((publicError) => ({ publicError }))), captured), deps.runPromise));
|
|
19672
|
+
if (!current()) return yield* Effect.fail(Errors.cancelled({ operation }));
|
|
19673
|
+
const checked = checkPayrollRunDetail(run, orgId, runId);
|
|
19674
|
+
return yield* checked.ok ? Effect.succeed(checked.value) : Effect.fail(checked.error);
|
|
19675
|
+
}), observationOnlyControls(controls), operation, deps.runPromise);
|
|
19676
|
+
},
|
|
19677
|
+
runs: (options) => call(runs.list, { orgId }, options, toPayrollRuns),
|
|
19678
|
+
authorizeRun: (input, options) => {
|
|
19679
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
19680
|
+
return runCapxulEffect(Effect.gen(function* () {
|
|
19681
|
+
if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation: AUTHORIZE_RUN }));
|
|
19682
|
+
const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
19683
|
+
return yield* awaitCapxulResult(() => authorizePayrollRun({
|
|
19684
|
+
deps,
|
|
19685
|
+
orgId,
|
|
19686
|
+
requestKeyScope
|
|
19687
|
+
}, input, carryInvocationParentSpan(controls, parent), options?.onRequestKey, options?.onResolved));
|
|
19688
|
+
}), observationOnlyControls(controls), AUTHORIZE_RUN, deps.runPromise);
|
|
19689
|
+
},
|
|
19690
|
+
terms: (options) => call(terms.list, { orgId }, options, (rates) => rates),
|
|
19691
|
+
groups: {
|
|
19692
|
+
list: (options) => call(groups.list, { orgId }, options, (saved) => mapPage(saved, toPayrollGroup)),
|
|
19693
|
+
save: (input, options) => call(groups.save, {
|
|
19694
|
+
orgId,
|
|
19695
|
+
...input.id === void 0 ? {} : { id: String(input.id) },
|
|
19696
|
+
name: input.name,
|
|
19697
|
+
tone: input.tone,
|
|
19698
|
+
members: input.members
|
|
19699
|
+
}, options, toPayrollGroup),
|
|
19700
|
+
remove: (id, options) => call(groups.remove, {
|
|
19701
|
+
orgId,
|
|
19702
|
+
id
|
|
19703
|
+
}, options, () => void 0)
|
|
19704
|
+
}
|
|
19705
|
+
};
|
|
19706
|
+
}
|
|
19707
|
+
const payrollUnavailable = () => Promise.resolve({
|
|
19708
|
+
ok: false,
|
|
19709
|
+
error: Errors.notImplemented("payroll", "executionComposition")
|
|
19710
|
+
});
|
|
19711
|
+
/** The bundle an unconfigured client exposes — every verb refuses, none throws. */
|
|
19712
|
+
function makeUnavailablePayrollMethods() {
|
|
19713
|
+
return {
|
|
19714
|
+
getRun: payrollUnavailable,
|
|
19715
|
+
subscribeRun: payrollUnavailable,
|
|
19716
|
+
runs: payrollUnavailable,
|
|
19717
|
+
authorizeRun: payrollUnavailable,
|
|
19718
|
+
terms: payrollUnavailable,
|
|
19719
|
+
groups: {
|
|
19720
|
+
list: payrollUnavailable,
|
|
19721
|
+
save: payrollUnavailable,
|
|
19722
|
+
remove: payrollUnavailable
|
|
19723
|
+
}
|
|
19724
|
+
};
|
|
19725
|
+
}
|
|
19726
|
+
//#endregion
|
|
19727
|
+
//#region src/accounts/command-approvals.ts
|
|
19728
|
+
/**
|
|
19729
|
+
* Keep a prepared command only when it is the one asked for and its operation
|
|
19730
|
+
* is the person's own. A mismatch is closed unsent when `close`, as the signed
|
|
19731
|
+
* path closes a new Payment; a retry or a request keeps its Payment open.
|
|
19732
|
+
*/
|
|
19733
|
+
async function checked(deps, signerAddress, prepared, expected, close) {
|
|
19734
|
+
if (!prepared.ok) return prepared;
|
|
19735
|
+
const session = deps.actor?.authSession() ?? null;
|
|
19736
|
+
if (session === null || deps.chainId === void 0) return {
|
|
19737
|
+
ok: false,
|
|
19738
|
+
error: Errors.notAuthenticated()
|
|
19739
|
+
};
|
|
19740
|
+
const evidence = prepared.value;
|
|
19741
|
+
if (await expected(evidence.request, evidence) && await verifiedPreparedCommand(evidence, session.email, signerAddress, deps.chainId)) return prepared;
|
|
19742
|
+
const refused = {
|
|
19743
|
+
ok: false,
|
|
19744
|
+
error: Errors.invalidInput("execution", "prepared command mismatch")
|
|
19745
|
+
};
|
|
19746
|
+
return close ? abandonPrepared(deps, evidence.executionId, "prepared_mismatch", refused) : refused;
|
|
19747
|
+
}
|
|
19748
|
+
/** A retry, a request payment or a payroll run, rather than a new Payment. */
|
|
19749
|
+
const isCommandApproval = (input) => "retry" in input || "request" in input || "payrollRun" in input;
|
|
19750
|
+
/** Prepare a retry, a request payment or a payroll run for `signerAddress`, checked. */
|
|
19751
|
+
function prepareVerifiedCommand(deps, input, signerAddress, signal) {
|
|
19752
|
+
if ("retry" in input) return prepareVerifiedRetry(deps, input, signerAddress, signal);
|
|
19753
|
+
if ("request" in input) return prepareVerifiedRequestPayment(deps, input, signerAddress, signal);
|
|
19754
|
+
return prepareVerifiedPayrollRun(deps, input, signerAddress, signal);
|
|
19755
|
+
}
|
|
19756
|
+
/** The retry of one Payment, prepared in the scope that paid it. */
|
|
19757
|
+
async function prepareVerifiedRetry(deps, input, signerAddress, signal) {
|
|
19758
|
+
const intent = {
|
|
19759
|
+
kind: "retry",
|
|
19760
|
+
paymentId: input.retry
|
|
19761
|
+
};
|
|
19762
|
+
return checked(deps, signerAddress, await runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => callOperation(deps.convexCall, lifecycles.prepareScoped, {
|
|
19763
|
+
signerAddress,
|
|
19764
|
+
intent,
|
|
19765
|
+
expectedScope: input.organizationId === void 0 ? { kind: "account" } : {
|
|
19766
|
+
kind: "organization",
|
|
19767
|
+
orgId: input.organizationId
|
|
19768
|
+
}
|
|
19769
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), lifecycleCommandCheck(intent, input.organizationId), false);
|
|
19770
|
+
}
|
|
19771
|
+
/** The Payment of one request, exactly as the request froze it. */
|
|
19772
|
+
async function prepareVerifiedRequestPayment(deps, input, signerAddress, signal) {
|
|
19773
|
+
const decoded = Schema.decodeResult(RequestPaymentConfirmationSchema, { onExcessProperty: "error" })(input.request);
|
|
19774
|
+
if (decoded._tag === "Failure") return {
|
|
19775
|
+
ok: false,
|
|
19776
|
+
error: Errors.invalidInput("confirmation", "invalid request confirmation")
|
|
19777
|
+
};
|
|
19778
|
+
const confirmation = decoded.success;
|
|
19779
|
+
return checked(deps, signerAddress, await runIfActive(signal, commands$1.prepareRequest.name, () => callOperation(deps.convexCall, commands$1.prepareRequest, {
|
|
19780
|
+
confirmation,
|
|
19781
|
+
signerAddress
|
|
19782
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise), requestPaymentCheck(confirmation), false);
|
|
19783
|
+
}
|
|
19784
|
+
/** One payroll run: the run the backend writes and the batch that pays it, checked together. */
|
|
19785
|
+
async function prepareVerifiedPayrollRun(deps, input, signerAddress, signal) {
|
|
19786
|
+
let run;
|
|
19787
|
+
try {
|
|
19788
|
+
run = decodePayrollRunInput(input.payrollRun);
|
|
19789
|
+
} catch (cause) {
|
|
19790
|
+
return {
|
|
19791
|
+
ok: false,
|
|
19792
|
+
error: asCapxulError(cause)
|
|
19793
|
+
};
|
|
19794
|
+
}
|
|
19795
|
+
const orgId = input.organizationId;
|
|
19796
|
+
const requestKey = run.requestKey ?? `payroll_${crypto.randomUUID()}`;
|
|
19797
|
+
const { permissionId, asset, period, items } = run;
|
|
19798
|
+
const authorized = await runIfActive(signal, runs.authorize.name, () => callOperation(deps.convexCall, runs.authorize, {
|
|
19799
|
+
orgId,
|
|
19800
|
+
permissionId,
|
|
19801
|
+
asset,
|
|
19802
|
+
requestKey,
|
|
19803
|
+
signerAddress,
|
|
19804
|
+
period,
|
|
19805
|
+
items
|
|
19806
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
19807
|
+
if (!authorized.ok) return authorized;
|
|
19808
|
+
return checked(deps, signerAddress, {
|
|
19809
|
+
ok: true,
|
|
19810
|
+
value: authorized.value.execution
|
|
19811
|
+
}, isAuthorizedRunBatch({
|
|
19812
|
+
orgId,
|
|
19813
|
+
permissionId,
|
|
19814
|
+
requestKey,
|
|
19815
|
+
asset,
|
|
19816
|
+
period,
|
|
19817
|
+
items
|
|
19818
|
+
}, authorized.value.run), authorized.value.replay.kind !== "accepted");
|
|
19819
|
+
}
|
|
19820
|
+
//#endregion
|
|
19821
|
+
//#region src/accounts/organization-approvals.ts
|
|
19822
|
+
/** The Organization Payment prepared for `signerAddress` and checked, before any approval. */
|
|
19823
|
+
async function prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal) {
|
|
19824
|
+
const { organizationId, permissionId, requestKey, lineage, ...item } = input;
|
|
19825
|
+
const intent = organizationPaymentIntent(organizationId, {
|
|
19826
|
+
permissionId,
|
|
19827
|
+
items: [item],
|
|
19828
|
+
...lineage === void 0 ? {} : { lineage },
|
|
19829
|
+
...requestKey === void 0 ? {} : { requestKey }
|
|
19830
|
+
});
|
|
19831
|
+
if (!intent.ok) return intent;
|
|
19832
|
+
const session = deps.actor?.authSession() ?? null;
|
|
19833
|
+
if (session === null || deps.chainId === void 0) return {
|
|
19834
|
+
ok: false,
|
|
19835
|
+
error: Errors.notAuthenticated()
|
|
19836
|
+
};
|
|
19837
|
+
const expected = {
|
|
19838
|
+
...intent.value,
|
|
19839
|
+
requestKey: requestKey ?? `pay_${crypto.randomUUID()}`
|
|
19840
|
+
};
|
|
19841
|
+
const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.organizationPayments.prepareExecution, () => callOperation(deps.convexCall, commands$1.prepareOrganization, {
|
|
19842
|
+
...expected,
|
|
19843
|
+
signerAddress
|
|
19844
|
+
}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
19845
|
+
if (!prepared.ok) return prepared;
|
|
19846
|
+
const evidence = prepared.value;
|
|
19847
|
+
return await matchesIntent(expected)(evidence.request, evidence) && await verifiedPreparedCommand(evidence, session.email, signerAddress, deps.chainId) ? {
|
|
19848
|
+
ok: true,
|
|
19849
|
+
value: evidence
|
|
19850
|
+
} : abandonPrepared(deps, evidence.executionId, "prepared_mismatch", {
|
|
19851
|
+
ok: false,
|
|
19852
|
+
error: Errors.invalidInput("execution", "prepared Organization Payment mismatch")
|
|
19853
|
+
});
|
|
19854
|
+
}
|
|
19855
|
+
/** One fresh Payment read: the caller's own, or one in the Organization's scope. */
|
|
19856
|
+
const readPayment = (deps, paymentId, organizationId, signal) => organizationId === void 0 ? runIfActive(signal, records.get.name, () => callOperation(deps.convexCall, records.get, { paymentId }, { fresh: true }).pipe(Effect.map((payment) => ({ payment })), Effect.mapError((publicError) => ({ publicError }))), deps.runPromise) : runIfActive(signal, records.getForOrganization.name, () => callOperation(deps.convexCall, records.getForOrganization, {
|
|
19857
|
+
orgId: organizationId,
|
|
19858
|
+
paymentId
|
|
19859
|
+
}, { fresh: true }).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
19860
|
+
/**
|
|
19861
|
+
* The Payments an approval carries, as they stand. A personal read names only
|
|
19862
|
+
* the caller's own Account's Payments, so an Organization's is read with its
|
|
19863
|
+
* `organizationId`; one that cannot be read is refused, never taken as ended.
|
|
19864
|
+
*/
|
|
19865
|
+
async function readApprovalPayments(deps, paymentIds, organizationId, signal) {
|
|
19866
|
+
const reads = await Promise.all(paymentIds.map((paymentId) => readPayment(deps, paymentId, organizationId, signal)));
|
|
19867
|
+
const payments = [];
|
|
19868
|
+
for (const [index, read] of reads.entries()) {
|
|
19869
|
+
if (!read.ok) return read;
|
|
19870
|
+
const { orgId, payment } = read.value;
|
|
19871
|
+
if (payment === null && organizationId === void 0) return {
|
|
19872
|
+
ok: false,
|
|
19873
|
+
error: Errors.invalidInput("organizationId", "this approval pays from an Organization: wait with its organizationId")
|
|
19874
|
+
};
|
|
19875
|
+
if (payment === null || payment.id !== paymentIds[index] || orgId !== organizationId) return {
|
|
19876
|
+
ok: false,
|
|
19877
|
+
error: Errors.invalidInput("paymentId", "the approval's Payments are not this scope's")
|
|
19878
|
+
};
|
|
19879
|
+
payments.push(normalizePaymentTiming(payment));
|
|
19880
|
+
}
|
|
19881
|
+
return {
|
|
19882
|
+
ok: true,
|
|
19883
|
+
value: payments
|
|
19884
|
+
};
|
|
19885
|
+
}
|
|
19886
|
+
//#endregion
|
|
19887
|
+
//#region src/accounts/approvals.ts
|
|
19888
|
+
function makeApprovalsMethods(deps) {
|
|
19889
|
+
const call = (name, row, args, signal) => runIfActive(signal, name, () => callOperation(deps.convexCall, row, args, { fresh: true }).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
|
|
19890
|
+
const get = (approvalId) => call(approvals.get.name, approvals.get, { approvalId });
|
|
19891
|
+
/**
|
|
19892
|
+
* Prepare for the signer the backend recorded when the Safe was claimed (the
|
|
19893
|
+
* person's own key signs later, on the approval page) and open the approval.
|
|
19894
|
+
*/
|
|
19895
|
+
const openApproval = async (signal, prepareFor) => {
|
|
19896
|
+
const session = deps.actor?.authSession() ?? null;
|
|
19897
|
+
if (deps.actor === void 0 || deps.chainId === void 0) return {
|
|
19898
|
+
ok: false,
|
|
19899
|
+
error: Errors.notImplemented("approvals", "prepareComposition")
|
|
19900
|
+
};
|
|
19901
|
+
if (session === null) return {
|
|
19902
|
+
ok: false,
|
|
19903
|
+
error: Errors.notAuthenticated()
|
|
19904
|
+
};
|
|
19905
|
+
const safe = await call(CAPXUL_OPERATIONS.approvals.prepare, safes.loadByAuthUserId, { authUserId: session.authUserId }, signal);
|
|
19906
|
+
if (!safe.ok) return safe;
|
|
19907
|
+
if (safe.value === null) return {
|
|
19908
|
+
ok: false,
|
|
19909
|
+
error: Errors.smartAccountMissing(session.authUserId)
|
|
19910
|
+
};
|
|
19911
|
+
if (safe.value.signerAddress === null) return {
|
|
19912
|
+
ok: false,
|
|
19913
|
+
error: Errors.wrongState({
|
|
19281
19914
|
method: CAPXUL_OPERATIONS.approvals.prepare,
|
|
19282
19915
|
currentState: "unclaimed",
|
|
19283
19916
|
validStates: ["claimed"]
|
|
19284
19917
|
})
|
|
19285
19918
|
};
|
|
19286
|
-
const prepared = await
|
|
19287
|
-
actor,
|
|
19288
|
-
chainId,
|
|
19289
|
-
convexCall: deps.convexCall,
|
|
19290
|
-
runPromise: deps.runPromise
|
|
19291
|
-
}, intent.value, safe.value.signerAddress, input.requestKey ?? `pay_${crypto.randomUUID()}`, signal);
|
|
19919
|
+
const prepared = await prepareFor(safe.value.signerAddress);
|
|
19292
19920
|
if (!prepared.ok) return prepared;
|
|
19293
|
-
await emitPreparedPaymentInitiated(deps, prepared.value);
|
|
19294
19921
|
const { executionId } = prepared.value;
|
|
19295
19922
|
const opened = await call(approvals.request.name, approvals.request, { executionId });
|
|
19296
19923
|
return opened.ok ? opened : abandonPrepared(deps, executionId, "cancelled", opened);
|
|
19297
19924
|
};
|
|
19925
|
+
const prepare = async (input, options) => {
|
|
19926
|
+
const signal = options?.signal;
|
|
19927
|
+
if (isCommandApproval(input)) return openApproval(signal, (signerAddress) => prepareVerifiedCommand(deps, input, signerAddress, signal));
|
|
19928
|
+
if ("organizationId" in input) return openApproval(signal, (signerAddress) => prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal));
|
|
19929
|
+
const intent = paymentExecutionIntent(input);
|
|
19930
|
+
if (!intent.ok) return intent;
|
|
19931
|
+
const { actor, chainId } = deps;
|
|
19932
|
+
return openApproval(signal, async (signerAddress) => {
|
|
19933
|
+
if (actor === void 0 || chainId === void 0) return {
|
|
19934
|
+
ok: false,
|
|
19935
|
+
error: Errors.notImplemented("approvals", "prepareComposition")
|
|
19936
|
+
};
|
|
19937
|
+
const prepared = await prepareVerifiedPersonalPayment({
|
|
19938
|
+
actor,
|
|
19939
|
+
chainId,
|
|
19940
|
+
convexCall: deps.convexCall,
|
|
19941
|
+
runPromise: deps.runPromise
|
|
19942
|
+
}, intent.value, signerAddress, input.requestKey ?? `pay_${crypto.randomUUID()}`, signal);
|
|
19943
|
+
if (prepared.ok) await emitPreparedPaymentInitiated(deps, prepared.value);
|
|
19944
|
+
return prepared;
|
|
19945
|
+
});
|
|
19946
|
+
};
|
|
19298
19947
|
const approve = async (approvalId) => {
|
|
19299
19948
|
const { signer } = deps;
|
|
19300
19949
|
if (signer === void 0) return {
|
|
@@ -19355,28 +20004,12 @@ function makeApprovalsMethods(deps) {
|
|
|
19355
20004
|
}
|
|
19356
20005
|
};
|
|
19357
20006
|
};
|
|
19358
|
-
const readPayments = async (approval, signal) => {
|
|
19359
|
-
const reads = await Promise.all(approval.operation.paymentIds.map((paymentId) => call(records.get.name, records.get, { paymentId }, signal)));
|
|
19360
|
-
const payments = [];
|
|
19361
|
-
for (const read of reads) {
|
|
19362
|
-
if (!read.ok) return read;
|
|
19363
|
-
if (read.value === null) return {
|
|
19364
|
-
ok: false,
|
|
19365
|
-
error: Errors.notImplemented("approvals", "wait.organizationPayment")
|
|
19366
|
-
};
|
|
19367
|
-
payments.push(normalizePaymentTiming(read.value));
|
|
19368
|
-
}
|
|
19369
|
-
return {
|
|
19370
|
-
ok: true,
|
|
19371
|
-
value: payments
|
|
19372
|
-
};
|
|
19373
|
-
};
|
|
19374
20007
|
/**
|
|
19375
20008
|
* One read of the approval: its outcome when it has ended, or null to read
|
|
19376
20009
|
* again. The Payments are read before anything is sent, so a send is never
|
|
19377
20010
|
* made for Payments this caller cannot follow.
|
|
19378
20011
|
*/
|
|
19379
|
-
const step = async (approvalId, signal) => {
|
|
20012
|
+
const step = async (approvalId, signal, organizationId) => {
|
|
19380
20013
|
const read = await call(approvals.get.name, approvals.get, { approvalId }, signal);
|
|
19381
20014
|
if (!read.ok) return read;
|
|
19382
20015
|
const approval = read.value;
|
|
@@ -19384,7 +20017,7 @@ function makeApprovalsMethods(deps) {
|
|
|
19384
20017
|
ok: true,
|
|
19385
20018
|
value: null
|
|
19386
20019
|
};
|
|
19387
|
-
const payments = await
|
|
20020
|
+
const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId ?? (approval.actor.kind === "organization" ? approval.actor.orgId : void 0), signal);
|
|
19388
20021
|
if (!payments.ok) return payments;
|
|
19389
20022
|
if (unsent(approval, payments.value)) {
|
|
19390
20023
|
const sent = await submit(approvalId);
|
|
@@ -19405,7 +20038,7 @@ function makeApprovalsMethods(deps) {
|
|
|
19405
20038
|
const signal = options?.signal;
|
|
19406
20039
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
19407
20040
|
const poll = async () => {
|
|
19408
|
-
const stepped = await step(approvalId, signal);
|
|
20041
|
+
const stepped = await step(approvalId, signal, options?.organizationId);
|
|
19409
20042
|
if (!stepped.ok) return stepped;
|
|
19410
20043
|
if (stepped.value !== null) return {
|
|
19411
20044
|
ok: true,
|
|
@@ -20608,10 +21241,803 @@ function makeSmartAccountMethods(deps) {
|
|
|
20608
21241
|
};
|
|
20609
21242
|
}
|
|
20610
21243
|
//#endregion
|
|
21244
|
+
//#region src/activity/documents.ts
|
|
21245
|
+
/** A reply its row does not admit is refused without the document body its decode error would carry. */
|
|
21246
|
+
function withoutDocumentBody(error) {
|
|
21247
|
+
return error.details?.step === "decodeOutput" ? Errors.invalidInput("documentHash", "invalid document read") : error;
|
|
21248
|
+
}
|
|
21249
|
+
/** Schema errors must not retain document bodies. */
|
|
21250
|
+
function decodeDocumentRead(schema, input) {
|
|
21251
|
+
return Effect.fromOption(Schema.decodeUnknownOption(schema)(input)).pipe(Effect.mapError(() => ({ publicError: Errors.invalidInput("documentHash", "invalid document read") })));
|
|
21252
|
+
}
|
|
21253
|
+
function makePaymentDocumentsMethods(deps) {
|
|
21254
|
+
/**
|
|
21255
|
+
* One document read by its row. The pair is decoded before anything is sent,
|
|
21256
|
+
* and a reply the row does not admit is refused without the body it carried.
|
|
21257
|
+
*/
|
|
21258
|
+
const readDocument = (row, ref, options) => {
|
|
21259
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
21260
|
+
return runPortEffect(Effect.gen(function* () {
|
|
21261
|
+
const pair = yield* decodeDocumentRead(PaymentDocumentReadRefSchema, ref);
|
|
21262
|
+
return yield* callOperation(deps.convexCall, row, copyInvocationObservation(controls, pair)).pipe(Effect.mapError((error) => ({ publicError: withoutDocumentBody(error) })));
|
|
21263
|
+
}), controls, row.name, deps.runPromise);
|
|
21264
|
+
};
|
|
21265
|
+
return {
|
|
21266
|
+
get: (ref, options) => readDocument(documents.get, ref, options),
|
|
21267
|
+
verify: (ref, options) => readDocument(documents.verify, ref, options),
|
|
21268
|
+
render: (ref, options) => readDocument(documents.render, ref, options)
|
|
21269
|
+
};
|
|
21270
|
+
}
|
|
21271
|
+
//#endregion
|
|
21272
|
+
//#region src/activity/stitching.ts
|
|
21273
|
+
/** Epoch milliseconds of the block that carried the row. */
|
|
21274
|
+
function blockMillis(row) {
|
|
21275
|
+
return Number(row.timestamp) * 1e3;
|
|
21276
|
+
}
|
|
21277
|
+
/** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
|
|
21278
|
+
function shortAddressLabel(address) {
|
|
21279
|
+
return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
21280
|
+
}
|
|
21281
|
+
function directionFor(safe, transfer) {
|
|
21282
|
+
const from = transfer.from.toLowerCase();
|
|
21283
|
+
const to = transfer.to.toLowerCase();
|
|
21284
|
+
if (from === safe && to === safe) return "self";
|
|
21285
|
+
return to === safe ? "in" : "out";
|
|
21286
|
+
}
|
|
21287
|
+
function withAnnotation(item, annotation) {
|
|
21288
|
+
return annotation?.counterpartyLabel === void 0 ? item : {
|
|
21289
|
+
...item,
|
|
21290
|
+
counterpartyLabel: annotation.counterpartyLabel
|
|
21291
|
+
};
|
|
21292
|
+
}
|
|
21293
|
+
/**
|
|
21294
|
+
* An indexed settlement confirms the intent. A missing row can be outside
|
|
21295
|
+
* this page or still backfilling; only the correction owner can prove rollback.
|
|
21296
|
+
*/
|
|
21297
|
+
function confirmPayment(intent, chain, expected) {
|
|
21298
|
+
if (chain !== void 0 && matchesPaymentFact(chain, expected)) return {
|
|
21299
|
+
...intent,
|
|
21300
|
+
status: "settled",
|
|
21301
|
+
...intent.execution === void 0 ? {} : { execution: {
|
|
21302
|
+
status: "settled",
|
|
21303
|
+
retryRequired: false
|
|
21304
|
+
} },
|
|
21305
|
+
receipt: {
|
|
21306
|
+
chainId: chain.chainId,
|
|
21307
|
+
transactionHash: chain.txHash
|
|
21308
|
+
},
|
|
21309
|
+
updatedAt: blockMillis(chain)
|
|
21310
|
+
};
|
|
21311
|
+
return intent;
|
|
21312
|
+
}
|
|
21313
|
+
function matchesPaymentFact(chain, expected) {
|
|
21314
|
+
return chain !== void 0 && expected !== void 0 && chain.chainId === expected.chainId && chain.contract.toLowerCase() === expected.contract.toLowerCase() && chain.sender.toLowerCase() === expected.sender.toLowerCase() && chain.recipient.toLowerCase() === expected.recipient.toLowerCase() && chain.token.toLowerCase() === expected.token.toLowerCase() && chain.amount.toString() === expected.rawAmount && chain.documentHash.toLowerCase() === expected.documentHash.toLowerCase() && chain.kind === expected.kind;
|
|
21315
|
+
}
|
|
21316
|
+
/**
|
|
21317
|
+
* One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
|
|
21318
|
+
* denominate its asset. The indexer serves only configured assets, so a row
|
|
21319
|
+
* the registry does not know means the SDK and that deployment disagree; it is
|
|
21320
|
+
* dropped rather than shown with a guessed currency.
|
|
21321
|
+
*/
|
|
21322
|
+
function isActivityVisible(item) {
|
|
21323
|
+
return !(item.kind === "payment" && item.direction === "in" && item.execution?.notSent);
|
|
21324
|
+
}
|
|
21325
|
+
function transferItem(safe, transfer, annotation) {
|
|
21326
|
+
const asset = configuredMoneyAssetById(transfer.assetId);
|
|
21327
|
+
if (asset === null) return null;
|
|
21328
|
+
const direction = directionFor(safe.toLowerCase(), transfer);
|
|
21329
|
+
const counterparty = direction === "in" ? transfer.from : transfer.to;
|
|
21330
|
+
const at = blockMillis(transfer);
|
|
21331
|
+
return withAnnotation({
|
|
21332
|
+
kind: "movement",
|
|
21333
|
+
id: transfer.id,
|
|
21334
|
+
classification: direction === "in" ? "deposit" : "movement_only_outflow",
|
|
21335
|
+
amount: {
|
|
21336
|
+
asset: asset.assetId,
|
|
21337
|
+
value: formatUnits(transfer.amount, asset.decimals)
|
|
21338
|
+
},
|
|
21339
|
+
direction,
|
|
21340
|
+
counterpartyLabel: shortAddressLabel(counterparty),
|
|
21341
|
+
receipt: {
|
|
21342
|
+
chainId: transfer.chainId,
|
|
21343
|
+
transactionHash: transfer.txHash
|
|
21344
|
+
},
|
|
21345
|
+
createdAt: at,
|
|
21346
|
+
updatedAt: at
|
|
21347
|
+
}, annotation);
|
|
21348
|
+
}
|
|
21349
|
+
/**
|
|
21350
|
+
* Money in, money out and the pending share, one bucket per exact asset.
|
|
21351
|
+
*/
|
|
21352
|
+
function summarizeItems(items) {
|
|
21353
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
21354
|
+
for (const item of items) {
|
|
21355
|
+
const metadata = configuredMoneyAssetById(item.amount.asset);
|
|
21356
|
+
if (metadata === null) continue;
|
|
21357
|
+
const key = item.amount.asset;
|
|
21358
|
+
const bucket = buckets.get(key) ?? {
|
|
21359
|
+
asset: item.amount.asset,
|
|
21360
|
+
in: 0n,
|
|
21361
|
+
out: 0n,
|
|
21362
|
+
pendingIn: 0n
|
|
21363
|
+
};
|
|
21364
|
+
const raw = rawUnits(item.amount.value, metadata.decimals);
|
|
21365
|
+
if (item.kind === "payment" && item.status !== "settled") {
|
|
21366
|
+
if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed" && !item.execution?.notSent) bucket.pendingIn += raw;
|
|
21367
|
+
} else if (item.direction === "in") bucket.in += raw;
|
|
21368
|
+
else if (item.direction === "out") bucket.out += raw;
|
|
21369
|
+
buckets.set(key, bucket);
|
|
21370
|
+
}
|
|
21371
|
+
return [...buckets.values()].map((bucket) => ({
|
|
21372
|
+
asset: bucket.asset,
|
|
21373
|
+
in: bucket.in.toString(),
|
|
21374
|
+
out: bucket.out.toString(),
|
|
21375
|
+
net: (bucket.in - bucket.out).toString(),
|
|
21376
|
+
pendingIn: bucket.pendingIn.toString()
|
|
21377
|
+
}));
|
|
21378
|
+
}
|
|
21379
|
+
/** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
|
|
21380
|
+
function rawUnits(value, decimals) {
|
|
21381
|
+
const negative = value.startsWith("-");
|
|
21382
|
+
const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
|
|
21383
|
+
const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
|
|
21384
|
+
return negative ? -raw : raw;
|
|
21385
|
+
}
|
|
21386
|
+
//#endregion
|
|
21387
|
+
//#region src/activity/cursor.ts
|
|
21388
|
+
function actorKey(actor) {
|
|
21389
|
+
return actor.kind === "account" ? `account:${actor.accountId}` : `organization:${actor.orgId}`;
|
|
21390
|
+
}
|
|
21391
|
+
function scopeKey(scope) {
|
|
21392
|
+
return `${scope.principalAccountId}|${actorKey(scope.actor)}|${scope.safe?.toLowerCase() ?? "none"}`;
|
|
21393
|
+
}
|
|
21394
|
+
function selectionKey(params) {
|
|
21395
|
+
const filter = params?.filter;
|
|
21396
|
+
return JSON.stringify([
|
|
21397
|
+
params?.range?.from ?? null,
|
|
21398
|
+
params?.range?.to ?? null,
|
|
21399
|
+
filter?.kind ?? null,
|
|
21400
|
+
filter?.direction ?? null,
|
|
21401
|
+
filter?.status === void 0 ? null : [...new Set(filter.status)].sort()
|
|
21402
|
+
]);
|
|
21403
|
+
}
|
|
21404
|
+
function validString(value, max) {
|
|
21405
|
+
return typeof value === "string" && value.length <= max;
|
|
21406
|
+
}
|
|
21407
|
+
function validPosition(position) {
|
|
21408
|
+
return position === null || typeof position === "object" && "timestamp" in position && typeof position.timestamp === "number" && Number.isSafeInteger(position.timestamp) && position.timestamp >= 0 && "id" in position && typeof position.id === "string" && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(position.id);
|
|
21409
|
+
}
|
|
21410
|
+
function isActivityCursor(cursor, scope, selection) {
|
|
21411
|
+
return typeof cursor === "object" && cursor !== null && cursorScopeMatches(cursor, scope, selection) && cursorProgressIsValid(cursor);
|
|
21412
|
+
}
|
|
21413
|
+
/** The cursor was issued for this scope and filter selection, at a valid checkpoint. */
|
|
21414
|
+
function cursorScopeMatches(cursor, scope, selection) {
|
|
21415
|
+
return "version" in cursor && cursor.version === 1 && "scope" in cursor && cursor.scope === scope && "selection" in cursor && cursor.selection === selection && "checkpoint" in cursor && typeof cursor.checkpoint === "number" && Number.isSafeInteger(cursor.checkpoint) && cursor.checkpoint >= 0 && "source" in cursor && (cursor.source === "indexed" || cursor.source === "fallback");
|
|
21416
|
+
}
|
|
21417
|
+
/** Each source's progress is well-formed and bounded. */
|
|
21418
|
+
function cursorProgressIsValid(cursor) {
|
|
21419
|
+
return "paymentCursor" in cursor && (cursor.paymentCursor === null || validString(cursor.paymentCursor, 2048)) && "paymentDone" in cursor && typeof cursor.paymentDone === "boolean" && "transferDone" in cursor && typeof cursor.transferDone === "boolean" && "degraded" in cursor && typeof cursor.degraded === "boolean" && "paymentPending" in cursor && Array.isArray(cursor.paymentPending) && cursor.paymentPending.length <= 100 && new Set(cursor.paymentPending).size === cursor.paymentPending.length && cursor.paymentPending.every((id) => validString(id, 256)) && "transferBefore" in cursor && validPosition(cursor.transferBefore);
|
|
21420
|
+
}
|
|
21421
|
+
function parseCursor(value, scope, selection) {
|
|
21422
|
+
if (value === void 0) return null;
|
|
21423
|
+
if (value.length > 4e4) throw Errors.invalidInput("cursor", "invalid Activity cursor");
|
|
21424
|
+
const cursor = JSON.parse(value);
|
|
21425
|
+
if (!isActivityCursor(cursor, scope, selection)) throw Errors.invalidInput("cursor", "Activity scope or filters changed; restart from the first page");
|
|
21426
|
+
return cursor;
|
|
21427
|
+
}
|
|
21428
|
+
function requireParams(params) {
|
|
21429
|
+
const limit = params?.limit ?? 25;
|
|
21430
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw Errors.invalidInput("limit", "must be an integer from 1 to 100");
|
|
21431
|
+
for (const value of [params?.range?.from, params?.range?.to]) if (value !== void 0 && !Number.isSafeInteger(value)) throw Errors.invalidInput("range", "must contain integer epoch milliseconds");
|
|
21432
|
+
const f = params?.filter;
|
|
21433
|
+
if (f?.kind !== void 0 && f.kind !== "payment" && f.kind !== "movement" || f?.direction !== void 0 && ![
|
|
21434
|
+
"in",
|
|
21435
|
+
"out",
|
|
21436
|
+
"self"
|
|
21437
|
+
].includes(f.direction) || f?.status !== void 0 && (!Array.isArray(f.status) || !f.status.every((status) => PAYMENT_STATUSES.includes(status)))) throw Errors.invalidInput("filter", "invalid Activity filter");
|
|
21438
|
+
}
|
|
21439
|
+
function matchesFilter(item, filter) {
|
|
21440
|
+
return (filter?.kind === void 0 || item.kind === filter.kind) && (filter?.direction === void 0 || item.direction === filter.direction) && (filter?.status === void 0 || item.kind === "payment" && filter.status.includes(item.status));
|
|
21441
|
+
}
|
|
21442
|
+
//#endregion
|
|
21443
|
+
//#region src/activity/indexed-pages.ts
|
|
21444
|
+
function chainRead(deps, table, input) {
|
|
21445
|
+
return deps.indexer.read(table, input).pipe(Effect.catch((error) => {
|
|
21446
|
+
if (error.publicError.details?.reason === "scope-refused" || ![
|
|
21447
|
+
"NETWORK_ERROR",
|
|
21448
|
+
"RATE_LIMITED",
|
|
21449
|
+
"PROVIDER_ERROR",
|
|
21450
|
+
"CAPABILITY_UNAVAILABLE",
|
|
21451
|
+
"TIMEOUT"
|
|
21452
|
+
].includes(error.publicCode)) return Effect.fail({ publicError: error.publicError });
|
|
21453
|
+
return Effect.succeed(null);
|
|
21454
|
+
}));
|
|
21455
|
+
}
|
|
21456
|
+
function boundedRange(range, checkpoint) {
|
|
21457
|
+
return {
|
|
21458
|
+
...range?.from === void 0 ? {} : { from: range.from },
|
|
21459
|
+
to: Math.min(checkpoint, range?.to ?? checkpoint)
|
|
21460
|
+
};
|
|
21461
|
+
}
|
|
21462
|
+
function unavailablePage(deps, scope, controls, params, state, first) {
|
|
21463
|
+
if (first) return fallbackPage(deps, scope, controls, params, state, true);
|
|
21464
|
+
return Effect.annotateCurrentSpan({
|
|
21465
|
+
read_source: "indexer",
|
|
21466
|
+
read_warning: "indexer-unavailable"
|
|
21467
|
+
}).pipe(Effect.as({
|
|
21468
|
+
items: [],
|
|
21469
|
+
nextCursor: null,
|
|
21470
|
+
checkpoint: state.checkpoint,
|
|
21471
|
+
observedAt: Date.now(),
|
|
21472
|
+
readWarning: "indexer-unavailable"
|
|
21473
|
+
}));
|
|
21474
|
+
}
|
|
21475
|
+
function readTransfers(deps, safe, range, state, wanted, first) {
|
|
21476
|
+
const from = Math.max(0, Math.ceil((range.from ?? 0) / 1e3));
|
|
21477
|
+
const to = Math.floor(range.to / 1e3);
|
|
21478
|
+
if (state.transferDone || from > to || !wanted && !first) return Effect.succeed([]);
|
|
21479
|
+
return chainRead(deps, "transfer", {
|
|
21480
|
+
safe: toAddress(safe),
|
|
21481
|
+
limit: wanted ? 100 : 1,
|
|
21482
|
+
timeline: {
|
|
21483
|
+
from,
|
|
21484
|
+
to,
|
|
21485
|
+
...state.transferBefore === null ? {} : { before: state.transferBefore }
|
|
21486
|
+
}
|
|
21487
|
+
});
|
|
21488
|
+
}
|
|
21489
|
+
function readPayments(deps, scope, controls, range, state, wanted) {
|
|
21490
|
+
const done = state.paymentDone || !wanted;
|
|
21491
|
+
if (state.paymentPending.length > 0 || done) return Effect.succeed({
|
|
21492
|
+
items: [],
|
|
21493
|
+
pending: state.paymentPending,
|
|
21494
|
+
cursor: state.paymentCursor,
|
|
21495
|
+
done
|
|
21496
|
+
});
|
|
21497
|
+
return convexPage(deps, scope.actor, controls, {
|
|
21498
|
+
limit: 100,
|
|
21499
|
+
range,
|
|
21500
|
+
filter: { kind: "payment" },
|
|
21501
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor }
|
|
21502
|
+
}).pipe(Effect.map((page) => ({
|
|
21503
|
+
items: page.items,
|
|
21504
|
+
pending: page.items.map((item) => item.id),
|
|
21505
|
+
cursor: page.nextCursor,
|
|
21506
|
+
done: page.nextCursor === null
|
|
21507
|
+
})));
|
|
21508
|
+
}
|
|
21509
|
+
function readPaymentFacts(deps, safe, settlements) {
|
|
21510
|
+
const offsets = Array.from({ length: Math.ceil(settlements.length / 50) }, (_, i) => i * 50);
|
|
21511
|
+
return Effect.forEach(offsets, (offset) => chainRead(deps, "payment", {
|
|
21512
|
+
safe: toAddress(safe),
|
|
21513
|
+
limit: 50,
|
|
21514
|
+
settlements: settlements.slice(offset, offset + 50)
|
|
21515
|
+
}), { concurrency: 2 }).pipe(Effect.map((pages) => pages.some((page) => page === null) ? null : pages.flatMap((page) => page ?? [])));
|
|
21516
|
+
}
|
|
21517
|
+
function indexedPage(deps, scope, controls, params, state, first) {
|
|
21518
|
+
return Effect.gen(function* () {
|
|
21519
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
21520
|
+
const wantsTransfers = params?.filter?.kind !== "payment" && params?.filter?.status === void 0;
|
|
21521
|
+
const found = yield* readTransfers(deps, scope.safe, range, state, wantsTransfers, first);
|
|
21522
|
+
if (found === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
21523
|
+
const transfers = wantsTransfers ? found : [];
|
|
21524
|
+
const page = yield* readPayments(deps, scope, controls, range, state, params?.filter?.kind !== "movement");
|
|
21525
|
+
const decoration = yield* decoratePage(deps, scope, controls, transfers, page, range);
|
|
21526
|
+
if (decoration === null) return yield* unavailablePage(deps, scope, controls, params, state, first);
|
|
21527
|
+
const { items, next } = mergePage(scope.safe, params?.filter, params?.limit ?? 25, state, page, decoration, transfers, wantsTransfers);
|
|
21528
|
+
const cursor = next.paymentDone && next.paymentPending.length === 0 && next.transferDone ? null : JSON.stringify(next);
|
|
21529
|
+
if (cursor !== null && cursor === params?.cursor) return yield* Effect.fail({ publicError: Errors.invalidInput("cursor", "Activity source did not advance") });
|
|
21530
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
21531
|
+
return {
|
|
21532
|
+
items,
|
|
21533
|
+
nextCursor: cursor,
|
|
21534
|
+
checkpoint: state.checkpoint,
|
|
21535
|
+
observedAt: Date.now()
|
|
21536
|
+
};
|
|
21537
|
+
});
|
|
21538
|
+
}
|
|
21539
|
+
//#endregion
|
|
21540
|
+
//#region src/activity/page-overlay.ts
|
|
21541
|
+
const MAX_OVERLAY_IDS = 200;
|
|
21542
|
+
function overlay(deps, actor, controls, input) {
|
|
21543
|
+
const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
|
|
21544
|
+
const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, i) => i * MAX_OVERLAY_IDS);
|
|
21545
|
+
return Effect.forEach(offsets, (offset) => callOperation(deps.convexCall, overlays.get, copyInvocationObservation(controls, {
|
|
21546
|
+
...actor === void 0 ? {} : { actor },
|
|
21547
|
+
...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
|
|
21548
|
+
...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
|
|
21549
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))), { concurrency: 2 }).pipe(Effect.flatMap((pages) => {
|
|
21550
|
+
const first = pages[0];
|
|
21551
|
+
if (pages.some((page) => scopeKey(page) !== scopeKey(first))) return Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
21552
|
+
return Effect.succeed({
|
|
21553
|
+
...first,
|
|
21554
|
+
links: pages.flatMap((page) => page.links),
|
|
21555
|
+
annotations: pages.flatMap((page) => page.annotations),
|
|
21556
|
+
payments: pages.flatMap((page) => page.payments)
|
|
21557
|
+
});
|
|
21558
|
+
}));
|
|
21559
|
+
}
|
|
21560
|
+
function decoratePage(deps, scope, controls, transfers, page, range) {
|
|
21561
|
+
return Effect.gen(function* () {
|
|
21562
|
+
const decoration = yield* overlay(deps, scope.actor, controls, {
|
|
21563
|
+
references: [...page.pending.map((id) => ({
|
|
21564
|
+
kind: "payment",
|
|
21565
|
+
id
|
|
21566
|
+
})), ...transfers.map((row) => ({
|
|
21567
|
+
kind: "movement",
|
|
21568
|
+
id: row.id
|
|
21569
|
+
}))],
|
|
21570
|
+
settlementIds: [...new Set(transfers.flatMap((row) => row.paymentSettlementId ? [row.paymentSettlementId] : []))]
|
|
21571
|
+
});
|
|
21572
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during the read") });
|
|
21573
|
+
const byId = new Map([...page.items, ...decoration.payments].map((item) => [item.id, item]));
|
|
21574
|
+
const settlements = [...new Set(decoration.links.map((link) => link.settlementId))];
|
|
21575
|
+
const facts = yield* readPaymentFacts(deps, scope.safe, settlements);
|
|
21576
|
+
if (facts === null) return null;
|
|
21577
|
+
const bySettlement = new Map(facts.map((row) => [row.settlementId, row]));
|
|
21578
|
+
const byPayment = new Map(decoration.links.map((link) => [link.paymentId, {
|
|
21579
|
+
fact: bySettlement.get(link.settlementId),
|
|
21580
|
+
expected: link.expected
|
|
21581
|
+
}]));
|
|
21582
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
21583
|
+
return {
|
|
21584
|
+
payments: page.pending.flatMap((id) => {
|
|
21585
|
+
const item = byId.get(id);
|
|
21586
|
+
if (item === void 0 || item.kind !== "payment" || item.createdAt > range.to || range.from !== void 0 && item.createdAt < range.from) return [];
|
|
21587
|
+
const confirmed = withAnnotation(confirmPayment(item, byPayment.get(id)?.fact, byPayment.get(id)?.expected), annotations.get(`payment:${id}`));
|
|
21588
|
+
return isActivityVisible(confirmed) ? [confirmed] : [];
|
|
21589
|
+
}),
|
|
21590
|
+
annotations,
|
|
21591
|
+
linked: new Set(decoration.links.filter((link) => matchesPaymentFact(bySettlement.get(link.settlementId), link.expected)).map((link) => link.settlementId))
|
|
21592
|
+
};
|
|
21593
|
+
});
|
|
21594
|
+
}
|
|
21595
|
+
function mergePage(safe, filter, limit, state, page, decoration, transfers, wantsTransfers) {
|
|
21596
|
+
const { payments, annotations, linked } = decoration;
|
|
21597
|
+
let pi = 0;
|
|
21598
|
+
let ti = 0;
|
|
21599
|
+
const items = [];
|
|
21600
|
+
const transferEnd = !wantsTransfers || transfers.length < 100;
|
|
21601
|
+
let transferBefore = state.transferBefore;
|
|
21602
|
+
while (items.length < limit) {
|
|
21603
|
+
if (pi === payments.length && !page.done || ti === transfers.length && !transferEnd) break;
|
|
21604
|
+
const payment = payments[pi];
|
|
21605
|
+
const row = transfers[ti];
|
|
21606
|
+
if (payment === void 0 && row === void 0) break;
|
|
21607
|
+
if (payment !== void 0 && (row === void 0 || payment.createdAt >= Number(row.timestamp) * 1e3)) {
|
|
21608
|
+
pi += 1;
|
|
21609
|
+
if (matchesFilter(payment, filter)) items.push(payment);
|
|
21610
|
+
} else if (row !== void 0) {
|
|
21611
|
+
ti += 1;
|
|
21612
|
+
transferBefore = {
|
|
21613
|
+
timestamp: Number(row.timestamp),
|
|
21614
|
+
id: row.id
|
|
21615
|
+
};
|
|
21616
|
+
if (row.paymentSettlementId && linked.has(row.paymentSettlementId)) continue;
|
|
21617
|
+
const item = transferItem(safe, row, annotations.get(`movement:${row.id}`));
|
|
21618
|
+
if (item !== null && matchesFilter(item, filter)) items.push(item);
|
|
21619
|
+
}
|
|
21620
|
+
}
|
|
21621
|
+
return {
|
|
21622
|
+
items,
|
|
21623
|
+
next: {
|
|
21624
|
+
...state,
|
|
21625
|
+
paymentCursor: page.cursor,
|
|
21626
|
+
paymentDone: page.done,
|
|
21627
|
+
paymentPending: payments.slice(pi).map((item) => item.id),
|
|
21628
|
+
transferBefore,
|
|
21629
|
+
transferDone: ti === transfers.length && transferEnd
|
|
21630
|
+
}
|
|
21631
|
+
};
|
|
21632
|
+
}
|
|
21633
|
+
function convexPage(deps, actor, controls, input) {
|
|
21634
|
+
return callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
|
|
21635
|
+
...input,
|
|
21636
|
+
actor
|
|
21637
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })));
|
|
21638
|
+
}
|
|
21639
|
+
function fallbackPage(deps, scope, controls, params, state, degraded) {
|
|
21640
|
+
return Effect.gen(function* () {
|
|
21641
|
+
const page = yield* convexPage(deps, scope.actor, controls, {
|
|
21642
|
+
limit: params?.limit ?? 25,
|
|
21643
|
+
...state.paymentCursor === null ? {} : { cursor: state.paymentCursor },
|
|
21644
|
+
range: {
|
|
21645
|
+
...params?.range?.from === void 0 ? {} : { from: params.range.from },
|
|
21646
|
+
to: Math.min(state.checkpoint, params?.range?.to ?? state.checkpoint)
|
|
21647
|
+
},
|
|
21648
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
21649
|
+
});
|
|
21650
|
+
const decoration = yield* overlay(deps, scope.actor, controls, { references: page.items.map((item) => ({
|
|
21651
|
+
kind: item.kind,
|
|
21652
|
+
id: item.id
|
|
21653
|
+
})) });
|
|
21654
|
+
if (scopeKey(decoration) !== scopeKey(scope)) return yield* Effect.fail({ publicError: Errors.invalidInput("actor", "Activity scope changed during fallback") });
|
|
21655
|
+
const annotations = new Map(decoration.annotations.map((annotation) => [`${annotation.reference.kind}:${annotation.reference.id}`, annotation]));
|
|
21656
|
+
yield* Effect.annotateCurrentSpan({
|
|
21657
|
+
read_source: "convex",
|
|
21658
|
+
...degraded ? { read_warning: "indexer-unavailable" } : {}
|
|
21659
|
+
});
|
|
21660
|
+
return {
|
|
21661
|
+
...page,
|
|
21662
|
+
items: page.items.filter(isActivityVisible).map((item) => withAnnotation(item, annotations.get(`${item.kind}:${item.id}`))),
|
|
21663
|
+
checkpoint: state.checkpoint,
|
|
21664
|
+
nextCursor: page.nextCursor === null ? null : JSON.stringify({
|
|
21665
|
+
...state,
|
|
21666
|
+
source: "fallback",
|
|
21667
|
+
paymentCursor: page.nextCursor,
|
|
21668
|
+
degraded
|
|
21669
|
+
}),
|
|
21670
|
+
...degraded ? { readWarning: "indexer-unavailable" } : {}
|
|
21671
|
+
};
|
|
21672
|
+
});
|
|
21673
|
+
}
|
|
21674
|
+
function readActivityPage(deps, actor, controls, params) {
|
|
21675
|
+
return Effect.gen(function* () {
|
|
21676
|
+
yield* Effect.try({
|
|
21677
|
+
try: () => requireParams(params),
|
|
21678
|
+
catch: (cause) => ({ publicError: asCapxulError(cause) })
|
|
21679
|
+
});
|
|
21680
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
21681
|
+
const selection = selectionKey(params);
|
|
21682
|
+
const cursor = yield* Effect.try({
|
|
21683
|
+
try: () => parseCursor(params?.cursor, scopeKey(scope), selection),
|
|
21684
|
+
catch: () => ({ publicError: Errors.invalidInput("cursor", "invalid or obsolete Activity cursor; restart the read") })
|
|
21685
|
+
});
|
|
21686
|
+
const state = cursor ?? {
|
|
21687
|
+
version: 1,
|
|
21688
|
+
scope: scopeKey(scope),
|
|
21689
|
+
selection,
|
|
21690
|
+
checkpoint: Date.now(),
|
|
21691
|
+
source: "indexed",
|
|
21692
|
+
paymentCursor: null,
|
|
21693
|
+
paymentDone: false,
|
|
21694
|
+
paymentPending: [],
|
|
21695
|
+
transferBefore: null,
|
|
21696
|
+
transferDone: false,
|
|
21697
|
+
degraded: false
|
|
21698
|
+
};
|
|
21699
|
+
const range = boundedRange(params?.range, state.checkpoint);
|
|
21700
|
+
if (range.from !== void 0 && range.from > range.to) return {
|
|
21701
|
+
items: [],
|
|
21702
|
+
nextCursor: null,
|
|
21703
|
+
checkpoint: state.checkpoint,
|
|
21704
|
+
observedAt: Date.now()
|
|
21705
|
+
};
|
|
21706
|
+
if (state.source === "fallback" || scope.safe === null) return yield* fallbackPage(deps, scope, controls, params, state, state.degraded);
|
|
21707
|
+
return yield* indexedPage(deps, scope, controls, params, state, cursor === null);
|
|
21708
|
+
});
|
|
21709
|
+
}
|
|
21710
|
+
function readActivitySummary(deps, actor, controls, params) {
|
|
21711
|
+
return Effect.gen(function* () {
|
|
21712
|
+
const totals = /* @__PURE__ */ new Map();
|
|
21713
|
+
let cursor;
|
|
21714
|
+
let count = 0;
|
|
21715
|
+
let readWarning;
|
|
21716
|
+
do {
|
|
21717
|
+
const page = yield* readActivityPage(deps, actor, controls, {
|
|
21718
|
+
limit: 100,
|
|
21719
|
+
...cursor === void 0 ? {} : { cursor },
|
|
21720
|
+
...params?.window === void 0 ? {} : { range: params.window }
|
|
21721
|
+
});
|
|
21722
|
+
count += page.items.length;
|
|
21723
|
+
readWarning ??= page.readWarning;
|
|
21724
|
+
for (const total of summarizeItems(page.items)) {
|
|
21725
|
+
const key = total.asset;
|
|
21726
|
+
const old = totals.get(key);
|
|
21727
|
+
totals.set(key, old === void 0 ? total : {
|
|
21728
|
+
...total,
|
|
21729
|
+
in: String(BigInt(old.in) + BigInt(total.in)),
|
|
21730
|
+
out: String(BigInt(old.out) + BigInt(total.out)),
|
|
21731
|
+
net: String(BigInt(old.net) + BigInt(total.net)),
|
|
21732
|
+
pendingIn: String(BigInt(old.pendingIn) + BigInt(total.pendingIn))
|
|
21733
|
+
});
|
|
21734
|
+
}
|
|
21735
|
+
cursor = page.nextCursor ?? void 0;
|
|
21736
|
+
} while (cursor !== void 0);
|
|
21737
|
+
return {
|
|
21738
|
+
window: {
|
|
21739
|
+
from: params?.window?.from ?? null,
|
|
21740
|
+
to: params?.window?.to ?? null
|
|
21741
|
+
},
|
|
21742
|
+
totals: [...totals.values()],
|
|
21743
|
+
count,
|
|
21744
|
+
observedAt: Date.now(),
|
|
21745
|
+
...readWarning === void 0 ? {} : { readWarning }
|
|
21746
|
+
};
|
|
21747
|
+
});
|
|
21748
|
+
}
|
|
21749
|
+
function subscribeActivity(deps, actor, controls, onChange) {
|
|
21750
|
+
return Effect.gen(function* () {
|
|
21751
|
+
const scope = yield* overlay(deps, actor, controls, {});
|
|
21752
|
+
if (scope.safe === null) return () => {};
|
|
21753
|
+
let delivered = false;
|
|
21754
|
+
return yield* deps.indexer.subscribe("transfer", {
|
|
21755
|
+
safe: toAddress(scope.safe),
|
|
21756
|
+
limit: 100
|
|
21757
|
+
}, (snapshot) => {
|
|
21758
|
+
if (snapshot.status === "ok") {
|
|
21759
|
+
if (delivered) onChange();
|
|
21760
|
+
delivered = true;
|
|
21761
|
+
}
|
|
21762
|
+
if (snapshot.status === "error") {
|
|
21763
|
+
delivered = true;
|
|
21764
|
+
onChange();
|
|
21765
|
+
}
|
|
21766
|
+
});
|
|
21767
|
+
});
|
|
21768
|
+
}
|
|
21769
|
+
//#endregion
|
|
21770
|
+
//#region src/activity/movement-detail.ts
|
|
21771
|
+
/** An indexed Movement has no Convex materialization. Read its exact native row. */
|
|
21772
|
+
function readIndexedMovement(deps, actor, controls, eventId) {
|
|
21773
|
+
return Effect.gen(function* () {
|
|
21774
|
+
const scope = yield* overlay(deps, actor, controls, { references: [{
|
|
21775
|
+
kind: "movement",
|
|
21776
|
+
id: eventId
|
|
21777
|
+
}] });
|
|
21778
|
+
if (scope.safe === null) return null;
|
|
21779
|
+
const safe = scope.safe.toLowerCase();
|
|
21780
|
+
const row = (yield* deps.indexer.read("transfer", {
|
|
21781
|
+
safe: toAddress(safe),
|
|
21782
|
+
limit: 1,
|
|
21783
|
+
eventId
|
|
21784
|
+
}).pipe(Effect.mapError((error) => ({ publicError: error.publicError }))))[0];
|
|
21785
|
+
if (row === void 0) return null;
|
|
21786
|
+
const blockNumber = Number(row.blockNumber);
|
|
21787
|
+
if (row.id !== eventId || row.from.toLowerCase() !== safe && row.to.toLowerCase() !== safe || !Number.isSafeInteger(blockNumber) || blockNumber < 0 || !Number.isSafeInteger(row.logIndex) || row.logIndex < 0) return yield* Effect.fail({ publicError: Errors.invalidInput("activity", "indexed Movement does not match the requested scope or evidence") });
|
|
21788
|
+
yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
|
|
21789
|
+
const documentSet = scope.movementDocuments.find((set) => set.movementId === row.id);
|
|
21790
|
+
return {
|
|
21791
|
+
kind: "movement",
|
|
21792
|
+
movementId: row.id,
|
|
21793
|
+
documents: documentSet?.documents ?? [],
|
|
21794
|
+
documentState: documentSet?.state ?? "pending",
|
|
21795
|
+
viewer: scope.actor,
|
|
21796
|
+
annotation: scope.annotations.find((annotation) => annotation.reference.kind === "movement" && annotation.reference.id === row.id) ?? null,
|
|
21797
|
+
evidence: {
|
|
21798
|
+
chainId: row.chainId,
|
|
21799
|
+
contractAddress: row.token,
|
|
21800
|
+
tokenAddress: row.token,
|
|
21801
|
+
transactionHash: row.txHash,
|
|
21802
|
+
blockNumber,
|
|
21803
|
+
blockHash: row.blockHash,
|
|
21804
|
+
inclusion: "included",
|
|
21805
|
+
verification: "verified",
|
|
21806
|
+
canonicalState: "finalized",
|
|
21807
|
+
transferLogIndex: row.logIndex,
|
|
21808
|
+
source: row.from,
|
|
21809
|
+
destination: row.to,
|
|
21810
|
+
rawAmount: row.amount.toString(),
|
|
21811
|
+
link: {
|
|
21812
|
+
movementId: row.id,
|
|
21813
|
+
depositPaymentId: null
|
|
21814
|
+
}
|
|
21815
|
+
}
|
|
21816
|
+
};
|
|
21817
|
+
});
|
|
21818
|
+
}
|
|
21819
|
+
//#endregion
|
|
21820
|
+
//#region src/activity/scope.ts
|
|
21821
|
+
/**
|
|
21822
|
+
* The activity backend gives its actor a different name than the other
|
|
21823
|
+
* financialOps functions: `{kind: "organization", orgId}`, and not
|
|
21824
|
+
* `{kind: "org", orgId}`. Thus it does not use `actorReferenceToBackend`.
|
|
21825
|
+
*
|
|
21826
|
+
* The `personal` case sends NO actor. This is correct, and it does not lose
|
|
21827
|
+
* data. `requireActivityActor` uses the signed-in Account when the actor is
|
|
21828
|
+
* absent, and `canReadActor` refuses all other account identifiers
|
|
21829
|
+
* (`packages/backend/convex/movement/activityAuth.ts`). Thus "personal" and
|
|
21830
|
+
* "absent" both name the one Account that an SDK caller can read. The SDK does
|
|
21831
|
+
* not hold an account identifier to send.
|
|
21832
|
+
*
|
|
21833
|
+
* The `switch` is exhaustive. A new `ActorReference` variant is a compile error
|
|
21834
|
+
* here. The conditional expression that this function replaced removed each
|
|
21835
|
+
* value that was not an organization.
|
|
21836
|
+
*/
|
|
21837
|
+
function activityActorField(actor) {
|
|
21838
|
+
if (actor === void 0 || actor.kind === "personal") return {};
|
|
21839
|
+
return { actor: {
|
|
21840
|
+
kind: "organization",
|
|
21841
|
+
orgId: actor.organizationId
|
|
21842
|
+
} };
|
|
21843
|
+
}
|
|
21844
|
+
/** Capture scope now; let the observed result boundary handle a malformed caller value. */
|
|
21845
|
+
function captureActivityInput(prepare) {
|
|
21846
|
+
try {
|
|
21847
|
+
return prepare();
|
|
21848
|
+
} catch (cause) {
|
|
21849
|
+
return Effect.die(cause);
|
|
21850
|
+
}
|
|
21851
|
+
}
|
|
21852
|
+
function observeActivityScope(actor) {
|
|
21853
|
+
const organizationId = actor?.kind === "organization" || actor?.kind === "org" ? safeEngineeringIdentifier(actor.orgId) : void 0;
|
|
21854
|
+
return Effect.annotateCurrentSpan({
|
|
21855
|
+
scope_kind: actor?.kind === "organization" || actor?.kind === "org" ? "organization" : "account",
|
|
21856
|
+
...organizationId === void 0 ? {} : { verified_organization_id: organizationId }
|
|
21857
|
+
});
|
|
21858
|
+
}
|
|
21859
|
+
function decodeActivityViewer(value) {
|
|
21860
|
+
const viewer = Option.getOrUndefined(Schema.decodeUnknownOption(ActorRefSchema)(value));
|
|
21861
|
+
if (viewer === void 0) return void 0;
|
|
21862
|
+
const id = viewer.kind === "organization" ? viewer.orgId : viewer.accountId;
|
|
21863
|
+
return safeEngineeringIdentifier(id) === void 0 ? void 0 : viewer;
|
|
21864
|
+
}
|
|
21865
|
+
/** The detail without the scope it was read for, which the SDK reports as telemetry instead. */
|
|
21866
|
+
function normalizeActivityDetail(response) {
|
|
21867
|
+
if (response === null) return null;
|
|
21868
|
+
const { viewer: _viewer, ...detail } = response;
|
|
21869
|
+
return detail;
|
|
21870
|
+
}
|
|
21871
|
+
//#endregion
|
|
21872
|
+
//#region src/activity/methods.ts
|
|
21873
|
+
function makeActivityMethods(deps) {
|
|
21874
|
+
const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
|
|
21875
|
+
indexer: deps.indexerRead,
|
|
21876
|
+
convexCall: deps.convexCall
|
|
21877
|
+
};
|
|
21878
|
+
/** One public call for the caller's chosen actor, read once at call start. */
|
|
21879
|
+
const forActor = (actorRef, options, operation, program) => {
|
|
21880
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
21881
|
+
return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(actorRef).actor)).pipe(Effect.flatMap((actor) => program(actor, controls))), controls, operation, deps.runPromise);
|
|
21882
|
+
};
|
|
21883
|
+
return {
|
|
21884
|
+
subscribe: (onChange, params, options) => forActor(params?.actor, options, CAPXUL_OPERATIONS.activity.subscribe, (actor, controls) => indexerDeps === void 0 ? Effect.succeed(() => {}) : Effect.suspend(() => subscribeActivity(indexerDeps, actor, controls, onChange)).pipe(Effect.tap(() => observeActivityScope(actor)))),
|
|
21885
|
+
list: (params, options) => forActor(params?.actor, options, items.list.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.list, copyInvocationObservation(controls, {
|
|
21886
|
+
...actor === void 0 ? {} : { actor },
|
|
21887
|
+
...params?.cursor === void 0 ? {} : { cursor: params.cursor },
|
|
21888
|
+
...params?.limit === void 0 ? {} : { limit: params.limit },
|
|
21889
|
+
...params?.range === void 0 ? {} : { range: params.range },
|
|
21890
|
+
...params?.filter === void 0 ? {} : { filter: params.filter }
|
|
21891
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.map((page) => ({
|
|
21892
|
+
...page,
|
|
21893
|
+
items: page.items.filter(isActivityVisible)
|
|
21894
|
+
})), Effect.tap(() => observeActivityScope(actor)))),
|
|
21895
|
+
summary: (params, options) => forActor(params?.actor, options, items.summary.name, (actor, controls) => Effect.suspend(() => indexerDeps === void 0 ? callOperation(deps.convexCall, items.summary, copyInvocationObservation(controls, {
|
|
21896
|
+
...actor === void 0 ? {} : { actor },
|
|
21897
|
+
...params?.window === void 0 ? {} : { window: params.window }
|
|
21898
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError }))) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor)))),
|
|
21899
|
+
get: (reference, options) => {
|
|
21900
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
21901
|
+
const requestedActor = actorReferenceToBackend(options?.actor);
|
|
21902
|
+
return runPortEffect(captureActivityInput(() => {
|
|
21903
|
+
const scope = requestedActor === void 0 ? void 0 : requestedActor.kind === "org" ? {
|
|
21904
|
+
kind: "org",
|
|
21905
|
+
orgId: requestedActor.orgId
|
|
21906
|
+
} : { kind: "account" };
|
|
21907
|
+
return Effect.succeed({
|
|
21908
|
+
actor: scope,
|
|
21909
|
+
referenceActor: reference.kind === "payment" ? activityActorField(reference.actor).actor : void 0
|
|
21910
|
+
});
|
|
21911
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => indexerDeps !== void 0 && reference.kind === "movement" && /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:[0-9]{1,10}$/.test(reference.id) ? readIndexedMovement(indexerDeps, actor?.kind === "org" ? {
|
|
21912
|
+
kind: "organization",
|
|
21913
|
+
orgId: actor.orgId
|
|
21914
|
+
} : void 0, controls, reference.id) : callOperation(deps.convexCall, items.get, copyInvocationObservation(controls, {
|
|
21915
|
+
reference: {
|
|
21916
|
+
kind: reference.kind,
|
|
21917
|
+
id: reference.id,
|
|
21918
|
+
...referenceActor === void 0 ? {} : { actor: referenceActor }
|
|
21919
|
+
},
|
|
21920
|
+
...actor === void 0 ? {} : { actor }
|
|
21921
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.flatMap((response) => {
|
|
21922
|
+
const detail = normalizeActivityDetail(response);
|
|
21923
|
+
if (response === null) return Effect.succeed(detail);
|
|
21924
|
+
const viewer = response.viewer === void 0 ? actor ?? referenceActor : decodeActivityViewer(response.viewer);
|
|
21925
|
+
return viewer === void 0 ? Effect.succeed(detail) : observeActivityScope(viewer).pipe(Effect.as(detail));
|
|
21926
|
+
})))), controls, items.get.name, deps.runPromise);
|
|
21927
|
+
},
|
|
21928
|
+
annotate: (input, options) => {
|
|
21929
|
+
const controls = deps.invocationControls?.(options) ?? options;
|
|
21930
|
+
return runPortEffect(captureActivityInput(() => {
|
|
21931
|
+
const actor = activityActorField(input.actor).actor;
|
|
21932
|
+
if (input.actor !== void 0 && input.reference.kind === "payment" && input.reference.actor !== void 0 && !sameActor(input.actor, input.reference.actor)) return Effect.fail({ publicError: Errors.invalidInput("reference.actor", "must match the annotation actor") });
|
|
21933
|
+
return Effect.succeed({
|
|
21934
|
+
actor,
|
|
21935
|
+
referenceActor: input.reference.kind === "payment" ? activityActorField(input.reference.actor).actor : void 0
|
|
21936
|
+
});
|
|
21937
|
+
}).pipe(Effect.flatMap(({ actor, referenceActor }) => Effect.suspend(() => callOperation(deps.convexCall, annotations.save, copyInvocationObservation(controls, {
|
|
21938
|
+
...actor === void 0 ? {} : { actor },
|
|
21939
|
+
reference: input.reference.kind === "payment" && referenceActor !== void 0 ? {
|
|
21940
|
+
kind: "payment",
|
|
21941
|
+
id: input.reference.id,
|
|
21942
|
+
actor: referenceActor
|
|
21943
|
+
} : {
|
|
21944
|
+
kind: input.reference.kind,
|
|
21945
|
+
id: input.reference.id
|
|
21946
|
+
},
|
|
21947
|
+
...input.counterpartyLabel === void 0 ? {} : { counterpartyLabel: input.counterpartyLabel },
|
|
21948
|
+
...input.accountingCategory === void 0 ? {} : { accountingCategory: input.accountingCategory },
|
|
21949
|
+
...input.memo === void 0 ? {} : { memo: input.memo }
|
|
21950
|
+
})).pipe(Effect.mapError((publicError) => ({ publicError })))).pipe(Effect.tap((annotation) => {
|
|
21951
|
+
const scope = decodeActivityViewer(annotation.actor);
|
|
21952
|
+
return scope === void 0 ? Effect.void : observeActivityScope(scope);
|
|
21953
|
+
})))), controls, annotations.save.name, deps.runPromise);
|
|
21954
|
+
}
|
|
21955
|
+
};
|
|
21956
|
+
}
|
|
21957
|
+
//#endregion
|
|
21958
|
+
//#region src/activity/phases.ts
|
|
21959
|
+
const PENDING = Object.freeze({
|
|
21960
|
+
terminal: false,
|
|
21961
|
+
outcome: null,
|
|
21962
|
+
needsAction: false
|
|
21963
|
+
});
|
|
21964
|
+
const AWAITING_ACTOR = Object.freeze({
|
|
21965
|
+
terminal: false,
|
|
21966
|
+
outcome: null,
|
|
21967
|
+
needsAction: true
|
|
21968
|
+
});
|
|
21969
|
+
const COMPLETED = Object.freeze({
|
|
21970
|
+
terminal: true,
|
|
21971
|
+
outcome: "good",
|
|
21972
|
+
needsAction: false
|
|
21973
|
+
});
|
|
21974
|
+
const FAILED = Object.freeze({
|
|
21975
|
+
terminal: true,
|
|
21976
|
+
outcome: "bad",
|
|
21977
|
+
needsAction: false
|
|
21978
|
+
});
|
|
21979
|
+
const WITHDRAWN = Object.freeze({
|
|
21980
|
+
terminal: true,
|
|
21981
|
+
outcome: "neutral",
|
|
21982
|
+
needsAction: false
|
|
21983
|
+
});
|
|
21984
|
+
/** Interpret a Payment status. `pending_claim` waits for the actor who reads it. */
|
|
21985
|
+
function paymentPhase(status) {
|
|
21986
|
+
switch (status) {
|
|
21987
|
+
case "pending":
|
|
21988
|
+
case "settling":
|
|
21989
|
+
case "scheduled":
|
|
21990
|
+
case "streaming": return PENDING;
|
|
21991
|
+
case "pending_claim": return AWAITING_ACTOR;
|
|
21992
|
+
case "settled": return COMPLETED;
|
|
21993
|
+
case "cancelled": return WITHDRAWN;
|
|
21994
|
+
case "failed":
|
|
21995
|
+
case "unresolved": return FAILED;
|
|
21996
|
+
default: return absurd(status);
|
|
21997
|
+
}
|
|
21998
|
+
}
|
|
21999
|
+
/** Interpret a payment Request as its ISSUER sees it. An issuer does not wait for itself. */
|
|
22000
|
+
function requestPhase(status) {
|
|
22001
|
+
switch (status) {
|
|
22002
|
+
case "draft":
|
|
22003
|
+
case "sent":
|
|
22004
|
+
case "viewed":
|
|
22005
|
+
case "pending_settlement": return PENDING;
|
|
22006
|
+
case "paid": return COMPLETED;
|
|
22007
|
+
case "declined":
|
|
22008
|
+
case "expired": return FAILED;
|
|
22009
|
+
case "cancelled": return WITHDRAWN;
|
|
22010
|
+
default: return absurd(status);
|
|
22011
|
+
}
|
|
22012
|
+
}
|
|
22013
|
+
/** Interpret the same request as its RECIPIENT sees it. `open` waits for the recipient. */
|
|
22014
|
+
function inboxPhase(status) {
|
|
22015
|
+
switch (status) {
|
|
22016
|
+
case "open": return AWAITING_ACTOR;
|
|
22017
|
+
case "approved": return PENDING;
|
|
22018
|
+
case "paid": return COMPLETED;
|
|
22019
|
+
case "declined":
|
|
22020
|
+
case "expired": return FAILED;
|
|
22021
|
+
case "cancelled": return WITHDRAWN;
|
|
22022
|
+
default: return absurd(status);
|
|
22023
|
+
}
|
|
22024
|
+
}
|
|
22025
|
+
//#endregion
|
|
20611
22026
|
//#region src/accounts/summaries.ts
|
|
20612
22027
|
function makeMeMethods(deps) {
|
|
20613
22028
|
return {
|
|
20614
22029
|
get: (options) => runIfActive(options?.signal, summaries.me.name, () => callOperation(deps.convexCall, summaries.me, {}).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise),
|
|
22030
|
+
home: (options) => runIfActive(options?.signal, summaries.home.name, () => (deps.chainId === void 0 ? Effect.fail(Errors.notImplemented("accounts", "me.home")) : callOperation(deps.convexCall, summaries.home, {
|
|
22031
|
+
chainId: deps.chainId,
|
|
22032
|
+
...options?.orgId === void 0 ? {} : { orgId: options.orgId }
|
|
22033
|
+
})).pipe(Effect.flatMap((wire) => Effect.try({
|
|
22034
|
+
try: () => ({
|
|
22035
|
+
...wire,
|
|
22036
|
+
balances: wire.balances === null ? null : accountPositionsFromWire(wire.balances),
|
|
22037
|
+
recentActivity: wire.recentActivity?.filter(isActivityVisible) ?? null
|
|
22038
|
+
}),
|
|
22039
|
+
catch: asCapxulError
|
|
22040
|
+
})), Effect.mapError((publicError) => ({ publicError }))), deps.runPromise),
|
|
20615
22041
|
depositInstructions: (options) => {
|
|
20616
22042
|
const controls = deps.invocationControls?.(options) ?? options;
|
|
20617
22043
|
return runPortEffect(callOperation(deps.convexCall, summaries.depositInstructions, copyInvocationObservation(controls, {})).pipe(Effect.mapError((publicError) => ({ publicError })), Effect.tap((instructions) => emitDepositInitiated(deps.telemetry, instructions, controls))), controls, summaries.depositInstructions.name, deps.runPromise);
|
|
@@ -21520,4 +22946,4 @@ function executeIdentityProductObservation(telemetry, record, state) {
|
|
|
21520
22946
|
return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
|
|
21521
22947
|
}
|
|
21522
22948
|
//#endregion
|
|
21523
|
-
export {
|
|
22949
|
+
export { parseAssetAmount as $, InvitationViewSchema as $n, SystemClockLayer as $r, version as $t, registerProductTelemetryInvocation as A, toJwtToken as Ai, details as An, toWeiAmount as Ar, safe4337ExecuteUserOpCallData as At, makeOrganizationPaymentsMethods as B, AuthCachePortTag as Bi, FixedOfferDraftSchema as Bn, runCapxulEffect as Br, failureEvidenceProps as Bt, AccountReadPortTag as C, parseAuthSession as Ci, CLI_INVOCATION_KINDS as Cn, toAssetId as Cr, SEND_BATCH_SIGNATURE as Ct, convexCallErrorFromCapxul as D, toEpochSeconds as Di, isCapxulOperation as Dn, isEvmAddress as Dr, TOKENS as Dt, ConvexCallPortTag as E, toEpochMs as Ei, CAPXUL_OPERATIONS as En, EVM_ADDRESS_RE$1 as Er, CONFIGURED_MONEY_ASSETS as Et, decodePayrollRunInput as F, FAILURE_MODES$1 as Fi, replacements as Fn, publicIdentityFailure as Fr, SAFE_SENTINEL_OWNERS as Ft, makeActorRelationshipMethods as G, treasuries as Gn, toCapxulResult as Gr, observationContextProps as Gt, makePaymentsMethods as H, members as Hn, awaitCapxulResult as Hr, postHogFailureObservation as Ht, toPayrollGroupId as I, isFailureMode as Ii, PreparedPermissionExecutionSchema as In, runIdentityFacade as Ir, callOperation as It, makeTargetsMethods as J, ORGANIZATION_SETUP_REASONS as Jn, EXPECTED_OPERATION_OUTCOMES as Jr, SDK_VERSION as Jt, makeCheckoutMethods as K, workspaces as Kn, formatTraceparent as Kr, withCapturedAttemptContext as Kt, makeAccountMethods as L, isRecord as Li, PermissionCommandViewSchema as Ln, runPortEffect as Lr, asCapxulError as Lt, makeApprovalsMethods as M, Errors as Mi, authorities as Mn, reference as Mr, buildSafeMultiSendExecTransactionCall as Mt, makePayrollMethods as N, CapxulError as Ni, commands as Nn, makeAuthMethods as Nr, BASE_SEPOLIA_CHAIN_ID as Nt, retryIdempotentRead as O, toAuthUserId as Oi, normalizeCapxulOperation as On, toAddress as Or, deriveCapxulSafeAddress as Ot, makeUnavailablePayrollMethods as P, isCapxulError as Pi, configurations as Pn, askIdentity as Pr, MULTI_SEND_CALL_ONLY as Pt, formatAssetAmount as Q, toOrgId as Qn, oauthBearerAuthClient as Qr, safeExceptionLabel as Qt, emitDepositInitiated as R, readField as Ri, PayrollGroupIdSchema as Rn, sessionFromActor as Rr, toPublicError$1 as Rt, valueIn as S, BrowserAuthCacheAdapter as Si, CLI_COMMAND_NAMES as Sn, currencySymbolFor as Sr, SAFE_ENABLE_MODULE_ABI as St, TelemetryPortTag as T, toDurationMs as Ti, stampTelemetryEnvelope as Tn, toExactAssetAmount as Tr, CAPXUL_PAYMENTS_V2_ADDRESS as Tt, waitForPaymentSubmission as U, roles as Un, defectAsCapxulError as Ur, observeFailedResult as Ut, makeOfframpMethods as V, invitations as Vn, withVerifiedOrganizationScope as Vr, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as Vt, makeDestinationsMethods as W, setups as Wn, runIfActive as Wr, observeSdkClient as Wt, assetAmountFromRaw as X, CHAIN_UPSTREAMS as Xn, carryInvocationParentSpan as Xr, normalizeExceptionOperation as Xt, subscribeFreshQuery as Y, ORGANIZATION_SETUP_STATUSES as Yn, ActorFailure as Yr, normalizeExceptionErrorKind as Yt, assetSymbolFor as Z, decodeChainCause as Zn, withInvocationParentSpan as Zr, projectSdkException as Zt, accountFromAccountPositions as _, AuthClientError as _i, toPartyId as _n, toBudgetId as _r, ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI as _t, paymentPhase as a, observationRequestHeaders as ai, isSignerNotReadySignal as an, images as ar, fingerprintPaymentIntent as at, MONEY_READ_RATES as b, globalLocalStorage as bi, redactTelemetryProps as bn, toRoleKey as br, ROLES_SET_UP_ABI as bt, makePaymentDocumentsMethods as c, readInvocationObservation as ci, signerFailure as cn, isBytes32 as cr, roleKeyForPermission as ct, SmartAccountPortTag as d, redactUrlSecrets as di, makeClientRequestMethods as dn, toLowerBytes32 as dr, CREATE_COMMITMENT_SIGNATURE as dt, ClockError as ei, isSettingUpLifecycle as en, hashPermissionReplacementPreview as er, requireAsset as et, smartAccountErrorFromCapxul as f, isCredentialField as fi, toCountryCode as fn, toLowerHex as fr, MODULE_PROXY_FACTORY_ABI as ft, ConvexAccountLayer as g, authClientPortFromPromiseAdapter as gi, toHandle as gn, EpochMsSchema$1 as gr, ROLES_ASSIGN_ROLES_ABI as gt, makeAccountsMethods as h, toPublishableKey as hi, HANDLE_RE as hn, DurationMsSchema$1 as hr, PARAMETER_TYPE as ht, inboxPhase as i, BetterAuthBrowserLayer as ii, isEcdsaSignatureHex as in, AppIdSchema$1 as ir, canonicalJson as it, wireChainId as j, toSessionToken as ji, onboardings as jn, absurd as jr, safe4337MultiSendUserOpCallData as jt, bindProductTelemetryInvocation as k, toEmail as ki, OPERATIONS as kn, toChainId as kr, normalizeSafeSaltEmail as kt, makeSmartAccountMethods as l, sanitizeObservationContext as li, resolveFailureMode as ln, toExactHex as lr, ZODIAC_MODULE_PROXY_FACTORY as lt, PonderIndexerAdapter as m, toAppId as mi, toTesterKind as mn, PAYMENT_STATUSES as mr, OPERATOR as mt, bootIdentityFlow as n, readClockNow as ni, causeChain as nn, CurrentPolicySchema as nr, isMoneyParseError as nt, requestPhase as o, attachInvocationObservation as oi, openfortSignerNotReadyCode as on, toAccountId as or, moneySafeOpDigest as ot, makeHoldingsMethods as p, toAllowedOrigin as pi, toKycTier as pn, PAYMENT_DIRECTIONS as pr, MULTI_SEND_SELECTOR as pt, isCheckoutSettlementConsumed as q, OrganizationSetupSchema as qn, CAPXUL_ERROR_CODES as qr, EXCEPTION_MESSAGE as qt, makeMeMethods as r, BetterAuthNodeLayer as ri, injectedWalletSigner as rn, diagnostics as rr, parseMoney as rt, makeActivityMethods as s, copyInvocationObservation as si, recoverRawDigestSigner as sn, PaymentDocumentOptionsSchema as sr, ORG_BUDGET_ACTIONS as st, executeIdentityProductObservation as t, ClockPortTag as ti, UNOBSERVABLE_SIGNER_STATUS as tn, decodePermissionIntent as tr, toRaw as tt, ConvexSmartAccountLayer as u, redactSecrets as ui, makeCurrentUserMethods as un, toLowerAddress as ur, resolveOrgPermissionIdentity as ut, accountPositionsFromWire as v, AuthClientPortTag as vi, validateHandle as vn, toPermissionAssignmentId as vr, ROLES_SET_ALLOWANCE_ABI as vt, accountReadErrorFromCapxul as w, parseCachedJwt as wi, EXCEPTION_EVENT_NAME as wn, toCurrencyCode as wr, SEND_SIGNATURE as wt, valueActivitySummary as x, InMemoryAuthCacheAdapter as xi, CLI_CAPABILITIES as xn, ChainIdSchema$1 as xr, ROLE_MANAGEMENT_SIGNATURES as xt, positionPeg as y, detectAuthCacheAdapter as yi, redactTelemetryEvent as yn, toPermissionId as yr, ROLES_SET_TRANSACTION_UNWRAPPER_ABI as yt, logDroppedEvent as z, AuthCacheError as zi, PayrollGroupInputSchema as zn, observationOnlyControls as zr, failureDetail as zt };
|