Skip to content

Bootstrapping the operator accounts at & after genesis #2614

Open

Description

@mmagician

USDCx Faucet Launch Plan

1. Generate the AccountId of the USDCx faucet

Needed for both testnet and mainnet.

1.1 Gather the AccountIds for all the roles

Roles, with the list of notes that each role should be capable of sending to the faucet:

  • ADMIN (4/5 multisig)
    • RBAC_CONFIG (grant / revoke / set-admin / renounce any of the five roles, incl. ADMIN itself)
    • FAUCET_METADATA_CONFIG (set_max_supply only; the other metadata fields are immutable)
    • MIN_BURN_AMOUNT_CONFIG (change the min. burn floor)
    • CONSTANT_FEE_POLICY_CONFIG (reprice accepted notes)
  • ATTEST_ADMIN (4/5 multisig)
    • SET_ATTESTER (enable / disable a Circle attester key commitment)
  • DOM_PAUSER (single-sig, 5x)
    • PAUSE_CONFIG, pause selector (halts mint and burn)
  • DOM_UNPAUSER (4/5 multisig)
    • PAUSE_CONFIG, unpause selector
  • BLK_MANAGER (single-sig)
    • BLOCKLIST_CONFIG (block / unblock an account)
  • RELAYER (single-sig, not part of the RBAC, we generate and hand to Gateway)
    • MINT (one per Circle deposit, carrying the attestation)

We need only the account ID of each holder to generate the AccountId for the faucet. Role holders accounts are not part of the genesis state (see step 7).

Depending on the auth scheme, we need to follow different procedures to gather the (final) account ID.

1.1.a Account generation via miden-client

We encourage the participant to use the miden-client CLI (version matching the network's protocol version).

  1. Install the CLI: cargo install miden-client-cli --version <CLI_VERSION> --locked.
  2. Create a directory dedicated to this account<>network pair: mkdir ~/.miden-<network>; cd ~/.miden-<network>, and initialise a local client home for the network the account is for:
  • miden-client init --local --network testnet
  • miden-client init --local --network <MAINNET_RPC_URL> for mainnet.
    Run every command below from that directory. The endpoint <MAINNET_RPC_URL> does not need to be reachable yet.
  1. Create the wallet: miden-client new-wallet --account-type public. This generates a Falcon512 key in the keystore and prints the account ID as a 0x hex value.
  2. Back up the directory. Its .miden-<network>/keystore holds the only copy of the signing key.

1.1.b Getting the AccountId and public key

For single signers, we need the AccountId. It would be printed at the end of the previous set of steps (as 0x hex). You can view it again: miden-client account -l.

For signers in the multisig (some signers will be single signers + multisig participants):
A multisig account is assembled from the approvers' public-key commitments. To get the pubkey:
miden-client keys --list

Send the coordinator the commitment and the scheme name (Falcon512Poseidon2).

1.1.c Account generation via Ledger

TBD

1.1.c Account generation via Bread Wallet

TBD


The coordinator assembling the multisig account, using the multisig interface (cc @Dominik1999).

1.2 Gather other data needed for seeding the account

This is only the attestation pubkey from Circle.

1.3 Derive the AccountId for the faucet itself

Done by the xusdc-genesis tool in miden-usdcx#222. It takes the five role IDs and the faucet parameters and writes usdcx-faucet.mac (nonce one, no seed), printing the ID in hex and in bech32 for each network. The ID depends only on the seed, code and storage, not on any chain state, so it can be derived and handed to Circle before any network exists.

2. Distributor account (DISTRIBUTOR)

To make the first tx on Miden, we need at least one account to hold the native token.
This could be a burner distributor account, whose only role is to have funds seeded at genesis, and send them out to the RELAYER (who submits mint attestations) and other bridge role accounts (DOM_PAUSER, etc.).

This account needs to be created pre-genesis, because after the DISTRIBUTOR is actually funded via the Ethereum transfer in the next step 3., there is one more change we need to make to the state of the faucet account before we create the genesis config TOML (see step 4).

To create this account, we need to:

  1. Generate a fresh account
  2. Amend its vault, so that it contains init_amount of the USDCx token (using the AccountId from the previous step)
  3. Re-assemble it together to create distributor.mac account file
  4. Use that file later for the funding service in Step 8
  5. Print the AccountId of the distributor, and use it for Step 3.

3. Send funds to the Ethereum/Sepolia USDCx contract

Prerequisite: Circle has registered the Miden domain (10007) on the target network's xReserve contract, and registered the USDCx faucet from step 1.3 as its remote token.

The deposit goes to the DISTRIBUTOR (from step 2) and must equal faucet.token_supply in config.json exactly:

  • mainnet: 100 USDC (100000000 with 6 decimals)
  • testnet: 1,000,000,000 USDC (1000000000000000)

The deposit can be made with cast (3a) or from a browser wallet (3b).

3a. cast CLI

3a.1 Import the Ethereum account into cast

The account must hold enough ETH for gas (two transactions) and the full USDC amount.

# private key from MetaMask: Account details -> Show private key
cast wallet import depositor --interactive
export FROM=$(cast wallet address --account depositor)

For a Ledger-backed account, skip the import and replace --account depositor with --ledger below.

3a.2 Set the network

Configure the RPC (e.g. Infura) and onchain addresses:

# mainnet
export RPC=<mainnet rpc url> \
  XRESERVE=0x8888888199b2Df864bf678259607d6D5EBb4e3Ce \
  USDC=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \
  CIRCLE_API=https://xreserve-api.circle.com

# sepolia
export RPC=<sepolia rpc url> \
  XRESERVE=0x008888878f94C0d87defdf0B07f46B93C1934442 \
  USDC=0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 \
  CIRCLE_API=https://xreserve-api-testnet.circle.com

export DOMAIN=10007
export AMOUNT=$(jq -r .faucet.token_supply config.json)
export RECIPIENT=<distributor "bytes32:" line printed by `xusdc-genesis new-distributor`>
export FAUCET=<faucet "bytes32:" line printed by `xusdc-genesis faucet`>

3a.3 Pre-flight checks

cast call $XRESERVE "isRemoteDomainRegistered(uint32)(bool)" $DOMAIN --rpc-url $RPC          # should be true
cast call $XRESERVE "getRemoteToken(uint32,address)(bytes32)" $DOMAIN $USDC --rpc-url $RPC   # should == $FAUCET
cast call $XRESERVE "domainDepositsPaused(uint32)(bool)" $DOMAIN --rpc-url $RPC              # should be false
cast call $USDC "balanceOf(address)(uint256)" $FROM --rpc-url $RPC                           # should be >= $AMOUNT
cast balance $FROM --ether --rpc-url $RPC                                                    # enough be for gas

3a.4 Deposit

cast send $USDC "approve(address,uint256)" $XRESERVE $AMOUNT \
  --account depositor --rpc-url $RPC

# dry run: must not revert
cast call $XRESERVE "depositToRemote(uint256,uint32,bytes32,address,uint256,bytes)" \
  $AMOUNT $DOMAIN $RECIPIENT $USDC 0 0x --from $FROM --rpc-url $RPC

cast send $XRESERVE "depositToRemote(uint256,uint32,bytes32,address,uint256,bytes)" \
  $AMOUNT $DOMAIN $RECIPIENT $USDC 0 0x --account depositor --rpc-url $RPC

3b. Alternative: deposit from a browser wallet (MetaMask or similar)

Instead of 3a, the same calls can be made from a browser wallet through a block explorer. Both xReserve implementations are verified on Blockscout. Switch the wallet to the target network, then open the contract pages:

On each page, in the Contract tab use "Read proxy" / "Write proxy" (connect the wallet for writes). All amounts are in base units (6 decimals).

  1. Pre-flight, on xReserve "Read proxy" (same expected outcomes as 3a.3):
    • isRemoteDomainRegistered(10007) -> true
    • getRemoteToken(10007, <USDC address>) -> the faucet's bytes32
    • domainDepositsPaused(10007) -> false
  2. On USDC "Write proxy", call approve:
    • spender: the xReserve address
    • value: the amount (100000000 on mainnet, 1000000000000000 on testnet)
  3. On xReserve "Write proxy", call depositToRemote:
    • value: the same amount
    • remoteDomain: 10007
    • remoteRecipient: the distributor's bytes32
    • localToken: the USDC address
    • maxFee: 0
    • hookData: 0x

Before confirming each transaction, check that the wallet's simulation doesn't show a revert.

4. Genesis state: re-generate faucet account

After the deposit from Step 3. is made on Ethereum, the relayer service, once booted, will query the Circle APIs and see one pending transfer - but since the distributor is funded at genesis, we should not process this first transfer, else we double book the amount.

The relayer needs to therefore skip this (and it would be rejected by the contract anyway) by inspecting the used_nonces storage of the faucet account. But since in Step 1.3 we didn't have the nonce yet, we need to "fix" this on the faucet account.

The overall flow is:

4a. Get the deposit nonce

The nonce is assigned by Circle when it attests the deposit, so it only appears in Circle's attestation feed after the deposit is final on Ethereum. Re-run until the list is non-empty (set CIRCLE_API, DOMAIN and RECIPIENT from Step 3):

curl -s "$CIRCLE_API/v1/remote-domains/$DOMAIN/attestations?pageSize=100" \
  | jq --arg r "${RECIPIENT#0x}" \
    '{used_nonces: [.attestations[].payload | ltrimstr("0x")
      | select(.[152:216] == ($r | ascii_downcase)) | "0x" + .[408:472]]}' \
  | tee nonces.json

The filter keeps only deposits to the distributor account. For each entry, bytes 76..108 of the DepositIntent payload are the recipient and bytes 204..236 are the nonce. The resulting nonces.json is the input to xusdc-genesis record-nonces in the next step.

4b. Re-configure the faucet with the nonce

Once we have the initial nonce, regenerate the faucet account (same AccountId as step 1.3; reads the faucet from step 1.3 and nonces.json from 4a):

xusdc-genesis record-nonces --faucet usdcx-faucet.mac --nonces nonces.json

This writes a new usdcx-faucet.genesis.mac. Do not use the pre-deposit-generated account from before.

Now, we can include this account, together with the DISTRIBUTOR account, in the genesis config file:

native_faucet = "usdcx-faucet.genesis.mac"

[[account]]
path = "distributor.mac"

This step happens ahead of launch.

5. Batch builder

Since we don't have a BATCH_FEE note, nor the collection thereof by the block builder, implemented, we don't need to worry about pre-funding the batch builder account at this point.

The batch builder's wallet account doesn't need to be pre-funded: it collects P2ID notes in the native asset, so it can pay the TX_FEE.

6. Launch

🚀

7. Post-genesis fund distribution to the operator roles

Aside from the relayer that submits mint requests to the faucet, the other operational bridge roles like ADMIN, ATTEST_ADMIN, etc. need to hold funds as well, so that they can be operational as soon as the network is up and running.

The role accounts will not exist onchain at genesis. Each holder deploys their own account with its first transaction, which consumes the P2ID note sent by the DISTRIBUTOR.

Ideally this would be scripted, so that all the required funds are distributed at once. I created https://github.com/0xMiden/infrastructure/issues/238 to track this.

USDCx

The accounts to be funded are as listed in 1.1 above.

AggLayer

The list of role accounts, and the list of notes that each role should be capable of sending to the faucet:

  • BRIDGE_ADMIN (4/5 multisig)
    • RBAC_CONFIG (grant / revoke / set-admin / renounce the five bridge roles and itself)
    • NETWORK_ACCOUNT_CONFIG (add / remove allowed note scripts, tx scripts and fee policies; select the active fee policy)
    • PAUSE_CONFIG, unpause selector (resume a paused bridge; PAUSER cannot)
  • PAUSER(s) - potentially several accounts (single-sig, 5x)
    • PAUSE_CONFIG, pause selector (halts bridge-out, claim, GER injection and faucet registration)
  • FAUCET_ADMIN, technically per faucet, but each faucet can set the same account as its admin. Same account as BRIDGE_ADMIN by default (4/5 multisig)
    • RBAC_CONFIG, to each faucet (rotate that faucet's ADMIN and FEE_MNGR)
    • NETWORK_ACCOUNT_CONFIG, to each faucet (allowlist and fee-policy changes)
  • FAUCET_MNGR (managed by Gateway)
    • CONFIG_AGG_BRIDGE (register a faucet and its metadata hash; one per bridged token at launch)
    • DEREGISTER_AGG_FAUCET (remove a faucet from the registry)
  • GER_INJECTOR (managed by Gateway)
    • UPDATE_GER (one per L1 global exit root update)
    • CLAIM (one per bridge-in; permissionless)
  • GER_REMOVER (managed by Gateway)
    • REMOVE_GER (revoke a registered GER)
  • FEE_MNGR (managed by Gateway)
    • CONSTANT_FEE_POLICY_CONFIG, to the bridge (reprice accepted notes)
    • CONSTANT_FEE_POLICY_CONFIG, to each faucet (account-local role; each faucet has its own FEE_MNGR members)

cc @mandrigin — could you confirm the list please?

Note

This initial funding step only covers the first few transactions - though we can make sure to cover a few thousand transactions e.g. with 10 USDC. Going forward, the operators need to ensure their accounts always remain funded (cc @mandrigin for AggLayer; @Keinberger for USDCx).

8. Funding service

For the partner mainnet, we want to have a funding service that sends funds to the allowlisted accounts, as described in #2561.

The funding service (node#2561) should be started with the DISTRIBUTOR account file from step 2 as --account-file.

This account can be funded in the same manner as the bridge operator roles in step 7.

Local deployment & devnet

As discussed offline with @Mirko-von-Leipzig, it might be easiest if in v0.17+, local and devnet environments also use the USDCx faucet as the native faucet, to keep things in sync.

Since there is no accounting to be tracked with Circle, we can:

  • In genesis, seed with a large amount of funds.
  • Set ourselves as the minting authority. This requires creating a service that submits signed MINT attestations against the USDCx faucet, thus adding a bit of operational setup. Perhaps this could be the role of the faucet operator service itself — TBD.

Activity

  1. added this to the v0.17 milestone on Sep 14, 2026
  2. bobbinth commented on Sep 19, 2026

    @bobbinth
    Contributor

    Thank you for writing this up! I've put all the steps up to the genesis block creation into a diagram below

    Image

    Overall, I think this will work - but there is also an alternative process that would be much simpler upfront, and a bit more work post-genesis. This may be a good tradeoff though. Here is a diagram for this process:

    Image

    The key difference here is that we have a "bootstrap" account that is set as the admin of the faucet at genesis, and we need only this account to create the faucet account. This means we don't need to collect inputs from other users upfront.

    Once the network is up and running, the following would need to happen:

    1. The bootstrap account sends a P2ID note to the funding service account (an alternative could be to use the bootstrap account as the funding service account - but separating these two feels a bit cleaner). Funding service accepts the initial funds in the first transaction.
      a. At this point, we have 3 accounts in the network: the bootstrap account, the funding service account, and the USDCx faucet account.
    2. We give out pin-codes to all relevant users, they create accounts using the wallet and send us their (account_id, pub_key) tuples (should be easy to export from the wallet). These would include:
      a. 5 IDs/keys for the "security council" accounts.
      b. Anything else?
    3. We create a 4-of-5 multisig account using the data from the previous step.
      a. For this, we probably will need to create an account offline, then add the ID to the allowlist on the node.
    4. We run a script that executes transactions against the bootstrap account to do the following:
      a. Sends funds to the multisig account, and probably additional funds to the signle-sig accounts that were created by users in step 2.
      b. Assigns roles in the USDCx faucet to relevant accounts (e.g., the ADMIN, ATTEST_ADMIN, and DOM_UNPAUSER get assigned to the multisig account) .
      c. Removes itself as the admin of the USDCx faucet account (i.e., transfers ownership to the multisig account).

    The main work here is to write the script for step 4 - but in return we get:

    • The participants of the security council don't need to do anything upfront. They interact with the network once it is up and running.
    • For local/devnet environments, we can skip steps 2-4 entirely and the faucet remains controlled by a single account.

    Open questions/tasks

    • Do we want the bootstrap account to be the same as the funding service account?
    • Can we build a nice UI to visualize the state of the USDCx faucet to see which roles are assigned to which accounts? Probably good to have the same thing for the Agglayer bridge too.
    • What is the process for creating the multisig account (i.e., step 3)?
    • What is the full list of accounts we need to provide as input into the script in step 4? Five "security council" accounts for sure, but there are probably more (e.g., for Agglayer).

    cc @Dominik1999

  3. Mirko-von-Leipzig commented on Sep 20, 2026

    @Mirko-von-Leipzig
    Collaborator

    I like the simplicity of the above.

    Is the idea that the sequencer will be started in some sort of special mode initially until all the required accounts are deployed? Or will we build those more manually? For example, in #2568 I add miden-node fee-collector deploy which appends a single block which deploys just that account.

    The benefit is largely that this more specialized code is separate from the normal runtime loop. For example we can't have fees enabled until all of this is settled, so the batch builder etc all need escape hatches if we want to use the normal sequencer flow.

    It also lets us avoid requiring a remote tx and batch prover since we can just prove that block or two locally.


    ah okay I see step (2) assumes that they can register using pin codes, so the sequencer would have to be "online" serving that. We can also just register their account IDs manually.. though I guess now the wallet flow won't let them create it without a pin code?

  4. bobbinth commented on Sep 20, 2026

    @bobbinth
    Contributor

    I think the main idea is to create the minimum set of accounts needed to run the network, and then to do most of the configuration after the network is up and running. My current understanding that the absolute minimum would be:

    • The native faucet account (i.e., USDCx).
    • The funding service account, which would be seeded with some funds and could also initially be the admin of the native faucet (as I described in the above comment).
    • The pass-through batch builder account which is needed to build batches. Though, maybe this is something that can be created as the first transaction (though, maybe we'll need fees for this)?
    • The fee collector account. This probably doesn't need to be a part of the genesis block, but needs to be created offline before the sequence is running (for the fee collection to work).

    All other accounts could be created either manually or via a script after the network is running. And then, we conceptually can split the deployment into 3 phases:

    1. Some pre-work that we need to do to build the genesis block (e.g., build the native faucet account, register it with Circle, get the attester public key).
    2. Network deployment - at this point, the network is up and running, but is not properly configured yet (i.e., the native faucet is controlled by a single account, there is no AggLayer bridge yet etc.).
    3. Network configuration - we ask external parties to create accounts on the network (should be possible with the wallet), and then run configuration script(s) to make sure the native faucet is configured correctly, the AggLayer bridge is deployed etc.

    Only after these 3 steps can we consider the network to be open to users.

  5. Mirko-von-Leipzig commented on Sep 20, 2026

    @Mirko-von-Leipzig
    Collaborator

    From the sequencer's perspective we need only two things deployed for normal/stead-state batch building:

    1. The pass through account. I call this the fee collection account.
      1. We also need or want the batch builder's wallet account ID so we can send the fees to the correct account. But this does not need to be deployed, only known and configured. Or we can dump or burn them.
    2. The USDCx faucet, because otherwise the batch builder cannot interact with the native asset.

    Deployment of (1) is implemented in #2568 (comment) as a standalone sequencer subcommand at the moment. Otherwise we run into race conditions and need more branching/edge case handling. I think its a fairly neat solution and I would prefer similar things for this initial setup. Notably this does not require the USDCx faucet because it doesn't actually use fees or the native token.

    ^^ the above is essentially the same as a script but since the network isn't actually running, it needs to know about sequencer/batch/block building stuff.

    Perhaps I'm just restating what you meant; I just want to clarify that I'd like to avoid reusing the steady-state sequencer runtime for this initial setup.

  6. bobbinth commented on Sep 20, 2026

    @bobbinth
    Contributor

    I think it is similar though I'm imagining 3 "genesis" accounts (i.e., accounts that exist in the genesis block):

    1. The native (USDCx) faucet.
    2. The fee collection account.
    3. The funding service account (which also is the temporary admin of the native faucet).

    With these 3 accounts, the network can run and bootstrap everything else. I think there are two differences between how you and I think:

    • iIUC, you are sing that the fee collection account gets deployed after genesis? If there is a way to do this, I'm fine with this.
    • You are not including the funding service account in the list. For testnet/mainnet this would be required, but I also think it doesn't hurt to have for local/devnet environment. This could be also viewed like "root" account of the network which can then configure everything else about the network once the blocks are being produced.
  7. Mirko-von-Leipzig commented on Sep 20, 2026

    @Mirko-von-Leipzig
    Collaborator

    I think there are two differences between how you and I think:

    • iIUC, you are sing that the fee collection account gets deployed after genesis? If there is a way to do this, I'm fine with this.

    There is; genesis would be fine as well of course. The awkward thing is that it requires a private key, so probably that private key should be held only by the sequencer (?) so it should create and deploy it? If we're okay sharing that key amongst parties then we can do it genesis (it can't mutate, mostly just an audit trail problem if it gets shared out of band and abused for anonymisers).

    • You are not including the funding service account in the list. For testnet/mainnet this would be required, but I also think it doesn't hurt to have for local/devnet environment. This could be also viewed like "root" account of the network which can then configure everything else about the network once the blocks are being produced.

    Yes, technically the sequencer doesn't need this to create blocks so I ignored it. But deploying both it and the admin account in genesis is fine. Simplifies things if its just.. there. I do want it on all networks for conformity. As you mention, it would be great if the only difference across networks was the auth for the faucets/admin accounts.

  8. Mirko-von-Leipzig commented on Sep 23, 2026

    @Mirko-von-Leipzig
    Collaborator

    The initial version has been deployed to devnet. It creates the usdcx faucet, funding service and batch builder account in genesis. The sequencer separately creates the fee collector pass through account as part of its bootstrap process - this works because it can append a dedicated block with just that deploy transaction, ignoring fees.

    What's still outstanding is how to (or a script/ui) to migrate the faucet operator account from the funding service to the multisig setup.

  9. bobbinth commented on Sep 24, 2026

    @bobbinth
    Contributor

    What's still outstanding is how to (or a script/ui) to migrate the faucet operator account from the funding service to the multisig setup.

    I'm assuming that this would be a either a stand-alone binary or a part of an exiting binary. At the high level, it would take (account_id, pub_key) tuples of 5 accounts and do the following:

    1. Create a 4-of-5 multisig account. We could also do this manually (cc @Dominik1999 in case that's simpler) - in this case, multisig account ID would be another input for the binary.
      a. We may also want to create several multisigs to assign different multisigs to different roles - but given that all people on these multisigs will probably be the same - that's probably an overkill.
    2. Set all relevant roles for this multisig in the USDCx faucet (ADMIN, DOM_UNPAUSER etc.).
    3. Set roles for single-sig accounts in the USDCx faucet (i.e., DOM_PAUSER).
    4. Removes the distributor account from any role in the USDCx faucet.

    One open question is whether we need to do anything for AggLayer accounts.

    cc @mmagician in case I missed some detail.

  10. mmagician commented on Sep 26, 2026

    @mmagician
    ContributorAuthor

    We could do this in a separate binary, but in my mind, this should all be done as part of the "bridge admin interface" that @Dominik1999 is building, so we have everything in one place:

    1. Creation of the multisig account(s) from the pubkeys
    2. Handle tx initiation. This should cover all the functionality that can be executed by the faucet. There are no state-changing tx scripts that are allowed, so in practice this means the creation of all notes which can be consumed by the faucet - and it includes RbacConfig notes which we are interested in here.
    3. Handle tx signing by co-signers (must support each tx that can be initiated in Step 2.)

    We may also want to create several multisigs to assign different multisigs to different roles - but given that all people on these multisigs will probably be the same - that's probably an overkill.

    For the roles we do want multisigs on (ADMIN, ATTEST_ADMIN, DOM_UNPAUSER) this could work, but it's worth noting that the Rust constructors for the faucet actually reject the same account ID for some of the roles (e.g. the account assigned to ADMIN cannot share the role as the DOM_PAUSER).

    1. Removes the distributor account

    Just to be clear - this means the setting of roles in steps 2 & 3 is still done by the old, single-sig admin, i.e. the distributor itself, right? I think that makes sense, it's faster to coordinate than the multisig.

    1. [...] from any role in the USDCx faucet.
      There will only ever be a single role held by the distributor: at genesis we set it as ADMIN, and we never grant it any other explicit role. So we just need to do one role ownership change. The steps (cc @Dominik1999 for doing this in the UI):
    1. Grant. The current admin sends an RBAC_CONFIG note to the faucet with RbacConfig::GrantRole { role: ADMIN, account: new_admin }.
    2. Revoke. One of the two admins sends a note with RbacConfig::RevokeRole { role: ADMIN, account: old_admin }.

    At the protocol level, there is no prevention of first revoking access to yourself - (it's a feature actually: it makes the account completely trustless), but perhaps we could add this check as part of the "bridge admin interface" (cc Dominik)? So the tool would detect if you are trying to send a RbacConfig::RevokeRole {...} note variant if there's only a single admin.

  11. bobbinth commented on Sep 26, 2026

    @bobbinth
    Contributor

    For the roles we do want multisigs on (ADMIN, ATTEST_ADMIN, DOM_UNPAUSER) this could work, but it's worth noting that the Rust constructors for the faucet actually reject the same account ID for some of the roles (e.g. the account assigned to ADMIN cannot share the role as the DOM_PAUSER).

    What combinations get rejected? In general, we probably don't need to have more than 1 multisig for now. We could, of course, create as many multisigs as needed, but they will all be identical (same 5 signers) - so, this probably complicates things.

    I'm assuming that we'll have 6 accounts to configure the faucet: 5 signlesig accounts + 1 multisig (4-of-5). Do we have a map of how these 6 account should map to different roles?

    Just to be clear - this means the setting of roles in steps 2 & 3 is still done by the old, single-sig admin, i.e. the distributor itself, right? I think that makes sense, it's faster to coordinate than the multisig.

    It would be easier to do all of the configuration from the singlesig account and then, as the last step, the account removes itself from the admin role. We can also invert this - the first thing the distirbutor account does is to set the multisig as the admin, but then all configuration changes will require 4 signers to be online. Doable - just quite a bit more complicated. On the other hand, maybe a good practice run to make sure everything is working via the multisig account.

  12. mmagician commented on Sep 28, 2026

    @mmagician
    ContributorAuthor

    What combinations get rejected?

    The rejections are mostly applied to mixing roles that are intended to be single sigs and roles that are meant to be multisigs, e.g. ADMIN and DOM_PAUSER. Having ADMIN and ATTEST_ADMIN, which are both multisigs, mapped to the same account ID, is ok. We don't need to worry about these combinations in our case, I just wanted to mentioned it for completeness.

    On the other hand, maybe a good practice run to make sure everything is working via the multisig account.

    That's a good argument. I think if we test the rotations on testnet, it's one thing, but doing it on mainnet should be exercised independently as well - and it's better to do it before the network is fully operational.

    Do we have a map of how these 6 account should map to different roles?

    There is one more PAUSER account handled by Predicate, call it P.
    Then we have 5 single-sig accounts A...E, and one multisig account M, configured as 4/5 threshold of the pubkeys of A..E:

    The accounts are mapped as follows (cc @Dominik1999 could you confirm this is correct please?):

    • ADMIN -> M
    • ATTEST_ADMIN -> M
    • BLK_MANAGER -> P
    • DOM_PAUSER- > [A, B, C, D, E]
    • DOM_UNPAUSER -> M
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions