[eth] Gas improvement: Use optimized version of BytesLib (#380)
* Copy BytesLib.sol to OptimizedBytesLib.sol * Remove `require`s from OptimizedBytesLib * Use OptimizedBytesLib * revert to use BytesLib for governance * Rename to UnsafeBytesLib + use it explicitly
This commit is contained in:
parent
1a5b31f41a
commit
ca7441fd68
|
@ -0,0 +1,502 @@
|
||||||
|
// SPDX-License-Identifier: Unlicense
|
||||||
|
/*
|
||||||
|
* @title Solidity Bytes Arrays Utils
|
||||||
|
* @author Gonçalo Sá <goncalo.sa@consensys.net>
|
||||||
|
*
|
||||||
|
* @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
|
||||||
|
* The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
|
||||||
|
*
|
||||||
|
* @notice This is the **unsafe** version of BytesLib which removed all the checks (out of bound, ...)
|
||||||
|
* to be more gas efficient.
|
||||||
|
*/
|
||||||
|
pragma solidity >=0.8.0 <0.9.0;
|
||||||
|
|
||||||
|
|
||||||
|
library UnsafeBytesLib {
|
||||||
|
function concat(
|
||||||
|
bytes memory _preBytes,
|
||||||
|
bytes memory _postBytes
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
bytes memory tempBytes;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// Get a location of some free memory and store it in tempBytes as
|
||||||
|
// Solidity does for memory variables.
|
||||||
|
tempBytes := mload(0x40)
|
||||||
|
|
||||||
|
// Store the length of the first bytes array at the beginning of
|
||||||
|
// the memory for tempBytes.
|
||||||
|
let length := mload(_preBytes)
|
||||||
|
mstore(tempBytes, length)
|
||||||
|
|
||||||
|
// Maintain a memory counter for the current write location in the
|
||||||
|
// temp bytes array by adding the 32 bytes for the array length to
|
||||||
|
// the starting location.
|
||||||
|
let mc := add(tempBytes, 0x20)
|
||||||
|
// Stop copying when the memory counter reaches the length of the
|
||||||
|
// first bytes array.
|
||||||
|
let end := add(mc, length)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Initialize a copy counter to the start of the _preBytes data,
|
||||||
|
// 32 bytes into its memory.
|
||||||
|
let cc := add(_preBytes, 0x20)
|
||||||
|
} lt(mc, end) {
|
||||||
|
// Increase both counters by 32 bytes each iteration.
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
cc := add(cc, 0x20)
|
||||||
|
} {
|
||||||
|
// Write the _preBytes data into the tempBytes memory 32 bytes
|
||||||
|
// at a time.
|
||||||
|
mstore(mc, mload(cc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the length of _postBytes to the current length of tempBytes
|
||||||
|
// and store it as the new length in the first 32 bytes of the
|
||||||
|
// tempBytes memory.
|
||||||
|
length := mload(_postBytes)
|
||||||
|
mstore(tempBytes, add(length, mload(tempBytes)))
|
||||||
|
|
||||||
|
// Move the memory counter back from a multiple of 0x20 to the
|
||||||
|
// actual end of the _preBytes data.
|
||||||
|
mc := end
|
||||||
|
// Stop copying when the memory counter reaches the new combined
|
||||||
|
// length of the arrays.
|
||||||
|
end := add(mc, length)
|
||||||
|
|
||||||
|
for {
|
||||||
|
let cc := add(_postBytes, 0x20)
|
||||||
|
} lt(mc, end) {
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
cc := add(cc, 0x20)
|
||||||
|
} {
|
||||||
|
mstore(mc, mload(cc))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the free-memory pointer by padding our last write location
|
||||||
|
// to 32 bytes: add 31 bytes to the end of tempBytes to move to the
|
||||||
|
// next 32 byte block, then round down to the nearest multiple of
|
||||||
|
// 32. If the sum of the length of the two arrays is zero then add
|
||||||
|
// one before rounding down to leave a blank 32 bytes (the length block with 0).
|
||||||
|
mstore(0x40, and(
|
||||||
|
add(add(end, iszero(add(length, mload(_preBytes)))), 31),
|
||||||
|
not(31) // Round down to the nearest 32 bytes.
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
|
||||||
|
assembly {
|
||||||
|
// Read the first 32 bytes of _preBytes storage, which is the length
|
||||||
|
// of the array. (We don't need to use the offset into the slot
|
||||||
|
// because arrays use the entire slot.)
|
||||||
|
let fslot := sload(_preBytes.slot)
|
||||||
|
// Arrays of 31 bytes or less have an even value in their slot,
|
||||||
|
// while longer arrays have an odd value. The actual length is
|
||||||
|
// the slot divided by two for odd values, and the lowest order
|
||||||
|
// byte divided by two for even values.
|
||||||
|
// If the slot is even, bitwise and the slot with 255 and divide by
|
||||||
|
// two to get the length. If the slot is odd, bitwise and the slot
|
||||||
|
// with -1 and divide by two.
|
||||||
|
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
|
||||||
|
let mlength := mload(_postBytes)
|
||||||
|
let newlength := add(slength, mlength)
|
||||||
|
// slength can contain both the length and contents of the array
|
||||||
|
// if length < 32 bytes so let's prepare for that
|
||||||
|
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
|
||||||
|
switch add(lt(slength, 32), lt(newlength, 32))
|
||||||
|
case 2 {
|
||||||
|
// Since the new array still fits in the slot, we just need to
|
||||||
|
// update the contents of the slot.
|
||||||
|
// uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
|
||||||
|
sstore(
|
||||||
|
_preBytes.slot,
|
||||||
|
// all the modifications to the slot are inside this
|
||||||
|
// next block
|
||||||
|
add(
|
||||||
|
// we can just add to the slot contents because the
|
||||||
|
// bytes we want to change are the LSBs
|
||||||
|
fslot,
|
||||||
|
add(
|
||||||
|
mul(
|
||||||
|
div(
|
||||||
|
// load the bytes from memory
|
||||||
|
mload(add(_postBytes, 0x20)),
|
||||||
|
// zero all bytes to the right
|
||||||
|
exp(0x100, sub(32, mlength))
|
||||||
|
),
|
||||||
|
// and now shift left the number of bytes to
|
||||||
|
// leave space for the length in the slot
|
||||||
|
exp(0x100, sub(32, newlength))
|
||||||
|
),
|
||||||
|
// increase length by the double of the memory
|
||||||
|
// bytes length
|
||||||
|
mul(mlength, 2)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case 1 {
|
||||||
|
// The stored value fits in the slot, but the combined value
|
||||||
|
// will exceed it.
|
||||||
|
// get the keccak hash to get the contents of the array
|
||||||
|
mstore(0x0, _preBytes.slot)
|
||||||
|
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
|
||||||
|
|
||||||
|
// save new length
|
||||||
|
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
|
||||||
|
|
||||||
|
// The contents of the _postBytes array start 32 bytes into
|
||||||
|
// the structure. Our first read should obtain the `submod`
|
||||||
|
// bytes that can fit into the unused space in the last word
|
||||||
|
// of the stored array. To get this, we read 32 bytes starting
|
||||||
|
// from `submod`, so the data we read overlaps with the array
|
||||||
|
// contents by `submod` bytes. Masking the lowest-order
|
||||||
|
// `submod` bytes allows us to add that value directly to the
|
||||||
|
// stored value.
|
||||||
|
|
||||||
|
let submod := sub(32, slength)
|
||||||
|
let mc := add(_postBytes, submod)
|
||||||
|
let end := add(_postBytes, mlength)
|
||||||
|
let mask := sub(exp(0x100, submod), 1)
|
||||||
|
|
||||||
|
sstore(
|
||||||
|
sc,
|
||||||
|
add(
|
||||||
|
and(
|
||||||
|
fslot,
|
||||||
|
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
|
||||||
|
),
|
||||||
|
and(mload(mc), mask)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
sc := add(sc, 1)
|
||||||
|
} lt(mc, end) {
|
||||||
|
sc := add(sc, 1)
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
} {
|
||||||
|
sstore(sc, mload(mc))
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := exp(0x100, sub(mc, end))
|
||||||
|
|
||||||
|
sstore(sc, mul(div(mload(mc), mask), mask))
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
// get the keccak hash to get the contents of the array
|
||||||
|
mstore(0x0, _preBytes.slot)
|
||||||
|
// Start copying to the last used word of the stored array.
|
||||||
|
let sc := add(keccak256(0x0, 0x20), div(slength, 32))
|
||||||
|
|
||||||
|
// save new length
|
||||||
|
sstore(_preBytes.slot, add(mul(newlength, 2), 1))
|
||||||
|
|
||||||
|
// Copy over the first `submod` bytes of the new data as in
|
||||||
|
// case 1 above.
|
||||||
|
let slengthmod := mod(slength, 32)
|
||||||
|
let mlengthmod := mod(mlength, 32)
|
||||||
|
let submod := sub(32, slengthmod)
|
||||||
|
let mc := add(_postBytes, submod)
|
||||||
|
let end := add(_postBytes, mlength)
|
||||||
|
let mask := sub(exp(0x100, submod), 1)
|
||||||
|
|
||||||
|
sstore(sc, add(sload(sc), and(mload(mc), mask)))
|
||||||
|
|
||||||
|
for {
|
||||||
|
sc := add(sc, 1)
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
} lt(mc, end) {
|
||||||
|
sc := add(sc, 1)
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
} {
|
||||||
|
sstore(sc, mload(mc))
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := exp(0x100, sub(mc, end))
|
||||||
|
|
||||||
|
sstore(sc, mul(div(mload(mc), mask), mask))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function slice(
|
||||||
|
bytes memory _bytes,
|
||||||
|
uint256 _start,
|
||||||
|
uint256 _length
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
|
||||||
|
bytes memory tempBytes;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
switch iszero(_length)
|
||||||
|
case 0 {
|
||||||
|
// Get a location of some free memory and store it in tempBytes as
|
||||||
|
// Solidity does for memory variables.
|
||||||
|
tempBytes := mload(0x40)
|
||||||
|
|
||||||
|
// The first word of the slice result is potentially a partial
|
||||||
|
// word read from the original array. To read it, we calculate
|
||||||
|
// the length of that partial word and start copying that many
|
||||||
|
// bytes into the array. The first word we copy will start with
|
||||||
|
// data we don't care about, but the last `lengthmod` bytes will
|
||||||
|
// land at the beginning of the contents of the new array. When
|
||||||
|
// we're done copying, we overwrite the full first word with
|
||||||
|
// the actual length of the slice.
|
||||||
|
let lengthmod := and(_length, 31)
|
||||||
|
|
||||||
|
// The multiplication in the next line is necessary
|
||||||
|
// because when slicing multiples of 32 bytes (lengthmod == 0)
|
||||||
|
// the following copy loop was copying the origin's length
|
||||||
|
// and then ending prematurely not copying everything it should.
|
||||||
|
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
|
||||||
|
let end := add(mc, _length)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// The multiplication in the next line has the same exact purpose
|
||||||
|
// as the one above.
|
||||||
|
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
|
||||||
|
} lt(mc, end) {
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
cc := add(cc, 0x20)
|
||||||
|
} {
|
||||||
|
mstore(mc, mload(cc))
|
||||||
|
}
|
||||||
|
|
||||||
|
mstore(tempBytes, _length)
|
||||||
|
|
||||||
|
//update free-memory pointer
|
||||||
|
//allocating the array padded to 32 bytes like the compiler does now
|
||||||
|
mstore(0x40, and(add(mc, 31), not(31)))
|
||||||
|
}
|
||||||
|
//if we want a zero-length slice let's just return a zero-length array
|
||||||
|
default {
|
||||||
|
tempBytes := mload(0x40)
|
||||||
|
//zero out the 32 bytes slice we are about to return
|
||||||
|
//we need to do it because Solidity does not garbage collect
|
||||||
|
mstore(tempBytes, 0)
|
||||||
|
|
||||||
|
mstore(0x40, add(tempBytes, 0x20))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
|
||||||
|
address tempAddress;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
|
||||||
|
uint8 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x1), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
|
||||||
|
uint16 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x2), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
|
||||||
|
uint32 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x4), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
|
||||||
|
uint64 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x8), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
|
||||||
|
uint96 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0xc), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
|
||||||
|
uint128 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x10), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
|
||||||
|
uint256 tempUint;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempUint := mload(add(add(_bytes, 0x20), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempUint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
|
||||||
|
bytes32 tempBytes32;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
tempBytes32 := mload(add(add(_bytes, 0x20), _start))
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempBytes32;
|
||||||
|
}
|
||||||
|
|
||||||
|
function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
let length := mload(_preBytes)
|
||||||
|
|
||||||
|
// if lengths don't match the arrays are not equal
|
||||||
|
switch eq(length, mload(_postBytes))
|
||||||
|
case 1 {
|
||||||
|
// cb is a circuit breaker in the for loop since there's
|
||||||
|
// no said feature for inline assembly loops
|
||||||
|
// cb = 1 - don't breaker
|
||||||
|
// cb = 0 - break
|
||||||
|
let cb := 1
|
||||||
|
|
||||||
|
let mc := add(_preBytes, 0x20)
|
||||||
|
let end := add(mc, length)
|
||||||
|
|
||||||
|
for {
|
||||||
|
let cc := add(_postBytes, 0x20)
|
||||||
|
// the next line is the loop condition:
|
||||||
|
// while(uint256(mc < end) + cb == 2)
|
||||||
|
} eq(add(lt(mc, end), cb), 2) {
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
cc := add(cc, 0x20)
|
||||||
|
} {
|
||||||
|
// if any of these checks fails then arrays are not equal
|
||||||
|
if iszero(eq(mload(mc), mload(cc))) {
|
||||||
|
// unsuccess:
|
||||||
|
success := 0
|
||||||
|
cb := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
// unsuccess:
|
||||||
|
success := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
function equalStorage(
|
||||||
|
bytes storage _preBytes,
|
||||||
|
bytes memory _postBytes
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// we know _preBytes_offset is 0
|
||||||
|
let fslot := sload(_preBytes.slot)
|
||||||
|
// Decode the length of the stored array like in concatStorage().
|
||||||
|
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
|
||||||
|
let mlength := mload(_postBytes)
|
||||||
|
|
||||||
|
// if lengths don't match the arrays are not equal
|
||||||
|
switch eq(slength, mlength)
|
||||||
|
case 1 {
|
||||||
|
// slength can contain both the length and contents of the array
|
||||||
|
// if length < 32 bytes so let's prepare for that
|
||||||
|
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
|
||||||
|
if iszero(iszero(slength)) {
|
||||||
|
switch lt(slength, 32)
|
||||||
|
case 1 {
|
||||||
|
// blank the last byte which is the length
|
||||||
|
fslot := mul(div(fslot, 0x100), 0x100)
|
||||||
|
|
||||||
|
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
|
||||||
|
// unsuccess:
|
||||||
|
success := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
// cb is a circuit breaker in the for loop since there's
|
||||||
|
// no said feature for inline assembly loops
|
||||||
|
// cb = 1 - don't breaker
|
||||||
|
// cb = 0 - break
|
||||||
|
let cb := 1
|
||||||
|
|
||||||
|
// get the keccak hash to get the contents of the array
|
||||||
|
mstore(0x0, _preBytes.slot)
|
||||||
|
let sc := keccak256(0x0, 0x20)
|
||||||
|
|
||||||
|
let mc := add(_postBytes, 0x20)
|
||||||
|
let end := add(mc, mlength)
|
||||||
|
|
||||||
|
// the next line is the loop condition:
|
||||||
|
// while(uint256(mc < end) + cb == 2)
|
||||||
|
for {} eq(add(lt(mc, end), cb), 2) {
|
||||||
|
sc := add(sc, 1)
|
||||||
|
mc := add(mc, 0x20)
|
||||||
|
} {
|
||||||
|
if iszero(eq(sload(sc), mload(mc))) {
|
||||||
|
// unsuccess:
|
||||||
|
success := 0
|
||||||
|
cb := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
// unsuccess:
|
||||||
|
success := 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import "../libraries/external/BytesLib.sol";
|
import "../libraries/external/UnsafeBytesLib.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/AbstractPyth.sol";
|
import "@pythnetwork/pyth-sdk-solidity/AbstractPyth.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||||
|
|
||||||
|
@ -12,8 +12,6 @@ import "./PythSetters.sol";
|
||||||
import "./PythInternalStructs.sol";
|
import "./PythInternalStructs.sol";
|
||||||
|
|
||||||
abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
using BytesLib for bytes;
|
|
||||||
|
|
||||||
function _initialize(
|
function _initialize(
|
||||||
address wormhole,
|
address wormhole,
|
||||||
uint16 pyth2WormholeChainId,
|
uint16 pyth2WormholeChainId,
|
||||||
|
@ -70,19 +68,19 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
|
|
||||||
// Check header
|
// Check header
|
||||||
{
|
{
|
||||||
uint32 magic = encoded.toUint32(index);
|
uint32 magic = UnsafeBytesLib.toUint32(encoded, index);
|
||||||
index += 4;
|
index += 4;
|
||||||
require(magic == 0x50325748, "invalid magic value");
|
require(magic == 0x50325748, "invalid magic value");
|
||||||
|
|
||||||
uint16 versionMajor = encoded.toUint16(index);
|
uint16 versionMajor = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
require(versionMajor == 3, "invalid version major, expected 3");
|
require(versionMajor == 3, "invalid version major, expected 3");
|
||||||
|
|
||||||
uint16 versionMinor = encoded.toUint16(index);
|
uint16 versionMinor = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
require(versionMinor >= 0, "invalid version minor, expected 0 or more");
|
require(versionMinor >= 0, "invalid version minor, expected 0 or more");
|
||||||
|
|
||||||
uint16 hdrSize = encoded.toUint16(index);
|
uint16 hdrSize = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
|
||||||
// NOTE(2022-04-19): Currently, only payloadId comes after
|
// NOTE(2022-04-19): Currently, only payloadId comes after
|
||||||
|
@ -90,16 +88,16 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
// separate offset to respect hdrSize, i.e.:
|
// separate offset to respect hdrSize, i.e.:
|
||||||
//
|
//
|
||||||
// uint hdrIndex = 0;
|
// uint hdrIndex = 0;
|
||||||
// bpa.header.payloadId = encoded.toUint8(index + hdrIndex);
|
// bpa.header.payloadId = UnsafeBytesLib.toUint8(encoded, index + hdrIndex);
|
||||||
// hdrIndex += 1;
|
// hdrIndex += 1;
|
||||||
//
|
//
|
||||||
// bpa.header.someNewField = encoded.toUint32(index + hdrIndex);
|
// bpa.header.someNewField = UnsafeBytesLib.toUint32(encoded, index + hdrIndex);
|
||||||
// hdrIndex += 4;
|
// hdrIndex += 4;
|
||||||
//
|
//
|
||||||
// // Skip remaining unknown header bytes
|
// // Skip remaining unknown header bytes
|
||||||
// index += bpa.header.hdrSize;
|
// index += bpa.header.hdrSize;
|
||||||
|
|
||||||
uint8 payloadId = encoded.toUint8(index);
|
uint8 payloadId = UnsafeBytesLib.toUint8(encoded, index);
|
||||||
|
|
||||||
// Skip remaining unknown header bytes
|
// Skip remaining unknown header bytes
|
||||||
index += hdrSize;
|
index += hdrSize;
|
||||||
|
@ -109,11 +107,11 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the number of attestations
|
// Parse the number of attestations
|
||||||
uint16 nAttestations = encoded.toUint16(index);
|
uint16 nAttestations = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
|
||||||
// Parse the attestation size
|
// Parse the attestation size
|
||||||
uint16 attestationSize = encoded.toUint16(index);
|
uint16 attestationSize = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
|
||||||
// Given the message is valid the arithmetic below should not overflow, and
|
// Given the message is valid the arithmetic below should not overflow, and
|
||||||
|
@ -134,22 +132,22 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
// Unused bytes32 product id
|
// Unused bytes32 product id
|
||||||
attestationIndex += 32;
|
attestationIndex += 32;
|
||||||
|
|
||||||
priceId = encoded.toBytes32(index + attestationIndex);
|
priceId = UnsafeBytesLib.toBytes32(encoded, index + attestationIndex);
|
||||||
attestationIndex += 32;
|
attestationIndex += 32;
|
||||||
|
|
||||||
info.price = int64(encoded.toUint64(index + attestationIndex));
|
info.price = int64(UnsafeBytesLib.toUint64(encoded, index + attestationIndex));
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
info.conf = encoded.toUint64(index + attestationIndex);
|
info.conf = UnsafeBytesLib.toUint64(encoded, index + attestationIndex);
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
info.expo = int32(encoded.toUint32(index + attestationIndex));
|
info.expo = int32(UnsafeBytesLib.toUint32(encoded, index + attestationIndex));
|
||||||
attestationIndex += 4;
|
attestationIndex += 4;
|
||||||
|
|
||||||
info.emaPrice = int64(encoded.toUint64(index + attestationIndex));
|
info.emaPrice = int64(UnsafeBytesLib.toUint64(encoded, index + attestationIndex));
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
info.emaConf = encoded.toUint64(index + attestationIndex);
|
info.emaConf = UnsafeBytesLib.toUint64(encoded, index + attestationIndex);
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -158,7 +156,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
// 1 = TRADING: The price feed is updating as expected.
|
// 1 = TRADING: The price feed is updating as expected.
|
||||||
// 2 = HALTED: The price feed is not currently updating because trading in the product has been halted.
|
// 2 = HALTED: The price feed is not currently updating because trading in the product has been halted.
|
||||||
// 3 = AUCTION: The price feed is not currently updating because an auction is setting the price.
|
// 3 = AUCTION: The price feed is not currently updating because an auction is setting the price.
|
||||||
uint8 status = encoded.toUint8(index + attestationIndex);
|
uint8 status = UnsafeBytesLib.toUint8(encoded, index + attestationIndex);
|
||||||
attestationIndex += 1;
|
attestationIndex += 1;
|
||||||
|
|
||||||
// Unused uint32 numPublishers
|
// Unused uint32 numPublishers
|
||||||
|
@ -170,7 +168,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
// Unused uint64 attestationTime
|
// Unused uint64 attestationTime
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
info.publishTime = encoded.toUint64(index + attestationIndex);
|
info.publishTime = UnsafeBytesLib.toUint64(encoded, index + attestationIndex);
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
if (status == 1) { // status == TRADING
|
if (status == 1) { // status == TRADING
|
||||||
|
@ -180,15 +178,15 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
// the previous price info that are passed here.
|
// the previous price info that are passed here.
|
||||||
|
|
||||||
// Previous publish time
|
// Previous publish time
|
||||||
info.publishTime = encoded.toUint64(index + attestationIndex);
|
info.publishTime = UnsafeBytesLib.toUint64(encoded, index + attestationIndex);
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
// Previous price
|
// Previous price
|
||||||
info.price = int64(encoded.toUint64(index + attestationIndex));
|
info.price = int64(UnsafeBytesLib.toUint64(encoded, index + attestationIndex));
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
|
|
||||||
// Previous confidence
|
// Previous confidence
|
||||||
info.conf = encoded.toUint64(index + attestationIndex);
|
info.conf = UnsafeBytesLib.toUint64(encoded, index + attestationIndex);
|
||||||
attestationIndex += 8;
|
attestationIndex += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,9 @@ contract PythGovernanceInstructions {
|
||||||
gi.targetChainId = encodedInstruction.toUint16(index);
|
gi.targetChainId = encodedInstruction.toUint16(index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
|
||||||
|
// As solidity performs math operations in a checked mode
|
||||||
|
// if the length of the encoded instruction be smaller than index
|
||||||
|
// it will revert. So we don't need any extra check.
|
||||||
gi.payload = encodedInstruction.slice(index, encodedInstruction.length - index);
|
gi.payload = encodedInstruction.slice(index, encodedInstruction.length - index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue