Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

state.gno

12.67 Kb · 313 lines
  1package manager
  2
  3import "gno.land/p/nt/ufmt/v0"
  4
  5type State struct {
  6	Roles   map[RoleId]*RoleConfig
  7	Targets map[string]*TargetConfig
  8}
  9
 10func NewState() State {
 11	return State{
 12		Roles:   make(map[RoleId]*RoleConfig),
 13		Targets: make(map[string]*TargetConfig),
 14	}
 15}
 16
 17// =================================================== GETTERS ====================================================
 18
 19// CanCall reports whether caller can call target selector immediately.
 20// This Gno port returns only immediate authorization because scheduled
 21// execution and execution delay are intentionally not ported.
 22// OpenZeppelin reference:
 23// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L140-L156
 24func (state *State) CanCall(caller address, target string, selector Selector) CanCallResult {
 25	if state.IsTargetClosed(target) {
 26		return NewCanCallResult(false)
 27	}
 28
 29	role := state.GetTargetFunctionRole(target, selector)
 30
 31	membership := state.HasRole(role, caller)
 32	if !membership.IsMember {
 33		return NewCanCallResult(false)
 34	}
 35
 36	return NewCanCallResult(true)
 37}
 38
 39// IsAuthorized is a boolean convenience wrapper around CanCall.
 40// OpenZeppelin reference:
 41// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L140-L156
 42func (state *State) IsAuthorized(caller address, target string, selector Selector) bool {
 43	return state.CanCall(caller, target, selector).Immediate
 44}
 45
 46// HasRole reports whether account currently has an active role membership.
 47// This Gno port returns membership only because execution delay is not ported.
 48// OpenZeppelin reference:
 49// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L211-L220
 50func (state *State) HasRole(role RoleId, account address) HasRoleResult {
 51	if role == PublicRole {
 52		return NewHasRoleResult(true)
 53	}
 54
 55	roleConfig := state.getRole(role)
 56	if roleConfig == nil {
 57		return NewHasRoleResult(false)
 58	}
 59
 60	return NewHasRoleResult(roleConfig.hasMember(account, CurrentTimePoint()))
 61}
 62
 63// GetRoleMemberSince returns when account's role membership became active,
 64// or zero if it is not a member. It corresponds to OZ/Union's since field;
 65// execution-delay fields are not ported here.
 66//
 67// OpenZeppelin: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L205
 68// Union: https://github.com/unionlabs/union/blob/8cff0ff34f6baa4cdb1e4650a08985dd05de0c5a/cosmwasm/access-manager/src/contract.rs#L1165
 69func (state *State) GetRoleMemberSince(role RoleId, account address) TimePoint {
 70	roleConfig := state.getRole(role)
 71	if roleConfig == nil {
 72		return 0
 73	}
 74
 75	access, found := roleConfig.Members[account]
 76	if !found {
 77		return 0
 78	}
 79
 80	return access.Since
 81}
 82
 83// GetRoleAdmin returns the admin role for role, defaulting to AdminRole.
 84// OpenZeppelin reference:
 85// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L183-L186
 86func (state *State) GetRoleAdmin(role RoleId) RoleId {
 87	roleConfig := state.getRole(role)
 88	if roleConfig == nil {
 89		return AdminRole
 90	}
 91
 92	return roleConfig.adminRole()
 93}
 94
 95// GetRoleGrantDelay returns the grant delay currently in effect for role. A
 96// change scheduled by SetGrantDelay is not reflected until its effect timePoint.
 97// OpenZeppelin: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L194-L196
 98// Union: https://github.com/unionlabs/union/blob/8cff0ff34f6baa4cdb1e4650a08985dd05de0c5a/cosmwasm/access-manager/src/contract.rs#L1138-L1144
 99func (state *State) GetRoleGrantDelay(role RoleId) uint32 {
100	roleConfig := state.getRole(role)
101	if roleConfig == nil {
102		return 0
103	}
104
105	return roleConfig.grantDelay(CurrentTimePoint())
106}
107
108// GetRoleGrantDelayEffect returns when role's scheduled grant delay change
109// takes effect. No OZ/Union equivalent; added to avoid direct field access.
110func (state *State) GetRoleGrantDelayEffect(role RoleId) TimePoint {
111	roleConfig := state.getRole(role)
112	if roleConfig == nil {
113		return 0
114	}
115
116	return roleConfig.GrantDelay.effectDate
117}
118
119// GetTargetFunctionRole returns the role required for target selector.
120// Unconfigured selectors default to AdminRole in this Gno representation.
121// OpenZeppelin reference:
122// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L173-L176
123func (state *State) GetTargetFunctionRole(target string, selector Selector) RoleId {
124	targetConfig := state.getTarget(target)
125	if targetConfig == nil {
126		return AdminRole
127	}
128
129	return targetConfig.functionRole(selector)
130}
131
132// IsTargetClosed reports whether target is closed.
133// OpenZeppelin reference:
134// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L168-L171
135func (state *State) IsTargetClosed(target string) bool {
136	targetConfig := state.getTarget(target)
137
138	return targetConfig != nil && targetConfig.isClosed()
139}
140
141// =============================================== ROLE MANAGEMENT ===============================================
142
143// GrantRole grants role membership to account and returns true for a new member.
144// This Gno port retains grant delay but intentionally omits OZ execution delay.
145// OpenZeppelin references:
146// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L233-L236
147// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L271-L298
148func (state *State) GrantRole(role RoleId, account address) bool {
149	if role == PublicRole {
150		panic(errorWithDetails(errAccessManagerLockedRole, ufmt.Sprintf("role %d is locked", role)))
151	}
152
153	state.ensureRole(role)
154
155	return state.getRole(role).grant(account, CurrentTimePoint())
156}
157
158// RevokeRole revokes role membership from account and reports whether it existed.
159// OpenZeppelin references:
160// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L238-L240
161// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L300-L319
162func (state *State) RevokeRole(role RoleId, account address) bool {
163	if role == PublicRole {
164		panic(errorWithDetails(errAccessManagerLockedRole, ufmt.Sprintf("role %d is locked", role)))
165	}
166
167	state.ensureRole(role)
168
169	if role == AdminRole && state.isLastAdmin(account) {
170		panic(errAccessManagerLastAdmin)
171	}
172
173	return state.getRole(role).revoke(account)
174}
175
176// isLastAdmin reports whether account is the only currently active AdminRole
177// member. Prevents bricking the system by removing the final admin.
178func (state *State) isLastAdmin(account address) bool {
179	adminRole := state.getRole(AdminRole)
180	if adminRole == nil {
181		return false
182	}
183
184	now := CurrentTimePoint()
185	if !adminRole.hasMember(account, now) {
186		return false
187	}
188
189	for member, access := range adminRole.Members {
190		if member != account && access.isActiveAt(now) {
191			return false
192		}
193	}
194
195	return true
196}
197
198// RenounceRole revokes caller-confirmed role membership from account.
199// OpenZeppelin reference:
200// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L243-L249
201func (state *State) RenounceRole(role RoleId, account address, callerConfirmation address) bool {
202	if account != callerConfirmation {
203		panic(errAccessManagerBadConfirmation)
204	}
205
206	return state.RevokeRole(role, account)
207}
208
209// SetRoleAdmin sets the admin role that can grant or revoke role.
210// OpenZeppelin references:
211// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L251-L254
212// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L321-L337
213func (state *State) SetRoleAdmin(role RoleId, admin RoleId) {
214	RequireUnlockedConfigRole(role)
215
216	state.ensureRole(role)
217	state.getRole(role).setAdmin(admin)
218}
219
220// SetGrantDelay schedules newDelay behind a setback; it never overwrites the
221// active delay on the spot. Callers read GetRoleGrantDelay for the current
222// value or GetRoleGrantDelayEffect for the effect timePoint.
223// AdminRole is not locked here (unlike SetRoleAdmin) — OZ/Union only reject PublicRole.
224// OpenZeppelin: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L262-L264,L362-L371
225// Union: https://github.com/unionlabs/union/blob/8cff0ff34f6baa4cdb1e4650a08985dd05de0c5a/cosmwasm/access-manager/src/contract.rs#L139-L148,L314-L345
226func (state *State) SetGrantDelay(role RoleId, newDelay uint32) {
227	if role == PublicRole {
228		panic(errorWithDetails(errAccessManagerLockedRole, ufmt.Sprintf("role %d is locked", role)))
229	}
230
231	state.ensureRole(role)
232	state.getRole(role).setGrantDelay(CurrentTimePoint(), newDelay)
233}
234
235// ============================================= FUNCTION MANAGEMENT ==============================================
236
237// SetTargetFunctionRole sets the role required for target selector.
238// OpenZeppelin reference:
239// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L385-L393
240func (state *State) SetTargetFunctionRole(target string, selector Selector, role RoleId) {
241	state.ensureTarget(target)
242	state.getTarget(target).setFunctionRole(selector, role)
243}
244
245// SetTargetFunctionRoles sets the same role for multiple target selectors.
246// OpenZeppelin reference:
247// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L373-L383
248func (state *State) SetTargetFunctionRoles(target string, selectors []Selector, role RoleId) {
249	state.ensureTarget(target)
250	state.getTarget(target).setFunctionRoles(selectors, role)
251}
252
253// =============================================== MODE MANAGEMENT ================================================
254
255// SetTargetClosed sets whether target rejects calls regardless of selector role.
256// OpenZeppelin references:
257// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L412-L416
258// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L418-L426
259func (state *State) SetTargetClosed(target string, closed bool) {
260	state.ensureTarget(target)
261	state.getTarget(target).setClosed(closed)
262}
263
264// ================================================= ADMIN LOGIC ==================================================
265
266// CanAdminRole reports whether caller has the admin role required for role.
267// OpenZeppelin reference:
268// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L652-L657
269func (state *State) CanAdminRole(role RoleId, caller address) CanCallResult {
270	adminRole := state.GetRoleAdmin(role)
271
272	membership := state.HasRole(adminRole, caller)
273	if !membership.IsMember {
274		return NewCanCallResult(false)
275	}
276
277	return NewCanCallResult(true)
278}
279
280// CanManageTarget reports whether caller can manage target configuration.
281// OpenZeppelin references:
282// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L101-L105
283// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/5fd1781b1454fd1ef8e722282f86f9293cacf256/contracts/access/manager/AccessManager.sol#L640-L650
284func (state *State) CanManageTarget(caller address) CanCallResult {
285	membership := state.HasRole(AdminRole, caller)
286	if !membership.IsMember {
287		return NewCanCallResult(false)
288	}
289
290	return NewCanCallResult(true)
291}
292
293func (state *State) getRole(role RoleId) *RoleConfig {
294	return state.Roles[role]
295}
296
297func (state *State) getTarget(target string) *TargetConfig {
298	return state.Targets[target]
299}
300
301func (state *State) ensureRole(role RoleId) {
302	roleConfig, found := state.Roles[role]
303	if !found || roleConfig == nil {
304		state.Roles[role] = NewRoleConfig(AdminRole, 0)
305	}
306}
307
308func (state *State) ensureTarget(target string) {
309	targetConfig, found := state.Targets[target]
310	if !found || targetConfig == nil {
311		state.Targets[target] = NewTargetConfig(false)
312	}
313}