@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.
- package/README.md +2 -0
- package/dist/abis/AuthorityRouter.d.ts +448 -0
- package/dist/abis/AuthorityRouter.js +584 -0
- package/dist/abis/CutoverVerifier.d.ts +194 -0
- package/dist/abis/CutoverVerifier.js +259 -0
- package/dist/abis/DirectDemocracyVotingNew.d.ts +168 -30
- package/dist/abis/DirectDemocracyVotingNew.js +214 -35
- package/dist/abis/EducationHubNew.d.ts +32 -75
- package/dist/abis/EducationHubNew.js +38 -94
- package/dist/abis/Executor.d.ts +56 -86
- package/dist/abis/Executor.js +68 -104
- package/dist/abis/HybridVotingNew.d.ts +279 -35
- package/dist/abis/HybridVotingNew.js +357 -40
- package/dist/abis/{EligibilityModuleNew.d.ts → MembershipAuthority.d.ts} +1437 -718
- package/dist/abis/{EligibilityModuleNew.js → MembershipAuthority.js} +1821 -899
- package/dist/abis/OrgDeployerNew.d.ts +193 -127
- package/dist/abis/OrgDeployerNew.js +244 -160
- package/dist/abis/OrgRegistry.d.ts +40 -0
- package/dist/abis/OrgRegistry.js +51 -0
- package/dist/abis/ParticipationToken.d.ts +32 -75
- package/dist/abis/ParticipationToken.js +38 -94
- package/dist/abis/PasskeyAccount.d.ts +28 -0
- package/dist/abis/PasskeyAccount.js +35 -0
- package/dist/abis/PaymasterHub.d.ts +527 -47
- package/dist/abis/PaymasterHub.js +750 -135
- package/dist/abis/QuickJoinNew.d.ts +59 -170
- package/dist/abis/QuickJoinNew.js +71 -215
- package/dist/abis/TaskManagerNew.d.ts +27 -44
- package/dist/abis/TaskManagerNew.js +31 -52
- package/dist/abis/UniversalAccountRegistry.d.ts +24 -0
- package/dist/abis/UniversalAccountRegistry.js +30 -0
- package/dist/abis/ZkEmailInvites.d.ts +44 -0
- package/dist/abis/ZkEmailInvites.js +55 -0
- package/dist/abis/index.d.ts +6 -4
- package/dist/abis/index.js +10 -7
- package/dist/chains.js +3 -3
- package/dist/error-catalog.js +125 -7
- package/dist/graph/documents/activity.d.ts +3 -1
- package/dist/graph/documents/activity.js +19 -14
- package/dist/graph/documents/org.d.ts +4 -4
- package/dist/graph/documents/org.js +10 -4
- package/dist/graph/documents/task.d.ts +10 -1
- package/dist/graph/documents/task.js +157 -3
- package/dist/graph/documents/user.d.ts +5 -30
- package/dist/graph/documents/user.js +2 -42
- package/dist/graph/documents/voting-classes.d.ts +23 -2
- package/dist/graph/documents/voting-classes.js +119 -10
- package/dist/metadata/index.d.ts +0 -1
- package/dist/metadata/index.js +1 -2
- package/dist/perms.d.ts +0 -15
- package/dist/perms.js +1 -21
- package/dist/preflight.d.ts +2 -2
- package/dist/preflight.js +14 -22
- package/dist/reads/authority.d.ts +33 -0
- package/dist/reads/authority.js +196 -0
- package/dist/reads/index.d.ts +1 -1
- package/dist/reads/index.js +2 -2
- package/dist/reads/org.d.ts +20 -20
- package/dist/reads/org.js +94 -28
- package/dist/reads/project.js +3 -3
- package/dist/reads/resolve.d.ts +1 -1
- package/dist/reads/resolve.js +12 -3
- package/dist/reads/task.d.ts +44 -22
- package/dist/reads/task.js +126 -67
- package/dist/reads/user.d.ts +8 -43
- package/dist/reads/user.js +27 -44
- package/dist/reads/vote.d.ts +26 -2
- package/dist/reads/vote.js +104 -23
- package/dist/tx/authority.d.ts +33 -0
- package/dist/tx/authority.js +62 -0
- package/dist/tx/governance.d.ts +6 -0
- package/dist/tx/governance.js +20 -2
- package/dist/tx/index.d.ts +1 -1
- package/dist/tx/index.js +2 -2
- package/dist/tx/org.d.ts +23 -18
- package/dist/tx/org.js +203 -67
- package/dist/tx/project.d.ts +3 -2
- package/dist/tx/project.js +28 -10
- package/dist/tx/task.d.ts +13 -24
- package/dist/tx/task.js +45 -85
- package/dist/tx/user.d.ts +2 -40
- package/dist/tx/user.js +2 -76
- package/dist/tx/vote.d.ts +5 -0
- package/dist/tx/vote.js +54 -43
- package/dist/version.d.ts +0 -5
- package/dist/version.js +1 -13
- package/package.json +1 -1
- package/dist/abis/ToggleModule.d.ts +0 -193
- package/dist/abis/ToggleModule.js +0 -255
- package/dist/metadata/role.d.ts +0 -38
- package/dist/metadata/role.js +0 -35
- package/dist/reads/eligibility.d.ts +0 -377
- package/dist/reads/eligibility.js +0 -687
- package/dist/tx/eligibility.d.ts +0 -511
- 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.
|
|
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
|
-
}
|
package/dist/preflight.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
45
|
-
export declare function
|
|
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.
|
|
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
|
|
126
|
-
'function
|
|
125
|
+
const AUTHORITY_MEMBERSHIP_IFACE = new ethers_1.ethers.utils.Interface([
|
|
126
|
+
'function isMember(uint256 subject,address user) view returns (bool)',
|
|
127
127
|
]);
|
|
128
|
-
/**
|
|
129
|
-
function
|
|
130
|
-
const id = ethers_1.ethers.BigNumber.from(
|
|
131
|
-
return {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/reads/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/reads/index.js
CHANGED
|
@@ -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.
|
|
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"));
|
package/dist/reads/org.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
250
|
+
membershipAuthorityAddress?: string | null;
|
|
251
251
|
participationTokenAddress?: string | null;
|
|
252
252
|
}, chainId?: number): Promise<any>;
|
|
253
253
|
/**
|