Skip to content

Commit fe60a7f

Browse files
authored
feat(indexer): canonical staking naming and local-dev resilience (#96)
* feat(indexer): canonical staking naming and local-dev resilience * refactor: canonicalize staking terminology across non-native flows
1 parent faae03f commit fe60a7f

86 files changed

Lines changed: 719 additions & 549 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AUDIT-PAYMENTS.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
**Date:** 2026-02-03
44
**Auditor:** Subagent (payments-audit)
5-
**Scope:** Payment distribution flow, operator vs restaker compensation
5+
**Scope:** Payment distribution flow, operator vs staker compensation
66

77
---
88

99
## Executive Summary
1010

11-
The claims under investigation are **TECHNICALLY VALID** but the design appears **INTENTIONAL** for a restaking protocol where operator compensation is based on **risk commitment** (exposure) rather than **capital provided** (stake).
11+
The claims under investigation are **TECHNICALLY VALID** but the design appears **INTENTIONAL** for a staking protocol where operator compensation is based on **risk commitment** (exposure) rather than **capital provided** (stake).
1212

1313
### Claims Investigated:
1414
1.**CONFIRMED**: "Operator payment share is based on exposureBps (their claimed commitment), NOT actual stake"
@@ -74,7 +74,7 @@ function joinService(uint64 serviceId, uint16 exposureBps) external whenNotPause
7474
```solidity
7575
function calculateOperatorPayments(
7676
uint256 totalOperatorAmount,
77-
uint256 totalRestakerAmount,
77+
uint256 totalStakerAmount,
7878
address[] memory operators,
7979
uint16[] memory exposures, // <-- This is exposureBps, NOT stake
8080
uint256 totalExposure
@@ -93,12 +93,12 @@ function calculateOperatorPayments(
9393
payments[i] = OperatorPayment({
9494
operator: operators[i],
9595
operatorShare: totalOperatorAmount - operatorDistributed,
96-
restakerShare: totalRestakerAmount - restakerDistributed
96+
stakerShare: totalStakerAmount - stakerDistributed
9797
});
9898
} else {
9999
// KEY CALCULATION: Payment based on exposure, NOT actual stake
100100
uint256 opShare = (totalOperatorAmount * exposure) / totalExposure;
101-
uint256 restakeShare = (totalRestakerAmount * exposure) / totalExposure;
101+
uint256 stakeShare = (totalStakerAmount * exposure) / totalExposure;
102102
// ...
103103
}
104104
}
@@ -110,14 +110,14 @@ The `exposure` variable in the payment calculation is **directly** the `exposure
110110

111111
---
112112

113-
## 3. Restaker Payment Path (Contrasting Behavior)
113+
## 3. Staker Payment Path (Contrasting Behavior)
114114

115115
**File:** `src/rewards/ServiceFeeDistributor.sol`
116116

117-
The restaker share flows to `ServiceFeeDistributor.distributeServiceFee()` which **DOES** use actual stake:
117+
The staker share flows to `ServiceFeeDistributor.distributeServiceFee()` which **DOES** use actual stake:
118118

119119
```solidity
120-
// Restaker distribution uses actual delegation scores
120+
// Staker distribution uses actual delegation scores
121121
mapping(address => mapping(bytes32 => uint256)) public totalAllScore;
122122
mapping(address => mapping(uint64 => mapping(bytes32 => uint256))) public totalFixedScore;
123123
```
@@ -129,7 +129,7 @@ uint256 scoreDelta = (amount * lockMultiplierBps) / BPS_DENOMINATOR;
129129
totalAllScore[operator][assetHash] += scoreDelta;
130130
```
131131

132-
**Restaker payments ARE weighted by actual capital provided.**
132+
**Staker payments ARE weighted by actual capital provided.**
133133

134134
---
135135

@@ -166,7 +166,7 @@ Higher exposure = more slashable stake. So `exposureBps` represents **risk commi
166166
1. **Risk-based compensation**: Operators are paid for slashing risk taken (exposure), not capital provided
167167
2. **Separation of concerns**:
168168
- Operator share → compensation for service provision and risk commitment
169-
- Restaker share → return on capital (correctly uses actual stake)
169+
- Staker share → return on capital (correctly uses actual stake)
170170
3. **Slashing alignment**: `exposureBps` directly affects slashable amount, creating economic alignment
171171

172172
### Evidence Supporting POTENTIAL ISSUE:
@@ -229,7 +229,7 @@ Payments._distributePayment()
229229
│ • developerAmount
230230
│ • protocolAmount
231231
│ • operatorAmount ──────────────────────┐
232-
│ • restakerAmount ───────────────────┐ │
232+
│ • stakerAmount ───────────────────┐ │
233233
│ │ │
234234
▼ │ │
235235
PaymentLib.calculateOperatorPayments() │ │
@@ -240,7 +240,7 @@ PaymentLib.calculateOperatorPayments() │ │
240240
├── operatorShare → _pendingRewards │ │
241241
│ (based on exposure) │ │
242242
│ │ │
243-
└── restakerShare ───────────────────────┘ │
243+
└── stakerShare ───────────────────────┘ │
244244
│ │
245245
▼ │
246246
ServiceFeeDistributor.distributeServiceFee()
@@ -249,7 +249,7 @@ PaymentLib.calculateOperatorPayments() │ │
249249
│ (Based on ACTUAL delegated amounts)
250250
251251
252-
Restaker rewards (stake-weighted)
252+
Staker rewards (stake-weighted)
253253
```
254254

255255
---
@@ -261,9 +261,9 @@ The payment system has **two distinct compensation models**:
261261
| Recipient | Compensation Basis | Variable Used | Correct? |
262262
|-----------|-------------------|---------------|----------|
263263
| Operators | Risk commitment (exposure) | `exposureBps` | By design |
264-
| Restakers | Capital provided (stake) | `totalAllScore` | ✅ Yes |
264+
| Stakers | Capital provided (stake) | `totalAllScore` | ✅ Yes |
265265

266-
The operator payment path using `exposureBps` instead of actual stake is **likely intentional** for a restaking protocol where operators are compensated for the slashing risk they accept, while restakers are compensated for capital provided.
266+
The operator payment path using `exposureBps` instead of actual stake is **likely intentional** for a staking protocol where operators are compensated for the slashing risk they accept, while stakers are compensated for capital provided.
267267

268268
**However**, without explicit documentation or proportionality guards, this creates potential for gaming if slashing is not credibly enforced.
269269

bindings/CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6565

6666
### Changed
6767

68-
- **BREAKING**: Renamed `IRestaking` to `IStaking` interface
69-
- **BREAKING**: Renamed `restaking` module to `staking`
70-
- **BREAKING**: Renamed `PaymentSplit.restakerBps` to `stakerBps`
71-
- Updated all bindings from TNT Core contracts with restaking→staking terminology refactoring
68+
- **BREAKING**: Normalized interface names to `IStaking`
69+
- **BREAKING**: Normalized module naming to `staking`
70+
- **BREAKING**: Normalized `PaymentSplit` staking weight field naming
71+
- Updated all bindings from TNT Core contracts with canonical staking terminology
7272

7373
## [0.6.1] - 2026-01-17
7474

bindings/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ name = "tnt-core-bindings"
33
version = "0.10.4"
44
edition = "2021"
55
rust-version = "1.81"
6-
description = "Rust bindings for TNT Core Solidity contracts (Tangle restaking protocol)"
6+
description = "Rust bindings for TNT Core Solidity contracts (Tangle staking protocol)"
77
license = "MIT OR Apache-2.0"
88
repository = "https://github.com/tangle-network/tnt-core"
99
homepage = "https://tangle.tools"
1010
documentation = "https://docs.rs/tnt-core-bindings"
1111
readme = "README.md"
12-
keywords = ["tangle", "restaking", "ethereum", "solidity", "alloy"]
12+
keywords = ["tangle", "staking", "ethereum", "solidity", "alloy"]
1313
categories = ["cryptography::cryptocurrencies", "api-bindings"]
1414
include = [
1515
"src/**/*",

bindings/TNT_CORE_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
32055b63efa6cb4c8f690f14a572c74275e5a411
1+
da2b9a8f57e6bf0af537b58c6f58f4b9fe0f5b67

bindings/abi/IMultiAssetDelegation.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

bindings/abi/ITangle.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

bindings/abi/ITangleFull.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

bindings/abi/ITangleSlashing.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

bindings/abi/MultiAssetDelegation.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

bindings/src/bindings/i_tangle.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10135,7 +10135,7 @@ interface ITangle {
1013510135
event OperatorRegistered(uint64 indexed blueprintId, address indexed operator, bytes ecdsaPublicKey, string rpcAddress);
1013610136
event OperatorRewardAccrued(uint64 indexed serviceId, address indexed operator, address indexed token, uint64 blueprintId, uint256 amount);
1013710137
event OperatorUnregistered(uint64 indexed blueprintId, address indexed operator);
10138-
event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 restakerPoolAmount);
10138+
event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakerPoolAmount);
1013910139
event RewardsClaimed(address indexed account, address indexed token, uint256 amount);
1014010140
event ServiceActivated(uint64 indexed serviceId, uint64 indexed requestId, uint64 indexed blueprintId);
1014110141
event ServiceApproved(uint64 indexed requestId, address indexed operator);
@@ -14166,7 +14166,7 @@ interface ITangle {
1416614166
"internalType": "uint256"
1416714167
},
1416814168
{
14169-
"name": "restakerPoolAmount",
14169+
"name": "stakerPoolAmount",
1417014170
"type": "uint256",
1417114171
"indexed": false,
1417214172
"internalType": "uint256"
@@ -16378,7 +16378,7 @@ event OperatorUnregistered(uint64 indexed blueprintId, address indexed operator)
1637816378
#[derive(Default, Debug, PartialEq, Eq, Hash)]
1637916379
/**Event with signature `PaymentDistributed(uint64,uint64,address,uint256,address,uint256,uint256,uint256,uint256)` and selector `0xbbd9474fbbb06eb636eb470aa71ad2133b5178d91593f96b8083204a60bea278`.
1638016380
```solidity
16381-
event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 restakerPoolAmount);
16381+
event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, address indexed token, uint256 grossAmount, address developerRecipient, uint256 developerAmount, uint256 protocolAmount, uint256 operatorPoolAmount, uint256 stakerPoolAmount);
1638216382
```*/
1638316383
#[allow(
1638416384
non_camel_case_types,
@@ -16405,7 +16405,7 @@ event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, a
1640516405
#[allow(missing_docs)]
1640616406
pub operatorPoolAmount: alloy::sol_types::private::primitives::aliases::U256,
1640716407
#[allow(missing_docs)]
16408-
pub restakerPoolAmount: alloy::sol_types::private::primitives::aliases::U256,
16408+
pub stakerPoolAmount: alloy::sol_types::private::primitives::aliases::U256,
1640916409
}
1641016410
#[allow(
1641116411
non_camel_case_types,
@@ -16456,7 +16456,7 @@ event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, a
1645616456
developerAmount: data.2,
1645716457
protocolAmount: data.3,
1645816458
operatorPoolAmount: data.4,
16459-
restakerPoolAmount: data.5,
16459+
stakerPoolAmount: data.5,
1646016460
}
1646116461
}
1646216462
#[inline]
@@ -16494,7 +16494,7 @@ event PaymentDistributed(uint64 indexed serviceId, uint64 indexed blueprintId, a
1649416494
> as alloy_sol_types::SolType>::tokenize(&self.operatorPoolAmount),
1649516495
<alloy::sol_types::sol_data::Uint<
1649616496
256,
16497-
> as alloy_sol_types::SolType>::tokenize(&self.restakerPoolAmount),
16497+
> as alloy_sol_types::SolType>::tokenize(&self.stakerPoolAmount),
1649816498
)
1649916499
}
1650016500
#[inline]

0 commit comments

Comments
 (0)