@poa-box/core 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +2 -0
  2. package/dist/abis/AuthorityRouter.d.ts +448 -0
  3. package/dist/abis/AuthorityRouter.js +584 -0
  4. package/dist/abis/CutoverVerifier.d.ts +194 -0
  5. package/dist/abis/CutoverVerifier.js +259 -0
  6. package/dist/abis/DirectDemocracyVotingNew.d.ts +168 -30
  7. package/dist/abis/DirectDemocracyVotingNew.js +214 -35
  8. package/dist/abis/EducationHubNew.d.ts +32 -75
  9. package/dist/abis/EducationHubNew.js +38 -94
  10. package/dist/abis/Executor.d.ts +56 -86
  11. package/dist/abis/Executor.js +68 -104
  12. package/dist/abis/HybridVotingNew.d.ts +279 -35
  13. package/dist/abis/HybridVotingNew.js +357 -40
  14. package/dist/abis/{EligibilityModuleNew.d.ts → MembershipAuthority.d.ts} +1437 -718
  15. package/dist/abis/{EligibilityModuleNew.js → MembershipAuthority.js} +1821 -899
  16. package/dist/abis/OrgDeployerNew.d.ts +193 -127
  17. package/dist/abis/OrgDeployerNew.js +244 -160
  18. package/dist/abis/OrgRegistry.d.ts +40 -0
  19. package/dist/abis/OrgRegistry.js +51 -0
  20. package/dist/abis/ParticipationToken.d.ts +32 -75
  21. package/dist/abis/ParticipationToken.js +38 -94
  22. package/dist/abis/PasskeyAccount.d.ts +28 -0
  23. package/dist/abis/PasskeyAccount.js +35 -0
  24. package/dist/abis/PaymasterHub.d.ts +527 -47
  25. package/dist/abis/PaymasterHub.js +750 -135
  26. package/dist/abis/QuickJoinNew.d.ts +59 -170
  27. package/dist/abis/QuickJoinNew.js +71 -215
  28. package/dist/abis/TaskManagerNew.d.ts +27 -44
  29. package/dist/abis/TaskManagerNew.js +31 -52
  30. package/dist/abis/UniversalAccountRegistry.d.ts +24 -0
  31. package/dist/abis/UniversalAccountRegistry.js +30 -0
  32. package/dist/abis/ZkEmailInvites.d.ts +44 -0
  33. package/dist/abis/ZkEmailInvites.js +55 -0
  34. package/dist/abis/index.d.ts +6 -4
  35. package/dist/abis/index.js +10 -7
  36. package/dist/chains.js +3 -3
  37. package/dist/error-catalog.js +125 -7
  38. package/dist/graph/documents/activity.d.ts +3 -1
  39. package/dist/graph/documents/activity.js +19 -14
  40. package/dist/graph/documents/org.d.ts +4 -4
  41. package/dist/graph/documents/org.js +10 -4
  42. package/dist/graph/documents/task.d.ts +10 -1
  43. package/dist/graph/documents/task.js +157 -3
  44. package/dist/graph/documents/user.d.ts +5 -30
  45. package/dist/graph/documents/user.js +2 -42
  46. package/dist/graph/documents/voting-classes.d.ts +23 -2
  47. package/dist/graph/documents/voting-classes.js +119 -10
  48. package/dist/metadata/index.d.ts +0 -1
  49. package/dist/metadata/index.js +1 -2
  50. package/dist/perms.d.ts +0 -15
  51. package/dist/perms.js +1 -21
  52. package/dist/preflight.d.ts +2 -2
  53. package/dist/preflight.js +14 -22
  54. package/dist/reads/authority.d.ts +33 -0
  55. package/dist/reads/authority.js +196 -0
  56. package/dist/reads/index.d.ts +1 -1
  57. package/dist/reads/index.js +2 -2
  58. package/dist/reads/org.d.ts +20 -20
  59. package/dist/reads/org.js +94 -28
  60. package/dist/reads/project.js +3 -3
  61. package/dist/reads/resolve.d.ts +1 -1
  62. package/dist/reads/resolve.js +12 -3
  63. package/dist/reads/task.d.ts +44 -22
  64. package/dist/reads/task.js +126 -67
  65. package/dist/reads/user.d.ts +8 -43
  66. package/dist/reads/user.js +27 -44
  67. package/dist/reads/vote.d.ts +26 -2
  68. package/dist/reads/vote.js +104 -23
  69. package/dist/tx/authority.d.ts +33 -0
  70. package/dist/tx/authority.js +62 -0
  71. package/dist/tx/governance.d.ts +6 -0
  72. package/dist/tx/governance.js +20 -2
  73. package/dist/tx/index.d.ts +1 -1
  74. package/dist/tx/index.js +2 -2
  75. package/dist/tx/org.d.ts +23 -18
  76. package/dist/tx/org.js +203 -67
  77. package/dist/tx/project.d.ts +3 -2
  78. package/dist/tx/project.js +28 -10
  79. package/dist/tx/task.d.ts +13 -24
  80. package/dist/tx/task.js +45 -85
  81. package/dist/tx/user.d.ts +2 -40
  82. package/dist/tx/user.js +2 -76
  83. package/dist/tx/vote.d.ts +5 -0
  84. package/dist/tx/vote.js +54 -43
  85. package/dist/version.d.ts +0 -5
  86. package/dist/version.js +1 -13
  87. package/package.json +1 -1
  88. package/dist/abis/ToggleModule.d.ts +0 -193
  89. package/dist/abis/ToggleModule.js +0 -255
  90. package/dist/metadata/role.d.ts +0 -38
  91. package/dist/metadata/role.js +0 -35
  92. package/dist/reads/eligibility.d.ts +0 -377
  93. package/dist/reads/eligibility.js +0 -687
  94. package/dist/tx/eligibility.d.ts +0 -511
  95. package/dist/tx/eligibility.js +0 -818
package/dist/perms.js CHANGED
@@ -5,11 +5,10 @@
5
5
  * TaskManager contract's per-member permission system.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.VOUCH_FLAG_COMBINE_HIERARCHY = exports.VOUCH_FLAG_ENABLED = exports.PERM_BITS = void 0;
8
+ exports.PERM_BITS = void 0;
9
9
  exports.parsePermList = parsePermList;
10
10
  exports.formatMask = formatMask;
11
11
  exports.describeMask = describeMask;
12
- exports.vouchConflictsWithDefaultEligibility = vouchConflictsWithDefaultEligibility;
13
12
  const errors_1 = require("./errors");
14
13
  exports.PERM_BITS = {
15
14
  create: 1,
@@ -99,22 +98,3 @@ function describeMask(mask) {
99
98
  }
100
99
  return sentences;
101
100
  }
102
- /**
103
- * EligibilityModule vouch-config flag bits (EligibilityModule.sol:156-157).
104
- * Packed into the `flags` byte of the VouchConfig tuple returned by getVouchConfig.
105
- */
106
- exports.VOUCH_FLAG_ENABLED = 0x01;
107
- exports.VOUCH_FLAG_COMBINE_HIERARCHY = 0x02;
108
- /**
109
- * Would enabling vouching and default-eligibility on the same hat conflict?
110
- *
111
- * Audit M-03 made these mutually exclusive in BOTH directions: a hat that everyone is eligible
112
- * for by default makes a vouch quorum a no-op, so the contract reverts
113
- * DefaultEligibilityConflictsWithVouch. The guard only fires when vouching is enabled AND
114
- * combineWithHierarchy is set — vouching alone, or without combine, is fine.
115
- */
116
- function vouchConflictsWithDefaultEligibility(vouchFlags, vouchQuorum, defaultEligible) {
117
- const enabled = (vouchFlags & exports.VOUCH_FLAG_ENABLED) !== 0 && vouchQuorum > 0;
118
- const combine = (vouchFlags & exports.VOUCH_FLAG_COMBINE_HIERARCHY) !== 0;
119
- return enabled && combine && defaultEligible;
120
- }
@@ -41,8 +41,8 @@ export declare function runPreflight(provider: ethers.providers.Provider, checks
41
41
  * (the CLI resolves it from env via resolveSponsoredConfig).
42
42
  */
43
43
  export declare function checkGasBalance(address: string, minWei?: ethers.BigNumber, sponsored?: SponsoredConfig): PreflightCheck;
44
- /** Wearer holds the given hat (IHats balanceOf(wearer, hatId) > 0). */
45
- export declare function checkHasHat(hatsAddress: string, wearer: string, hatId: ethers.BigNumberish): PreflightCheck;
44
+ /** Live accepted-and-eligible membership check for transaction preflight. */
45
+ export declare function checkSubjectMembership(authorityAddress: string, user: string, subject: ethers.BigNumberish): PreflightCheck;
46
46
  /** Username is unregistered (UAR getAddressOfUsername returns the zero address). */
47
47
  export declare function checkUsernameFree(registryAddr: string, username: string): PreflightCheck;
48
48
  /**
package/dist/preflight.js CHANGED
@@ -10,7 +10,7 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.runPreflight = runPreflight;
12
12
  exports.checkGasBalance = checkGasBalance;
13
- exports.checkHasHat = checkHasHat;
13
+ exports.checkSubjectMembership = checkSubjectMembership;
14
14
  exports.checkUsernameFree = checkUsernameFree;
15
15
  exports.checkTaskStatus = checkTaskStatus;
16
16
  exports.checkProposalActive = checkProposalActive;
@@ -122,29 +122,21 @@ function checkGasBalance(address, minWei, sponsored) {
122
122
  },
123
123
  };
124
124
  }
125
- const HATS_IFACE = new ethers_1.ethers.utils.Interface([
126
- 'function balanceOf(address wearer, uint256 hatId) view returns (uint256 balance)',
125
+ const AUTHORITY_MEMBERSHIP_IFACE = new ethers_1.ethers.utils.Interface([
126
+ 'function isMember(uint256 subject,address user) view returns (bool)',
127
127
  ]);
128
- /** Wearer holds the given hat (IHats balanceOf(wearer, hatId) > 0). */
129
- function checkHasHat(hatsAddress, wearer, hatId) {
130
- const id = ethers_1.ethers.BigNumber.from(hatId);
131
- return {
132
- label: 'role (hat)',
133
- call: { to: hatsAddress, data: HATS_IFACE.encodeFunctionData('balanceOf', [wearer, id]) },
134
- interpret: (returnData, success) => {
135
- if (!success || !returnData || returnData === '0x') {
136
- return { ok: false, detail: 'could not read hat balance', suggestion: 'verify the Hats contract address' };
137
- }
138
- const balance = HATS_IFACE.decodeFunctionResult('balanceOf', returnData)[0];
139
- if (!balance.isZero())
128
+ /** Live accepted-and-eligible membership check for transaction preflight. */
129
+ function checkSubjectMembership(authorityAddress, user, subject) {
130
+ const id = ethers_1.ethers.BigNumber.from(subject);
131
+ return { label: 'authority membership',
132
+ call: { to: authorityAddress, data: AUTHORITY_MEMBERSHIP_IFACE.encodeFunctionData('isMember', [id, user]) },
133
+ interpret: (data, success) => {
134
+ if (!success || !data || data === '0x')
135
+ return { ok: false, detail: 'Could not read MembershipAuthority membership' };
136
+ if (AUTHORITY_MEMBERSHIP_IFACE.decodeFunctionResult('isMember', data)[0])
140
137
  return { ok: true };
141
- return {
142
- ok: false,
143
- detail: `${wearer} does not wear hat ${id.toString()}`,
144
- suggestion: 'list org roles with pop org roles, then request one via pop role apply',
145
- };
146
- },
147
- };
138
+ return { ok: false, detail: `${user} is not a member of subject ${id.toString()}`, suggestion: 'List roles with pop org roles and accept eligible roles with pop role claim' };
139
+ } };
148
140
  }
149
141
  let uarInterface = null;
150
142
  function getUarInterface() {
@@ -0,0 +1,33 @@
1
+ /** Current membership and permissions come exclusively from MembershipAuthority.
2
+ * Historical Role/User/task/proposal entities retain their original IDs. */
3
+ import type { GraphClient } from '../graph/client';
4
+ export interface AuthorityReadiness {
5
+ membershipAuthority?: {
6
+ id?: string;
7
+ isRouterBound?: boolean;
8
+ cutoverAt?: string | null;
9
+ } | null;
10
+ }
11
+ export declare function isAuthorityReady(org: AuthorityReadiness | null | undefined): boolean;
12
+ export declare const FETCH_ORG_AUTHORITY = "query OrgAuthority($id: Bytes!) {\n organization(id: $id) { id name membershipAuthority { id isRouterBound cutoverAt paused } }\n}";
13
+ export declare const FETCH_AUTHORITY_SUBJECTS = "query AuthoritySubjects($orgId: Bytes!, $lastId: String!) {\n subjects(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {\n id subjectId kind name metadataCID imageURI maxMembers memberCount activeMemberCount defaultAllow\n vouchConfig { quorum voucherSubjectId epoch }\n managerConfig { managerSubjectId caps delaySecs }\n memberRoles(first: 1000, where: { isActive: true }) { role { id subjectId name } }\n }\n}";
14
+ export declare const FETCH_AUTHORITY_MEMBERSHIPS = "query AuthorityMemberships($orgId: Bytes!, $lastId: String!) {\n subjectMemberships(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {\n id user userUsername accepted eligible isMember claimable acceptedAt seededWhilePaused\n ruleKind eligibilitySource emailVerified vouchCount vouchEpoch vouchMet\n rule { kind author delegable sticky }\n subject { id subjectId kind name defaultAllow vouchConfig { quorum epoch } }\n pendingAction { id pendingId action activatesAt status }\n }\n}";
15
+ /** Delegated offers are consumed by claim; only grants/removals use finalize. */
16
+ export declare const FETCH_AUTHORITY_PENDING = "query AuthorityPendingActions($orgId: Bytes!, $lastId: String!) {\n pendingActions(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {\n id pendingId action user actor actorUsername activatesAt status createdAt resolvedAt cancelledBy transactionHash\n subject { id subjectId kind name }\n }\n}";
17
+ export declare const FETCH_AUTHORITY_PERMS = "query AuthorityPerms($orgId: Bytes!, $lastId: String!) {\n permRows(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {\n id subject { id subjectId name kind } permKey ctx word value exists inheritGlobal\n }\n}";
18
+ export declare const FETCH_AUTHORITY_VOUCHES = "query AuthorityVouches($orgId: Bytes!, $lastId: String!) {\n subjectVouchRecords(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {\n id subject { id subjectId } user voucher voucherUsername active epoch seeded vouchedAt\n config { epoch quorum voucherSubjectId }\n }\n}";
19
+ /** Cursor pagination avoids presenting a capped first page as a complete permission/member list. */
20
+ export declare function readAuthorityRows(client: GraphClient, orgId: string, document: string, field: string, chainId?: number): Promise<any[]>;
21
+ /** The index deliberately re-folds only ACCEPTED rows after subject-level configuration changes.
22
+ * Recompute the eligibility projection from current inputs so unaccepted offers/renounced seats do
23
+ * not retain a stale default or vouch quorum. `claimable` means eligible and unaccepted only: the
24
+ * pause, capacity and pending-offer checks still apply when the user actually calls claim(). */
25
+ export declare function projectAuthorityMembership(row: any): any;
26
+ export declare function readAuthorityMemberships(client: GraphClient, orgId: string, chainId?: number): Promise<any[]>;
27
+ /** Low-level projection over already complete history. Callers supplying authority memberships must
28
+ * pass the org executor and retired eligibility addresses in systemAddresses. Prefer readAuthorityUsers
29
+ * to resolve that identity context and all history pages automatically. The optional argument keeps
30
+ * the existing pure-helper API compatible; it cannot infer system identities from a subject's name.
31
+ * An absent User entity is unknown history, not evidence of a zero balance or a new join. */
32
+ export declare function projectAuthorityUsers(users: any[], memberships: any[], systemAddresses?: readonly string[]): any[];
33
+ export declare function readAuthorityUsers(client: GraphClient, orgId: string, users: any[], chainId?: number): Promise<any[]>;
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FETCH_AUTHORITY_VOUCHES = exports.FETCH_AUTHORITY_PERMS = exports.FETCH_AUTHORITY_PENDING = exports.FETCH_AUTHORITY_MEMBERSHIPS = exports.FETCH_AUTHORITY_SUBJECTS = exports.FETCH_ORG_AUTHORITY = void 0;
4
+ exports.isAuthorityReady = isAuthorityReady;
5
+ exports.readAuthorityRows = readAuthorityRows;
6
+ exports.projectAuthorityMembership = projectAuthorityMembership;
7
+ exports.readAuthorityMemberships = readAuthorityMemberships;
8
+ exports.projectAuthorityUsers = projectAuthorityUsers;
9
+ exports.readAuthorityUsers = readAuthorityUsers;
10
+ function isAuthorityReady(org) {
11
+ const authority = org?.membershipAuthority;
12
+ return !!authority?.id && /^0x[0-9a-f]{40}$/i.test(authority.id) && !/^0x0{40}$/i.test(authority.id)
13
+ && authority.isRouterBound === true && authority.cutoverAt != null && /^\d+$/.test(String(authority.cutoverAt)) && BigInt(authority.cutoverAt) > 0n;
14
+ }
15
+ exports.FETCH_ORG_AUTHORITY = `query OrgAuthority($id: Bytes!) {
16
+ organization(id: $id) { id name membershipAuthority { id isRouterBound cutoverAt paused } }
17
+ }`;
18
+ exports.FETCH_AUTHORITY_SUBJECTS = `query AuthoritySubjects($orgId: Bytes!, $lastId: String!) {
19
+ subjects(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
20
+ id subjectId kind name metadataCID imageURI maxMembers memberCount activeMemberCount defaultAllow
21
+ vouchConfig { quorum voucherSubjectId epoch }
22
+ managerConfig { managerSubjectId caps delaySecs }
23
+ memberRoles(first: 1000, where: { isActive: true }) { role { id subjectId name } }
24
+ }
25
+ }`;
26
+ exports.FETCH_AUTHORITY_MEMBERSHIPS = `query AuthorityMemberships($orgId: Bytes!, $lastId: String!) {
27
+ subjectMemberships(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
28
+ id user userUsername accepted eligible isMember claimable acceptedAt seededWhilePaused
29
+ ruleKind eligibilitySource emailVerified vouchCount vouchEpoch vouchMet
30
+ rule { kind author delegable sticky }
31
+ subject { id subjectId kind name defaultAllow vouchConfig { quorum epoch } }
32
+ pendingAction { id pendingId action activatesAt status }
33
+ }
34
+ }`;
35
+ /** Delegated offers are consumed by claim; only grants/removals use finalize. */
36
+ exports.FETCH_AUTHORITY_PENDING = `query AuthorityPendingActions($orgId: Bytes!, $lastId: String!) {
37
+ pendingActions(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
38
+ id pendingId action user actor actorUsername activatesAt status createdAt resolvedAt cancelledBy transactionHash
39
+ subject { id subjectId kind name }
40
+ }
41
+ }`;
42
+ exports.FETCH_AUTHORITY_PERMS = `query AuthorityPerms($orgId: Bytes!, $lastId: String!) {
43
+ permRows(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
44
+ id subject { id subjectId name kind } permKey ctx word value exists inheritGlobal
45
+ }
46
+ }`;
47
+ exports.FETCH_AUTHORITY_VOUCHES = `query AuthorityVouches($orgId: Bytes!, $lastId: String!) {
48
+ subjectVouchRecords(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
49
+ id subject { id subjectId } user voucher voucherUsername active epoch seeded vouchedAt
50
+ config { epoch quorum voucherSubjectId }
51
+ }
52
+ }`;
53
+ /** Cursor pagination avoids presenting a capped first page as a complete permission/member list. */
54
+ async function readAuthorityRows(client, orgId, document, field, chainId) {
55
+ const rows = [];
56
+ let lastId = '';
57
+ for (;;) {
58
+ const result = await client.query(document, { orgId, lastId }, chainId);
59
+ const page = result[field];
60
+ if (!Array.isArray(page))
61
+ throw new Error(`MembershipAuthority index unavailable: missing ${field}. Update the subgraph; legacy fallback is unsupported.`);
62
+ rows.push(...page);
63
+ if (page.length < 1000)
64
+ return rows;
65
+ const next = String(page[page.length - 1].id);
66
+ if (next <= lastId)
67
+ throw new Error('MembershipAuthority pagination did not advance');
68
+ lastId = next;
69
+ }
70
+ }
71
+ /** The index deliberately re-folds only ACCEPTED rows after subject-level configuration changes.
72
+ * Recompute the eligibility projection from current inputs so unaccepted offers/renounced seats do
73
+ * not retain a stale default or vouch quorum. `claimable` means eligible and unaccepted only: the
74
+ * pause, capacity and pending-offer checks still apply when the user actually calls claim(). */
75
+ function projectAuthorityMembership(row) {
76
+ const subject = row.subject;
77
+ if (subject?.kind !== 'Role' || typeof subject.defaultAllow !== 'boolean'
78
+ || typeof row.accepted !== 'boolean' || typeof row.emailVerified !== 'boolean'
79
+ || !['None', 'Grant', 'Ban'].includes(row.ruleKind)
80
+ || !Object.prototype.hasOwnProperty.call(subject, 'vouchConfig')) {
81
+ // Partial caller-supplied rows cannot be re-folded. Preserve them as explicitly cached data;
82
+ // the complete FETCH_AUTHORITY_MEMBERSHIPS document always supplies these non-null inputs.
83
+ return { ...row, membershipProjection: 'indexed-cache' };
84
+ }
85
+ const config = subject.vouchConfig;
86
+ const effectiveVouchCount = config && String(row.vouchEpoch) === String(config.epoch)
87
+ ? Number(row.vouchCount) : 0;
88
+ const vouchMet = Number(config?.quorum ?? 0) > 0 && effectiveVouchCount >= Number(config.quorum);
89
+ const eligibilitySource = row.ruleKind === 'Ban' ? 'ExplicitBan'
90
+ : row.ruleKind === 'Grant' ? 'ExplicitGrant'
91
+ : row.emailVerified ? 'EmailVerified'
92
+ : vouchMet ? 'VouchQuorum'
93
+ : subject.defaultAllow ? 'SubjectDefault' : 'None';
94
+ const eligible = eligibilitySource !== 'ExplicitBan' && eligibilitySource !== 'None';
95
+ return { ...row,
96
+ indexedEligibility: { eligible: row.eligible, isMember: row.isMember, claimable: row.claimable,
97
+ eligibilitySource: row.eligibilitySource, vouchMet: row.vouchMet },
98
+ eligible, isMember: row.accepted && eligible, claimable: !row.accepted && eligible,
99
+ eligibilitySource, vouchMet, effectiveVouchCount, membershipProjection: 'current-authority-inputs',
100
+ };
101
+ }
102
+ async function readAuthorityMemberships(client, orgId, chainId) {
103
+ return (await readAuthorityRows(client, orgId, exports.FETCH_AUTHORITY_MEMBERSHIPS, 'subjectMemberships', chainId))
104
+ .map(projectAuthorityMembership);
105
+ }
106
+ /** Historical users are paginated separately from memberships: either collection can exceed a page.
107
+ * The retired eligibility address is identity metadata only, matching the subgraph's system-user
108
+ * exclusions. It never supplies a current membership or permission decision. */
109
+ const AUTHORITY_USER_HISTORY = `query AuthorityUserHistory($orgId: Bytes!, $lastId: String!) {
110
+ organization(id: $orgId) { executorContract { id } eligibilityModule { id } }
111
+ users(first: 1000, orderBy: id, where: { organization: $orgId, id_gt: $lastId }) {
112
+ id address participationTokenBalance totalTasksCompleted totalTasksCancelled totalVotes totalModulesCompleted
113
+ totalClaimsAmount totalPaymentsAmount totalTokenRequestsAmount
114
+ firstSeenAt firstSeenAtBlock lastActiveAt lastActiveAtBlock joinMethod account { username }
115
+ }
116
+ }`;
117
+ async function readUserHistory(client, orgId, chainId) {
118
+ const users = [];
119
+ let lastId = '';
120
+ let systemAddresses = [];
121
+ for (;;) {
122
+ const result = await client.query(AUTHORITY_USER_HISTORY, { orgId, lastId }, chainId);
123
+ if (!result.organization || !Array.isArray(result.users))
124
+ throw new Error('Organization user history is unavailable');
125
+ systemAddresses = [result.organization.executorContract?.id, result.organization.eligibilityModule?.id].filter(Boolean);
126
+ users.push(...result.users);
127
+ if (result.users.length < 1000)
128
+ return { users, systemAddresses };
129
+ const next = String(result.users[result.users.length - 1].id);
130
+ if (next <= lastId)
131
+ throw new Error('Organization user history pagination did not advance');
132
+ lastId = next;
133
+ }
134
+ }
135
+ /** Low-level projection over already complete history. Callers supplying authority memberships must
136
+ * pass the org executor and retired eligibility addresses in systemAddresses. Prefer readAuthorityUsers
137
+ * to resolve that identity context and all history pages automatically. The optional argument keeps
138
+ * the existing pure-helper API compatible; it cannot infer system identities from a subject's name.
139
+ * An absent User entity is unknown history, not evidence of a zero balance or a new join. */
140
+ function projectAuthorityUsers(users, memberships, systemAddresses = []) {
141
+ const excluded = new Set(systemAddresses.map(address => address.toLowerCase()));
142
+ const rows = new Map();
143
+ for (const user of users) {
144
+ const address = user.address.toLowerCase();
145
+ if (excluded.has(address))
146
+ continue;
147
+ const previous = rows.get(address);
148
+ const account = previous?.account && user.account
149
+ ? { ...previous.account, ...user.account }
150
+ : user.account === undefined ? previous?.account : user.account;
151
+ rows.set(address, { ...previous, ...user, account, historyIndexed: user.historyIndexed ?? true,
152
+ currentHatIds: [], subjects: [], membershipStatus: 'Inactive' });
153
+ }
154
+ for (const membership of memberships) {
155
+ if (membership.subject.kind !== 'Role')
156
+ continue;
157
+ const address = membership.user.toLowerCase();
158
+ if (excluded.has(address))
159
+ continue;
160
+ if (!rows.has(address))
161
+ rows.set(address, {
162
+ address, account: membership.userUsername ? { username: membership.userUsername } : null,
163
+ participationTokenBalance: null, totalTasksCompleted: null, totalVotes: null,
164
+ totalModulesCompleted: null, firstSeenAt: null, lastActiveAt: null, joinMethod: null,
165
+ historyIndexed: false, currentHatIds: [], subjects: [], membershipStatus: 'Inactive',
166
+ });
167
+ const row = rows.get(address);
168
+ row.subjects.push(membership);
169
+ if (membership.isMember) {
170
+ if (!row.currentHatIds.includes(membership.subject.subjectId))
171
+ row.currentHatIds.push(membership.subject.subjectId);
172
+ row.membershipStatus = 'Active';
173
+ }
174
+ }
175
+ return [...rows.values()];
176
+ }
177
+ async function readAuthorityUsers(client, orgId, users, chainId) {
178
+ const [history, memberships] = await Promise.all([
179
+ readUserHistory(client, orgId, chainId),
180
+ readAuthorityMemberships(client, orgId, chainId),
181
+ ]);
182
+ // Keep richer caller-specific fields (profile tasks, churn counters, etc.), while full history
183
+ // supplies canonical metrics even when the caller fetched only its first 20/100/1000 users.
184
+ return projectAuthorityUsers([...users, ...history.users], memberships, history.systemAddresses)
185
+ .map(user => ({ ...user, id: user.id ?? `${orgId.toLowerCase()}-${user.address.toLowerCase()}` }))
186
+ .sort((a, b) => {
187
+ // Member reports use the first rows as top holders; a cursor's ID order is not balance order.
188
+ if (a.participationTokenBalance == null)
189
+ return b.participationTokenBalance == null ? 0 : 1;
190
+ if (b.participationTokenBalance == null)
191
+ return -1;
192
+ const left = BigInt(a.participationTokenBalance);
193
+ const right = BigInt(b.participationTokenBalance);
194
+ return left > right ? -1 : left < right ? 1 : 0;
195
+ });
196
+ }
@@ -4,7 +4,6 @@
4
4
  */
5
5
  export * from './resolve';
6
6
  export * as education from './education';
7
- export * as eligibility from './eligibility';
8
7
  export * as org from './org';
9
8
  export * as paymaster from './paymaster';
10
9
  export * as project from './project';
@@ -14,3 +13,4 @@ export * as treasury from './treasury';
14
13
  export * as user from './user';
15
14
  export * as vote from './vote';
16
15
  export * as zkemail from './zkemail';
16
+ export * as authority from './authority';
@@ -40,10 +40,9 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  };
41
41
  })();
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.zkemail = exports.vote = exports.user = exports.treasury = exports.token = exports.task = exports.project = exports.paymaster = exports.org = exports.eligibility = exports.education = void 0;
43
+ exports.authority = exports.zkemail = exports.vote = exports.user = exports.treasury = exports.token = exports.task = exports.project = exports.paymaster = exports.org = exports.education = void 0;
44
44
  __exportStar(require("./resolve"), exports);
45
45
  exports.education = __importStar(require("./education"));
46
- exports.eligibility = __importStar(require("./eligibility"));
47
46
  exports.org = __importStar(require("./org"));
48
47
  exports.paymaster = __importStar(require("./paymaster"));
49
48
  exports.project = __importStar(require("./project"));
@@ -53,3 +52,4 @@ exports.treasury = __importStar(require("./treasury"));
53
52
  exports.user = __importStar(require("./user"));
54
53
  exports.vote = __importStar(require("./vote"));
55
54
  exports.zkemail = __importStar(require("./zkemail"));
55
+ exports.authority = __importStar(require("./authority"));
@@ -22,6 +22,11 @@ export interface OrgModuleRef {
22
22
  }
23
23
  /** Raw Organization row as returned by FETCH_ORG_FULL_DATA. */
24
24
  export interface OrganizationFullRow {
25
+ membershipAuthority: {
26
+ id: string;
27
+ isRouterBound: boolean;
28
+ cutoverAt: string | null;
29
+ } | null;
25
30
  id: string;
26
31
  name: string | null;
27
32
  metadataHash: string | null;
@@ -64,7 +69,6 @@ export interface OrganizationFullRow {
64
69
  modules: any[];
65
70
  } | null;
66
71
  executorContract: OrgModuleRef | null;
67
- eligibilityModule: OrgModuleRef | null;
68
72
  paymentManager: OrgModuleRef | null;
69
73
  users: Array<{
70
74
  id: string;
@@ -75,7 +79,8 @@ export interface OrganizationFullRow {
75
79
  avatar: string | null;
76
80
  } | null;
77
81
  } | null;
78
- participationTokenBalance: string;
82
+ participationTokenBalance: string | null;
83
+ historyIndexed?: boolean;
79
84
  membershipStatus: string;
80
85
  currentHatIds: string[] | null;
81
86
  totalTasksCompleted: string | null;
@@ -95,7 +100,7 @@ export interface OrganizationFullRow {
95
100
  }>;
96
101
  }
97
102
  /** Verbatim from src/commands/org/list.ts. */
98
- export declare const LIST_ORGS_QUERY = "\n query ListOrgs($first: Int!) {\n organizations(first: $first, orderBy: deployedAt, orderDirection: desc) {\n id\n name\n deployedAt\n }\n }\n";
103
+ export declare const LIST_ORGS_QUERY = "\n query ListOrgs($first: Int!) {\n organizations(where: { membershipAuthority_: { isRouterBound: true, cutoverAt_gt: \"0\" } }, first: $first, orderBy: deployedAt, orderDirection: desc) {\n id\n name\n deployedAt\n }\n }\n";
99
104
  /** Verbatim from src/commands/org/members.ts. */
100
105
  export declare const FETCH_MEMBERS = "\n query FetchMembers($orgId: Bytes!) {\n organization(id: $orgId) {\n participationToken {\n totalSupply\n }\n users(orderBy: participationTokenBalance, orderDirection: desc, first: 100) {\n address\n participationTokenBalance\n membershipStatus\n totalTasksCompleted\n totalVotes\n firstSeenAt\n account {\n username\n }\n }\n }\n }\n";
101
106
  /**
@@ -149,7 +154,8 @@ export declare function listOrgsAllChains(client: GraphClient, first?: number):
149
154
  export interface UserOrganizationRow {
150
155
  id: string;
151
156
  membershipStatus: string;
152
- participationTokenBalance: string;
157
+ participationTokenBalance: string | null;
158
+ historyIndexed?: boolean;
153
159
  totalTasksCompleted: string | null;
154
160
  totalVotes: string | null;
155
161
  organization: {
@@ -165,6 +171,7 @@ export interface UserOrganizationRow {
165
171
  * Port of the `pop org list --member` read — src/commands/org/list.ts
166
172
  * (FETCH_USER_ORGANIZATIONS): active memberships for an address.
167
173
  */
174
+ export declare const USER_AUTHORITY_ORGS_QUERY = "query UserAuthorityOrganizations($userAddress: Bytes!, $lastId: String!) {\n subjectMemberships(first: 1000, orderBy: id, where: {\n user: $userAddress, isMember: true, id_gt: $lastId, subject_: { kind: Role }\n }) {\n id organization { id name metadataHash membershipAuthority { id isRouterBound cutoverAt } participationToken { symbol } }\n userEntity { id participationTokenBalance totalTasksCompleted totalVotes }\n }\n}";
168
175
  export declare function listUserOrganizations(client: GraphClient, userAddress: string, chainId?: number): Promise<UserOrganizationRow[]>;
169
176
  export interface OrgMembersResult {
170
177
  participationToken: {
@@ -172,7 +179,9 @@ export interface OrgMembersResult {
172
179
  } | null;
173
180
  users: Array<{
174
181
  address: string;
175
- participationTokenBalance: string;
182
+ participationTokenBalance: string | null;
183
+ /** False for a real authority wallet whose historical User entity is not indexed. */
184
+ historyIndexed?: boolean;
176
185
  membershipStatus: string;
177
186
  totalTasksCompleted: string | null;
178
187
  totalVotes: string | null;
@@ -184,7 +193,7 @@ export interface OrgMembersResult {
184
193
  }
185
194
  /**
186
195
  * Port of the `pop org members` read — src/commands/org/members.ts.
187
- * Raw member rows ordered by PT balance desc (top 100), plus totalSupply for
196
+ * Complete current member rows, plus totalSupply for
188
197
  * share math. Null when the org is not indexed.
189
198
  */
190
199
  export declare function listMembers(client: GraphClient, orgId: string, chainId?: number): Promise<OrgMembersResult | null>;
@@ -210,29 +219,20 @@ export interface OrgRolesResult {
210
219
  }>;
211
220
  users: Array<{
212
221
  address: string;
213
- participationTokenBalance: string;
222
+ participationTokenBalance: string | null;
223
+ historyIndexed?: boolean;
214
224
  membershipStatus: string;
215
225
  currentHatIds: string[] | null;
216
226
  account: {
217
227
  username: string | null;
218
228
  } | null;
219
229
  }>;
220
- eligibilityModule: {
230
+ membershipAuthority: {
221
231
  id: string;
222
232
  } | null;
223
233
  } | null;
224
234
  }
225
- /**
226
- * Port of the `pop org roles` read — src/commands/org/roles.ts.
227
- *
228
- * Tier 0 carries Role.hat.vouchConfig (present + populated on Gnosis and
229
- * Arbitrum; a null vouchConfig means "never configured", verified equal to
230
- * isVouchingEnabled()==false / quorum==0 on-chain). Tier 1 is the
231
- * pre-vouchConfig schema; GraphQL validates the whole document, so the
232
- * unknown-field fallback is what keeps older deployments working. When
233
- * tierIndex === 1 serves, the CLI falls back to batched EligibilityModule
234
- * reads through Multicall3 — that RPC path stays host-side.
235
- */
235
+ /** Current roles and membership are authority-owned; continuity fields retain their public names. */
236
236
  export declare function listRoles(client: GraphClient, orgId: string, chainId?: number): Promise<{
237
237
  data: OrgRolesResult;
238
238
  tierIndex: number;
@@ -247,7 +247,7 @@ export declare function listRoles(client: GraphClient, orgId: string, chainId?:
247
247
  export declare function getOrgActivity(client: GraphClient, modules: {
248
248
  orgId: string;
249
249
  hybridVotingAddress?: string | null;
250
- eligibilityModuleAddress?: string | null;
250
+ membershipAuthorityAddress?: string | null;
251
251
  participationTokenAddress?: string | null;
252
252
  }, chainId?: number): Promise<any>;
253
253
  /**