Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- AggregatedDataFeedStore
- Optimization enabled
- true
- Compiler version
- v0.8.28+commit.7893614a
- Optimization runs
- 200
- EVM Version
- paris
- Verified at
- 2025-06-02T12:47:52.046279Z
Constructor Arguments
0x000000000000000000000000ccc260a4228420b9da421adb477eb028162ce042
Arg [0] (address) : 0xccc260a4228420b9da421adb477eb028162ce042
contracts/AggregatedDataFeedStore.sol
// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: Copyright (c) 2024-2025 Schelling Point Labs Inc. pragma solidity ^0.8.28; // ___ __ __ _ __ __ __ // / _ )/ /__ ____/ /__ ___ ___ ___ ___ ___ / |/ /__ / /__ _____ ____/ /__ // / _ / / _ \/ __/ '_/(_-</ -_) _ \(_-</ -_) / / -_) __/ |/|/ / _ \/ __/ '_/ // /____/_/\___/\__/_/\_\/___/\__/_//_/___/\__/ /_/|_/\__/\__/|__,__/\___/_/ /_/\_\ // _____ ____ ___ ___ ___ ________ // / __/ | / / |/ / / _ | / _ \/ __/ __/ // / _/ | |/ / /|_/ / / __ |/ // / _/_\ \ // /___/ |___/_/ /_/ /_/ |_/____/_/ /___/ // // Website: https://blocksense.network/ // Git Repository: https://github.com/blocksense-network/blocksense /// @title AggregatedDataFeedStore /// @author Aneta Tsvetkova /// @notice Contract that stores data feeds of different strides contract AggregatedDataFeedStore { address internal constant DATA_FEED_ADDRESS = 0x0000000100000000000000000000000000000000; address internal constant RING_BUFFER_TABLE_ADDRESS = 0x00000000FFf00000000000000000000000000000; address internal immutable ACCESS_CONTROL; /// @notice Topic to be emitted on update /// @dev keccak256("DataFeedsUpdated(uint256)") bytes32 internal constant DATA_FEEDS_UPDATE_EVENT_TOPIC = 0xe64378c8d8a289137204264780c7669f3860a703795c6f0574d925d473a4a2a7; /* Storage layout: Management space: [0 to 2**128-2**116) 0x0000 - latest blocknumber 0x0001 - implementation slot (UpgradeableProxy) 0x0002 - admin slot (UpgradeableProxy) Ring buffer index table: [2**128-2**116 to 2**128) Data feed space: [2**128 to 2**160) */ constructor(address accessControl) { ACCESS_CONTROL = accessControl; } fallback() external payable { /* READ - 1st bit of selector is 1; selector is 1 byte */ /* @dev cannot read more than a feed's space -> reading all of feed's historical data is possible */ assembly { // Load selector from memory let selector := calldataload(0x00) /* <selector 1b> <stride 1b> <feedId 15b> (<index 2b> <startSlot? 4b> <slots? 4b> | <startSlot? 4b> <slots? 4b>) */ if and( selector, 0x8000000000000000000000000000000000000000000000000000000000000000 ) { let stride := byte(1, selector) let feedId := shr(136, shl(16, selector)) // ensure feedId is in range [0-2**115) and stride is in range [0-32) if or(gt(feedId, 0x7ffffffffffffffffffffffffffff), gt(stride, 31)) { revert(0, 0) } let data := calldataload(17) selector := shr(248, selector) // getDataAtIndex(uint8 stride, uint120 feedId, uint16 index, uint32 startSlot?, uint32 slots?) returns (bytes) if eq(selector, 0x86) { // how many slots in this stride: 2**stride let strideSlots := shl(stride, 1) let index := shr(240, data) // ensure index is in range [0-8192) if gt(index, 0x1fff) { revert(0, 0) } // base feed index: (feedId * 2**13) * 2**stride // find start slot for ring buffer index: baseFeedIndex + index * 2**stride let startDataIndex := add( shl(stride, shl(13, feedId)), shl(stride, index) ) // `startSlot` and `slots` are used to read a slice from the feed // check `_callSingleDataFeed` in contracts/libraries/ADFS.sol for more info about `calldatasize` if gt(calldatasize(), 19) { // last index of feed: (feedId + 1) * 2**13 * 2**stride - 1 let lastFeedIndex := sub(shl(stride, shl(13, add(feedId, 1))), 1) // read startSlot from calldata let startSlot := shr(224, shl(16, data)) startDataIndex := add(startDataIndex, startSlot) // strideSlots = slots strideSlots := shr(224, shl(48, data)) if iszero(strideSlots) { strideSlots := shl(stride, 1) } // ensure the caller is not trying to read past the end of the feed if gt(add(startDataIndex, sub(strideSlots, 1)), lastFeedIndex) { revert(0, 0) } } let initialPos := add(shl(stride, DATA_FEED_ADDRESS), startDataIndex) let len := 0 let ptr := mload(0x40) switch strideSlots // if stride is 0, read 1 slot (no need for a loop - saves gas) case 1 { mstore(add(ptr, len), sload(initialPos)) len := 32 } default { for { let i := 0 } lt(i, strideSlots) { i := add(i, 1) len := add(len, 32) } { let feedData := sload(add(initialPos, i)) mstore(add(ptr, len), feedData) } } return(ptr, len) } // feedId%16 * 16 // mod is represented as `feedId % 2^4 = feedId & (2^4 - 1)` let pos := shl(4, and(feedId, 15)) let index := shr( sub(240, pos), and( sload( add( RING_BUFFER_TABLE_ADDRESS, // find index table slot: (2**115 * stride + feedId)/16 shr(4, add(shl(115, stride), feedId)) ) ), shr( pos, 0xFFFF000000000000000000000000000000000000000000000000000000000000 ) ) ) let len := 0 let ptr := mload(0x40) // 0x83 will call both getLatestIndex and getLatestSingleData // 0x85 will call both getLatestIndex and getLatestData // getLatestIndex(uint8 stride, uint120 feedId) returns (uint16) if and(selector, 0x01) { len := 32 mstore(ptr, index) } // getLatestSingleData(uint8 stride, uint120 feedId) returns (bytes) if and(selector, 0x02) { // find start index for index: feedId * 2**13 + index let startDataIndex := add(shl(13, feedId), index) // load feed data from storage and store it in memory mstore( add(ptr, len), sload(add(shl(stride, DATA_FEED_ADDRESS), startDataIndex)) ) return(ptr, add(len, 32)) } // getLatestData(uint8 stride, uint120 feedId, uint32 startSlot?, uint32 slots?) returns (bytes) if and(selector, 0x04) { // how many slots in this stride: 2**stride let strideSlots := shl(stride, 1) // base feed index: (feedId * 2**13) * 2**stride // find start index for index: baseFeedIndex + index * 2**stride let startDataIndex := add( shl(stride, shl(13, feedId)), shl(stride, index) ) // `startSlot` and `slots` are used to read a slice from the feed // check `_callSingleDataFeed` in contracts/libraries/ADFS.sol for more info about `calldatasize` if gt(calldatasize(), 19) { // last index of feed: (feedId + 1) * 2**13 * 2**stride - 1 let lastFeedIndex := sub(shl(stride, shl(13, add(feedId, 1))), 1) // read startSlot from calldata let startSlot := shr(224, data) startDataIndex := add(startDataIndex, startSlot) // strideSlots = slots strideSlots := shr(224, shl(32, data)) if iszero(strideSlots) { strideSlots := shl(stride, 1) } // ensure the caller is not trying to read past the end of the feed if gt(add(startDataIndex, sub(strideSlots, 1)), lastFeedIndex) { revert(0, 0) } } let initialPos := add(shl(stride, DATA_FEED_ADDRESS), startDataIndex) for { let i := 0 } lt(i, strideSlots) { i := add(i, 1) len := add(len, 32) } { mstore(add(ptr, len), sload(add(initialPos, i))) } } return(ptr, len) } } /* WRITE - 1st bit of selector is 0 */ /* IMPORTANT! Selector 0x00 is reserved for admin operations via Upgradeable Proxy. It should never be implemented as a write operation in this contract to prevent selector collisions. */ address accessControl = ACCESS_CONTROL; /* ┌--------------------- index table data --------------------┐ │ │ ┌---------------------- feed 1 ----------------------------┬-- feed 2 .. feed N --┼-------------- row 1 --------------┬---- row 2 .. row N ---┤ ┌────────┬───────────┬──────────┬──────┬────────────┬──────────────┬────────────┬─────┬────┬──────────────────────┬────────────┬─────┬────────────────┬───────────────────────┐ │selector│blocknumber│# of feeds│stride│index length│feedId + index│bytes length│bytes│data│ .. │index length│index│index table data│ .. │ ├────────┼───────────┼──────────┼──────┼────────────┼──────────────┼────────────┼─────┼────┼──────────────────────┼────────────┼─────┼────────────────┼───────────────────────┤ │ 1b │ 8b │ 4b │ 1b │ 1b │ Xb │ 1b │ Yb │ Zb │ .. │ 1b │ Xb │ 32b │ .. │ └────────┴───────────┴──────────┴──────┴────────────┴──────────────┴────────────┴─────┴────┴──────────────────────┴────────────┴─────┴────────────────┴───────────────────────┘ │ ▲ │ ▲ │ ▲ │ ▲ └-------------┘ └-----------┘ └----┘ └---------┘ X=index length Y=bytes length Z=bytes X=index length */ assembly { let ptr := mload(0x40) mstore(ptr, shl(96, caller())) // call AC and store return value (32 bytes) at memory location ptr let success := staticcall(gas(), accessControl, ptr, 20, ptr, 32) // revert if call failed or caller not authorized if iszero(and(success, mload(ptr))) { revert(0, 0) } mstore(ptr, 0) let data := calldataload(0) // setFeeds(bytes) if eq(byte(0, data), 0x01) { /////////////////////////////////// // Update Blocksense blocknumber // /////////////////////////////////// let newBlockNumber := shr(192, shl(8, data)) let prevBlockNumber := sload(0x00) // ensure it is strictly increasing if eq(gt(newBlockNumber, prevBlockNumber), 0) { revert(0x00, 0x00) } sstore(0x00, newBlockNumber) /////////////////////////////////// // Update feeds // /////////////////////////////////// let len := calldatasize() let feedsCount := shr(224, shl(72, data)) // selector (1b) + blocknumber (8b) + feeds count (4b) = 13b // points to the start of the feeds data let pointer := 13 /* ┌───────────────────────────────┐ ......... ┌───────────────────────────────┐ │ stride 0 (32b) │ │ stride 2 (128b) │ │───────────────────────────────│ │───────────────────────────────│ ├───┌───┌───┌───┌───┌───┌───┌───┤ ├───────────────┌───────────────┤ feed id 0 │ │ │ │ │ │ │ │ │ feed id 0 │ │ │ │ │ │ │ │ │ ├───└───└───└───└───└───└───└───┤ ├───────────────└───────────────┤ │ │ │ │ │ ..... 2**13 indices ...... │ │ ..... 2**13 indices ...... │ │ │ │ │ ├───┌───┌───┌───┌───┌───┌───┌───┤ ├───────────────┌───────────────┤ feed id 1 │ │ │ │ │ │ │ │ │ feed id 1 │ │ │ │ │ │ │ │ │ ├───└───└───└───└───└───└───└───┤ ├───────────────└───────────────┤ . │ │ . │ │ . │ ..... 2**13 indices ...... │ . │ ..... 2**13 indices ...... │ │ │ │ │ feed id N └───────────────────────────────┘ feed id N └───────────────────────────────┘ */ for { let i := 0x00 } lt(i, feedsCount) { i := add(i, 0x01) } { let metadata := calldataload(pointer) let stride := byte(0, metadata) if gt(stride, 31) { revert(0, 0) } // get correct stride address based on provided stride let strideAddress := shl(stride, DATA_FEED_ADDRESS) // get length of index (feedId + index) let indexLength := byte(1, metadata) // bytes to bits as shift op code works with bits (for next line) let indexLengthBits := shl(3, indexLength) // get index (max: 16b) let index := shr(sub(256, indexLengthBits), shl(16, metadata)) // get length of bytes to write (max: 5b) let bytesLength := byte(add(2, indexLength), metadata) // get bytes to write based on bytes length above (in most cases will save gas from calldata length) let bytesToWrite := shr( sub(256, shl(3, bytesLength)), shl(add(24, indexLengthBits), metadata) ) // where actual data (to be written) starts pointer := add(pointer, add(3, add(indexLength, bytesLength))) // divide by 32 to get number of slots to write let slots := shr(5, bytesToWrite) // remaining bytes to write // remainderSlot = bytesToWrite % 32 let remainderSlot := and(bytesToWrite, 31) // cannot write outside stride space if gt( // calculate address of last slot at which input data will be stored add(index, sub(add(slots, gt(remainderSlot, 0)), 1)), // maxWriteAddress: next stride address - current stride address - 1 sub(sub(shl(add(stride, 1), DATA_FEED_ADDRESS), strideAddress), 1) ) { revert(0, 0) } // store full 32 bytes data for { let j := 0x00 } lt(j, slots) { j := add(j, 0x01) pointer := add(pointer, 0x20) } { sstore(add(strideAddress, add(index, j)), calldataload(pointer)) } // store remainder slot (if any) if remainderSlot { let remainderSlotData := calldataload(pointer) sstore( add(strideAddress, add(index, slots)), shr(sub(256, shl(3, remainderSlot)), calldataload(pointer)) ) pointer := add(pointer, remainderSlot) } } //////////////////////////////////// // Update ring buffer index table // //////////////////////////////////// /* ┌───────────────────────────────────────────────────────────────┐ │ latest ring buffer index table │ │───────────────────────────────────────────────────────────────│ ├───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┤slot 0 feed ids 0-15 │2b │2b │2b │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │2b │2b │2b │ ├───└───└───└───└───└───└───└───└───└───└───└───└───└───└───└───┤ 32b │ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 │ │ │ │ │ │ ..................... │ │ │ ├───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┌───┤slot 312 feed ids 4992-5008 │2b │2b │2b │ . │ . │ . │ . │ . │ . │ . │ . │ . │ . │2b │2b │2b │ ├───└───└───└───└───└───└───└───└───└───└───└───└───└───└───└───┤ 32b │ │ └───────────────────────────────────────────────────────────────┘ max id: (2**115)*32-1 max slot index: 2**116-1 */ // This is where the latest ring buffer index for each feed id is stored for { } lt(pointer, len) { pointer := add(pointer, 0x20) } { let indexTableData := calldataload(pointer) // how many bytes to read for index (max: 15b) let indexLength := byte(0, indexTableData) // get slot at which to store latest ring buffer indices let slot := shr(sub(256, shl(3, indexLength)), shl(8, indexTableData)) // slot must always be less than 2**116 if gt(slot, 0xfffffffffffffffffffffffffffff) { revert(0, 0) } // update pointer to start start of index data pointer := add(pointer, add(indexLength, 1)) sstore(add(RING_BUFFER_TABLE_ADDRESS, slot), calldataload(pointer)) } /////////////////////////////////// // Emit DataFeedsUpdated event // /////////////////////////////////// // store blocknumber at slot 0 in memory mstore(0x00, newBlockNumber) // Emit event with new block number log1(0x00, 0x20, DATA_FEEDS_UPDATE_EVENT_TOPIC) return(0x00, 0x00) } revert(0x00, 0x00) } } }
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"paris"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"accessControl","internalType":"address"}]},{"type":"fallback","stateMutability":"payable"}]
Contract Creation Code
0x60a0604052348015600f57600080fd5b506040516104a23803806104a2833981016040819052602c91603c565b6001600160a01b0316608052606a565b600060208284031215604d57600080fd5b81516001600160a01b0381168114606357600080fd5b9392505050565b60805161041d610085600039600061020d015261041d6000f3fe6080604052600035600160ff1b811615610201578060011a8160101b60881c601f82116e07ffffffffffffffffffffffffffff8211171561003f57600080fd5b6011358360f81c93506086840361010b576001831b8160f01c611fff81111561006757600080fd5b841b600d84901b851b0160133611156100bc5763ffffffff60b084901c8116925060d084901c160160018401600d1b851b60001901826100a8576001861b92505b8060018403830111156100ba57600080fd5b505b80600160801b861b019050600060405183600181146100fc5760005b858110156100f65784810154838501526020909301926001016100d8565b50508181f35b83548383015260209250508181f35b600f821660041b6001600160f01b0319811c838560731b0160041c610fff60741b0154168160f0031c90506000604051600187161561014c57602091508281525b600287161561017057600160801b861b600d86901b84010154818301526020820181f35b60048716156101fd576001861b83871b86600d1b881b0160133611156101d05763ffffffff60c087901c16915060e086901c0160001960018801600d1b891b01826101bc576001891b92505b8060018403830111156101ce57600080fd5b505b600160801b881b0160005b828110156101f95781810154848601526020909401936001016101db565b5050505b8181f35b506040513360601b81527f000000000000000000000000000000000000000000000000000000000000000090602081601481855afa8151811661024357600080fd5b50600081525060003560018160001a036103e2578060081b60c01c60005460008183110361027057600080fd5b5080600055368260481b60e01c600d60005b8281101561035e5781358060001a601f81111561029e57600080fd5b600160801b811b8260011a8060031b8460101b81610100031c85836002011a86836018011b8160031b610100031c965080840160030189019850508560051c9250601f86169550600184600160801b600188011b03036001600088118501038201111561030a57600080fd5b600094505b8285101561033057873585820185015560018501945060208801975061030f565b851561034d5787358660031b610100031c83820185015585880197505b505050505050600181019050610282565b505b828110156103b257803591508160001a8260081b8160031b610100031c92506e0fffffffffffffffffffffffffffff83111561039b57600080fd5b016001810135610fff60741b830155602101610360565b50505080600052507fe64378c8d8a289137204264780c7669f3860a703795c6f0574d925d473a4a2a760206000a1005b600080fdfea2646970667358221220e8068dbe395db7fb6a5aae9a33faf6c8f50c1363a8668f99a6f96c132303734264736f6c634300081c0033000000000000000000000000ccc260a4228420b9da421adb477eb028162ce042
Deployed ByteCode
0x6080604052600035600160ff1b811615610201578060011a8160101b60881c601f82116e07ffffffffffffffffffffffffffff8211171561003f57600080fd5b6011358360f81c93506086840361010b576001831b8160f01c611fff81111561006757600080fd5b841b600d84901b851b0160133611156100bc5763ffffffff60b084901c8116925060d084901c160160018401600d1b851b60001901826100a8576001861b92505b8060018403830111156100ba57600080fd5b505b80600160801b861b019050600060405183600181146100fc5760005b858110156100f65784810154838501526020909301926001016100d8565b50508181f35b83548383015260209250508181f35b600f821660041b6001600160f01b0319811c838560731b0160041c610fff60741b0154168160f0031c90506000604051600187161561014c57602091508281525b600287161561017057600160801b861b600d86901b84010154818301526020820181f35b60048716156101fd576001861b83871b86600d1b881b0160133611156101d05763ffffffff60c087901c16915060e086901c0160001960018801600d1b891b01826101bc576001891b92505b8060018403830111156101ce57600080fd5b505b600160801b881b0160005b828110156101f95781810154848601526020909401936001016101db565b5050505b8181f35b506040513360601b81527f000000000000000000000000ccc260a4228420b9da421adb477eb028162ce04290602081601481855afa8151811661024357600080fd5b50600081525060003560018160001a036103e2578060081b60c01c60005460008183110361027057600080fd5b5080600055368260481b60e01c600d60005b8281101561035e5781358060001a601f81111561029e57600080fd5b600160801b811b8260011a8060031b8460101b81610100031c85836002011a86836018011b8160031b610100031c965080840160030189019850508560051c9250601f86169550600184600160801b600188011b03036001600088118501038201111561030a57600080fd5b600094505b8285101561033057873585820185015560018501945060208801975061030f565b851561034d5787358660031b610100031c83820185015585880197505b505050505050600181019050610282565b505b828110156103b257803591508160001a8260081b8160031b610100031c92506e0fffffffffffffffffffffffffffff83111561039b57600080fd5b016001810135610fff60741b830155602101610360565b50505080600052507fe64378c8d8a289137204264780c7669f3860a703795c6f0574d925d473a4a2a760206000a1005b600080fdfea2646970667358221220e8068dbe395db7fb6a5aae9a33faf6c8f50c1363a8668f99a6f96c132303734264736f6c634300081c0033