Conversation
52bb4af to
538047d
Compare
401a465 to
483982d
Compare
…red bonding Core Contracts: - FleetIdentity (ERC-721): UUID-based fleet ownership with geographic registration - Two-level registration: Country (ISO 3166-1) or Local (admin area) - Geometric bond tiers: BASE_BOND * 2^tier (local), *16 for country - Operator delegation for cold/hot wallet separation - O(1) setOperator via uuidTotalTierBonds tracking - ServiceProvider (ERC-721): Service endpoint URL ownership - SwarmRegistryL1: SSTORE2 filter storage for Ethereum L1 - SwarmRegistryUniversal: Native bytes storage for ZkSync Era Registration Model: - Fresh registration: caller becomes owner+operator, pays BASE_BOND + tierBond - Owned-to-Registered: only operator can register, pays tierBond - Multi-region: same UUID can register in multiple regions at same level - Burn refunds: BASE_BOND to owner (on last token), tierBond to operator Discovery: - buildHighestBondedUuidBundle: priority-ordered bundle (up to 20 UUIDs) - XOR filter membership verification for tag validation - Inclusion hints for optimal tier selection Documentation: - Complete technical specification in src/swarms/doc/ - ISO 3166-2 admin area mappings for 18 countries
3bc4f36 to
540f23a
Compare
- Modify computeSwarmId to use (fleetUuid, filterData, fingerprintSize, tagType) instead of (fleetUuid, providerId, filterData) - This ensures swarm identity is based on immutable fleet/filter properties, not mutable provider relationship - Remove updateSwarmFilter() function since changing filter changes swarm identity - Update provider validation to use try/catch pattern making ProviderDoesNotExist error reachable - Add test_checkMembership_tinyFilter_returnsFalse for m==0 edge case - Coverage: SwarmRegistryL1 97.01%, SwarmRegistryUniversal 98.55% (123 tests passing)
- Fix Swarm ID derivation: uses keccak256(fleetUuid, filter, fpSize, tagType), not keccak256(fleetUuid, providerId, filter). ProviderId is mutable. - Remove non-existent updateSwarmFilter function (filter is immutable as part of identity) - Remove non-existent unregisterToOwned() and releaseUuid() - everything uses burn() - Remove non-existent registerFleetLocalWithOperator - use claimUuid(uuid, operator) instead - Fix TIER_CAPACITY: 10 members per tier (was incorrectly 4) - Fix operator permissions: can set operator for owned UUIDs - Fix burn flow: registered tokens burned by operator, owned-only by owner - Fix tier management: only operator can promote/demote registered tokens - Update struct field order in code examples to match actual contracts
- Convert FleetIdentity, ServiceProvider, SwarmRegistryL1, SwarmRegistryUniversal to UUPS upgradeable pattern - Implement ERC1967Proxy for transparent proxy pattern with storage isolation - Add Ownable2Step for safer ownership transfers - Preserve all public APIs and storage layouts Deployment: - Add DeploySwarmUpgradeable.s.sol for initial deployment - Add UpgradeSwarm.s.sol helper script for contract upgrades - Support both SwarmRegistryL1 (L1 SSTORE2) and SwarmRegistryUniversal (L2) deployments Testing: - Migrate all existing tests to upgradeable versions - 12 ServiceProvider tests - 216 FleetIdentity tests - 13 FleetIdentityFairness tests - 69 SwarmRegistryUniversal tests - 60 SwarmRegistryL1 tests - Add test coverage: 95.74% FleetIdentity, 94.41% SwarmRegistryL1, 95.92% SwarmRegistryUniversal, 85% ServiceProvider - Add comprehensive upgrade demo (test/upgrade-demo/) showing - Convert FleetIdentity, ServiceProvider, SwarmRegistryL1, SwarmRegistryUniversal to UUPS upgradeable pattern - Impleost- Implement ERC1967Proxy for transparent proxy pattern with storage isolation - Add Ownable2Step for safer ost- Add Ownable2Step for safer ownership transfers - Preserve all public APIs .m- Preserve all public APIs and storage layouts t Deployment: - Add DeploySwarmUpgradeable.s.sn f- Add Depll - Add UpgradeSwarm.s.sol helper script for contract upgr r- Support both SwarmRegistryL1 (L1 SSTORE2) and SwarmRegistEI Testing: - Migrate all existing tests to upgradeable versions - 12 ServiceProvider Rem- Migran- - 12 ServiceProvider tests - 216 FleetIdentity erviceProvider.sol, etc.) - All - 13 FleetIdentityFairneea - 69 SwarmRegistryUniversal tes
There was a problem hiding this comment.
Pull request overview
Implements UUPS-upgradeable versions of the BLE swarm registry system (FleetIdentity, ServiceProvider, SwarmRegistry) with supporting deployment/upgrade scripts, tests, CI coverage, and extensive technical documentation (including ISO 3166 mappings).
Changes:
- Added UUPS-upgradeable swarm contracts (Universal + L1 variants) and deployment/upgrade scripts.
- Added/updated Foundry tests (upgrade tests, ServiceProvider tests, FleetIdentity “fairness” analysis) and CI coverage enforcement.
- Added comprehensive docs for registration/discovery/operations/upgradeability plus ISO 3166-2 admin-area mapping references.
Reviewed changes
Copilot reviewed 54 out of 61 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| test/upgradeable/UpgradeableContracts.t.sol | Adds upgradeability/initializer/storage-persistence tests using V2 mock implementations |
| test/upgrade-demo/TestUpgradeOnAnvil.s.sol | Adds anvil-zksync demo script to deploy V1, create state, upgrade to V2, and verify |
| test/upgrade-demo/README.md | Documents how to run the upgrade demo on anvil-zksync |
| test/contentsign/PaymentMiddleware.t.sol | Removes unused console/SafeERC20 imports from tests |
| test/contentsign/BaseContentSign.t.sol | Removes unused console import from tests |
| test/helpers/MockERC20.sol | Adds a reusable ERC20 mock for tests (mint + configurable decimals) |
| test/ServiceProvider.t.sol | Adds unit + fuzz tests for ServiceProviderUpgradeable |
| test/FleetIdentityFairness.t.sol | Adds economic/fairness scenario tests around bundle selection and tier economics |
| src/swarms/doc/upgradeable-contracts.md | Adds upgradeability architecture guide and operational runbooks |
| src/swarms/doc/swarm-operations.md | Adds swarm registration/approval/update/deletion operational documentation |
| src/swarms/doc/maintenance.md | Adds operator delegation + tier maintenance guidance |
| src/swarms/doc/lifecycle.md | Adds state machine docs for UUID registration and swarm lifecycle |
| src/swarms/doc/iso3166-reference.md | Adds ISO 3166-1/2 encoding reference and usage examples |
| src/swarms/doc/iso3166-2/840-United_States.md | Adds USA admin-area mapping table |
| src/swarms/doc/iso3166-2/826-United_Kingdom.md | Adds UK admin-area mapping table |
| src/swarms/doc/iso3166-2/756-Switzerland.md | Adds Switzerland admin-area mapping table |
| src/swarms/doc/iso3166-2/724-Spain.md | Adds Spain admin-area mapping table |
| src/swarms/doc/iso3166-2/710-South_Africa.md | Adds South Africa admin-area mapping table |
| src/swarms/doc/iso3166-2/643-Russia.md | Adds Russia admin-area mapping table |
| src/swarms/doc/iso3166-2/566-Nigeria.md | Adds Nigeria admin-area mapping table |
| src/swarms/doc/iso3166-2/484-Mexico.md | Adds Mexico admin-area mapping table |
| src/swarms/doc/iso3166-2/410-South_Korea.md | Adds South Korea admin-area mapping table |
| src/swarms/doc/iso3166-2/392-Japan.md | Adds Japan admin-area mapping table |
| src/swarms/doc/iso3166-2/380-Italy.md | Adds Italy admin-area mapping table |
| src/swarms/doc/iso3166-2/356-India.md | Adds India admin-area mapping table |
| src/swarms/doc/iso3166-2/276-Germany.md | Adds Germany admin-area mapping table |
| src/swarms/doc/iso3166-2/250-France.md | Adds France admin-area mapping table |
| src/swarms/doc/iso3166-2/156-China.md | Adds China admin-area mapping table |
| src/swarms/doc/iso3166-2/124-Canada.md | Adds Canada admin-area mapping table |
| src/swarms/doc/iso3166-2/076-Brazil.md | Adds Brazil admin-area mapping table |
| src/swarms/doc/iso3166-2/036-Australia.md | Adds Australia admin-area mapping table |
| src/swarms/doc/fleet-registration.md | Adds detailed fleet/UUID registration flows + economics |
| src/swarms/doc/discovery.md | Adds client discovery flows and membership verification guidance |
| src/swarms/doc/data-model.md | Adds contract/interface diagrams and data model descriptions |
| src/swarms/doc/assistant-guide.md | Adds system architecture guide intended for assistant/agent context |
| src/swarms/doc/README.md | Adds documentation hub/index for the swarm technical specification |
| src/swarms/SwarmRegistryUniversalUpgradeable.sol | Introduces UUPS-upgradeable universal registry storing filters in bytes |
| src/swarms/SwarmRegistryL1Upgradeable.sol | Introduces UUPS-upgradeable L1 registry storing filters via SSTORE2 |
| src/swarms/ServiceProviderUpgradeable.sol | Introduces UUPS-upgradeable ERC721 ownership of provider URLs |
| src/swarms/FleetIdentityUpgradeable.sol | Introduces UUPS-upgradeable FleetIdentity with tiered bonds + operator model |
| script/UpgradeSwarm.s.sol | Adds upgrade script for deployed proxies with optional reinitializer calldata |
| script/DeploySwarmUpgradeable.s.sol | Adds deployment script for proxies + implementations |
| remappings.txt | Updates Foundry remappings for OZ, OZ-upgradeable, and solady |
| lib/solady | Adds solady as a git submodule |
| lib/openzeppelin-contracts-upgradeable | Adds OZ upgradeable contracts as a git submodule |
| foundry.toml | Enables optimizer and adds lint config |
| foundry.lock | Locks dependency revisions/tags for Foundry |
| .vscode/settings.json | Updates VS Code Solidity formatter settings + adds terminal auto-approve config |
| .gitmodules | Registers new git submodules (solady + openzeppelin-contracts-upgradeable) |
| .github/workflows/checks.yml | Updates CI test command and adds coverage job + threshold checking |
| .github/copilot-instructions.md | Adds repo-specific Copilot instructions for Solidity/ZkSync work |
| .cspell.json | Updates spelling config to include new docs and terms |
| .agent/rules/solidity_zksync.md | Adds agent rules for Solidity/ZkSync standards |
Comments suppressed due to low confidence (3)
test/upgradeable/UpgradeableContracts.t.sol:1
- The comment implies the V1
__gapwas reduced, but in an inherited V2 mock the original V1 gap still exists in the layout and the new variables are appended after it (the extra__gap_v2does not “reduce” the inherited gap). To avoid misleading readers about upgrade-safe storage layout, either adjust the comment to reflect the actual layout or structure the mock V2 in a way that truly consumes the V1 gap.
test/FleetIdentityFairness.t.sol:1 - The test name references an “8x” multiplier, but the assertions in the body verify a 16× multiplier (
COUNTRY_BOND_MULTIPLIER() == 16andcountryBond == localBond * 16). Renaming the test to reflect 16× will make the intent clearer and avoid confusion when reading failures.
src/swarms/doc/upgradeable-contracts.md:1 - This shell example is likely to fail as written because the inline
#comment comes after a line-continuation backslash, and0x \includes trailing characters. Consider rewriting the snippet so the0xargument is clean (e.g.,0xon its own line without a trailing backslash+comment), and move the explanatory comment to a separate line.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Check if coverage is below 95% | ||
| THRESHOLD=95 | ||
| if awk "BEGIN {exit !($COVERAGE < $THRESHOLD)}"; then | ||
| echo "Error: Line coverage ($COVERAGE%) is below the required threshold ($THRESHOLD%)" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "Coverage check passed: $COVERAGE% >= $THRESHOLD%" |
There was a problem hiding this comment.
The coverage-threshold condition is inverted: as written, it will fail when coverage is >= threshold and pass when coverage is below threshold. Flip the awk predicate/exit code so the job exits non-zero only when COVERAGE < THRESHOLD (or compare COVERAGE >= THRESHOLD for the passing case).
| # Check if coverage is below 95% | |
| THRESHOLD=95 | |
| if awk "BEGIN {exit !($COVERAGE < $THRESHOLD)}"; then | |
| echo "Error: Line coverage ($COVERAGE%) is below the required threshold ($THRESHOLD%)" | |
| exit 1 | |
| fi | |
| echo "Coverage check passed: $COVERAGE% >= $THRESHOLD%" | |
| # Check if coverage meets or exceeds 95% | |
| THRESHOLD=95 | |
| if awk "BEGIN {exit ($COVERAGE >= $THRESHOLD ? 0 : 1)}"; then | |
| echo "Coverage check passed: $COVERAGE% >= $THRESHOLD%" | |
| else | |
| echo "Error: Line coverage ($COVERAGE%) is below the required threshold ($THRESHOLD%)" | |
| exit 1 | |
| fi |
| if (countryCode == 0) revert InvalidCountryCode(); | ||
| if (adminCode == 0) revert AdminAreaRequired(); |
There was a problem hiding this comment.
buildHighestBondedUuidBundle validates countryCode == 0 and adminCode == 0 but does not enforce the documented bounds (countryCode <= 999, adminCode <= 255). This makes the view behave inconsistently with the registration methods and can produce meaningless region keys for out-of-range inputs; add the same upper-bound checks used elsewhere in the contract.
| if (countryCode == 0) revert InvalidCountryCode(); | |
| if (adminCode == 0) revert AdminAreaRequired(); | |
| if (countryCode == 0 || countryCode > MAX_COUNTRY_CODE) revert InvalidCountryCode(); | |
| if (adminCode == 0 || adminCode > 255) revert AdminAreaRequired(); |
| }, | ||
| "chat.tools.terminal.autoApprove": { | ||
| "forge": true |
There was a problem hiding this comment.
Enabling repo-wide terminal auto-approval for forge can cause unsafe command execution for contributors using compatible chat tooling (it changes behavior outside the Solidity code). Consider removing this from the repository settings (or moving it to personal/user settings) so running terminal commands always requires explicit user confirmation.
| }, | |
| "chat.tools.terminal.autoApprove": { | |
| "forge": true |
| pragma solidity ^0.8.24; | ||
|
|
||
| import {Script, console} from "forge-std/Script.sol"; | ||
| import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; |
There was a problem hiding this comment.
This import is not referenced anywhere in the script. Removing it reduces clutter and avoids implying that the script depends on UUPS internals beyond calling upgradeToAndCall on the proxy contract types.
| import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; |
| } else { | ||
| console.log("Calling upgradeTo..."); | ||
| proxyContract.upgradeToAndCall(impl, ""); | ||
| } |
There was a problem hiding this comment.
The log message says upgradeTo... but the code calls upgradeToAndCall (with empty calldata). Either call upgradeTo(impl) in the no-reinit branch, or update the log message to match the actual function being invoked to prevent operator confusion during upgrades.
|
|
||
| All contracts are **permissionless** — access control is enforced through NFT ownership rather than admin roles. `FleetIdentity` additionally requires an ERC-20 bond (e.g. NODL) to register a fleet, acting as an anti-spam / anti-abuse mechanism. | ||
|
|
||
| Both NFT contracts support **burning**. For `FleetIdentity`, owned-only tokens can be burned by the owner (refunds BASE*BOND), while registered tokens can only be burned by the operator (refunds tier bond). Burning a `ServiceProvider` token requires owner rights. Burning either NFT makes any swarms referencing that token \_orphaned*. |
There was a problem hiding this comment.
Correct the bond constant formatting: BASE*BOND should be BASE_BOND.
| Both NFT contracts support **burning**. For `FleetIdentity`, owned-only tokens can be burned by the owner (refunds BASE*BOND), while registered tokens can only be burned by the operator (refunds tier bond). Burning a `ServiceProvider` token requires owner rights. Burning either NFT makes any swarms referencing that token \_orphaned*. | |
| Both NFT contracts support **burning**. For `FleetIdentity`, owned-only tokens can be burned by the owner (refunds BASE_BOND), while registered tokens can only be burned by the operator (refunds tier bond). Burning a `ServiceProvider` token requires owner rights. Burning either NFT makes any swarms referencing that token \_orphaned*. |
- Reorder params: (owner, bondToken, baseBond, countryMultiplier) - bondToken required; baseBond=0 uses DEFAULT_BASE_BOND (1e18) - countryMultiplier=0 uses DEFAULT_COUNTRY_BOND_MULTIPLIER (16) - Simplify countryBondMultiplier() getter (no more fallback logic) - Update all test files with new signature - Coverage: 96.77% line, 95.58% branch
…rint - Replace uint8 fingerprintSize with FingerprintSize enum (BITS_8, BITS_16) - Optimize _readFingerprint: direct byte access without loops/divisions - Simplify checkMembership m calculation with ternary operator - Remove InvalidFingerprintSize error (enum enforces valid values) - Update tests for both SwarmRegistryL1 and SwarmRegistryUniversal - Add missing technical terms to cspell dictionary
…ests - Add getFilterData() function to SwarmRegistryL1Upgradeable for parity with Universal - Install lcov in CI workflow before running coverage report action (fixes genhtml missing error) - Add coverage tests for 8-bit fingerprint path and upgrade authorization - Fix test string identifiers for spellcheck compliance
LCOV of commit
|
- SwarmRegistryL1Upgradeable uses SSTORE2 which relies on EXTCODECOPY - EXTCODECOPY is not supported on ZkSync Era, causing tx drops - Updated test to use regular anvil instead of anvil-zksync - Added Phase 1B to verify V1 initializers run correctly - Updated README with correct instructions and warnings
- Create src/swarms/interfaces/ directory with:
- SwarmTypes.sol: shared enums (RegistrationLevel, SwarmStatus, TagType,
FingerprintSize) for use by contracts and external integrators
- IFleetIdentity.sol: public interface for FleetIdentityUpgradeable
- IServiceProvider.sol: public interface for ServiceProviderUpgradeable
- ISwarmRegistry.sol: common interface for SwarmRegistry variants
- Update contracts to import shared enums from SwarmTypes.sol instead of
defining them inline, reducing duplication
- Update tests to use imported enums from SwarmTypes.sol
This follows the recommended UUPS pattern of defining public interfaces
separately to make the expected API surface explicit across upgrades.
The zgosalvez/github-actions-report-lcov action was checking 95% coverage against the entire project (32%). The custom shell script step below correctly filters and checks coverage for src/swarms/* files only.
- Add Interface Files section to data-model.md - Add interfaces/ link to README documentation table - Reference SwarmTypes.sol location in assistant-guide.md
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 58 out of 65 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (3)
src/swarms/interfaces/ISwarmRegistry.sol:1
SwarmRegistryUniversalUpgradeableemitsSwarmRegisteredwith an extrafilterSizeargument, which changes the event signature/topic0 and makes this interface event incorrect for Universal deployments. Consider standardizing the event signature across implementations (e.g., adduint32 filterSizeto the interface and also emit it from the L1 implementation, or emit a second “extended” event name in Universal while keeping the canonical 4-arg event for compatibility).
test/upgradeable/UpgradeableContracts.t.sol:1- In an inherited V2 implementation, adding
__gap_v2does not “reduce” the parent contract’s__gap; the parent__gapremains part of the layout and the new variables are appended after it. To avoid teaching a misleading upgrade pattern, either (a) remove/reword the “reduce gap” comments, or (b) demonstrate the real gap-consumption approach by updating the original contract’s__gapsize in a V2 copy rather than adding a second gap in the child.
test/FleetIdentityFairness.t.sol:1 - The test name says “8xMultiplier” but the assertions validate a 16× multiplier. Rename the test to reflect the actual invariant being checked (e.g.,
test_economicAdvantage_16xMultiplier) to keep intent clear.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| console.log(" Implementation:", fleetIdentityImpl); | ||
|
|
||
| bytes memory fleetIdentityInitData = | ||
| abi.encodeWithSelector(FleetIdentityUpgradeable.initialize.selector, bondToken, baseBond, owner); |
There was a problem hiding this comment.
The initializer calldata for FleetIdentityUpgradeable.initialize appears to use a different parameter order/arity than the tests in this PR (which call initialize(owner, bondToken, baseBond, ...)). As written, this is likely to revert at runtime or fail to compile if the signature doesn’t match; update the deploy script to use abi.encodeCall(FleetIdentityUpgradeable.initialize, (...)) with the exact initializer argument order used by the contract/tests.
| abi.encodeWithSelector(FleetIdentityUpgradeable.initialize.selector, bondToken, baseBond, owner); | |
| abi.encodeCall(FleetIdentityUpgradeable.initialize, (owner, bondToken, baseBond)); |
| if (keccak256(bytes(contractType)) == keccak256("ServiceProvider")) { | ||
| newImpl = _upgradeServiceProvider(proxyAddress, reinitData); | ||
| } else if (keccak256(bytes(contractType)) == keccak256("FleetIdentity")) { | ||
| newImpl = _upgradeFleetIdentity(proxyAddress, reinitData); | ||
| } else if (keccak256(bytes(contractType)) == keccak256("SwarmRegistryUniversal")) { | ||
| newImpl = _upgradeSwarmRegistryUniversal(proxyAddress, reinitData); | ||
| } else if (keccak256(bytes(contractType)) == keccak256("SwarmRegistryL1")) { | ||
| newImpl = _upgradeSwarmRegistryL1(proxyAddress, reinitData); | ||
| } else { | ||
| revert("Invalid CONTRACT_TYPE. Use: ServiceProvider, FleetIdentity, SwarmRegistryUniversal, SwarmRegistryL1"); | ||
| } |
There was a problem hiding this comment.
keccak256("...") is not valid because keccak256 expects bytes (not a string literal). Use keccak256(bytes("ServiceProvider")) (and likewise for the other constants), or compare strings via keccak256(bytes(contractType)) == keccak256(bytes(CONSTANT)) where CONSTANT is a string.
| } | ||
| if (filter.length > MAX_FILTER_SIZE) { | ||
| revert FilterTooLarge(); | ||
| } |
There was a problem hiding this comment.
registerSwarm does not validate fpSize values beyond the enum type, but Solidity enums can still carry invalid underlying values supplied by callers. Since checkMembership branches on fpSize and otherwise defaults to the 16-bit path, an invalid fpSize can cause inconsistent/misleading membership behavior. Add an explicit check that fpSize is either FingerprintSize.BITS_8 or FingerprintSize.BITS_16 (and optionally enforce filter.length parity for BITS_16 if that’s a requirement of your encoding).
| } | |
| } | |
| if (fpSize != FingerprintSize.BITS_8 && fpSize != FingerprintSize.BITS_16) { | |
| revert("Invalid fingerprint size"); | |
| } |
| @@ -0,0 +1,142 @@ | |||
| # Lifecycle & State Machines | |||
There was a problem hiding this comment.
The markdown code fences use quadruple backticks (````) around a mermaid block, which will likely render incorrectly (and the opening fence at line 107 appears unmatched/unnecessary). Use consistent triple-backtick fences and ensure each opening fence has a matching closing fence so the document renders reliably.
| | :------ | :------------------------ | :------------------------------ | ------------------------------- | | ||
| | Owned | `BASE_BOND` | Owner | | ||
| | Local | `BASE_BOND × 2^tier` | Operator (owner paid BASE_BOND) | Operator (owner paid BASE_BOND) | |
There was a problem hiding this comment.
This table’s header/separator rows define 3 columns but the separator row defines 4, and the “Local” row repeats the “Who Pays” cell (4 columns). Fix the markdown table to a consistent column count so it renders correctly and doesn’t confuse the “Who Pays” semantics.
| | :------ | :------------------------ | :------------------------------ | ------------------------------- | | |
| | Owned | `BASE_BOND` | Owner | | |
| | Local | `BASE_BOND × 2^tier` | Operator (owner paid BASE_BOND) | Operator (owner paid BASE_BOND) | | |
| | :------ | :------------------------ | :------------------------------ | | |
| | Owned | `BASE_BOND` | Owner | | |
| | Local | `BASE_BOND × 2^tier` | Operator (owner paid BASE_BOND) | |
BLE Swarm Registry System with Geographic Tiered Bonding
A complete BLE tag registry enabling decentralized device discovery using cryptographic membership proofs.
Core Contracts
BASE_BOND * 2^tier(local),*16for countrysetOperatorviauuidTotalTierBondstrackingRegistration Model
BASE_BOND + tierBondtierBondtierBondper regiontierBondtierBond, owner getsBASE_BONDDiscovery
buildHighestBondedUuidBundle: Priority-ordered bundle (up to 20 UUIDs)localInclusionHint,countryInclusionHint) for optimal tier selectionDocumentation
Complete technical specification in
src/swarms/doc/:Checklist