So i was curious if you could write a contract, that retrieves the StateRoot from the last mined block. I found a Repo from Keydonix, who used the stateRoot from previous blocks to calculate a more recent price change for dexes. The solidity file below was written by him, unfortunately, i know very little about assembly code. I can see that the contract stores more than 16 variables, which causes the error of "Stack too deep(31)". In line 33, he comments that the variables could be compressed into a single add function to save gas. Could anyone help me with how i would go about writing that in assembly? Also, seeing as its a library i was thinking on perhaps making another library to lower the burden of high variable count on a single contract but wasnt sure seeing as its assembly.
Repo: https://github.com/Keydonix/uniswap-oracle/
Best regards Mango
pragma solidity ^0.6.8;
library BlockVerifier {
function extractStateRootAndTimestamp(bytes memory rlpBytes) internal view returns (bytes32 stateRoot, uint256 blockTimestamp, uint256 blockNumber) {
assembly {
function revertWithReason(message, length) {
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(4, 0x20)
mstore(0x24, length)
mstore(0x44, message)
revert(0, add(0x44, length))
}
function readDynamic(prefixPointer) -> dataPointer, dataLength {
let value := byte(0, mload(prefixPointer))
switch lt(value, 0x80)
case 1 {
dataPointer := prefixPointer
dataLength := 1
}
case 0 {
dataPointer := add(prefixPointer, 1)
dataLength := sub(value, 0x80)
}
}
// get the length of the data
let rlpLength := mload(rlpBytes)
// move pointer forward, ahead of length
rlpBytes := add(rlpBytes, 0x20)
// we know the length of the block will be between 483 bytes and 709 bytes, which means it will have 2 length bytes after the prefix byte, so we can skip 3 bytes in
// CONSIDER: we could save a trivial amount of gas by compressing most of this into a single add instruction
let parentHashPrefixPointer := add(rlpBytes, 3)
let parentHashPointer := add(parentHashPrefixPointer, 1)
let uncleHashPrefixPointer := add(parentHashPointer, 32)
let uncleHashPointer := add(uncleHashPrefixPointer, 1)
let minerAddressPrefixPointer := add(uncleHashPointer, 32)
let minerAddressPointer := add(minerAddressPrefixPointer, 1)
let stateRootPrefixPointer := add(minerAddressPointer, 20)
let stateRootPointer := add(stateRootPrefixPointer, 1)
let transactionRootPrefixPointer := add(stateRootPointer, 32)
let transactionRootPointer := add(transactionRootPrefixPointer, 1)
let receiptsRootPrefixPointer := add(transactionRootPointer, 32)
let receiptsRootPointer := add(receiptsRootPrefixPointer, 1)
let logsBloomPrefixPointer := add(receiptsRootPointer, 32)
let logsBloomPointer := add(logsBloomPrefixPointer, 3)
let difficultyPrefixPointer := add(logsBloomPointer, 256)
let difficultyPointer, difficultyLength := readDynamic(difficultyPrefixPointer)
let blockNumberPrefixPointer := add(difficultyPointer, difficultyLength)
let blockNumberPointer, blockNumberLength := readDynamic(blockNumberPrefixPointer)
let gasLimitPrefixPointer := add(blockNumberPointer, blockNumberLength)
let gasLimitPointer, gasLimitLength := readDynamic(gasLimitPrefixPointer)
let gasUsedPrefixPointer := add(gasLimitPointer, gasLimitLength)
let gasUsedPointer, gasUsedLength := readDynamic(gasUsedPrefixPointer)
let timestampPrefixPointer := add(gasUsedPointer, gasUsedLength)
let timestampPointer, timestampLength := readDynamic(timestampPrefixPointer)
blockNumber := shr(sub(256, mul(blockNumberLength, 8)), mload(blockNumberPointer))
let blockHash := blockhash(blockNumber)
let rlpHash := keccak256(rlpBytes, rlpLength)
if iszero(eq(blockHash, rlpHash)) { revertWithReason("blockHash != rlpHash", 20) }
stateRoot := mload(stateRootPointer)
blockTimestamp := shr(sub(256, mul(timestampLength, 8)), mload(timestampPointer))
}
}
}