Source Code
Overview
ETH Balance
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 20877521 | 1 min ago | 0 ETH | ||||
| 20877476 | 2 mins ago | 0 ETH | ||||
| 20877431 | 4 mins ago | 0 ETH | ||||
| 20877385 | 5 mins ago | 0 ETH | ||||
| 20877341 | 7 mins ago | 0 ETH | ||||
| 20877296 | 8 mins ago | 0 ETH | ||||
| 20877254 | 10 mins ago | 0 ETH | ||||
| 20877251 | 10 mins ago | 0 ETH | ||||
| 20877206 | 11 mins ago | 0 ETH | ||||
| 20877164 | 13 mins ago | 0 ETH | ||||
| 20877161 | 13 mins ago | 0 ETH | ||||
| 20877119 | 14 mins ago | 0 ETH | ||||
| 20877116 | 14 mins ago | 0 ETH | ||||
| 20877074 | 16 mins ago | 0 ETH | ||||
| 20877071 | 16 mins ago | 0 ETH | ||||
| 20877029 | 17 mins ago | 0 ETH | ||||
| 20877026 | 17 mins ago | 0 ETH | ||||
| 20876984 | 19 mins ago | 0 ETH | ||||
| 20876981 | 19 mins ago | 0 ETH | ||||
| 20876939 | 20 mins ago | 0 ETH | ||||
| 20876935 | 20 mins ago | 0 ETH | ||||
| 20876894 | 22 mins ago | 0 ETH | ||||
| 20876891 | 22 mins ago | 0 ETH | ||||
| 20876846 | 23 mins ago | 0 ETH | ||||
| 20876801 | 25 mins ago | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
AggregatedDataFeedStore
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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)
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"accessControl","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"stateMutability":"payable","type":"fallback"}]Contract Creation Code
60a0604052348015600f57600080fd5b506040516104a23803806104a2833981016040819052602c91603c565b6001600160a01b0316608052606a565b600060208284031215604d57600080fd5b81516001600160a01b0381168114606357600080fd5b9392505050565b60805161041d610085600039600061020d015261041d6000f3fe6080604052600035600160ff1b811615610201578060011a8160101b60881c601f82116e07ffffffffffffffffffffffffffff8211171561003f57600080fd5b6011358360f81c93506086840361010b576001831b8160f01c611fff81111561006757600080fd5b841b600d84901b851b0160133611156100bc5763ffffffff60b084901c8116925060d084901c160160018401600d1b851b60001901826100a8576001861b92505b8060018403830111156100ba57600080fd5b505b80600160801b861b019050600060405183600181146100fc5760005b858110156100f65784810154838501526020909301926001016100d8565b50508181f35b83548383015260209250508181f35b600f821660041b6001600160f01b0319811c838560731b0160041c610fff60741b0154168160f0031c90506000604051600187161561014c57602091508281525b600287161561017057600160801b861b600d86901b84010154818301526020820181f35b60048716156101fd576001861b83871b86600d1b881b0160133611156101d05763ffffffff60c087901c16915060e086901c0160001960018801600d1b891b01826101bc576001891b92505b8060018403830111156101ce57600080fd5b505b600160801b881b0160005b828110156101f95781810154848601526020909401936001016101db565b5050505b8181f35b506040513360601b81527f000000000000000000000000000000000000000000000000000000000000000090602081601481855afa8151811661024357600080fd5b50600081525060003560018160001a036103e2578060081b60c01c60005460008183110361027057600080fd5b5080600055368260481b60e01c600d60005b8281101561035e5781358060001a601f81111561029e57600080fd5b600160801b811b8260011a8060031b8460101b81610100031c85836002011a86836018011b8160031b610100031c965080840160030189019850508560051c9250601f86169550600184600160801b600188011b03036001600088118501038201111561030a57600080fd5b600094505b8285101561033057873585820185015560018501945060208801975061030f565b851561034d5787358660031b610100031c83820185015585880197505b505050505050600181019050610282565b505b828110156103b257803591508160001a8260081b8160031b610100031c92506e0fffffffffffffffffffffffffffff83111561039b57600080fd5b016001810135610fff60741b830155602101610360565b50505080600052507fe64378c8d8a289137204264780c7669f3860a703795c6f0574d925d473a4a2a760206000a1005b600080fdfea2646970667358221220e8068dbe395db7fb6a5aae9a33faf6c8f50c1363a8668f99a6f96c132303734264736f6c634300081c0033000000000000000000000000ccc260a4228420b9da421adb477eb028162ce042
Deployed Bytecode
0x6080604052600035600160ff1b811615610201578060011a8160101b60881c601f82116e07ffffffffffffffffffffffffffff8211171561003f57600080fd5b6011358360f81c93506086840361010b576001831b8160f01c611fff81111561006757600080fd5b841b600d84901b851b0160133611156100bc5763ffffffff60b084901c8116925060d084901c160160018401600d1b851b60001901826100a8576001861b92505b8060018403830111156100ba57600080fd5b505b80600160801b861b019050600060405183600181146100fc5760005b858110156100f65784810154838501526020909301926001016100d8565b50508181f35b83548383015260209250508181f35b600f821660041b6001600160f01b0319811c838560731b0160041c610fff60741b0154168160f0031c90506000604051600187161561014c57602091508281525b600287161561017057600160801b861b600d86901b84010154818301526020820181f35b60048716156101fd576001861b83871b86600d1b881b0160133611156101d05763ffffffff60c087901c16915060e086901c0160001960018801600d1b891b01826101bc576001891b92505b8060018403830111156101ce57600080fd5b505b600160801b881b0160005b828110156101f95781810154848601526020909401936001016101db565b5050505b8181f35b506040513360601b81527f000000000000000000000000ccc260a4228420b9da421adb477eb028162ce04290602081601481855afa8151811661024357600080fd5b50600081525060003560018160001a036103e2578060081b60c01c60005460008183110361027057600080fd5b5080600055368260481b60e01c600d60005b8281101561035e5781358060001a601f81111561029e57600080fd5b600160801b811b8260011a8060031b8460101b81610100031c85836002011a86836018011b8160031b610100031c965080840160030189019850508560051c9250601f86169550600184600160801b600188011b03036001600088118501038201111561030a57600080fd5b600094505b8285101561033057873585820185015560018501945060208801975061030f565b851561034d5787358660031b610100031c83820185015585880197505b505050505050600181019050610282565b505b828110156103b257803591508160001a8260081b8160031b610100031c92506e0fffffffffffffffffffffffffffff83111561039b57600080fd5b016001810135610fff60741b830155602101610360565b50505080600052507fe64378c8d8a289137204264780c7669f3860a703795c6f0574d925d473a4a2a760206000a1005b600080fdfea2646970667358221220e8068dbe395db7fb6a5aae9a33faf6c8f50c1363a8668f99a6f96c132303734264736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ccc260a4228420b9da421adb477eb028162ce042
-----Decoded View---------------
Arg [0] : accessControl (address): 0xCCC260a4228420B9Da421aDB477Eb028162CE042
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000ccc260a4228420b9da421adb477eb028162ce042
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.