feat: 4055 Implement Owner Management for Smart Contracts#5655
Conversation
…nds. Signed-off-by: dima <nosid91@gmail.com>
e064636 to
a37f098
Compare
andrewb1269
left a comment
There was a problem hiding this comment.
Approve the two package.json files. Please ensure other folks review the other files in the PR. Thank you!
| ## Owner Management | ||
|
|
||
| The smart contracts use a two-step ownership transfer pattern for security. This allows safe transfer of ownership without the risk of losing access to the contract. |
There was a problem hiding this comment.
Putting a block here until another reviewer also approves. Please @ me and I can remove the block once another reviewer approves the PR.
|
Hello. I was checking your PR 5655 and found that there are errors after Wipe contract creation, and Retire contract cannot be created. I tried to create both in UI. There is a error like this: 2026-02-09T13:43:02.133Z [GUARDIAN_SERVICE]: TypeError: no matching event (argument="key", value="0xbca31226f48d9d9a4c4759ba05c7e55d4d155ef264dd1e7f462df37b89647fa9", code=INVALID_ARGUMENT, version=6.15.0) Also I've found that this issue was already presented in the 4054 story commit dd244b1 Could you please take a look? Steps:
|
|
@Pyatakov Hello, I would like to know why it was closed? Regarding the bug, I found the cause - manual ABI generation. I fixed it in the commit nosid91@826dcbe. I did not respond because I did not have time to fully test the above to double-check its correctness. I planned to set aside time for this this week. Sorry for the delay. |
|
Hi @nosid91 2026-02-18T12:17:00.659Z [GUARDIAN_SERVICE]: RangeError: data out-of-bounds (buffer=0x, length=0, offset=32, code=BUFFER_OVERRUN, version=6.15.0) |
Signed-off-by: dima <nosid91@gmail.com>
The Access.sol contract declares events with `indexed` parameters (OwnerAdded, OwnerRemoved, OwnerProposed), but the TypeScript ABI was missing `indexed` and decodeEventLog was called without topics. This caused BUFFER_OVERRUN when decoding events with empty log.data. Signed-off-by: dima <nosid91@gmail.com>
826dcbe to
7263c96
Compare
The ownership management functions (proposeOwner, claimOwner, removeOwner) increased contract bytecode size, causing INSUFFICIENT_GAS on deployment. Updated gas defaults based on actual testnet usage: - RETIRE_SINGLE_CONTRACT_GAS: 6M -> 7M (actual: 6.3M) - RETIRE_DOUBLE_CONTRACT_GAS: 7M -> 8M (actual: 7.3M) Signed-off-by: dima <nosid91@gmail.com>
Hi, again sorry for the delay. Fixed the
|
|
Hi @nosid91 I checked your fix. Now wipe and retire contracts are created, also tokens can be retired using these contracts. Steps to reproduce: Actual: nothing happened Expected: green "Admin" badge should appear in the Permissions column for this imported policy Also I have some questions and suggestions about changing the role feature:
2. Maybe it would be good to have console.log for unsuccessful try, but not sure. 3. Also it would be great to have normal error message in the hashscan if it is possible. now it is just https://imgur.com/HN13KDt 4. And last but most important IMO: we need to somehow add the contract in the wipe/retire contracts list of the SR, if it was proposed and claimed by new SR. Now it is impossible to work with proposed and claimed contract in UI. Or maybe I just don't know how to properly get it for new user. |
Hi! Thanks for the thorough review and the detailed reproduction steps. Points 1-2: Agreed, I'll add the account addresses to all console.log messages and improve error output for failed attempts. |
18eec91 to
6febcf6
Compare
Replace custom NoPermissions() errors with require string messages for human-readable output in Hashscan. Add account addresses to CLI log messages for propose/claim/remove owner commands. Signed-off-by: dima <nosid91@gmail.com>
…ords Add OwnerAdded, OwnerRemoved, OwnerProposed event handlers to both syncWipeContract and syncRetireContract. On OwnerAdded, automatically create a contract DB record for the new owner if one doesn't exist. On OwnerRemoved, reset permissions to 0. Send notifications for all ownership events. Increase default Wipe contract deploy gas to 4M. Signed-off-by: dima <nosid91@gmail.com>
6febcf6 to
696f688
Compare
|
@kirill-tolochko
CLI logs:
Ownership event sync:
How to testCase 1 - new owner exists in guardian:
Case 2 - new owner is not in the db:
let me know if anything needs adjustments or if you'd like me to test any other edge cases. Thanks! |
|
@nosid91 1. Now proposed user is able to manage contract - view requests, add/remove admins etc. It shouldn't happen until a proposal is claimed in my understanding. Or should be? Also user gets a badge "Owner" in the UI. Just in case if you isn't able to reproduce, you can try my contracts |
|
Btw I forgot to mention that I also had to change } from '@hashgraph/sdk'; to } from '@hiero-ledger/sdk'; in guardian-cli/helpers/topic.helper.ts Does it work to you without changing? |
yeah it works on my end, but only because the ownership commands (propose/claim/remove) go through |
…ecord on removal Signed-off-by: dima <nosid91@gmail.com>
fixed both, was a service-level issue not smart contract
tested: propose without claim - only notification, no contract in dashboard (screenshot attached). claim - Owner badge only. remove - contract gone |
Signed-off-by: dima <nosid91@gmail.com>
|
@kirill-tolochko
|
|
Hi @nosid91 But remove-owner command works correctly for any else owner who isn't creator of the contract. Looks like it's impossible to remove any permission from the creator such as ownership/admin/manager roles? |
hey, just to clarify - |
|
Please, take a look here https://hashscan.io/testnet/contract/0.0.8030982/calls |
|
But it works correcty, if you remove new owner (that wasn't a creator) and then try to propose-owner by this removed new owner https://hashscan.io/testnet/transaction/1772017129.941019030/result |
…patibility Signed-off-by: dima <nosid91@gmail.com>
damn...
|
|
Now propose-owner works as expected for user who has already been removed from ownership! But.. won't work properly for ECDSA accounts, isn't it? Instead of getting |
Signed-off-by: dima <nosid91@gmail.com>
|
@kirill-tolochko good catch, same issue on the service side - |
a05fba2 to
40b44df
Compare
|
Now it's ok and can be merged! @Pyatakov |



Description
This PR implements a secure mechanism to transfer and manage the OWNER role in Guardian smart contracts to address security audit findings (#4055).
Access.sol(proposeOwner,claimOwner,removeOwner)propose-owner,claim-owner,remove-owner)Related issue(s)
Fixes #4055
Notes for Reviewer
Why Two-Step Ownership Transfer?
The implementation uses a two-step ownership transfer pattern (propose + claim) instead of direct transfer for the following security reasons:
Prevents accidental transfers: Direct ownership transfer to an incorrect address (typos, copy-paste errors) would result in permanent loss of contract control. The two-step pattern requires the new owner to actively claim ownership, confirming they have access to the destination address.
Validates recipient capability: The claim step verifies that the new owner can actually execute transactions, ensuring they have the private key and proper account setup.
Provides recovery window: If an owner accidentally proposes the wrong address, they can simply propose a different address to overwrite the pending owner before the claim occurs.
Industry standard: This pattern is widely adopted in production contracts (OpenZeppelin's
Ownable2Step, major DeFi protocols) as a security best practice.Security Constraints
pendingOwneris cleared after successful claim (prevents replay)Backwards Compatibility
Checklist