@capxul/sdk 4.20.0-beta.4 → 4.20.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,51 +19,44 @@ const client = created.value;
19
19
  Every public operation resolves a `Promise<CapxulResult<T>>`. Domain failures
20
20
  are values, not thrown exceptions.
21
21
 
22
- ## Money input and display
22
+ ## Asset amount input and display
23
23
 
24
- Use the public money helpers at form and display boundaries. They do not
25
- convert a money value to a JavaScript number.
24
+ Use the asset helpers at Payment form and display boundaries. They keep the
25
+ exact AssetId and do not convert a value to a JavaScript number.
26
26
 
27
27
  ```ts
28
- import { formatMoney, parseMoney } from "@capxul/sdk";
28
+ import { formatAssetAmount, parseAssetAmount, requireAsset, TOKENS } from "@capxul/sdk";
29
29
 
30
- const amount = parseMoney("1,000.25", {
31
- currency: account.available.currency,
32
- decimals: account.available.decimals,
33
- });
30
+ const amount = parseAssetAmount("1,000.25", requireAsset(TOKENS.BASE_SEPOLIA.CAPXUL_TEST_USDC));
34
31
 
35
32
  if ("kind" in amount) {
36
33
  console.error(amount.reason); // closed code — the app owns the sentence (ADR-0023)
37
34
  } else {
38
- console.log(formatMoney(amount)); // $1,000.25
39
- console.log(formatMoney(amount, { grammar: "code" })); // 1,000.25 USD
35
+ console.log(formatAssetAmount(amount)); // 1,000.25 USDC
40
36
  }
41
37
  ```
42
38
 
43
- `parseMoney` returns validation errors as values. `formatMoney` rejects a
44
- malformed `Money` record with `INVALID_INPUT`.
39
+ `parseAssetAmount` returns validation errors as values. Payment, request,
40
+ Budget, Payroll, and activity amounts require AssetAmount. Fiat Money remains
41
+ separate valuation data.
45
42
 
46
- ## The asset a screen shows
43
+ ## Value all asset positions
47
44
 
48
- `holdings.primary` picks the one asset a hero balance or a summary total
49
- should show. It returns the renderable holding with the largest available
50
- balance. A chain row that carries no `decimals`, no `symbol`, or a symbol that
51
- stands for no supported currency is skipped, because it cannot be rendered.
45
+ Use `holdings.current` for raw positions. Use `valueIn` to value exact asset
46
+ quantities in a display currency. A token symbol does not identify an asset.
47
+ Quantities from different assets cannot be compared as a headline balance.
52
48
 
53
49
  ```ts
54
- import { formatMoney, type Holding } from "@capxul/sdk";
55
-
56
- const result = await client.holdings.primary();
50
+ import { formatMoney, valueIn } from "@capxul/sdk";
57
51
 
58
- if (result.ok && result.value !== null) {
59
- const holding: Holding = result.value;
60
- console.log(holding.symbol); // "USDX" — the token symbol the chain reported
61
- console.log(formatMoney(holding.available, { grammar: "code" })); // e.g. "1,250.50 USD"
52
+ const valuation = valueIn(amounts, displayCurrency, rates);
53
+ if (valuation.total !== null) {
54
+ console.log(formatMoney(valuation.total));
62
55
  }
63
56
  ```
64
57
 
65
- `null` means no row is renderable. It is a value, not an error, so give the
66
- screen its own rule for showing nothing.
58
+ Keep `valuation.unrated` visible beside the known subtotal. A missing rate
59
+ does not mean a zero balance.
67
60
 
68
61
  ## Observability
69
62
 
@@ -105,6 +98,11 @@ The backend remains the authority for reserved names and global uniqueness.
105
98
  The method returns only after the client requirement reaches a ready Account.
106
99
  The React frontend does not use this journey yet.
107
100
 
101
+ A Node host that keeps login outside the browser can pass `openfortSignerFactory`.
102
+ The factory receives only the validated Openfort and auth bootstrap fields. Its
103
+ browser page can use `createOpenfortBrowserSigner` with host-owned access-token
104
+ and encryption-session callbacks. It does not need a second login or private SDK import.
105
+
108
106
  ## Published entry points
109
107
 
110
108
  The packed npm package exposes only:
@@ -173,3 +171,36 @@ operator secrets and prove only the path named by the command. See
173
171
  The browser production path owns its Openfort signer when no signer is supplied.
174
172
  Current integration recovery evidence and its published-package boundary are
175
173
  tracked in the [#870 receipt](../../docs/receipts/issue-870-openfort-signer-recovery-RECEIPT-2026-07-19.md).
174
+
175
+ ### Node session storage
176
+
177
+ A Node host can pass `nodeSessionStorage: { load, save }` to
178
+ `createCapxulClient`. Both methods return promises. `load` returns an opaque
179
+ BetterAuth credential or `null`; `save` receives the credential or `null` on
180
+ logout. Store it with owner-only access and isolate it by application and
181
+ environment. Do not print it or use a cached `AuthSession` as backend authority.
182
+ The SDK validates the credential and expiry with BetterAuth on process restart.
183
+ With durable storage, each adapter serializes session reads, OTP verification,
184
+ sign-out, and Convex token reads. A delayed response cannot clear or rotate a
185
+ credential from a later operation on that adapter. Cancellation while queued
186
+ returns immediately and prevents that operation from reaching the provider.
187
+ Hosts that share storage across clients or processes must fence writes, for
188
+ example with an atomic compare-and-set; the adapter cannot order other clients.
189
+
190
+ For separate OTP request and verification processes, retain the email and
191
+ original request timestamp. After `auth.getSession()` confirms signed-out state,
192
+ call `auth.resumeOtp({ email, requestedAt })`, then `auth.verifyOtp({ email,
193
+ code })`. Resume restores pending input only. It does not send another OTP or
194
+ authenticate the caller. Keep OTP values out of arguments, logs, and stored data.
195
+
196
+ ### Existing financial documents
197
+
198
+ Use `paymentDocuments.verify({ documentHash, contentHash })` to verify stored
199
+ content. Use `paymentDocuments.render` with the same pair for safe semantic HTML.
200
+ Call `paymentDocuments.get` only for a deliberate download. Its `bytesBase64`
201
+ contains the exact stored bytes. Do not log document results or error payloads.
202
+ Get references from Payment or Movement activity detail. References include
203
+ kind, format, and role. A hash is not permission to read a document.
204
+ Reads check current Payment participants or current Movement actor access.
205
+ Missing and unauthorized pairs return the same refusal. These methods do not
206
+ create documents or add Payment links.
@@ -1,4 +1,4 @@
1
- import { concat, encodeAbiParameters, encodeFunctionData, encodePacked, getContractAddress, keccak256, padHex, stringToHex, toBytes, toEventSelector, toFunctionSelector, toFunctionSignature } from "viem";
1
+ import { concat, encodeAbiParameters, encodeFunctionData, encodePacked, getContractAddress, keccak256, padHex, toBytes, toEventSelector, toFunctionSelector, toFunctionSignature } from "viem";
2
2
  import { Context, Data, Effect, Layer, Result } from "effect";
3
3
  //#region ../errors/src/secret-material.ts
4
4
  const CREDENTIAL_FIELDS = /* @__PURE__ */ new Set([
@@ -807,8 +807,10 @@ const Errors = {
807
807
  * enumerates the states the method accepts.
808
808
  */
809
809
  wrongState: (details) => new CapxulError("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
810
- ...details,
811
- validStates: [...details.validStates]
810
+ method: details.method,
811
+ currentState: details.currentState,
812
+ validStates: [...details.validStates],
813
+ ...details.organizationId === void 0 ? {} : { organizationId: details.organizationId }
812
814
  } })
813
815
  };
814
816
  //#endregion
@@ -941,6 +943,12 @@ function toHandle(raw) {
941
943
  if (typeof raw !== "string" || !HANDLE_RE.test(raw)) throw Errors.invalidInput("handle", invalidValueReason("must be 3-30 characters of a-z, 0-9, underscore, or hyphen", raw));
942
944
  return raw;
943
945
  }
946
+ function toPermissionId(raw) {
947
+ return toNonEmptyStringBrand(raw, "permissionId");
948
+ }
949
+ function toPermissionAssignmentId(raw) {
950
+ return toNonEmptyStringBrand(raw, "permissionAssignmentId");
951
+ }
944
952
  function toBudgetId(raw) {
945
953
  return toNonEmptyStringBrand(raw, "budgetId");
946
954
  }
@@ -1061,42 +1069,11 @@ function invalidValueReason(prefix, raw) {
1061
1069
  }
1062
1070
  //#endregion
1063
1071
  //#region ../config/src/tokens.ts
1064
- /** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
1065
- const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
1066
- const USDX_DEPLOYMENT_START_BLOCK = 39860173;
1067
- /**
1068
- * The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
1069
- * (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
1070
- * the decimals so a holdings row can never carry one token's address with
1071
- * another token's display symbol. This is the display symbol, not the
1072
- * currency: see `USDX_CURRENCY` below.
1073
- */
1074
- const USDX_SYMBOL = "USDX";
1075
1072
  const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
1076
- const USDX_ASSET = {
1077
- assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
1078
- chainId: toChainId(84532),
1079
- symbol: USDX_SYMBOL,
1080
- peg: toCurrencyCode("USD"),
1081
- tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
1082
- deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
1083
- decimals: 6,
1084
- source: "settlement"
1085
- };
1086
- /**
1087
- * The settlement tier: rows the exact-transfer path and the Movement scanner
1088
- * admit, each carrying the legacy `currency` projection those readers expect.
1089
- * Adding an owned test asset here would put candidate liquidity on the
1090
- * money-write path, so M03's rows go to the candidate tier below instead.
1091
- */
1092
- const CONFIGURED_MONEY_ASSETS = [{
1093
- ...USDX_ASSET,
1094
- currency: USDX_ASSET.peg
1095
- }];
1096
1073
  /**
1097
1074
  * The candidate tier: five owned test assets, every field read back from Base
1098
- * Sepolia after the deployment that created it (chain 84532, blocks
1099
- * 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
1075
+ * Sepolia after the deployment that created it (chain 84532, block
1076
+ * 46,690,061). Real contracts, authored liquidity, no settlement
1100
1077
  * admission — so rollback is removing consumers, never relabelling a
1101
1078
  * deployed address or deleting its history.
1102
1079
  *
@@ -1105,70 +1082,94 @@ const CONFIGURED_MONEY_ASSETS = [{
1105
1082
  * address returning the same symbol, which is why the resolvers below key on
1106
1083
  * `assetId`, or on chain plus address, and never on a symbol.
1107
1084
  */
1108
- const SYNTHETIC_TEST_ASSETS = [
1109
- {
1110
- assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
1085
+ const SYNTHETIC_TEST_ASSET_FIXTURES = {
1086
+ usdc: {
1087
+ assetId: assetIdFor(84532, "0x06ba8b863ca77490b0e7008427562bdbfcff730e"),
1111
1088
  chainId: toChainId(84532),
1112
- tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
1089
+ tokenAddress: "0x06ba8b863ca77490b0e7008427562bdbfcff730e",
1113
1090
  symbol: "USDC",
1114
1091
  decimals: 6,
1115
1092
  peg: toCurrencyCode("USD"),
1116
- deploymentStartBlock: 46465978,
1117
- deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
1093
+ deploymentStartBlock: 46690061,
1094
+ deploymentTransaction: "0x2661b7af5f62b9e31c707a59746cda65b90ba4448852317ac1968162fb94ff52",
1118
1095
  source: SYNTHETIC_TEST_ASSET_SOURCE
1119
1096
  },
1120
- {
1121
- assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
1097
+ usdt: {
1098
+ assetId: assetIdFor(84532, "0xcb5f361ae070e75d573941b8cd54995fa3876d75"),
1122
1099
  chainId: toChainId(84532),
1123
- tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
1100
+ tokenAddress: "0xcb5f361ae070e75d573941b8cd54995fa3876d75",
1124
1101
  symbol: "USDT",
1125
1102
  decimals: 6,
1126
1103
  peg: toCurrencyCode("USD"),
1127
- deploymentStartBlock: 46465979,
1128
- deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
1104
+ deploymentStartBlock: 46690061,
1105
+ deploymentTransaction: "0x98e5303f97974a31eddb61ad030072d900ce7283ce8080bfe0b9e5ea6f297749",
1129
1106
  source: SYNTHETIC_TEST_ASSET_SOURCE
1130
1107
  },
1131
- {
1132
- assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
1108
+ weth: {
1109
+ assetId: assetIdFor(84532, "0x85b7bd39ce3df9a85135e1e307225dd435f67e47"),
1133
1110
  chainId: toChainId(84532),
1134
- tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
1111
+ tokenAddress: "0x85b7bd39ce3df9a85135e1e307225dd435f67e47",
1135
1112
  symbol: "WETH",
1136
1113
  decimals: 18,
1137
1114
  peg: null,
1138
- deploymentStartBlock: 46465980,
1139
- deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
1115
+ deploymentStartBlock: 46690061,
1116
+ deploymentTransaction: "0x48dfacc01c15adc34f6c629aec5c9ff7bb84176d448017da0d0d4fdaf8b5a68a",
1140
1117
  source: SYNTHETIC_TEST_ASSET_SOURCE
1141
1118
  },
1142
- {
1143
- assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
1119
+ wbtc: {
1120
+ assetId: assetIdFor(84532, "0xcf433a81f507dcc88c2c404650ffafb40750b451"),
1144
1121
  chainId: toChainId(84532),
1145
- tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
1122
+ tokenAddress: "0xcf433a81f507dcc88c2c404650ffafb40750b451",
1146
1123
  symbol: "WBTC",
1147
1124
  decimals: 8,
1148
1125
  peg: null,
1149
- deploymentStartBlock: 46465981,
1150
- deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
1126
+ deploymentStartBlock: 46690061,
1127
+ deploymentTransaction: "0xa975fcda0a67509742243bbfc386032d2e0d6ca78404c255fae2e4ffae1c7f4d",
1151
1128
  source: SYNTHETIC_TEST_ASSET_SOURCE
1152
1129
  },
1153
- {
1154
- assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
1130
+ cngn: {
1131
+ assetId: assetIdFor(84532, "0xda515a7267e190710f0003f951c62bcfd70d6b2a"),
1155
1132
  chainId: toChainId(84532),
1156
- tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
1133
+ tokenAddress: "0xda515a7267e190710f0003f951c62bcfd70d6b2a",
1157
1134
  symbol: "cNGN",
1158
1135
  decimals: 6,
1159
1136
  peg: toCurrencyCode("NGN"),
1160
- deploymentStartBlock: 46465982,
1161
- deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
1137
+ deploymentStartBlock: 46690061,
1138
+ deploymentTransaction: "0x6f6b0302448c7cd7db5c118a8250e548c6e72821df373e5a27550e5cdfa635a1",
1162
1139
  source: SYNTHETIC_TEST_ASSET_SOURCE
1163
1140
  }
1141
+ };
1142
+ const SYNTHETIC_TEST_ASSETS = [
1143
+ SYNTHETIC_TEST_ASSET_FIXTURES.usdc,
1144
+ SYNTHETIC_TEST_ASSET_FIXTURES.usdt,
1145
+ SYNTHETIC_TEST_ASSET_FIXTURES.weth,
1146
+ SYNTHETIC_TEST_ASSET_FIXTURES.wbtc,
1147
+ SYNTHETIC_TEST_ASSET_FIXTURES.cngn
1164
1148
  ];
1165
1149
  toChainId(84532);
1166
1150
  /**
1151
+ * The settlement tier: the owned test assets admitted to the exact-transfer
1152
+ * path and to the treasury reads. USDC, USDT and WETH are equal assets.
1153
+ * There is no default token.
1154
+ */
1155
+ const SETTLEMENT_ASSETS_BASE_SEPOLIA = Object.freeze({
1156
+ CAPXUL_TEST_USDC: SYNTHETIC_TEST_ASSET_FIXTURES.usdc,
1157
+ CAPXUL_TEST_USDT: SYNTHETIC_TEST_ASSET_FIXTURES.usdt,
1158
+ CAPXUL_TEST_WETH: SYNTHETIC_TEST_ASSET_FIXTURES.weth
1159
+ });
1160
+ const CONFIGURED_MONEY_ASSETS = Object.freeze(Object.values(SETTLEMENT_ASSETS_BASE_SEPOLIA));
1161
+ /** Public AssetIds for the admitted Base Sepolia test assets. */
1162
+ const TOKENS = Object.freeze({ BASE_SEPOLIA: Object.freeze({
1163
+ CAPXUL_TEST_USDC: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_USDC.assetId,
1164
+ CAPXUL_TEST_USDT: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_USDT.assetId,
1165
+ CAPXUL_TEST_WETH: SETTLEMENT_ASSETS_BASE_SEPOLIA.CAPXUL_TEST_WETH.assetId
1166
+ }) });
1167
+ /**
1167
1168
  * Every known asset in ONE identity space. The tiers above declare admission;
1168
1169
  * this is the only list an identity resolves against, so an asset is never
1169
1170
  * two different things depending on which list a caller happened to read.
1170
1171
  */
1171
- const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
1172
+ const MONEY_ASSET_REGISTRY = [...SYNTHETIC_TEST_ASSETS];
1172
1173
  CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
1173
1174
  /** The registry row for `assetId` across every tier, or `null`. */
1174
1175
  function configuredMoneyAssetById(assetId) {
@@ -1222,7 +1223,7 @@ function normalizeSafeSaltEmail(email) {
1222
1223
  * `keccak256(namespace + ":" + normalizeSafeSaltEmail(email))`.
1223
1224
  *
1224
1225
  * Namespace defaults to `CAPXUL_SAFE_SALT_NAMESPACE`
1225
- * (`"capxul:staging:email:v1"`) — the current Capxul scheme. Pass a
1226
+ * (`"capxul:staging:email:v2"`) — the current Capxul scheme. Pass a
1226
1227
  * different namespace only for pinned pre-v2 artifacts
1227
1228
  * (`CAPXUL_SAFE_SALT_NAMESPACE_V1`, faucet Safe) or when bumping the scheme.
1228
1229
  *
@@ -1231,7 +1232,7 @@ function normalizeSafeSaltEmail(email) {
1231
1232
  * @returns The Capxul-namespaced salt nonce as a `bigint`
1232
1233
  */
1233
1234
  function computeCapxulSafeSaltNonce(input) {
1234
- const namespace = input.namespace ?? "capxul:staging:email:v1";
1235
+ const namespace = input.namespace ?? "capxul:staging:email:v2";
1235
1236
  const normalizedEmail = normalizeSafeSaltEmail(input.email);
1236
1237
  return BigInt(keccak256(toBytes(`${namespace}:${normalizedEmail}`)));
1237
1238
  }
@@ -1407,6 +1408,17 @@ function normalizeBindingEmail(email) {
1407
1408
  return normalizeSafeSaltEmail(email);
1408
1409
  }
1409
1410
  //#endregion
1411
+ //#region ../config/src/role-dsl.ts
1412
+ const ADMIN_ROLE_LABEL = "Admin";
1413
+ function normalizeOrgRoleLabel(label) {
1414
+ const normalized = label.trim().replace(/\s+/g, " ");
1415
+ if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
1416
+ return normalized;
1417
+ }
1418
+ function orgRoleKeyForLabel(label) {
1419
+ return keccak256(toBytes(normalizeOrgRoleLabel(label).toLowerCase()));
1420
+ }
1421
+ //#endregion
1410
1422
  //#region ../config/src/capxul-payments-v2.ts
1411
1423
  /** The exact compiled CapxulPaymentsV2 ABI. */
1412
1424
  const CAPXUL_PAYMENTS_V2_ABI = [
@@ -2052,38 +2064,6 @@ const CAPXUL_PAYMENTS_V2_ABI = [
2052
2064
  ];
2053
2065
  /** Immutable CapxulPaymentsV2 deployment on Base Sepolia. */
2054
2066
  const CAPXUL_PAYMENTS_V2_ADDRESS = "0x7a5c25aB7ce91d7a475B71cd1ec33E230291f8a4";
2055
- //#endregion
2056
- //#region ../config/src/route.ts
2057
- /** USDX on Base Sepolia — the only settlement token in v1. */
2058
- const USDX_BASE_SEPOLIA_TOKEN = {
2059
- chainId: BASE_SEPOLIA_CHAIN_ID,
2060
- address: USDX_ADDRESS_BASE_SEPOLIA,
2061
- decimals: 6
2062
- };
2063
- USDX_BASE_SEPOLIA_TOKEN.chainId;
2064
- USDX_BASE_SEPOLIA_TOKEN.chainId;
2065
- 10n ** BigInt(6);
2066
- /**
2067
- * The bootstrap manage role. It administers people and roles, cannot spend,
2068
- * and is not a signer of the Organization Safe; "Owner" implied the latter,
2069
- * so the label is Admin (ruled 2026-09-06).
2070
- */
2071
- const ADMIN_ROLE_LABEL = "Admin";
2072
- function normalizeOrgRoleLabel(label) {
2073
- const normalized = label.trim().replace(/\s+/g, " ");
2074
- if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
2075
- return normalized;
2076
- }
2077
- function orgRoleKeyForLabel(label) {
2078
- return keccak256(toBytes(normalizeOrgRoleLabel(label).toLowerCase()));
2079
- }
2080
- padHex(stringToHex("FM_DAILY"), {
2081
- size: 32,
2082
- dir: "right"
2083
- }), padHex(stringToHex("TL_DAILY"), {
2084
- size: 32,
2085
- dir: "right"
2086
- });
2087
2067
  padHex(concat([MULTI_SEND_CALL_ONLY, "0x8d80ff0a"]), {
2088
2068
  dir: "right",
2089
2069
  size: 32
@@ -2096,9 +2076,9 @@ function paymentsV2Signature(name) {
2096
2076
  paymentsV2Signature("send");
2097
2077
  paymentsV2Signature("sendBatch");
2098
2078
  paymentsV2Signature("createCommitment");
2099
- paymentsV2Signature("cancel");
2100
- paymentsV2Signature("redirect");
2101
- new Map([
2079
+ //#endregion
2080
+ //#region ../config/src/org-bootstrap.ts
2081
+ const ROLE_AUTHORITY_EVENT_BY_FUNCTION_SELECTOR = new Map([
2102
2082
  ["assignRoles(address,bytes32[],bool[])", "AssignRoles(address,bytes32[],bool[])"],
2103
2083
  ["allowTarget(bytes32,address,uint8)", "AllowTarget(bytes32,address,uint8)"],
2104
2084
  ["scopeTarget(bytes32,address)", "ScopeTarget(bytes32,address)"],
@@ -2108,6 +2088,8 @@ new Map([
2108
2088
  ["revokeFunction(bytes32,address,bytes4)", "RevokeFunction(bytes32,address,bytes4)"],
2109
2089
  ["setAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)", "SetAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)"]
2110
2090
  ].map(([functionSignature, eventSignature]) => [toFunctionSelector(functionSignature), toEventSelector(eventSignature)]));
2091
+ toEventSelector("AssignRoles(address,bytes32[],bool[])");
2092
+ new Set([...ROLE_AUTHORITY_EVENT_BY_FUNCTION_SELECTOR.values()].map((topic) => topic.toLowerCase()));
2111
2093
  //#endregion
2112
2094
  //#region src/ports/auth-client.ts
2113
2095
  var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
@@ -2485,4 +2467,4 @@ function readExchange(body) {
2485
2467
  };
2486
2468
  }
2487
2469
  //#endregion
2488
- export { toPartyId as $, SUPPORTED_CURRENCY_CODES as A, toAuthUserId as B, ASSET_ID_RE as C, isChainUpstream as Ct, COUNTRY_CODE_RE as D, isCredentialField as Dt, CLIENT_REQUEST_ID_RE as E, containsSensitiveMaterial as Et, toAccountId as F, toDurationMs as G, toChainId as H, toAddress as I, toEpochSeconds as J, toEmail as K, toAllowedOrigin as L, ZERO_BYTES32 as M, assetIdFor as N, EMAIL_RE as O, redactSecrets as Ot, currencySymbolFor as P, toOrgId as Q, toAppId as R, APP_ID_RE as S, failureFingerprint as St, CLIENT_GRANT_ID_RE as T, revertSummaryText as Tt, toCountryCode as U, toBudgetId as V, toCurrencyCode as W, toJwtToken as X, toHandle as Y, toKycTier as Z, normalizeBindingEmail as _, FAILURE_MODES as _t, parseAuthSession as a, toTesterKind as at, configuredMoneyAssetById as b, chainEvidenceLabel as bt, AuthCachePortTag as c, validateHandle as ct, authClientPortFromPromiseAdapter as d, CAPXUL_ERROR_CODES as dt, toPayrollGroupId as et, AuthClientError as f, CapxulError as ft, CAPXUL_PAYMENTS_V2_ADDRESS as g, CHAIN_UPSTREAMS as gt, orgRoleKeyForLabel as h, isCapxulError as ht, BrowserAuthCacheAdapter as i, toSessionToken as it, WEI_RE as j, EVM_ADDRESS_RE as k, redactUrlSecrets as kt, SystemClockLayer as l, HANDLE_RE as lt, ADMIN_ROLE_LABEL as m, Errors as mt, readClockNow as n, toPublishableKey as nt, parseCachedJwt as o, toTxHash as ot, AuthClientPortTag as p, EXPECTED_OPERATION_OUTCOMES as pt, toEpochMs as q, InMemoryAuthCacheAdapter as r, toRoleKey as rt, AuthCacheError as s, toWeiAmount as st, oauthBearerAuthClient as t, toPayrollRunId as tt, ClockPortTag as u, decodeConvexError as ut, BASE_SEPOLIA_CHAIN_ID as v, boundedResponseHeaders as vt, BYTES32_RE as w, isFailureMode as wt, ACCOUNT_ID_RE as x, decodeChainCause as xt, deriveCapxulSafeAddress as y, chainCauseProperties as yt, toAssetId as z };
2470
+ export { toJwtToken as $, COUNTRY_CODE_RE as A, revertSummaryText as At, toAllowedOrigin as B, configuredMoneyAssetById as C, boundedResponseHeaders as Ct, BYTES32_RE as D, failureFingerprint as Dt, ASSET_ID_RE as E, decodeChainCause as Et, ZERO_BYTES32 as F, toChainId as G, toAssetId as H, assetIdFor as I, toDurationMs as J, toCountryCode as K, currencySymbolFor as L, EVM_ADDRESS_RE as M, isCredentialField as Mt, SUPPORTED_CURRENCY_CODES as N, redactSecrets as Nt, CLIENT_GRANT_ID_RE as O, isChainUpstream as Ot, WEI_RE as P, redactUrlSecrets as Pt, toHandle as Q, toAccountId as R, TOKENS as S, FAILURE_MODES as St, APP_ID_RE as T, chainEvidenceLabel as Tt, toAuthUserId as U, toAppId as V, toBudgetId as W, toEpochMs as X, toEmail as Y, toEpochSeconds as Z, normalizeBindingEmail as _, CapxulError as _t, parseAuthSession as a, toPermissionAssignmentId as at, CONFIGURED_MONEY_ASSETS as b, isCapxulError as bt, AuthCachePortTag as c, toRoleKey as ct, authClientPortFromPromiseAdapter as d, toTxHash as dt, toKycTier as et, AuthClientError as f, toWeiAmount as ft, orgRoleKeyForLabel as g, CAPXUL_ERROR_CODES as gt, ADMIN_ROLE_LABEL as h, decodeConvexError as ht, BrowserAuthCacheAdapter as i, toPayrollRunId as it, EMAIL_RE as j, containsSensitiveMaterial as jt, CLIENT_REQUEST_ID_RE as k, isFailureMode as kt, SystemClockLayer as l, toSessionToken as lt, CAPXUL_PAYMENTS_V2_ADDRESS as m, HANDLE_RE as mt, readClockNow as n, toPartyId as nt, parseCachedJwt as o, toPermissionId as ot, AuthClientPortTag as p, validateHandle as pt, toCurrencyCode as q, InMemoryAuthCacheAdapter as r, toPayrollGroupId as rt, AuthCacheError as s, toPublishableKey as st, oauthBearerAuthClient as t, toOrgId as tt, ClockPortTag as u, toTesterKind as ut, BASE_SEPOLIA_CHAIN_ID as v, EXPECTED_OPERATION_OUTCOMES as vt, ACCOUNT_ID_RE as w, chainCauseProperties as wt, SYNTHETIC_TEST_ASSET_FIXTURES as x, CHAIN_UPSTREAMS as xt, deriveCapxulSafeAddress as y, Errors as yt, toAddress as z };
@@ -254,6 +254,7 @@ declare const Errors: {
254
254
  readonly method: string;
255
255
  readonly currentState: string;
256
256
  readonly validStates: readonly string[];
257
+ readonly organizationId?: string;
257
258
  }) => CapxulError;
258
259
  };
259
260
  //#endregion
@@ -310,10 +311,27 @@ type Money = {
310
311
  };
311
312
  type Account$1 = {
312
313
  readonly id: AccountId;
313
- readonly balance: Money;
314
- readonly available: Money;
314
+ readonly balances: readonly AccountPosition[];
315
+ /** Fiat valuation of `balances`. `null` means at least one position has no rate. */
316
+ readonly balance: Money | null;
317
+ readonly available: Money | null;
318
+ };
319
+ /**
320
+ * One asset position on an Account. `asset` is the identity; `symbol` and
321
+ * `decimals` are display metadata. `balance` and `available` are major-unit
322
+ * decimal strings at `decimals`, never a fiat `Money`.
323
+ */
324
+ type AccountPosition = {
325
+ readonly asset: AssetId;
326
+ readonly symbol: string;
327
+ readonly decimals: number;
328
+ readonly balance: string;
329
+ readonly available: string;
315
330
  };
316
331
  type AuthUserId = Brand<string, "AuthUserId">;
332
+ /** Public native connection identifiers; never bearer credentials. */
333
+ type ClientRequestId = Brand<string, "ClientRequestId">;
334
+ type ClientGrantId = Brand<string, "ClientGrantId">;
317
335
  type AnonymousDistinctId = Brand<string, "AnonymousDistinctId">;
318
336
  type AccountId = Brand<string, "AccountId">;
319
337
  type PartyId = Brand<string, "PartyId">;
@@ -374,8 +392,10 @@ declare const SUPPORTED_CURRENCIES: readonly [{
374
392
  }];
375
393
  type SupportedCurrencyCode = (typeof SUPPORTED_CURRENCIES)[number]["code"];
376
394
  declare function toAddress(raw: unknown): Address$1;
395
+ declare function toAccountId(raw: unknown): AccountId;
377
396
  declare function toPartyId(raw: unknown): PartyId;
378
397
  declare function toHandle(raw: unknown): Handle;
398
+ declare function toOrgId(raw: unknown): OrgId;
379
399
  declare function toCountryCode(raw: unknown): CountryCode;
380
400
  //#endregion
381
401
  //#region ../config/src/capxul-payments-v2.d.ts
@@ -625,4 +645,4 @@ interface OAuthBearerAuthClientInput {
625
645
  */
626
646
  declare function oauthBearerAuthClient(input: OAuthBearerAuthClientInput): AuthClientPort;
627
647
  //#endregion
628
- export { PayrollGroupId as $, AllowanceKey as A, ChainId as B, AuthCachePort as C, ChainCause as Ct, Account$1 as D, RevertSummary as Dt, CAPXUL_PAYMENTS_V2_ADDRESS as E, FailureMode as Et, AssetId as F, EVM_ADDRESS_RE as G, CurrencyCode as H, AuthSession as I, Handle as J, Email as K, AuthUserId as L, AnonymousDistinctId as M, AppId as N, AccountId as O, AssetAmount as P, PaymentCommandId as Q, BlockNumber as R, AuthCacheError as S, CHAIN_UPSTREAMS as St, CachedJwt as T, FAILURE_MODES as Tt, DocumentHash as U, CountryCode as V, DurationMs as W, OrgId as X, Money as Y, PartyId as Z, Session as _, CapxulErrorCode as _t, Eip1193RequestProvider as a, RoleKey as at, AuthClientPort as b, Failure as bt, injectedWalletSigner as c, TesterKind as ct, AccountRequirement as d, toAddress as dt, PayrollRunId as et, Eip1193Provider as f, toCountryCode as ft, Profile as g, CapxulError as gt, CapxulResult as h, CAPXUL_ERROR_CODES as ht, CapxulSigner as i, PublishableKey as it, AllowedOrigin as j, Address$1 as k, AccountProvider as l, TxHash as lt, localPrivateKeyAccountProvider as m, toPartyId as mt, oauthBearerAuthClient as n, PermissionId as nt, SignerStatus as o, SessionToken as ot, eip1193AccountProvider as p, toHandle as pt, EpochMs as q, CapxulDigestSigner as r, Profile$1 as rt, SignerStatusStore as s, SmartAccount$1 as st, OAuthBearerAuthClientInput as t, PermissionAssignmentId as tt, AccountProviderSource as u, WeiAmount as ut, SmartAccount as v, CapxulErrorDetails as vt, AuthCachePortTag as w, ChainUpstream as wt, CanSendOtpStatus as x, isCapxulError as xt, ClockPort as y, Errors as yt, BudgetId as z };
648
+ export { OrgId as $, Address$1 as A, FAILURE_MODES as At, BudgetId as B, AuthCachePort as C, CapxulErrorDetails as Ct, Account$1 as D, CHAIN_UPSTREAMS as Dt, CAPXUL_PAYMENTS_V2_ADDRESS as E, isCapxulError as Et, AssetAmount as F, CurrencyCode as G, ClientGrantId as H, AssetId as I, EVM_ADDRESS_RE as J, DocumentHash as K, AuthSession as L, AllowedOrigin as M, RevertSummary as Mt, AnonymousDistinctId as N, AccountId as O, ChainCause as Ot, AppId as P, Money as Q, AuthUserId as R, AuthCacheError as S, CapxulErrorCode as St, CachedJwt as T, Failure as Tt, ClientRequestId as U, ChainId as V, CountryCode as W, EpochMs as X, Email as Y, Handle as Z, Session as _, toHandle as _t, Eip1193RequestProvider as a, PermissionId as at, AuthClientPort as b, CAPXUL_ERROR_CODES as bt, injectedWalletSigner as c, RoleKey as ct, AccountRequirement as d, TesterKind as dt, PartyId as et, Eip1193Provider as f, TxHash as ft, Profile as g, toCountryCode as gt, CapxulResult as h, toAddress as ht, CapxulSigner as i, PermissionAssignmentId as it, AllowanceKey as j, FailureMode as jt, AccountPosition as k, ChainUpstream as kt, AccountProvider as l, SessionToken as lt, localPrivateKeyAccountProvider as m, toAccountId as mt, oauthBearerAuthClient as n, PayrollGroupId as nt, SignerStatus as o, Profile$1 as ot, eip1193AccountProvider as p, WeiAmount as pt, DurationMs as q, CapxulDigestSigner as r, PayrollRunId as rt, SignerStatusStore as s, PublishableKey as st, OAuthBearerAuthClientInput as t, PaymentCommandId as tt, AccountProviderSource as u, SmartAccount$1 as ut, SmartAccount as v, toOrgId as vt, AuthCachePortTag as w, Errors as wt, CanSendOtpStatus as x, CapxulError as xt, ClockPort as y, toPartyId as yt, BlockNumber as z };