Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fuzz = { runs = 1_000 }
gas_reports = ["*"]
libs = ["dependencies"]
# optimizer = true (default)
optimizer_runs = 200
optimizer_runs = 1
fs_permissions = [{ access = "read-write", path = "./" }]
solc = "0.8.26"
evm_version = "cancun"
Expand Down
6 changes: 6 additions & 0 deletions script/DemoSimulation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ import { TangleBlueprintsManagementFacet } from "../src/facets/tangle/TangleBlue
import { TangleOperatorsFacet } from "../src/facets/tangle/TangleOperatorsFacet.sol";
import { TangleServicesRequestsFacet } from "../src/facets/tangle/TangleServicesRequestsFacet.sol";
import { TangleServicesFacet } from "../src/facets/tangle/TangleServicesFacet.sol";
import { TangleServicesViewsFacet } from "../src/facets/tangle/TangleServicesViewsFacet.sol";
import { TangleServicesLifecycleFacet } from "../src/facets/tangle/TangleServicesLifecycleFacet.sol";
import { TangleJobsFacet } from "../src/facets/tangle/TangleJobsFacet.sol";
import { TangleJobsAggregationFacet } from "../src/facets/tangle/TangleJobsAggregationFacet.sol";
import { TangleJobsRFQFacet } from "../src/facets/tangle/TangleJobsRFQFacet.sol";
import { TangleQuotesFacet } from "../src/facets/tangle/TangleQuotesFacet.sol";
import { TangleQuotesExtensionFacet } from "../src/facets/tangle/TangleQuotesExtensionFacet.sol";
import { TanglePaymentsFacet } from "../src/facets/tangle/TanglePaymentsFacet.sol";
import { TanglePaymentsRewardsFacet } from "../src/facets/tangle/TanglePaymentsRewardsFacet.sol";
import { TanglePaymentsDistributionFacet } from "../src/facets/tangle/TanglePaymentsDistributionFacet.sol";
import { TangleSlashingFacet } from "../src/facets/tangle/TangleSlashingFacet.sol";
import { StakingOperatorsFacet } from "../src/facets/staking/StakingOperatorsFacet.sol";
import { StakingDepositsFacet } from "../src/facets/staking/StakingDepositsFacet.sol";
Expand Down Expand Up @@ -177,13 +180,16 @@ contract DemoSimulation is Script, BlueprintDefinitionHelper {
router.registerFacet(address(new TangleOperatorsFacet()));
router.registerFacet(address(new TangleServicesRequestsFacet()));
router.registerFacet(address(new TangleServicesFacet()));
router.registerFacet(address(new TangleServicesViewsFacet()));
router.registerFacet(address(new TangleServicesLifecycleFacet()));
router.registerFacet(address(new TangleJobsFacet()));
router.registerFacet(address(new TangleJobsAggregationFacet()));
router.registerFacet(address(new TangleJobsRFQFacet()));
router.registerFacet(address(new TangleQuotesFacet()));
router.registerFacet(address(new TangleQuotesExtensionFacet()));
router.registerFacet(address(new TanglePaymentsFacet()));
router.registerFacet(address(new TanglePaymentsRewardsFacet()));
router.registerFacet(address(new TanglePaymentsDistributionFacet()));
router.registerFacet(address(new TangleSlashingFacet()));
}

Expand Down
6 changes: 6 additions & 0 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ import { TangleBlueprintsManagementFacet } from "../src/facets/tangle/TangleBlue
import { TangleOperatorsFacet } from "../src/facets/tangle/TangleOperatorsFacet.sol";
import { TangleServicesRequestsFacet } from "../src/facets/tangle/TangleServicesRequestsFacet.sol";
import { TangleServicesFacet } from "../src/facets/tangle/TangleServicesFacet.sol";
import { TangleServicesViewsFacet } from "../src/facets/tangle/TangleServicesViewsFacet.sol";
import { TangleServicesLifecycleFacet } from "../src/facets/tangle/TangleServicesLifecycleFacet.sol";
import { TangleJobsFacet } from "../src/facets/tangle/TangleJobsFacet.sol";
import { TangleJobsAggregationFacet } from "../src/facets/tangle/TangleJobsAggregationFacet.sol";
import { TangleJobsRFQFacet } from "../src/facets/tangle/TangleJobsRFQFacet.sol";
import { TangleQuotesFacet } from "../src/facets/tangle/TangleQuotesFacet.sol";
import { TangleQuotesExtensionFacet } from "../src/facets/tangle/TangleQuotesExtensionFacet.sol";
import { TanglePaymentsFacet } from "../src/facets/tangle/TanglePaymentsFacet.sol";
import { TanglePaymentsRewardsFacet } from "../src/facets/tangle/TanglePaymentsRewardsFacet.sol";
import { TanglePaymentsDistributionFacet } from "../src/facets/tangle/TanglePaymentsDistributionFacet.sol";
import { TangleSlashingFacet } from "../src/facets/tangle/TangleSlashingFacet.sol";
import { StakingOperatorsFacet } from "../src/facets/staking/StakingOperatorsFacet.sol";
import { StakingDepositsFacet } from "../src/facets/staking/StakingDepositsFacet.sol";
Expand Down Expand Up @@ -398,13 +401,16 @@ contract DeployV2 is DeployScriptBase {
router.registerFacet(address(new TangleOperatorsFacet()));
router.registerFacet(address(new TangleServicesRequestsFacet()));
router.registerFacet(address(new TangleServicesFacet()));
router.registerFacet(address(new TangleServicesViewsFacet()));
router.registerFacet(address(new TangleServicesLifecycleFacet()));
router.registerFacet(address(new TangleJobsFacet()));
router.registerFacet(address(new TangleJobsAggregationFacet()));
router.registerFacet(address(new TangleJobsRFQFacet()));
router.registerFacet(address(new TangleQuotesFacet()));
router.registerFacet(address(new TangleQuotesExtensionFacet()));
router.registerFacet(address(new TanglePaymentsFacet()));
router.registerFacet(address(new TanglePaymentsRewardsFacet()));
router.registerFacet(address(new TanglePaymentsDistributionFacet()));
router.registerFacet(address(new TangleSlashingFacet()));
}

Expand Down
6 changes: 6 additions & 0 deletions script/DeployContractsOnly.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ import { TangleBlueprintsManagementFacet } from "../src/facets/tangle/TangleBlue
import { TangleOperatorsFacet } from "../src/facets/tangle/TangleOperatorsFacet.sol";
import { TangleServicesRequestsFacet } from "../src/facets/tangle/TangleServicesRequestsFacet.sol";
import { TangleServicesFacet } from "../src/facets/tangle/TangleServicesFacet.sol";
import { TangleServicesViewsFacet } from "../src/facets/tangle/TangleServicesViewsFacet.sol";
import { TangleServicesLifecycleFacet } from "../src/facets/tangle/TangleServicesLifecycleFacet.sol";
import { TangleJobsFacet } from "../src/facets/tangle/TangleJobsFacet.sol";
import { TangleJobsAggregationFacet } from "../src/facets/tangle/TangleJobsAggregationFacet.sol";
import { TangleJobsRFQFacet } from "../src/facets/tangle/TangleJobsRFQFacet.sol";
import { TangleQuotesFacet } from "../src/facets/tangle/TangleQuotesFacet.sol";
import { TangleQuotesExtensionFacet } from "../src/facets/tangle/TangleQuotesExtensionFacet.sol";
import { TanglePaymentsFacet } from "../src/facets/tangle/TanglePaymentsFacet.sol";
import { TanglePaymentsRewardsFacet } from "../src/facets/tangle/TanglePaymentsRewardsFacet.sol";
import { TanglePaymentsDistributionFacet } from "../src/facets/tangle/TanglePaymentsDistributionFacet.sol";
import { TangleSlashingFacet } from "../src/facets/tangle/TangleSlashingFacet.sol";
import { StakingOperatorsFacet } from "../src/facets/staking/StakingOperatorsFacet.sol";
import { StakingDepositsFacet } from "../src/facets/staking/StakingDepositsFacet.sol";
Expand Down Expand Up @@ -127,13 +130,16 @@ contract DeployContractsOnly is Script {
tangle.registerFacet(address(new TangleOperatorsFacet()));
tangle.registerFacet(address(new TangleServicesRequestsFacet()));
tangle.registerFacet(address(new TangleServicesFacet()));
tangle.registerFacet(address(new TangleServicesViewsFacet()));
tangle.registerFacet(address(new TangleServicesLifecycleFacet()));
tangle.registerFacet(address(new TangleJobsFacet()));
tangle.registerFacet(address(new TangleJobsAggregationFacet()));
tangle.registerFacet(address(new TangleJobsRFQFacet()));
tangle.registerFacet(address(new TangleQuotesFacet()));
tangle.registerFacet(address(new TangleQuotesExtensionFacet()));
tangle.registerFacet(address(new TanglePaymentsFacet()));
tangle.registerFacet(address(new TanglePaymentsRewardsFacet()));
tangle.registerFacet(address(new TanglePaymentsDistributionFacet()));
tangle.registerFacet(address(new TangleSlashingFacet()));
}
}
6 changes: 6 additions & 0 deletions script/LocalTestnet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ import { TangleBlueprintsManagementFacet } from "../src/facets/tangle/TangleBlue
import { TangleOperatorsFacet } from "../src/facets/tangle/TangleOperatorsFacet.sol";
import { TangleServicesRequestsFacet } from "../src/facets/tangle/TangleServicesRequestsFacet.sol";
import { TangleServicesFacet } from "../src/facets/tangle/TangleServicesFacet.sol";
import { TangleServicesViewsFacet } from "../src/facets/tangle/TangleServicesViewsFacet.sol";
import { TangleServicesLifecycleFacet } from "../src/facets/tangle/TangleServicesLifecycleFacet.sol";
import { TangleJobsFacet } from "../src/facets/tangle/TangleJobsFacet.sol";
import { TangleJobsAggregationFacet } from "../src/facets/tangle/TangleJobsAggregationFacet.sol";
import { TangleJobsRFQFacet } from "../src/facets/tangle/TangleJobsRFQFacet.sol";
import { TangleQuotesFacet } from "../src/facets/tangle/TangleQuotesFacet.sol";
import { TangleQuotesExtensionFacet } from "../src/facets/tangle/TangleQuotesExtensionFacet.sol";
import { TanglePaymentsFacet } from "../src/facets/tangle/TanglePaymentsFacet.sol";
import { TanglePaymentsRewardsFacet } from "../src/facets/tangle/TanglePaymentsRewardsFacet.sol";
import { TanglePaymentsDistributionFacet } from "../src/facets/tangle/TanglePaymentsDistributionFacet.sol";
import { TangleSlashingFacet } from "../src/facets/tangle/TangleSlashingFacet.sol";
import { StakingOperatorsFacet } from "../src/facets/staking/StakingOperatorsFacet.sol";
import { StakingDepositsFacet } from "../src/facets/staking/StakingDepositsFacet.sol";
Expand Down Expand Up @@ -1326,13 +1329,16 @@ contract LocalTestnetSetup is Script, BlueprintDefinitionHelper {
router.registerFacet(address(new TangleOperatorsFacet()));
router.registerFacet(address(new TangleServicesRequestsFacet()));
router.registerFacet(address(new TangleServicesFacet()));
router.registerFacet(address(new TangleServicesViewsFacet()));
router.registerFacet(address(new TangleServicesLifecycleFacet()));
router.registerFacet(address(new TangleJobsFacet()));
router.registerFacet(address(new TangleJobsAggregationFacet()));
router.registerFacet(address(new TangleJobsRFQFacet()));
router.registerFacet(address(new TangleQuotesFacet()));
router.registerFacet(address(new TangleQuotesExtensionFacet()));
router.registerFacet(address(new TanglePaymentsFacet()));
router.registerFacet(address(new TanglePaymentsRewardsFacet()));
router.registerFacet(address(new TanglePaymentsDistributionFacet()));
router.registerFacet(address(new TangleSlashingFacet()));
}

Expand Down
26 changes: 11 additions & 15 deletions src/TangleStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -410,21 +410,17 @@ abstract contract TangleStorage {
mapping(address => uint256) internal _pendingDisputeBondRefunds;

// ═══════════════════════════════════════════════════════════════════════════
// TWAP SUBSCRIPTION BILLING — PER-OPERATOR CURSORS
// ═══════════════════════════════════════════════════════════════════════════
// Stores the cumulative stake-seconds value last attributed to each
// (service, operator) pair. Replaces the earlier aggregate-only cursor that
// produced a bogus `cumDelta` whenever the active-operator set changed
// between bills (sum-over-old-set vs sum-over-new-set is not a valid delta).
//
// Per-operator cursors make the billing window correct under joins, leaves,
// and rejoins: each operator is attributed only the stake-seconds they
// accrued *while bonded to this service* over the billed period.

/// @notice Service ID => Operator => cumulative stake-seconds at the
/// operator's most recent attribution event for this service
/// (join, prior bill). Zero sentinel = "never attributed."
mapping(uint64 => mapping(address => uint256)) internal _twapCursorByOp;
// TWAP SUBSCRIPTION BILLING — PER-(SERVICE, OPERATOR, ASSET) CURSORS
// ═══════════════════════════════════════════════════════════════════════════
// The bill weight is the integral of `stake × commitmentBps` over the period,
// per (operator, asset) the service requires. Cursors track the cumulative
// stake-seconds last attributed for each (service, op, asset) so cumDelta is
// correct under joins, leaves, rejoins, and per-asset commitment changes.

/// @notice Service ID => Operator => keccak256(asset.kind, asset.token) =>
/// cum stake-seconds at the most recent attribution event (activation
/// seed, join hook, prior bill). Zero sentinel = "never attributed."
mapping(uint64 => mapping(address => mapping(bytes32 => uint256))) internal _twapCursorByOpAsset;

// ═══════════════════════════════════════════════════════════════════════════
// RESERVED STORAGE GAP
Expand Down
7 changes: 7 additions & 0 deletions src/config/ProtocolConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ library ProtocolConfig {
/// @notice Maximum TTL for service requests (365 days)
uint64 internal constant MAX_SERVICE_TTL = 365 days;

/// @notice Maximum cumulative TTL a service may accumulate across extensions.
/// @dev Per-call validation bounds `additionalTtl` to `MAX_SERVICE_TTL`; without a
/// cumulative cap a long-lived service could grow `svc.ttl` arbitrarily by
/// chaining extensions. Four years is large enough for any realistic
/// production deployment yet small enough to keep escrow exposure bounded.
uint64 internal constant MAX_CUMULATIVE_SERVICE_TTL = 4 * MAX_SERVICE_TTL;

/// @notice Default request expiry grace period (1 hour)
/// @dev Operators have this additional time to approve after expiry
uint64 internal constant REQUEST_EXPIRY_GRACE_PERIOD = 1 hours;
Expand Down
10 changes: 7 additions & 3 deletions src/core/JobsRFQ.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ abstract contract JobsRFQ is Base {

// Verify quotes and compute total cost
uint64 effectiveMaxQuoteAge = _maxQuoteAge > 0 ? _maxQuoteAge : ProtocolConfig.MAX_QUOTE_AGE;
uint256 totalPrice = _verifyQuotesAndRecordOperators(serviceId, jobIndex, quotes, effectiveMaxQuoteAge);
uint256 totalPrice =
_verifyQuotesAndRecordOperators(serviceId, jobIndex, quotes, effectiveMaxQuoteAge, msg.sender);

if (totalPrice > 0 && !_isPaymentAssetAllowedByManager(bp.manager, serviceId, address(0))) {
revert Errors.TokenNotAllowed(address(0));
Expand Down Expand Up @@ -132,7 +133,8 @@ abstract contract JobsRFQ is Base {
uint64 serviceId,
uint8 jobIndex,
Types.SignedJobQuote[] calldata quotes,
uint64 maxQuoteAge
uint64 maxQuoteAge,
address expectedRequester
)
private
returns (uint256 totalPrice)
Expand Down Expand Up @@ -174,7 +176,9 @@ abstract contract JobsRFQ is Base {

// Verify EIP-712 signature and mark as used. Domain separator is recomputed
// per-call against current chainid so cross-fork replay is impossible.
SignatureLib.verifyAndMarkJobQuoteUsed(_usedQuotes, _domainSeparatorView(), quote, maxQuoteAge);
SignatureLib.verifyAndMarkJobQuoteUsed(
_usedQuotes, _domainSeparatorView(), quote, maxQuoteAge, expectedRequester
);

totalPrice += quote.details.price;
}
Expand Down
Loading
Loading