Skip to content

Feat: Refactor multisig to use signer addresses instead of public keys#694

Merged
dzikowski merged 21 commits intomainfrom
feat/multisig-improved
Nov 6, 2025
Merged

Feat: Refactor multisig to use signer addresses instead of public keys#694
dzikowski merged 21 commits intomainfrom
feat/multisig-improved

Conversation

@dzikowski
Copy link
Copy Markdown
Collaborator

@dzikowski dzikowski commented Oct 17, 2025

Key improvements in the new flow:

  1. Existing wallets can participate in multiple multisig wallets.
  2. It is possible to add signers to multisig wallet knowing only their addresses (no public keys required)

Details:

The authorization in SDK is built around public keys. In the most common flow you register a user, you need to provide user alias (similar to username), and user public key. As a result two objects saved on chain:

GCUP/<eth-addr>      => UserProfile { alias: "client|username", roles: [...]  }
GCPK/client|username => PublicKey { publicKey: <the-key> }

The authorization process:

  1. requires a DTO signed with the user's private key
  2. resolves the public key from the DTO and signature
  3. calculates eth address from the public key
  4. fetches user profile by eth address (after this step user is authenticated)
  5. verifies user roles (authorization)

The first pass on multisig implementation used the same approach. That was reasonable, because of the relevant low shift, small incremental change. Instead of a single public key a multisig registration requires multiple public keys, and saves multiple objects on chain:

GCUP/<eth-addr1>     => UserProfile { alias: "client|multisig", roles: [...]  }
GCUP/<eth-addr2>     => UserProfile { alias: "client|multisig", roles: [...]  }
GCPK/client|multisig => PublicKey { publicKeys: [key1, key2] }

The multisig authorization process:

  1. requires a DTO with multiple signatures
  2. resolves all signers public keys
  3. calculates eth addresses
  4. fetches all relevant user profiles
  5. verifies if public keys are signers and if quorum is met (after this step multisig user is authenticated)
  6. verifies multisig user roles (authorization)

But that approach turned out to be problematic when we started discussions about actually using multisig profiles (wallets). There were two major friction points:

  1. We require GCUP/<eth-addr> to be unique on chain. That means any public key needs to be unique, and you cannot use your existing metamask account to sign your personal account and also participate in multisig wallets.
  2. It is hard to get public key from popular wallets. And in the most common scenario when you want to register a multisig profile, you need to ask other people to provide their public keys. The ethereum address seems to be a more natural value to provide.

All of that led to major reimplementatation of the multisig feature which have been done in this PR. The most important change is that multisig profiles no longer require a list of public keys. Instead they require a list of user refs (can be both eth address and gala chain user alias) - and that resolves a friction point number 1. Additionally, we no longer need to save public keys (GCPK objects) what resolves friction point number 2. Any existing user on GalaChain (excluding TON users) can now participate as a signer in a multisig profile.

In the new flow on registration you need to provide user alias and signer addresses, for instance:

{ alias: "client|multisig2", signers: ["<eth-addr>", "client|user"] }

And it saves only one object on chain:

GCUP/client|multisig2 => UserProfile { signers: ["eth|<eth-addr>", "client|user"] }

Notes:

  • previously we had GCUP/<eth-addr>
  • signers saved on chain are user aliases. So they are unambiguous. If we have an eth address in the dto that belongs to a registered user client|user2, then the value client|user2 will be saved as the signer.

In this case the authorization flow:

  1. requires a DTO with multiple signatures
    a. and multisig user alias in signerAddress (in this case client|multisig2)
    b. and dtoOperation parameter (protection against using dto for wrong method)
    c. and dtoExpiresAt parameter (additional security enforcement)
  2. resolves all signers public keys
  3. calculates eth addresses
  4. fetches all relevant user profiles
  5. verifies if users are signers and if quorum is met (after this step multisig user is authenticated)
  6. verifies multisig user roles (authorization)

That also means the core change is not in the authorization process, but in the API (signerAddress, dtoExpiresAt), and the underlying data model (signers instead of publicKeys).

Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
@dzikowski dzikowski marked this pull request as draft October 17, 2025 20:33
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
- Updated `ChainCallDTO` to enforce requirements for multisig, including mandatory `signerAddress` and `dtoOperation`.
- Modified signature validation to throw errors for unsupported multisig operations.
- Enhanced `PublicKeyContract` to streamline multisig user registration and signer management.
- Improved test cases to reflect changes in multisig logic and ensure proper validation.

Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
… of the key

Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
@dzikowski dzikowski marked this pull request as ready for review October 24, 2025 12:22
@dzikowski dzikowski changed the title Feat: Multisig improved Feat: Refactor multisig to use signer addresses instead of public keys Oct 29, 2025
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
@dzikowski dzikowski merged commit 13fdd4c into main Nov 6, 2025
13 checks passed
@dzikowski dzikowski deleted the feat/multisig-improved branch November 6, 2025 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant