Repository: xmtp/xmtpd
Pull request #830: Add parameter registry ABI and implement set maxcanonical
Author: login: mkysel
Created: 2025-05-22T17:18:04Z | Merged: 2025-05-22T19:04:52Z

### Implement parameter registry ABI and SetMaxCanonical method to set maximum canonical nodes in the node registry using SettlementChainParameterRegistry contract
Adds support for the `SettlementChainParameterRegistry` contract by:

* Implements `SetMaxCanonical` method in [registryAdmin.go](https://github.com/xmtp/xmtpd/pull/830/files#diff-d2eeb0eee280c4bdf2aa11a0222e19583a009cbd7aa6ee4fa791be3a892b43d9) to set maximum canonical nodes parameter using the new parameter registry contract
* Generates Go bindings for `SettlementChainParameterRegistry` contract in [SettlementChainParameterRegistry.go](https://github.com/xmtp/xmtpd/pull/830/files#diff-e94e38cc57ae616da281a1488aae0a9e27a819c3370c0d4d5f4cb4fc2990ba89)
* Adds configuration options and environment variables for parameter registry contract address in [options.go](https://github.com/xmtp/xmtpd/pull/830/files#diff-6731fb6f709392ce3e37d3b0c42074cddbce566dad2bab86af24ba7585eeb57c) and [local.env](https://github.com/xmtp/xmtpd/pull/830/files#diff-8f5c74fc1c72e37751a8a758aad091605c7771b2bb5d5a772d51c97836d85161)
* Includes test coverage for `SetMaxCanonical` functionality in [registryAdmin_test.go](https://github.com/xmtp/xmtpd/pull/830/files#diff-baedd79ae4c5f9d210df9a5947319b007f4e2ed378ad9db4745f952fc2538d3c)

#### 📍Where to Start
Start with the `SetMaxCanonical` method implementation in [registryAdmin.go](https://github.com/xmtp/xmtpd/pull/830/files#diff-d2eeb0eee280c4bdf2aa11a0222e19583a009cbd7aa6ee4fa791be3a892b43d9) which shows how the parameter registry contract is used to set the maximum canonical nodes parameter.

----

_[Macroscope](https://app.macroscope.com) summarized ae54a96._

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Added support for interacting with the Settlement Chain Parameter Registry contract, enabling management of chain parameters through the application.
  - Introduced a new configuration option to specify the Parameter Registry contract address.
- **Bug Fixes**
  - Enhanced error handling and logging when updating the maximum canonical nodes limit.
- **Tests**
  - Added tests to verify correct behavior when setting the maximum canonical nodes limit, including repeated calls with the same value.
- **Chores**
  - Updated environment variables and configuration defaults to include the Parameter Registry contract address.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/dev/gen/abi b/dev/gen/abi
index 0c3777037..dcc2c8a8b 100755
--- a/dev/gen/abi
+++ b/dev/gen/abi
@@ -16,7 +16,7 @@ mkdir -p "${build_dir}" "${output_dir}" "${abi_dir}"
 curl_cmd="curl -L -s -f"
 owner="xmtp"
 repository="smart-contracts"
-contracts=("GroupMessageBroadcaster" "IdentityUpdateBroadcaster" "NodeRegistry" "RateRegistry" "PayerRegistry" "PayerReportManager")
+contracts=("GroupMessageBroadcaster" "IdentityUpdateBroadcaster" "NodeRegistry" "RateRegistry" "PayerRegistry" "PayerReportManager" "SettlementChainParameterRegistry")
 
 function check_release() {
     local build_info_url="${release_url}/build-info.json"
diff --git a/dev/local.env b/dev/local.env
index b84e87b2e..1c14770f1 100755
--- a/dev/local.env
+++ b/dev/local.env
@@ -21,6 +21,7 @@ export XMTPD_SETTLEMENT_CHAIN_RATE_REGISTRY_ADDRESS="0xc76Fdbd3b026b73fcC03eB1D5
 export XMTPD_SETTLEMENT_CHAIN_PAYER_REGISTRY_ADDRESS="0x6ADCc064469C3b69ED6Fa4DbeFA21490FD200D7c"
 export XMTPD_SETTLEMENT_CHAIN_PAYER_REPORT_MANAGER_ADDRESS="0x2aA7BC557FF0b9B55FFD82274706DD2aD37E687B"
 export XMTPD_SETTLEMENT_CHAIN_DISTRIBUTION_MANAGER_ADDRESS="0x235A6C8052385FAc0919090DfF51290f8bBe5bFc"
+export XMTPD_SETTLEMENT_CHAIN_PARAMETER_REGISTRY_ADDRESS="0x7FFc148AF5f00C56D78Bce732fC79a08007eC8be"
 
 # Application Chain Options
 export XMTPD_APP_CHAIN_GROUP_MESSAGE_BROADCAST_ADDRESS="0x7D7df4ed008d64b9b816007b69dcbf559b9C5494"
diff --git a/pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go b/pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go
new file mode 100644
index 000000000..d6c95ab1f
--- /dev/null
+++ b/pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go
@@ -0,0 +1,1048 @@
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package settlementchainparameterregistry
+
+import (
+	"errors"
+	"math/big"
+	"strings"
+
+	ethereum "github.com/ethereum/go-ethereum"
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/accounts/abi/bind"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+	_ = errors.New
+	_ = big.NewInt
+	_ = strings.NewReader
+	_ = ethereum.NotFound
+	_ = bind.Bind
+	_ = common.Big1
+	_ = types.BloomLookup
+	_ = event.NewSubscription
+	_ = abi.ConvertType
+)
+
+// SettlementChainParameterRegistryMetaData contains all meta data concerning the SettlementChainParameterRegistry contract.
+var SettlementChainParameterRegistryMetaData = &bind.MetaData{
+	ABI: "[{\"type\":\"function\",\"name\":\"adminParameterKey\",\"inputs\":[],\"outputs\":[{\"name\":\"key_\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"get\",\"inputs\":[{\"name\":\"keys_\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"}],\"outputs\":[{\"name\":\"values_\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"get\",\"inputs\":[{\"name\":\"key_\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[{\"name\":\"value_\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"implementation\",\"inputs\":[],\"outputs\":[{\"name\":\"implementation_\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"admins_\",\"type\":\"address[]\",\"internalType\":\"address[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isAdmin\",\"inputs\":[{\"name\":\"account_\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"isAdmin_\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"migrate\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"migratorParameterKey\",\"inputs\":[],\"outputs\":[{\"name\":\"key_\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"pure\"},{\"type\":\"function\",\"name\":\"set\",\"inputs\":[{\"name\":\"keys_\",\"type\":\"bytes[]\",\"internalType\":\"bytes[]\"},{\"name\":\"values_\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"set\",\"inputs\":[{\"name\":\"key_\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"value_\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Migrated\",\"inputs\":[{\"name\":\"migrator\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ParameterSet\",\"inputs\":[{\"name\":\"key\",\"type\":\"bytes\",\"indexed\":true,\"internalType\":\"bytes\"},{\"name\":\"value\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ArrayLengthMismatch\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"EmptyCode\",\"inputs\":[{\"name\":\"migrator_\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"MigrationFailed\",\"inputs\":[{\"name\":\"migrator_\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"revertData_\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"type\":\"error\",\"name\":\"NoKeyComponents\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NoKeys\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotAdmin\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ParameterOutOfTypeBounds\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"StringsInsufficientHexLength\",\"inputs\":[{\"name\":\"value\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ZeroMigrator\",\"inputs\":[]}]",
+	Bin: "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b61123f806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106100b9575f3560e01c80638aab82ba116100725780639f40b625116100585780639f40b62514610191578063a224cee714610199578063d6d7d525146101ac575f5ffd5b80638aab82ba146101745780638fd3ab8014610189575f5ffd5b806324d7806c116100a257806324d7806c146100e55780635c60da1b1461010d5780637257a3e314610154575f5ffd5b80631df893cc146100bd57806323d56420146100d2575b5f5ffd5b6100d06100cb366004610dac565b6101cd565b005b6100d06100e0366004610e56565b6102ff565b6100f86100f3366004610e9e565b61036d565b60405190151581526020015b60405180910390f35b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610104565b610167610162366004610ed1565b610390565b6040516101049190610f10565b61017c6104ab565b6040516101049190610f9e565b6100d06104cb565b61017c6104e8565b6100d06101a7366004610ed1565b610508565b6101bf6101ba366004610fb0565b6106e5565b604051908152602001610104565b6101d561072f565b5f83900361020f576040517f0190983500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b828114610248576040517fa24a13a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced69850005f5b848110156102f7576102ef8287878481811061028957610289610fe3565b905060200281019061029b9190611010565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508992508891508690508181106102e3576102e3610fe3565b9050602002013561076e565b60010161026b565b505050505050565b61030761072f565b6103687fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced698500084848080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525086925061076e915050565b505050565b5f8061038861038361037d6104e8565b856107d1565b6107ec565b141592915050565b60605f8290036103cc576040517f0190983500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8167ffffffffffffffff8111156103e5576103e5611071565b60405190808252806020026020018201604052801561040e578160200160208202803683370190505b5090507fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced69850005f5b838110156104a3578185858381811061044f5761044f610fe3565b90506020028101906104619190611010565b60405161046f92919061109e565b90815260200160405180910390205483828151811061049057610490610fe3565b6020908102919091010152600101610434565b505092915050565b60606040518060600160405280602e81526020016111dc602e9139905090565b6104e66104e16104dc6103836104ab565b610832565b610885565b565b60606040518060600160405280602d81526020016111af602d9139905090565b5f610511610a90565b805490915060ff68010000000000000000820416159067ffffffffffffffff165f8115801561053d5750825b90505f8267ffffffffffffffff1660011480156105595750303b155b905081158015610567575080155b1561059e576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016600117855583156105ff5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b7fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced69850005f6106296104e8565b90505f5b888110156106785761067083610669848d8d8681811061064f5761064f610fe3565b90506020020160208101906106649190610e9e565b6107d1565b600161076e565b60010161062d565b50505083156106dc5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f7fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced6985000604051610717908590859061109e565b90815260200160405180910390205490505b92915050565b6107383361036d565b6104e6576040517f7bfa4b9f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80835f018360405161078091906110c4565b90815260405190819003602001812082905561079d9084906110c4565b604051908190038120907f9577bebd11e9d897c6432d7db290e4a2101d3e13e93e0bb00ca291c37ff6bc54905f90a3505050565b60606107e5836107e084610ab8565b610ac3565b9392505050565b5f7fefab3f4eb315eafaa267b58974a509c07c739fbfe8e62b4eff49c4ced698500060405161081c9084906110c4565b9081526020016040518091039020549050919050565b5f73ffffffffffffffffffffffffffffffffffffffff821115610881576040517f37f4f14800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b73ffffffffffffffffffffffffffffffffffffffff81166108d2576040517f0d626a3200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff8216907fa2e7361c23d7820040603b83c0cd3f494d377bac69736377d75bb56c651a5098905f90a25f5f8273ffffffffffffffffffffffffffffffffffffffff166040515f60405180830381855af49150503d805f8114610966576040519150601f19603f3d011682016040523d82523d5f602084013e61096b565b606091505b5091509150816109b45782816040517f68b0b16b0000000000000000000000000000000000000000000000000000000081526004016109ab9291906110cf565b60405180910390fd5b80511580156109d8575073ffffffffffffffffffffffffffffffffffffffff83163b155b15610a27576040517f626c416100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016109ab565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a2505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610729565b606061072982610b26565b6060826040518060400160405280600181526020017f2e0000000000000000000000000000000000000000000000000000000000000081525083604051602001610b0f939291906110fd565b604051602081830303815290604052905092915050565b606061072973ffffffffffffffffffffffffffffffffffffffff831660146060825f610b53846002611150565b610b5e906002611167565b67ffffffffffffffff811115610b7657610b76611071565b6040519080825280601f01601f191660200182016040528015610ba0576020820181803683370190505b5090507f3000000000000000000000000000000000000000000000000000000000000000815f81518110610bd657610bd6610fe3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110610c3857610c38610fe3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f610c72856002611150565b610c7d906001611167565b90505b6001811115610d19577f303132333435363738396162636465660000000000000000000000000000000083600f1660108110610cbe57610cbe610fe3565b1a60f81b828281518110610cd457610cd4610fe3565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a90535060049290921c91610d128161117a565b9050610c80565b508115610d5c576040517fe22e27eb00000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044016109ab565b949350505050565b5f5f83601f840112610d74575f5ffd5b50813567ffffffffffffffff811115610d8b575f5ffd5b6020830191508360208260051b8501011115610da5575f5ffd5b9250929050565b5f5f5f5f60408587031215610dbf575f5ffd5b843567ffffffffffffffff811115610dd5575f5ffd5b610de187828801610d64565b909550935050602085013567ffffffffffffffff811115610e00575f5ffd5b610e0c87828801610d64565b95989497509550505050565b5f5f83601f840112610e28575f5ffd5b50813567ffffffffffffffff811115610e3f575f5ffd5b602083019150836020828501011115610da5575f5ffd5b5f5f5f60408486031215610e68575f5ffd5b833567ffffffffffffffff811115610e7e575f5ffd5b610e8a86828701610e18565b909790965060209590950135949350505050565b5f60208284031215610eae575f5ffd5b813573ffffffffffffffffffffffffffffffffffffffff811681146107e5575f5ffd5b5f5f60208385031215610ee2575f5ffd5b823567ffffffffffffffff811115610ef8575f5ffd5b610f0485828601610d64565b90969095509350505050565b602080825282518282018190525f918401906040840190835b81811015610f47578351835260209384019390920191600101610f29565b509095945050505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6107e56020830184610f52565b5f5f60208385031215610fc1575f5ffd5b823567ffffffffffffffff811115610fd7575f5ffd5b610f0485828601610e18565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5f83357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611043575f5ffd5b83018035915067ffffffffffffffff82111561105d575f5ffd5b602001915036819003821315610da5575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b818382375f9101908152919050565b5f81518060208401855e5f93019283525090919050565b5f6107e582846110ad565b73ffffffffffffffffffffffffffffffffffffffff83168152604060208201525f610d5c6040830184610f52565b5f61111a61111461110e84886110ad565b866110ad565b846110ad565b95945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b808202811582820484141761072957610729611123565b8082018082111561072957610729611123565b5f8161118857611188611123565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019056fe786d74702e736574746c656d656e74436861696e506172616d6574657252656769737472792e697341646d696e786d74702e736574746c656d656e74436861696e506172616d6574657252656769737472792e6d69677261746f72a26469706673582212202f8bde0545d0b7eadc02733bcbd2c9be69fb5c6615f405b50bb69d1dd919d3b764736f6c634300081c0033",
+}
+
+// SettlementChainParameterRegistryABI is the input ABI used to generate the binding from.
+// Deprecated: Use SettlementChainParameterRegistryMetaData.ABI instead.
+var SettlementChainParameterRegistryABI = SettlementChainParameterRegistryMetaData.ABI
+
+// SettlementChainParameterRegistryBin is the compiled bytecode used for deploying new contracts.
+// Deprecated: Use SettlementChainParameterRegistryMetaData.Bin instead.
+var SettlementChainParameterRegistryBin = SettlementChainParameterRegistryMetaData.Bin
+
+// DeploySettlementChainParameterRegistry deploys a new Ethereum contract, binding an instance of SettlementChainParameterRegistry to it.
+func DeploySettlementChainParameterRegistry(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SettlementChainParameterRegistry, error) {
+	parsed, err := SettlementChainParameterRegistryMetaData.GetAbi()
+	if err != nil {
+		return common.Address{}, nil, nil, err
+	}
+	if parsed == nil {
+		return common.Address{}, nil, nil, errors.New("GetABI returned nil")
+	}
+
+	address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SettlementChainParameterRegistryBin), backend)
+	if err != nil {
+		return common.Address{}, nil, nil, err
+	}
+	return address, tx, &SettlementChainParameterRegistry{SettlementChainParameterRegistryCaller: SettlementChainParameterRegistryCaller{contract: contract}, SettlementChainParameterRegistryTransactor: SettlementChainParameterRegistryTransactor{contract: contract}, SettlementChainParameterRegistryFilterer: SettlementChainParameterRegistryFilterer{contract: contract}}, nil
+}
+
+// SettlementChainParameterRegistry is an auto generated Go binding around an Ethereum contract.
+type SettlementChainParameterRegistry struct {
+	SettlementChainParameterRegistryCaller     // Read-only binding to the contract
+	SettlementChainParameterRegistryTransactor // Write-only binding to the contract
+	SettlementChainParameterRegistryFilterer   // Log filterer for contract events
+}
+
+// SettlementChainParameterRegistryCaller is an auto generated read-only Go binding around an Ethereum contract.
+type SettlementChainParameterRegistryCaller struct {
+	contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// SettlementChainParameterRegistryTransactor is an auto generated write-only Go binding around an Ethereum contract.
+type SettlementChainParameterRegistryTransactor struct {
+	contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// SettlementChainParameterRegistryFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
+type SettlementChainParameterRegistryFilterer struct {
+	contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// SettlementChainParameterRegistrySession is an auto generated Go binding around an Ethereum contract,
+// with pre-set call and transact options.
+type SettlementChainParameterRegistrySession struct {
+	Contract     *SettlementChainParameterRegistry // Generic contract binding to set the session for
+	CallOpts     bind.CallOpts                     // Call options to use throughout this session
+	TransactOpts bind.TransactOpts                 // Transaction auth options to use throughout this session
+}
+
+// SettlementChainParameterRegistryCallerSession is an auto generated read-only Go binding around an Ethereum contract,
+// with pre-set call options.
+type SettlementChainParameterRegistryCallerSession struct {
+	Contract *SettlementChainParameterRegistryCaller // Generic contract caller binding to set the session for
+	CallOpts bind.CallOpts                           // Call options to use throughout this session
+}
+
+// SettlementChainParameterRegistryTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+// with pre-set transact options.
+type SettlementChainParameterRegistryTransactorSession struct {
+	Contract     *SettlementChainParameterRegistryTransactor // Generic contract transactor binding to set the session for
+	TransactOpts bind.TransactOpts                           // Transaction auth options to use throughout this session
+}
+
+// SettlementChainParameterRegistryRaw is an auto generated low-level Go binding around an Ethereum contract.
+type SettlementChainParameterRegistryRaw struct {
+	Contract *SettlementChainParameterRegistry // Generic contract binding to access the raw methods on
+}
+
+// SettlementChainParameterRegistryCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+type SettlementChainParameterRegistryCallerRaw struct {
+	Contract *SettlementChainParameterRegistryCaller // Generic read-only contract binding to access the raw methods on
+}
+
+// SettlementChainParameterRegistryTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+type SettlementChainParameterRegistryTransactorRaw struct {
+	Contract *SettlementChainParameterRegistryTransactor // Generic write-only contract binding to access the raw methods on
+}
+
+// NewSettlementChainParameterRegistry creates a new instance of SettlementChainParameterRegistry, bound to a specific deployed contract.
+func NewSettlementChainParameterRegistry(address common.Address, backend bind.ContractBackend) (*SettlementChainParameterRegistry, error) {
+	contract, err := bindSettlementChainParameterRegistry(address, backend, backend, backend)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistry{SettlementChainParameterRegistryCaller: SettlementChainParameterRegistryCaller{contract: contract}, SettlementChainParameterRegistryTransactor: SettlementChainParameterRegistryTransactor{contract: contract}, SettlementChainParameterRegistryFilterer: SettlementChainParameterRegistryFilterer{contract: contract}}, nil
+}
+
+// NewSettlementChainParameterRegistryCaller creates a new read-only instance of SettlementChainParameterRegistry, bound to a specific deployed contract.
+func NewSettlementChainParameterRegistryCaller(address common.Address, caller bind.ContractCaller) (*SettlementChainParameterRegistryCaller, error) {
+	contract, err := bindSettlementChainParameterRegistry(address, caller, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryCaller{contract: contract}, nil
+}
+
+// NewSettlementChainParameterRegistryTransactor creates a new write-only instance of SettlementChainParameterRegistry, bound to a specific deployed contract.
+func NewSettlementChainParameterRegistryTransactor(address common.Address, transactor bind.ContractTransactor) (*SettlementChainParameterRegistryTransactor, error) {
+	contract, err := bindSettlementChainParameterRegistry(address, nil, transactor, nil)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryTransactor{contract: contract}, nil
+}
+
+// NewSettlementChainParameterRegistryFilterer creates a new log filterer instance of SettlementChainParameterRegistry, bound to a specific deployed contract.
+func NewSettlementChainParameterRegistryFilterer(address common.Address, filterer bind.ContractFilterer) (*SettlementChainParameterRegistryFilterer, error) {
+	contract, err := bindSettlementChainParameterRegistry(address, nil, nil, filterer)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryFilterer{contract: contract}, nil
+}
+
+// bindSettlementChainParameterRegistry binds a generic wrapper to an already deployed contract.
+func bindSettlementChainParameterRegistry(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+	parsed, err := SettlementChainParameterRegistryMetaData.GetAbi()
+	if err != nil {
+		return nil, err
+	}
+	return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+	return _SettlementChainParameterRegistry.Contract.SettlementChainParameterRegistryCaller.contract.Call(opts, result, method, params...)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.SettlementChainParameterRegistryTransactor.contract.Transfer(opts)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.SettlementChainParameterRegistryTransactor.contract.Transact(opts, method, params...)
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+	return _SettlementChainParameterRegistry.Contract.contract.Call(opts, result, method, params...)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.contract.Transfer(opts)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.contract.Transact(opts, method, params...)
+}
+
+// AdminParameterKey is a free data retrieval call binding the contract method 0x9f40b625.
+//
+// Solidity: function adminParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) AdminParameterKey(opts *bind.CallOpts) ([]byte, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "adminParameterKey")
+
+	if err != nil {
+		return *new([]byte), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte)
+
+	return out0, err
+
+}
+
+// AdminParameterKey is a free data retrieval call binding the contract method 0x9f40b625.
+//
+// Solidity: function adminParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) AdminParameterKey() ([]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.AdminParameterKey(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// AdminParameterKey is a free data retrieval call binding the contract method 0x9f40b625.
+//
+// Solidity: function adminParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) AdminParameterKey() ([]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.AdminParameterKey(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// Get is a free data retrieval call binding the contract method 0x7257a3e3.
+//
+// Solidity: function get(bytes[] keys_) view returns(bytes32[] values_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) Get(opts *bind.CallOpts, keys_ [][]byte) ([][32]byte, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "get", keys_)
+
+	if err != nil {
+		return *new([][32]byte), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new([][32]byte)).(*[][32]byte)
+
+	return out0, err
+
+}
+
+// Get is a free data retrieval call binding the contract method 0x7257a3e3.
+//
+// Solidity: function get(bytes[] keys_) view returns(bytes32[] values_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Get(keys_ [][]byte) ([][32]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.Get(&_SettlementChainParameterRegistry.CallOpts, keys_)
+}
+
+// Get is a free data retrieval call binding the contract method 0x7257a3e3.
+//
+// Solidity: function get(bytes[] keys_) view returns(bytes32[] values_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) Get(keys_ [][]byte) ([][32]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.Get(&_SettlementChainParameterRegistry.CallOpts, keys_)
+}
+
+// Get0 is a free data retrieval call binding the contract method 0xd6d7d525.
+//
+// Solidity: function get(bytes key_) view returns(bytes32 value_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) Get0(opts *bind.CallOpts, key_ []byte) ([32]byte, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "get0", key_)
+
+	if err != nil {
+		return *new([32]byte), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte)
+
+	return out0, err
+
+}
+
+// Get0 is a free data retrieval call binding the contract method 0xd6d7d525.
+//
+// Solidity: function get(bytes key_) view returns(bytes32 value_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Get0(key_ []byte) ([32]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.Get0(&_SettlementChainParameterRegistry.CallOpts, key_)
+}
+
+// Get0 is a free data retrieval call binding the contract method 0xd6d7d525.
+//
+// Solidity: function get(bytes key_) view returns(bytes32 value_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) Get0(key_ []byte) ([32]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.Get0(&_SettlementChainParameterRegistry.CallOpts, key_)
+}
+
+// Implementation is a free data retrieval call binding the contract method 0x5c60da1b.
+//
+// Solidity: function implementation() view returns(address implementation_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) Implementation(opts *bind.CallOpts) (common.Address, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "implementation")
+
+	if err != nil {
+		return *new(common.Address), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
+
+	return out0, err
+
+}
+
+// Implementation is a free data retrieval call binding the contract method 0x5c60da1b.
+//
+// Solidity: function implementation() view returns(address implementation_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Implementation() (common.Address, error) {
+	return _SettlementChainParameterRegistry.Contract.Implementation(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// Implementation is a free data retrieval call binding the contract method 0x5c60da1b.
+//
+// Solidity: function implementation() view returns(address implementation_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) Implementation() (common.Address, error) {
+	return _SettlementChainParameterRegistry.Contract.Implementation(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// IsAdmin is a free data retrieval call binding the contract method 0x24d7806c.
+//
+// Solidity: function isAdmin(address account_) view returns(bool isAdmin_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) IsAdmin(opts *bind.CallOpts, account_ common.Address) (bool, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "isAdmin", account_)
+
+	if err != nil {
+		return *new(bool), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new(bool)).(*bool)
+
+	return out0, err
+
+}
+
+// IsAdmin is a free data retrieval call binding the contract method 0x24d7806c.
+//
+// Solidity: function isAdmin(address account_) view returns(bool isAdmin_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) IsAdmin(account_ common.Address) (bool, error) {
+	return _SettlementChainParameterRegistry.Contract.IsAdmin(&_SettlementChainParameterRegistry.CallOpts, account_)
+}
+
+// IsAdmin is a free data retrieval call binding the contract method 0x24d7806c.
+//
+// Solidity: function isAdmin(address account_) view returns(bool isAdmin_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) IsAdmin(account_ common.Address) (bool, error) {
+	return _SettlementChainParameterRegistry.Contract.IsAdmin(&_SettlementChainParameterRegistry.CallOpts, account_)
+}
+
+// MigratorParameterKey is a free data retrieval call binding the contract method 0x8aab82ba.
+//
+// Solidity: function migratorParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCaller) MigratorParameterKey(opts *bind.CallOpts) ([]byte, error) {
+	var out []interface{}
+	err := _SettlementChainParameterRegistry.contract.Call(opts, &out, "migratorParameterKey")
+
+	if err != nil {
+		return *new([]byte), err
+	}
+
+	out0 := *abi.ConvertType(out[0], new([]byte)).(*[]byte)
+
+	return out0, err
+
+}
+
+// MigratorParameterKey is a free data retrieval call binding the contract method 0x8aab82ba.
+//
+// Solidity: function migratorParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) MigratorParameterKey() ([]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.MigratorParameterKey(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// MigratorParameterKey is a free data retrieval call binding the contract method 0x8aab82ba.
+//
+// Solidity: function migratorParameterKey() pure returns(bytes key_)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryCallerSession) MigratorParameterKey() ([]byte, error) {
+	return _SettlementChainParameterRegistry.Contract.MigratorParameterKey(&_SettlementChainParameterRegistry.CallOpts)
+}
+
+// Initialize is a paid mutator transaction binding the contract method 0xa224cee7.
+//
+// Solidity: function initialize(address[] admins_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactor) Initialize(opts *bind.TransactOpts, admins_ []common.Address) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.contract.Transact(opts, "initialize", admins_)
+}
+
+// Initialize is a paid mutator transaction binding the contract method 0xa224cee7.
+//
+// Solidity: function initialize(address[] admins_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Initialize(admins_ []common.Address) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Initialize(&_SettlementChainParameterRegistry.TransactOpts, admins_)
+}
+
+// Initialize is a paid mutator transaction binding the contract method 0xa224cee7.
+//
+// Solidity: function initialize(address[] admins_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorSession) Initialize(admins_ []common.Address) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Initialize(&_SettlementChainParameterRegistry.TransactOpts, admins_)
+}
+
+// Migrate is a paid mutator transaction binding the contract method 0x8fd3ab80.
+//
+// Solidity: function migrate() returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactor) Migrate(opts *bind.TransactOpts) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.contract.Transact(opts, "migrate")
+}
+
+// Migrate is a paid mutator transaction binding the contract method 0x8fd3ab80.
+//
+// Solidity: function migrate() returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Migrate() (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Migrate(&_SettlementChainParameterRegistry.TransactOpts)
+}
+
+// Migrate is a paid mutator transaction binding the contract method 0x8fd3ab80.
+//
+// Solidity: function migrate() returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorSession) Migrate() (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Migrate(&_SettlementChainParameterRegistry.TransactOpts)
+}
+
+// Set is a paid mutator transaction binding the contract method 0x1df893cc.
+//
+// Solidity: function set(bytes[] keys_, bytes32[] values_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactor) Set(opts *bind.TransactOpts, keys_ [][]byte, values_ [][32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.contract.Transact(opts, "set", keys_, values_)
+}
+
+// Set is a paid mutator transaction binding the contract method 0x1df893cc.
+//
+// Solidity: function set(bytes[] keys_, bytes32[] values_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Set(keys_ [][]byte, values_ [][32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Set(&_SettlementChainParameterRegistry.TransactOpts, keys_, values_)
+}
+
+// Set is a paid mutator transaction binding the contract method 0x1df893cc.
+//
+// Solidity: function set(bytes[] keys_, bytes32[] values_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorSession) Set(keys_ [][]byte, values_ [][32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Set(&_SettlementChainParameterRegistry.TransactOpts, keys_, values_)
+}
+
+// Set0 is a paid mutator transaction binding the contract method 0x23d56420.
+//
+// Solidity: function set(bytes key_, bytes32 value_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactor) Set0(opts *bind.TransactOpts, key_ []byte, value_ [32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.contract.Transact(opts, "set0", key_, value_)
+}
+
+// Set0 is a paid mutator transaction binding the contract method 0x23d56420.
+//
+// Solidity: function set(bytes key_, bytes32 value_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistrySession) Set0(key_ []byte, value_ [32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Set0(&_SettlementChainParameterRegistry.TransactOpts, key_, value_)
+}
+
+// Set0 is a paid mutator transaction binding the contract method 0x23d56420.
+//
+// Solidity: function set(bytes key_, bytes32 value_) returns()
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryTransactorSession) Set0(key_ []byte, value_ [32]byte) (*types.Transaction, error) {
+	return _SettlementChainParameterRegistry.Contract.Set0(&_SettlementChainParameterRegistry.TransactOpts, key_, value_)
+}
+
+// SettlementChainParameterRegistryInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryInitializedIterator struct {
+	Event *SettlementChainParameterRegistryInitialized // Event containing the contract specifics and raw log
+
+	contract *bind.BoundContract // Generic contract to use for unpacking event data
+	event    string              // Event name to use for unpacking event data
+
+	logs chan types.Log        // Log channel receiving the found contract events
+	sub  ethereum.Subscription // Subscription for errors, completion and termination
+	done bool                  // Whether the subscription completed delivering logs
+	fail error                 // Occurred error to stop iteration
+}
+
+// Next advances the iterator to the subsequent event, returning whether there
+// are any more events found. In case of a retrieval or parsing error, false is
+// returned and Error() can be queried for the exact failure.
+func (it *SettlementChainParameterRegistryInitializedIterator) Next() bool {
+	// If the iterator failed, stop iterating
+	if it.fail != nil {
+		return false
+	}
+	// If the iterator completed, deliver directly whatever's available
+	if it.done {
+		select {
+		case log := <-it.logs:
+			it.Event = new(SettlementChainParameterRegistryInitialized)
+			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+				it.fail = err
+				return false
+			}
+			it.Event.Raw = log
+			return true
+
+		default:
+			return false
+		}
+	}
+	// Iterator still in progress, wait for either a data or an error event
+	select {
+	case log := <-it.logs:
+		it.Event = new(SettlementChainParameterRegistryInitialized)
+		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+			it.fail = err
+			return false
+		}
+		it.Event.Raw = log
+		return true
+
+	case err := <-it.sub.Err():
+		it.done = true
+		it.fail = err
+		return it.Next()
+	}
+}
+
+// Error returns any retrieval or parsing error occurred during filtering.
+func (it *SettlementChainParameterRegistryInitializedIterator) Error() error {
+	return it.fail
+}
+
+// Close terminates the iteration process, releasing any pending underlying
+// resources.
+func (it *SettlementChainParameterRegistryInitializedIterator) Close() error {
+	it.sub.Unsubscribe()
+	return nil
+}
+
+// SettlementChainParameterRegistryInitialized represents a Initialized event raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryInitialized struct {
+	Version uint64
+	Raw     types.Log // Blockchain specific contextual infos
+}
+
+// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
+//
+// Solidity: event Initialized(uint64 version)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) FilterInitialized(opts *bind.FilterOpts) (*SettlementChainParameterRegistryInitializedIterator, error) {
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.FilterLogs(opts, "Initialized")
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryInitializedIterator{contract: _SettlementChainParameterRegistry.contract, event: "Initialized", logs: logs, sub: sub}, nil
+}
+
+// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
+//
+// Solidity: event Initialized(uint64 version)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *SettlementChainParameterRegistryInitialized) (event.Subscription, error) {
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.WatchLogs(opts, "Initialized")
+	if err != nil {
+		return nil, err
+	}
+	return event.NewSubscription(func(quit <-chan struct{}) error {
+		defer sub.Unsubscribe()
+		for {
+			select {
+			case log := <-logs:
+				// New log arrived, parse the event and forward to the user
+				event := new(SettlementChainParameterRegistryInitialized)
+				if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Initialized", log); err != nil {
+					return err
+				}
+				event.Raw = log
+
+				select {
+				case sink <- event:
+				case err := <-sub.Err():
+					return err
+				case <-quit:
+					return nil
+				}
+			case err := <-sub.Err():
+				return err
+			case <-quit:
+				return nil
+			}
+		}
+	}), nil
+}
+
+// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2.
+//
+// Solidity: event Initialized(uint64 version)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) ParseInitialized(log types.Log) (*SettlementChainParameterRegistryInitialized, error) {
+	event := new(SettlementChainParameterRegistryInitialized)
+	if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Initialized", log); err != nil {
+		return nil, err
+	}
+	event.Raw = log
+	return event, nil
+}
+
+// SettlementChainParameterRegistryMigratedIterator is returned from FilterMigrated and is used to iterate over the raw logs and unpacked data for Migrated events raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryMigratedIterator struct {
+	Event *SettlementChainParameterRegistryMigrated // Event containing the contract specifics and raw log
+
+	contract *bind.BoundContract // Generic contract to use for unpacking event data
+	event    string              // Event name to use for unpacking event data
+
+	logs chan types.Log        // Log channel receiving the found contract events
+	sub  ethereum.Subscription // Subscription for errors, completion and termination
+	done bool                  // Whether the subscription completed delivering logs
+	fail error                 // Occurred error to stop iteration
+}
+
+// Next advances the iterator to the subsequent event, returning whether there
+// are any more events found. In case of a retrieval or parsing error, false is
+// returned and Error() can be queried for the exact failure.
+func (it *SettlementChainParameterRegistryMigratedIterator) Next() bool {
+	// If the iterator failed, stop iterating
+	if it.fail != nil {
+		return false
+	}
+	// If the iterator completed, deliver directly whatever's available
+	if it.done {
+		select {
+		case log := <-it.logs:
+			it.Event = new(SettlementChainParameterRegistryMigrated)
+			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+				it.fail = err
+				return false
+			}
+			it.Event.Raw = log
+			return true
+
+		default:
+			return false
+		}
+	}
+	// Iterator still in progress, wait for either a data or an error event
+	select {
+	case log := <-it.logs:
+		it.Event = new(SettlementChainParameterRegistryMigrated)
+		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+			it.fail = err
+			return false
+		}
+		it.Event.Raw = log
+		return true
+
+	case err := <-it.sub.Err():
+		it.done = true
+		it.fail = err
+		return it.Next()
+	}
+}
+
+// Error returns any retrieval or parsing error occurred during filtering.
+func (it *SettlementChainParameterRegistryMigratedIterator) Error() error {
+	return it.fail
+}
+
+// Close terminates the iteration process, releasing any pending underlying
+// resources.
+func (it *SettlementChainParameterRegistryMigratedIterator) Close() error {
+	it.sub.Unsubscribe()
+	return nil
+}
+
+// SettlementChainParameterRegistryMigrated represents a Migrated event raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryMigrated struct {
+	Migrator common.Address
+	Raw      types.Log // Blockchain specific contextual infos
+}
+
+// FilterMigrated is a free log retrieval operation binding the contract event 0xa2e7361c23d7820040603b83c0cd3f494d377bac69736377d75bb56c651a5098.
+//
+// Solidity: event Migrated(address indexed migrator)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) FilterMigrated(opts *bind.FilterOpts, migrator []common.Address) (*SettlementChainParameterRegistryMigratedIterator, error) {
+
+	var migratorRule []interface{}
+	for _, migratorItem := range migrator {
+		migratorRule = append(migratorRule, migratorItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.FilterLogs(opts, "Migrated", migratorRule)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryMigratedIterator{contract: _SettlementChainParameterRegistry.contract, event: "Migrated", logs: logs, sub: sub}, nil
+}
+
+// WatchMigrated is a free log subscription operation binding the contract event 0xa2e7361c23d7820040603b83c0cd3f494d377bac69736377d75bb56c651a5098.
+//
+// Solidity: event Migrated(address indexed migrator)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) WatchMigrated(opts *bind.WatchOpts, sink chan<- *SettlementChainParameterRegistryMigrated, migrator []common.Address) (event.Subscription, error) {
+
+	var migratorRule []interface{}
+	for _, migratorItem := range migrator {
+		migratorRule = append(migratorRule, migratorItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.WatchLogs(opts, "Migrated", migratorRule)
+	if err != nil {
+		return nil, err
+	}
+	return event.NewSubscription(func(quit <-chan struct{}) error {
+		defer sub.Unsubscribe()
+		for {
+			select {
+			case log := <-logs:
+				// New log arrived, parse the event and forward to the user
+				event := new(SettlementChainParameterRegistryMigrated)
+				if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Migrated", log); err != nil {
+					return err
+				}
+				event.Raw = log
+
+				select {
+				case sink <- event:
+				case err := <-sub.Err():
+					return err
+				case <-quit:
+					return nil
+				}
+			case err := <-sub.Err():
+				return err
+			case <-quit:
+				return nil
+			}
+		}
+	}), nil
+}
+
+// ParseMigrated is a log parse operation binding the contract event 0xa2e7361c23d7820040603b83c0cd3f494d377bac69736377d75bb56c651a5098.
+//
+// Solidity: event Migrated(address indexed migrator)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) ParseMigrated(log types.Log) (*SettlementChainParameterRegistryMigrated, error) {
+	event := new(SettlementChainParameterRegistryMigrated)
+	if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Migrated", log); err != nil {
+		return nil, err
+	}
+	event.Raw = log
+	return event, nil
+}
+
+// SettlementChainParameterRegistryParameterSetIterator is returned from FilterParameterSet and is used to iterate over the raw logs and unpacked data for ParameterSet events raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryParameterSetIterator struct {
+	Event *SettlementChainParameterRegistryParameterSet // Event containing the contract specifics and raw log
+
+	contract *bind.BoundContract // Generic contract to use for unpacking event data
+	event    string              // Event name to use for unpacking event data
+
+	logs chan types.Log        // Log channel receiving the found contract events
+	sub  ethereum.Subscription // Subscription for errors, completion and termination
+	done bool                  // Whether the subscription completed delivering logs
+	fail error                 // Occurred error to stop iteration
+}
+
+// Next advances the iterator to the subsequent event, returning whether there
+// are any more events found. In case of a retrieval or parsing error, false is
+// returned and Error() can be queried for the exact failure.
+func (it *SettlementChainParameterRegistryParameterSetIterator) Next() bool {
+	// If the iterator failed, stop iterating
+	if it.fail != nil {
+		return false
+	}
+	// If the iterator completed, deliver directly whatever's available
+	if it.done {
+		select {
+		case log := <-it.logs:
+			it.Event = new(SettlementChainParameterRegistryParameterSet)
+			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+				it.fail = err
+				return false
+			}
+			it.Event.Raw = log
+			return true
+
+		default:
+			return false
+		}
+	}
+	// Iterator still in progress, wait for either a data or an error event
+	select {
+	case log := <-it.logs:
+		it.Event = new(SettlementChainParameterRegistryParameterSet)
+		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+			it.fail = err
+			return false
+		}
+		it.Event.Raw = log
+		return true
+
+	case err := <-it.sub.Err():
+		it.done = true
+		it.fail = err
+		return it.Next()
+	}
+}
+
+// Error returns any retrieval or parsing error occurred during filtering.
+func (it *SettlementChainParameterRegistryParameterSetIterator) Error() error {
+	return it.fail
+}
+
+// Close terminates the iteration process, releasing any pending underlying
+// resources.
+func (it *SettlementChainParameterRegistryParameterSetIterator) Close() error {
+	it.sub.Unsubscribe()
+	return nil
+}
+
+// SettlementChainParameterRegistryParameterSet represents a ParameterSet event raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryParameterSet struct {
+	Key   common.Hash
+	Value [32]byte
+	Raw   types.Log // Blockchain specific contextual infos
+}
+
+// FilterParameterSet is a free log retrieval operation binding the contract event 0x9577bebd11e9d897c6432d7db290e4a2101d3e13e93e0bb00ca291c37ff6bc54.
+//
+// Solidity: event ParameterSet(bytes indexed key, bytes32 indexed value)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) FilterParameterSet(opts *bind.FilterOpts, key [][]byte, value [][32]byte) (*SettlementChainParameterRegistryParameterSetIterator, error) {
+
+	var keyRule []interface{}
+	for _, keyItem := range key {
+		keyRule = append(keyRule, keyItem)
+	}
+	var valueRule []interface{}
+	for _, valueItem := range value {
+		valueRule = append(valueRule, valueItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.FilterLogs(opts, "ParameterSet", keyRule, valueRule)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryParameterSetIterator{contract: _SettlementChainParameterRegistry.contract, event: "ParameterSet", logs: logs, sub: sub}, nil
+}
+
+// WatchParameterSet is a free log subscription operation binding the contract event 0x9577bebd11e9d897c6432d7db290e4a2101d3e13e93e0bb00ca291c37ff6bc54.
+//
+// Solidity: event ParameterSet(bytes indexed key, bytes32 indexed value)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) WatchParameterSet(opts *bind.WatchOpts, sink chan<- *SettlementChainParameterRegistryParameterSet, key [][]byte, value [][32]byte) (event.Subscription, error) {
+
+	var keyRule []interface{}
+	for _, keyItem := range key {
+		keyRule = append(keyRule, keyItem)
+	}
+	var valueRule []interface{}
+	for _, valueItem := range value {
+		valueRule = append(valueRule, valueItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.WatchLogs(opts, "ParameterSet", keyRule, valueRule)
+	if err != nil {
+		return nil, err
+	}
+	return event.NewSubscription(func(quit <-chan struct{}) error {
+		defer sub.Unsubscribe()
+		for {
+			select {
+			case log := <-logs:
+				// New log arrived, parse the event and forward to the user
+				event := new(SettlementChainParameterRegistryParameterSet)
+				if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "ParameterSet", log); err != nil {
+					return err
+				}
+				event.Raw = log
+
+				select {
+				case sink <- event:
+				case err := <-sub.Err():
+					return err
+				case <-quit:
+					return nil
+				}
+			case err := <-sub.Err():
+				return err
+			case <-quit:
+				return nil
+			}
+		}
+	}), nil
+}
+
+// ParseParameterSet is a log parse operation binding the contract event 0x9577bebd11e9d897c6432d7db290e4a2101d3e13e93e0bb00ca291c37ff6bc54.
+//
+// Solidity: event ParameterSet(bytes indexed key, bytes32 indexed value)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) ParseParameterSet(log types.Log) (*SettlementChainParameterRegistryParameterSet, error) {
+	event := new(SettlementChainParameterRegistryParameterSet)
+	if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "ParameterSet", log); err != nil {
+		return nil, err
+	}
+	event.Raw = log
+	return event, nil
+}
+
+// SettlementChainParameterRegistryUpgradedIterator is returned from FilterUpgraded and is used to iterate over the raw logs and unpacked data for Upgraded events raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryUpgradedIterator struct {
+	Event *SettlementChainParameterRegistryUpgraded // Event containing the contract specifics and raw log
+
+	contract *bind.BoundContract // Generic contract to use for unpacking event data
+	event    string              // Event name to use for unpacking event data
+
+	logs chan types.Log        // Log channel receiving the found contract events
+	sub  ethereum.Subscription // Subscription for errors, completion and termination
+	done bool                  // Whether the subscription completed delivering logs
+	fail error                 // Occurred error to stop iteration
+}
+
+// Next advances the iterator to the subsequent event, returning whether there
+// are any more events found. In case of a retrieval or parsing error, false is
+// returned and Error() can be queried for the exact failure.
+func (it *SettlementChainParameterRegistryUpgradedIterator) Next() bool {
+	// If the iterator failed, stop iterating
+	if it.fail != nil {
+		return false
+	}
+	// If the iterator completed, deliver directly whatever's available
+	if it.done {
+		select {
+		case log := <-it.logs:
+			it.Event = new(SettlementChainParameterRegistryUpgraded)
+			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+				it.fail = err
+				return false
+			}
+			it.Event.Raw = log
+			return true
+
+		default:
+			return false
+		}
+	}
+	// Iterator still in progress, wait for either a data or an error event
+	select {
+	case log := <-it.logs:
+		it.Event = new(SettlementChainParameterRegistryUpgraded)
+		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+			it.fail = err
+			return false
+		}
+		it.Event.Raw = log
+		return true
+
+	case err := <-it.sub.Err():
+		it.done = true
+		it.fail = err
+		return it.Next()
+	}
+}
+
+// Error returns any retrieval or parsing error occurred during filtering.
+func (it *SettlementChainParameterRegistryUpgradedIterator) Error() error {
+	return it.fail
+}
+
+// Close terminates the iteration process, releasing any pending underlying
+// resources.
+func (it *SettlementChainParameterRegistryUpgradedIterator) Close() error {
+	it.sub.Unsubscribe()
+	return nil
+}
+
+// SettlementChainParameterRegistryUpgraded represents a Upgraded event raised by the SettlementChainParameterRegistry contract.
+type SettlementChainParameterRegistryUpgraded struct {
+	Implementation common.Address
+	Raw            types.Log // Blockchain specific contextual infos
+}
+
+// FilterUpgraded is a free log retrieval operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b.
+//
+// Solidity: event Upgraded(address indexed implementation)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) FilterUpgraded(opts *bind.FilterOpts, implementation []common.Address) (*SettlementChainParameterRegistryUpgradedIterator, error) {
+
+	var implementationRule []interface{}
+	for _, implementationItem := range implementation {
+		implementationRule = append(implementationRule, implementationItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.FilterLogs(opts, "Upgraded", implementationRule)
+	if err != nil {
+		return nil, err
+	}
+	return &SettlementChainParameterRegistryUpgradedIterator{contract: _SettlementChainParameterRegistry.contract, event: "Upgraded", logs: logs, sub: sub}, nil
+}
+
+// WatchUpgraded is a free log subscription operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b.
+//
+// Solidity: event Upgraded(address indexed implementation)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) WatchUpgraded(opts *bind.WatchOpts, sink chan<- *SettlementChainParameterRegistryUpgraded, implementation []common.Address) (event.Subscription, error) {
+
+	var implementationRule []interface{}
+	for _, implementationItem := range implementation {
+		implementationRule = append(implementationRule, implementationItem)
+	}
+
+	logs, sub, err := _SettlementChainParameterRegistry.contract.WatchLogs(opts, "Upgraded", implementationRule)
+	if err != nil {
+		return nil, err
+	}
+	return event.NewSubscription(func(quit <-chan struct{}) error {
+		defer sub.Unsubscribe()
+		for {
+			select {
+			case log := <-logs:
+				// New log arrived, parse the event and forward to the user
+				event := new(SettlementChainParameterRegistryUpgraded)
+				if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Upgraded", log); err != nil {
+					return err
+				}
+				event.Raw = log
+
+				select {
+				case sink <- event:
+				case err := <-sub.Err():
+					return err
+				case <-quit:
+					return nil
+				}
+			case err := <-sub.Err():
+				return err
+			case <-quit:
+				return nil
+			}
+		}
+	}), nil
+}
+
+// ParseUpgraded is a log parse operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b.
+//
+// Solidity: event Upgraded(address indexed implementation)
+func (_SettlementChainParameterRegistry *SettlementChainParameterRegistryFilterer) ParseUpgraded(log types.Log) (*SettlementChainParameterRegistryUpgraded, error) {
+	event := new(SettlementChainParameterRegistryUpgraded)
+	if err := _SettlementChainParameterRegistry.contract.UnpackLog(event, "Upgraded", log); err != nil {
+		return nil, err
+	}
+	event.Raw = log
+	return event, nil
+}
diff --git a/pkg/blockchain/registryAdmin.go b/pkg/blockchain/registryAdmin.go
index c18ab7805..0c2ff066e 100644
--- a/pkg/blockchain/registryAdmin.go
+++ b/pkg/blockchain/registryAdmin.go
@@ -5,6 +5,9 @@ import (
 	"crypto/ecdsa"
 	"encoding/hex"
 	"fmt"
+	"strings"
+
+	paramReg "github.com/xmtp/xmtpd/pkg/abi/settlementchainparameterregistry"
 
 	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 	"github.com/ethereum/go-ethereum/common"
@@ -16,6 +19,10 @@ import (
 	"go.uber.org/zap"
 )
 
+const (
+	NODE_REGISTRY_MAX_CANONICAL_NODES_KEY = "xmtp.nodeRegistry.maxCanonicalNodes"
+)
+
 type INodeRegistryAdmin interface {
 	AddNode(
 		ctx context.Context,
@@ -30,10 +37,11 @@ type INodeRegistryAdmin interface {
 }
 
 type nodeRegistryAdmin struct {
-	client   *ethclient.Client
-	signer   TransactionSigner
-	logger   *zap.Logger
-	contract *noderegistry.NodeRegistry
+	client            *ethclient.Client
+	signer            TransactionSigner
+	logger            *zap.Logger
+	nodeContract      *noderegistry.NodeRegistry
+	parameterContract *paramReg.SettlementChainParameterRegistry
 }
 
 var _ INodeRegistryAdmin = &nodeRegistryAdmin{}
@@ -44,7 +52,7 @@ func NewNodeRegistryAdmin(
 	signer TransactionSigner,
 	contractsOptions config.ContractsOptions,
 ) (*nodeRegistryAdmin, error) {
-	contract, err := noderegistry.NewNodeRegistry(
+	nodeContract, err := noderegistry.NewNodeRegistry(
 		common.HexToAddress(contractsOptions.SettlementChain.NodeRegistryAddress),
 		client,
 	)
@@ -52,11 +60,20 @@ func NewNodeRegistryAdmin(
 		return nil, err
 	}
 
+	paramContract, err := paramReg.NewSettlementChainParameterRegistry(
+		common.HexToAddress(contractsOptions.SettlementChain.ParameterRegistryAddress),
+		client,
+	)
+	if err != nil {
+		return nil, err
+	}
+
 	return &nodeRegistryAdmin{
-		client:   client,
-		signer:   signer,
-		logger:   logger.Named("NodeRegistryAdmin"),
-		contract: contract,
+		client:            client,
+		signer:            signer,
+		logger:            logger.Named("NodeRegistryAdmin"),
+		nodeContract:      nodeContract,
+		parameterContract: paramContract,
 	}, nil
 }
 
@@ -79,7 +96,7 @@ func (n *nodeRegistryAdmin) AddNode(
 		n.logger,
 		n.client,
 		func(opts *bind.TransactOpts) (*types.Transaction, error) {
-			return n.contract.AddNode(
+			return n.nodeContract.AddNode(
 				opts,
 				ownerAddress,
 				signingKey,
@@ -87,7 +104,7 @@ func (n *nodeRegistryAdmin) AddNode(
 			)
 		},
 		func(log *types.Log) (interface{}, error) {
-			return n.contract.ParseNodeAdded(*log)
+			return n.nodeContract.ParseNodeAdded(*log)
 		},
 		func(event interface{}) {
 			nodeAdded, ok := event.(*noderegistry.NodeRegistryNodeAdded)
@@ -115,13 +132,13 @@ func (n *nodeRegistryAdmin) AddToNetwork(
 		n.logger,
 		n.client,
 		func(opts *bind.TransactOpts) (*types.Transaction, error) {
-			return n.contract.AddToNetwork(
+			return n.nodeContract.AddToNetwork(
 				opts,
 				nodeId,
 			)
 		},
 		func(log *types.Log) (interface{}, error) {
-			return n.contract.ParseNodeAddedToCanonicalNetwork(*log)
+			return n.nodeContract.ParseNodeAddedToCanonicalNetwork(*log)
 		},
 		func(event interface{}) {
 			nodeAdded, ok := event.(*noderegistry.NodeRegistryNodeAddedToCanonicalNetwork)
@@ -148,13 +165,13 @@ func (n *nodeRegistryAdmin) RemoveFromNetwork(
 		n.logger,
 		n.client,
 		func(opts *bind.TransactOpts) (*types.Transaction, error) {
-			return n.contract.RemoveFromNetwork(
+			return n.nodeContract.RemoveFromNetwork(
 				opts,
 				nodeId,
 			)
 		},
 		func(log *types.Log) (interface{}, error) {
-			return n.contract.ParseNodeRemovedFromCanonicalNetwork(*log)
+			return n.nodeContract.ParseNodeRemovedFromCanonicalNetwork(*log)
 		},
 		func(event interface{}) {
 			nodeRemoved, ok := event.(*noderegistry.NodeRegistryNodeRemovedFromCanonicalNetwork)
@@ -182,14 +199,14 @@ func (n *nodeRegistryAdmin) SetHttpAddress(
 		n.logger,
 		n.client,
 		func(opts *bind.TransactOpts) (*types.Transaction, error) {
-			return n.contract.SetHttpAddress(
+			return n.nodeContract.SetHttpAddress(
 				opts,
 				nodeId,
 				httpAddress,
 			)
 		},
 		func(log *types.Log) (interface{}, error) {
-			return n.contract.ParseHttpAddressUpdated(*log)
+			return n.nodeContract.ParseHttpAddressUpdated(*log)
 		},
 		func(event interface{}) {
 			httpAddressUpdated, ok := event.(*noderegistry.NodeRegistryHttpAddressUpdated)
@@ -211,20 +228,53 @@ func (n *nodeRegistryAdmin) SetMaxCanonical(
 	ctx context.Context,
 	limit uint8,
 ) error {
-	// TODO set the actual limit
+	err := ExecuteTransaction(
+		ctx,
+		n.signer,
+		n.logger,
+		n.client,
+		func(opts *bind.TransactOpts) (*types.Transaction, error) {
+			key := []byte(NODE_REGISTRY_MAX_CANONICAL_NODES_KEY)
 
-	return ExecuteTransaction(
+			var value [32]byte
+			// store uint8 in the last byte for big-endian compatibility
+			value[31] = limit
+
+			return n.parameterContract.Set0(opts, key, value)
+		},
+		func(log *types.Log) (interface{}, error) {
+			return n.parameterContract.ParseParameterSet(*log)
+		},
+		func(event interface{}) {
+			parameterSet, ok := event.(*paramReg.SettlementChainParameterRegistryParameterSet)
+			if !ok {
+				n.logger.Error(
+					"unexpected event type, not of type SettlementChainParameterRegistryParameterSet",
+				)
+				return
+			}
+			n.logger.Info("set parameter",
+				zap.String("key", NODE_REGISTRY_MAX_CANONICAL_NODES_KEY),
+				zap.Uint8("parameter", parameterSet.Value[31]),
+			)
+		},
+	)
+	if err != nil {
+		return err
+	}
+
+	err = ExecuteTransaction(
 		ctx,
 		n.signer,
 		n.logger,
 		n.client,
 		func(opts *bind.TransactOpts) (*types.Transaction, error) {
-			return n.contract.UpdateMaxCanonicalNodes(
+			return n.nodeContract.UpdateMaxCanonicalNodes(
 				opts,
 			)
 		},
 		func(log *types.Log) (interface{}, error) {
-			return n.contract.ParseMaxCanonicalNodesUpdated(*log)
+			return n.nodeContract.ParseMaxCanonicalNodesUpdated(*log)
 		},
 		func(event interface{}) {
 			maxCanonicalUpdated, ok := event.(*noderegistry.NodeRegistryMaxCanonicalNodesUpdated)
@@ -239,4 +289,14 @@ func (n *nodeRegistryAdmin) SetMaxCanonical(
 			)
 		},
 	)
+	if err != nil {
+		if strings.Contains(err.Error(), "NoChange") {
+			n.logger.Info("No update needed",
+				zap.Uint8("limit", limit),
+			)
+			return nil
+		}
+		return err
+	}
+	return nil
 }
diff --git a/pkg/blockchain/registryAdmin_test.go b/pkg/blockchain/registryAdmin_test.go
index 03da2a6da..3a685b956 100644
--- a/pkg/blockchain/registryAdmin_test.go
+++ b/pkg/blockchain/registryAdmin_test.go
@@ -80,3 +80,14 @@ func TestAddNodeBadOwner(t *testing.T) {
 	err := registry.AddNode(ctx, owner, &privateKey.PublicKey, httpAddress)
 	require.ErrorContains(t, err, "invalid owner address provided")
 }
+
+func TestSetMaxCanonical(t *testing.T) {
+	registry, _, ctx := buildRegistry(t)
+
+	err := registry.SetMaxCanonical(ctx, 16)
+	require.NoError(t, err)
+
+	// do it again to make sure the command does not fail with NoChange
+	err = registry.SetMaxCanonical(ctx, 16)
+	require.NoError(t, err)
+}
diff --git a/pkg/config/options.go b/pkg/config/options.go
index 19c363b8f..77290ae18 100644
--- a/pkg/config/options.go
+++ b/pkg/config/options.go
@@ -46,6 +46,7 @@ type SettlementChainOptions struct {
 	NodeRegistryRefreshInterval time.Duration `long:"node-registry-refresh-interval" env:"XMTPD_SETTLEMENT_CHAIN_NODE_REGISTRY_REFRESH_INTERVAL" description:"Refresh interval for the nodes registry" default:"60s"`
 	RateRegistryAddress         string        `long:"rate-registry-address"          env:"XMTPD_SETTLEMENT_CHAIN_RATE_REGISTRY_ADDRESS"          description:"Rate registry contract address"`
 	RateRegistryRefreshInterval time.Duration `long:"rate-registry-refresh-interval" env:"XMTPD_SETTLEMENT_CHAIN_RATE_REGISTRY_REFRESH_INTERVAL" description:"Refresh interval for the rate registry"  default:"300s"`
+	ParameterRegistryAddress    string        `long:"parameter-registry-address"     env:"XMTPD_SETTLEMENT_CHAIN_PARAMETER_REGISTRY_ADDRESS"     description:"Parameter Registry contract address"`
 }
 
 type DbOptions struct {
diff --git a/pkg/testutils/config.go b/pkg/testutils/config.go
index 35be40fef..b2cf7526d 100644
--- a/pkg/testutils/config.go
+++ b/pkg/testutils/config.go
@@ -18,6 +18,7 @@ const (
 	NODE_REGISTRY_ADDRESS             = "0xf82fd040a85154B4914a6cF81aCc3F83316c72CD"
 	GROUP_MESSAGE_BROADCAST_ADDRESS   = "0x7D7df4ed008d64b9b816007b69dcbf559b9C5494"
 	IDENTITY_UPDATE_BROADCAST_ADDRESS = "0xDCbC334a97c6a8DBe7d673bd52fA56718708BC9C"
+	PARAMETER_REGISTRY_ADDRESS        = "0x7FFc148AF5f00C56D78Bce732fC79a08007eC8be"
 )
 
 func NewContractsOptions(rpcUrl string) config.ContractsOptions {
@@ -36,6 +37,7 @@ func NewContractsOptions(rpcUrl string) config.ContractsOptions {
 			RateRegistryRefreshInterval: 10 * time.Second,
 			RateRegistryAddress:         RATE_REGISTRY_ADDRESS,
 			NodeRegistryAddress:         NODE_REGISTRY_ADDRESS,
+			ParameterRegistryAddress:    PARAMETER_REGISTRY_ADDRESS,
 		},
 	}
 }


Reviews:
user: login: mkysel
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-22T17:19:23Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (6)</summary><blockquote>

<details>
<summary>pkg/blockchain/registryAdmin.go (5)</summary><blockquote>

`22-24`: **Consider shortening / hashing the parameter‐key to avoid oversized topic hashing**

`NODE_REGISTRY_MAX_CANONICAL_NODES_KEY` is 35 bytes long (`"xmtp.nodeRegistry.maxCanonicalNodes"`).  
Although `bytes` is allowed as the indexed event parameter in `SettlementChainParameterRegistry`, the ABI encoder will **hash the entire value** to fit into a single topic (32 bytes).  
Long keys therefore provide no extra distinguishability on-chain, but incur extra gas when used elsewhere (e.g. as storage keys or map look-ups).

If backwards-compatibility isn’t required, think about:

```diff
-const NODE_REGISTRY_MAX_CANONICAL_NODES_KEY = "xmtp.nodeRegistry.maxCanonicalNodes"
+// keccak256("xmtp.nodeRegistry.maxCanonicalNodes") truncated / encoded
+var nodeRegistryMaxCanonicalKey = crypto.Keccak256([]byte("xmtp.nodeRegistry.maxCanonicalNodes"))
```

…and re-using the `nodeRegistryMaxCanonicalKey` byte slice everywhere.

---

`55-70`: **Avoid repeated contract instantiation logic**

Both contract instances are created with almost identical boiler-plate.  
Extracting a small helper function reduces duplication, centralises error handling, and simplifies future upgrades:

```diff
+func mustBind[T any](bindFn func(common.Address, *ethclient.Client) (T, error), addr string, client *ethclient.Client) (T, error) {
+    return bindFn(common.HexToAddress(addr), client)
+}
 ...
-nodeContract, err := noderegistry.NewNodeRegistry(
-    common.HexToAddress(contractsOptions.SettlementChain.NodeRegistryAddress),
-    client,
-)
+nodeContract, err := mustBind(noderegistry.NewNodeRegistry,
+    contractsOptions.SettlementChain.NodeRegistryAddress, client)
 ...
-paramContract, err := paramReg.NewSettlementChainParameterRegistry(
-    common.HexToAddress(contractsOptions.SettlementChain.ParameterRegistryAddress),
-    client,
-)
+paramContract, err := mustBind(paramReg.NewSettlementChainParameterRegistry,
+    contractsOptions.SettlementChain.ParameterRegistryAddress, client)
```
This keeps `NewNodeRegistryAdmin` readable and uniform.

---

`231-244`: **Pre-allocate immutable data to save per-call allocations**

`key := []byte(NODE_REGISTRY_MAX_CANONICAL_NODES_KEY)` allocates on every invocation.  
Store the byte slice as a `var` alongside the constant:

```go
var nodeRegistryMaxCanonicalKey = []byte(NODE_REGISTRY_MAX_CANONICAL_NODES_KEY)
...
key := nodeRegistryMaxCanonicalKey
```

This shaves a small amount of work from what may become a hot path in administration tooling.

---

`292-300`: **“NoChange” error detection is brittle**

String-contains checks on error text are fragile and may break with upstream changes.  
Prefer a typed error or inspect the revert data directly if the bindings expose one.

If that is not available, at least compare against a constant:

```go
const errNoChange = "NoChange"
...
if errors.Is(err, errNoChange) || strings.Contains(err.Error(), errNoChange) { ... }
```

---

`266-275`: **Potential redundant on-chain transaction**

`UpdateMaxCanonicalNodes()` is called unconditionally after setting the parameter, even if the parameter value hasn’t changed.  
You already swallow the “NoChange” revert, but that still pays gas for a failed transaction.

Consider optimising:

1. Call `parameterContract.Get0` first to see whether `limit` differs.
2. Only fire `Set0` / `UpdateMaxCanonicalNodes` when necessary.

This preserves idempotency while avoiding wasted gas.

</blockquote></details>
<details>
<summary>pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go (1)</summary><blockquote>

`1-36`: **Generated file – add linter directives**

Because this file is generated, linters such as `staticcheck` or `golangci-lint` may flag stylistic issues outside your control.  
Add the conventional directive at the top to silence them:

```go
// Code generated - DO NOT EDIT.
//go:build !lint
// +build !lint
```

(or whichever tag your tooling respects).  
This keeps CI noise low and focuses reviewers on hand-written code.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2789dfd553274a7dfa4dc33d7aa84e13768a64f3 and ae54a9673a8917e28761370deceb8b1ff36d3df8.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `dev/gen/abi` is excluded by `!**/gen/**`

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `dev/local.env` (1 hunks)
* `pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go` (1 hunks)
* `pkg/blockchain/registryAdmin.go` (10 hunks)
* `pkg/blockchain/registryAdmin_test.go` (1 hunks)
* `pkg/config/options.go` (1 hunks)
* `pkg/testutils/config.go` (2 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: Push Docker Images to GitHub Packages (xmtpd-cli)
* GitHub Check: Upgrade Tests
* GitHub Check: Test (Node)

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>dev/local.env (1)</summary>

`24-24`: **LGTM: Added new Parameter Registry address environment variable.**

The addition of the `XMTPD_SETTLEMENT_CHAIN_PARAMETER_REGISTRY_ADDRESS` environment variable is consistent with the existing pattern for settlement chain contracts.

</details>
<details>
<summary>pkg/config/options.go (1)</summary>

`49-49`: **LGTM: Added Parameter Registry address configuration.**

The new `ParameterRegistryAddress` field in the `SettlementChainOptions` struct properly follows the established pattern for contract address configurations and correctly maps to the environment variable added in `local.env`.

</details>
<details>
<summary>pkg/blockchain/registryAdmin_test.go (1)</summary>

`84-93`: **LGTM: Good test coverage for the SetMaxCanonical functionality.**

The test verifies that the `SetMaxCanonical` method works correctly and handles repeated calls with the same value without errors. It follows the existing testing patterns in the file.

</details>
<details>
<summary>pkg/testutils/config.go (2)</summary>

`21-21`: **LGTM: Added Parameter Registry address constant.**

The new constant for the Parameter Registry address is correctly defined and follows the established naming pattern.

---

`40-40`: **LGTM: Updated test configuration with Parameter Registry address.**

The `NewContractsOptions` function is properly updated to include the Parameter Registry address in the returned configuration.

</details>
<details>
<summary>pkg/blockchain/registryAdmin.go (1)</summary>

`71-77`: **Field ordering hint**

Placing fields that are frequently accessed together next to each other (e.g. `nodeContract`, `parameterContract`) can improve cache locality and readability. Consider grouping the two contract handles.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T17:21:52Z
user: login: neekolas
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:47:17Z

Review comments:
diff_hunk: @@ -211,20 +228,53 @@ func (n *nodeRegistryAdmin) SetMaxCanonical(
 	ctx context.Context,
 	limit uint8,
 ) error {
-	// TODO set the actual limit
+	err := ExecuteTransaction(
+		ctx,
+		n.signer,
+		n.logger,
+		n.client,
+		func(opts *bind.TransactOpts) (*types.Transaction, error) {
+			key := []byte(NODE_REGISTRY_MAX_CANONICAL_NODES_KEY)
 
-	return ExecuteTransaction(
+			var value [32]byte
+			// store uint8 in the last byte for big-endian compatibility
+			value[31] = limit
path: pkg/blockchain/registryAdmin.go
user: login: mkysel
body: FYI @deluca-mike. This is the endian-ness we need
created_at: 2025-05-22T17:19:23Z
author_association: COLLABORATOR
diff_hunk: @@ -211,20 +228,53 @@ func (n *nodeRegistryAdmin) SetMaxCanonical(
 	ctx context.Context,
 	limit uint8,
 ) error {
-	// TODO set the actual limit
+	err := ExecuteTransaction(
+		ctx,
+		n.signer,
+		n.logger,
+		n.client,
+		func(opts *bind.TransactOpts) (*types.Transaction, error) {
+			key := []byte(NODE_REGISTRY_MAX_CANONICAL_NODES_KEY)
 
-	return ExecuteTransaction(
+			var value [32]byte
+			// store uint8 in the last byte for big-endian compatibility
+			value[31] = limit
+
+			return n.parameterContract.Set0(opts, key, value)
+		},
path: pkg/blockchain/registryAdmin.go
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Big-endian encoded value may collide with future multi-byte parameters**

Storing the `uint8` in `value[31]` is correct for a single byte, but if the parameter were ever upgraded to a wider width (say `uint16`), the encoding would silently break.

Guard against future misuse:

```go
if limit == 0 {
    return fmt.Errorf("limit must be >0")
}
var value [32]byte
value[31] = limit // keep explicit comment
```

Or encode with `binary.BigEndian.PutUint32(value[28:], uint32(limit))` for better forward-compatibility.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In pkg/blockchain/registryAdmin.go around lines 240 to 244, the code stores a
uint8 value directly in the last byte of a 32-byte array, which works now but
risks breaking if the parameter width increases in the future. To fix this, add
a guard clause to ensure the limit is greater than zero, then encode the limit
using a multi-byte big-endian encoding like binary.BigEndian.PutUint32 starting
at value[28] to reserve space for potential wider values, while keeping the
explicit comment about storing the value for clarity.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-22T17:21:52Z
author_association: CONTRIBUTOR

PR comments:
user: login: graphite-app[bot]
created_at: 2025-05-22T17:18:11Z
body: ## How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:
- <kbd>Queue</kbd> - adds this PR to the back of the merge queue
- <kbd>Hotfix</kbd> - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using [this link](https://app.graphite.dev/invite/github/xmtp?ref=merge-queue-instructions-comment&prId=6258680570).

<h2></h2>
<sub>

An organization admin has enabled the [Graphite Merge Queue](https://graphite.dev/docs/graphite-merge-queue) in this repository.

</sub>

<sub>

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

</sub>
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-22T17:18:59Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

A new Ethereum smart contract binding for `SettlementChainParameterRegistry` was introduced, along with corresponding configuration, environment variable, and test updates. The blockchain registry admin logic was updated to interact with this new contract for setting parameters, and a new test verifies this functionality. Configuration structures and test utilities were extended to support the parameter registry address.

## Changes

| File(s)                                                                                   | Change Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| pkg/abi/settlementchainparameterregistry/SettlementChainParameterRegistry.go              | Added a comprehensive Go binding for the `SettlementChainParameterRegistry` Ethereum smart contract, including deployment, function calls, event filters/watchers/parsers, and session handling.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| pkg/blockchain/registryAdmin.go                                                          | Integrated the new `SettlementChainParameterRegistry` contract into the `nodeRegistryAdmin` struct and logic. Updated the constructor to instantiate both `nodeContract` and `parameterContract`. Modified `SetMaxCanonical` to set the parameter in the new contract before updating the node registry, with enhanced error handling and logging.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| pkg/blockchain/registryAdmin_test.go                                                     | Added `TestSetMaxCanonical` to test setting the max canonical nodes parameter via the new registry admin logic, including idempotency verification.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| pkg/config/options.go                                                                    | Added `ParameterRegistryAddress` field to the `SettlementChainOptions` struct, with metadata for command-line flag, environment variable, and description.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| pkg/testutils/config.go                                                                  | Introduced `PARAMETER_REGISTRY_ADDRESS` constant and updated `NewContractsOptions` to set the new address in the `SettlementChainOptions` struct.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| dev/local.env                                                                            | Added `XMTPD_SETTLEMENT_CHAIN_PARAMETER_REGISTRY_ADDRESS` environment variable with the new contract address.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Admin as nodeRegistryAdmin
    participant ParamReg as SettlementChainParameterRegistry
    participant NodeReg as NodeRegistry

    Admin->>ParamReg: Set0(key, value) (set max canonical nodes parameter)
    Note right of ParamReg: Emits ParameterSet event
    Admin->>NodeReg: UpdateMaxCanonicalNodes(limit)
    alt NoChange error
        Admin->>Admin: Log "no update needed"
    else Success
        Note right of NodeReg: Emits MaxCanonicalNodesUpdated event
    end
```

```mermaid
sequenceDiagram
    participant Test as TestSetMaxCanonical
    participant Admin as nodeRegistryAdmin

    Test->>Admin: SetMaxCanonical(ctx, 16)
    Admin-->>Test: nil (success)
    Test->>Admin: SetMaxCanonical(ctx, 16)
    Admin-->>Test: nil (idempotent success)
```

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.
> 
> <details>
> <summary>🔧 golangci-lint (1.64.8)</summary>
> 
> Error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
> Failed executing command with error: you are using a configuration file for golangci-lint v2 with golangci-lint v1: please use golangci-lint v2
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=xmtp/xmtpd&utm_content=830):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR