¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.9+commit.e5eed63a
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 13: AddressAliasHelper.sol
// SPDX-License-Identifier: Apache-2.0/*
* Copyright 2019-2021, Offchain Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/pragmasolidity ^0.8.7;libraryAddressAliasHelper{
uint160constant offset =uint160(0x1111000000000000000000000000000000001111);
/// @notice Utility function that converts the address in the L1 that submitted a tx to/// the inbox to the msg.sender viewed in the L2/// @param l1Address the address in the L1 that triggered the tx to L2/// @return l2Address L2 address as viewed in msg.senderfunctionapplyL1ToL2Alias(address l1Address) internalpurereturns (address l2Address) {
unchecked {
l2Address =address(uint160(l1Address) + offset);
}
}
/// @notice Utility function that converts the msg.sender viewed in the L2 to the/// address in the L1 that submitted a tx to the inbox/// @param l2Address L2 address as viewed in msg.sender/// @return l1Address the address in the L1 that triggered the tx to L2functionundoL1ToL2Alias(address l2Address) internalpurereturns (address l1Address) {
unchecked {
l1Address =address(uint160(l2Address) - offset);
}
}
}
Código Fuente del Contrato
Archivo 2 de 13: CanonicalTransactionChain.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/* Library Imports */import { AddressAliasHelper } from"../../standards/AddressAliasHelper.sol";
import { Lib_OVMCodec } from"../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from"../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */import { ICanonicalTransactionChain } from"./ICanonicalTransactionChain.sol";
import { IChainStorageContainer } from"./IChainStorageContainer.sol";
/**
* @title CanonicalTransactionChain
* @dev The Canonical Transaction Chain (CTC) contract is an append-only log of transactions
* which must be applied to the rollup state. It defines the ordering of rollup transactions by
* writing them to the 'CTC:batches' instance of the Chain Storage Container.
* The CTC also allows any account to 'enqueue' an L2 transaction, which will require that the
* Sequencer will eventually append it to the rollup state.
*
* Runtime target: EVM
*/contractCanonicalTransactionChainisICanonicalTransactionChain, Lib_AddressResolver{
/*************
* Constants *
*************/// L2 tx gas-relateduint256publicconstant MIN_ROLLUP_TX_GAS =100000;
uint256publicconstant MAX_ROLLUP_TX_SIZE =50000;
// The approximate cost of calling the enqueue functionuint256public enqueueGasCost;
// The ratio of the cost of L1 gas to the cost of L2 gasuint256public l2GasDiscountDivisor;
// The amount of L2 gas which can be forwarded to L2 without spam prevention via 'gas burn'.// Calculated as the product of l2GasDiscountDivisor * enqueueGasCost.// See comments in enqueue() for further detail.uint256public enqueueL2GasPrepaid;
// Encoding-related (all in bytes)uint256internalconstant BATCH_CONTEXT_SIZE =16;
uint256internalconstant BATCH_CONTEXT_LENGTH_POS =12;
uint256internalconstant BATCH_CONTEXT_START_POS =15;
uint256internalconstant TX_DATA_HEADER_SIZE =3;
uint256internalconstant BYTES_TILL_TX_DATA =65;
/*************
* Variables *
*************/uint256public maxTransactionGasLimit;
/***************
* Queue State *
***************/uint40private _nextQueueIndex; // index of the first queue element not yet included
Lib_OVMCodec.QueueElement[] queueElements;
/***************
* Constructor *
***************/constructor(address _libAddressManager,
uint256 _maxTransactionGasLimit,
uint256 _l2GasDiscountDivisor,
uint256 _enqueueGasCost
) Lib_AddressResolver(_libAddressManager) {
maxTransactionGasLimit = _maxTransactionGasLimit;
l2GasDiscountDivisor = _l2GasDiscountDivisor;
enqueueGasCost = _enqueueGasCost;
enqueueL2GasPrepaid = _l2GasDiscountDivisor * _enqueueGasCost;
}
/**********************
* Function Modifiers *
**********************//**
* Modifier to enforce that, if configured, only the Burn Admin may
* successfully call a method.
*/modifieronlyBurnAdmin() {
require(msg.sender== libAddressManager.owner(), "Only callable by the Burn Admin.");
_;
}
/*******************************
* Authorized Setter Functions *
*******************************//**
* Allows the Burn Admin to update the parameters which determine the amount of gas to burn.
* The value of enqueueL2GasPrepaid is immediately updated as well.
*/functionsetGasParams(uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost)
externalonlyBurnAdmin{
enqueueGasCost = _enqueueGasCost;
l2GasDiscountDivisor = _l2GasDiscountDivisor;
// See the comment in enqueue() for the rationale behind this formula.
enqueueL2GasPrepaid = _l2GasDiscountDivisor * _enqueueGasCost;
emit L2GasParamsUpdated(l2GasDiscountDivisor, enqueueGasCost, enqueueL2GasPrepaid);
}
/********************
* Public Functions *
********************//**
* Accesses the batch storage container.
* @return Reference to the batch storage container.
*/functionbatches() publicviewreturns (IChainStorageContainer) {
return IChainStorageContainer(resolve("ChainStorageContainer-CTC-batches"));
}
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
*/functiongetTotalElements() publicviewreturns (uint256 _totalElements) {
(uint40 totalElements, , , ) = _getBatchExtraData();
returnuint256(totalElements);
}
/**
* Retrieves the total number of batches submitted.
* @return _totalBatches Total submitted batches.
*/functiongetTotalBatches() publicviewreturns (uint256 _totalBatches) {
return batches().length();
}
/**
* Returns the index of the next element to be enqueued.
* @return Index for the next queue element.
*/functiongetNextQueueIndex() publicviewreturns (uint40) {
return _nextQueueIndex;
}
/**
* Returns the timestamp of the last transaction.
* @return Timestamp for the last transaction.
*/functiongetLastTimestamp() publicviewreturns (uint40) {
(, , uint40 lastTimestamp, ) = _getBatchExtraData();
return lastTimestamp;
}
/**
* Returns the blocknumber of the last transaction.
* @return Blocknumber for the last transaction.
*/functiongetLastBlockNumber() publicviewreturns (uint40) {
(, , , uint40 lastBlockNumber) = _getBatchExtraData();
return lastBlockNumber;
}
/**
* Gets the queue element at a particular index.
* @param _index Index of the queue element to access.
* @return _element Queue element at the given index.
*/functiongetQueueElement(uint256 _index)
publicviewreturns (Lib_OVMCodec.QueueElement memory _element)
{
return queueElements[_index];
}
/**
* Get the number of queue elements which have not yet been included.
* @return Number of pending queue elements.
*/functiongetNumPendingQueueElements() publicviewreturns (uint40) {
returnuint40(queueElements.length) - _nextQueueIndex;
}
/**
* Retrieves the length of the queue, including
* both pending and canonical transactions.
* @return Length of the queue.
*/functiongetQueueLength() publicviewreturns (uint40) {
returnuint40(queueElements.length);
}
/**
* Adds a transaction to the queue.
* @param _target Target L2 contract to send the transaction to.
* @param _gasLimit Gas limit for the enqueued L2 transaction.
* @param _data Transaction data.
*/functionenqueue(address _target,
uint256 _gasLimit,
bytesmemory _data
) external{
require(
_data.length<= MAX_ROLLUP_TX_SIZE,
"Transaction data size exceeds maximum for rollup transaction."
);
require(
_gasLimit <= maxTransactionGasLimit,
"Transaction gas limit exceeds maximum for rollup transaction."
);
require(_gasLimit >= MIN_ROLLUP_TX_GAS, "Transaction gas limit too low to enqueue.");
// Transactions submitted to the queue lack a method for paying gas fees to the Sequencer.// So we need to prevent spam attacks by ensuring that the cost of enqueueing a transaction// from L1 to L2 is not underpriced. For transaction with a high L2 gas limit, we do this by// burning some extra gas on L1. Of course there is also some intrinsic cost to enqueueing a// transaction, so we want to make sure not to over-charge (by burning too much L1 gas).// Therefore, we define 'enqueueL2GasPrepaid' as the L2 gas limit above which we must burn// additional gas on L1. This threshold is the product of two inputs:// 1. enqueueGasCost: the base cost of calling this function.// 2. l2GasDiscountDivisor: the ratio between the cost of gas on L1 and L2. This is a// positive integer, meaning we assume L2 gas is always less costly.// The calculation below for gasToConsume can be seen as converting the difference (between// the specified L2 gas limit and the prepaid L2 gas limit) to an L1 gas amount.if (_gasLimit > enqueueL2GasPrepaid) {
uint256 gasToConsume = (_gasLimit - enqueueL2GasPrepaid) / l2GasDiscountDivisor;
uint256 startingGas =gasleft();
// Although this check is not necessary (burn below will run out of gas if not true), it// gives the user an explicit reason as to why the enqueue attempt failed.require(startingGas > gasToConsume, "Insufficient gas for L2 rate limiting burn.");
uint256 i;
while (startingGas -gasleft() < gasToConsume) {
i++;
}
}
// Apply an aliasing unless msg.sender == tx.origin. This prevents an attack in which a// contract on L1 has the same address as a contract on L2 but doesn't have the same code.// We can safely ignore this for EOAs because they're guaranteed to have the same "code"// (i.e. no code at all). This also makes it possible for users to interact with contracts// on L2 even when the Sequencer is down.address sender;
if (msg.sender==tx.origin) {
sender =msg.sender;
} else {
sender = AddressAliasHelper.applyL1ToL2Alias(msg.sender);
}
bytes32 transactionHash =keccak256(abi.encode(sender, _target, _gasLimit, _data));
queueElements.push(
Lib_OVMCodec.QueueElement({
transactionHash: transactionHash,
timestamp: uint40(block.timestamp),
blockNumber: uint40(block.number)
})
);
uint256 queueIndex = queueElements.length-1;
emit TransactionEnqueued(sender, _target, _gasLimit, _data, queueIndex, block.timestamp);
}
/**
* Allows the sequencer to append a batch of transactions.
* @dev This function uses a custom encoding scheme for efficiency reasons.
* .param _shouldStartAtElement Specific batch we expect to start appending to.
* .param _totalElementsToAppend Total number of batch elements we expect to append.
* .param _contexts Array of batch contexts.
* .param _transactionDataFields Array of raw transaction data.
*/functionappendSequencerBatch() external{
uint40 shouldStartAtElement;
uint24 totalElementsToAppend;
uint24 numContexts;
assembly {
shouldStartAtElement :=shr(216, calldataload(4))
totalElementsToAppend :=shr(232, calldataload(9))
numContexts :=shr(232, calldataload(12))
}
require(
shouldStartAtElement == getTotalElements(),
"Actual batch start index does not match expected start index."
);
require(
msg.sender== resolve("OVM_Sequencer"),
"Function can only be called by the Sequencer."
);
uint40 nextTransactionPtr =uint40(
BATCH_CONTEXT_START_POS + BATCH_CONTEXT_SIZE * numContexts
);
require(msg.data.length >= nextTransactionPtr, "Not enough BatchContexts provided.");
// Counter for number of sequencer transactions appended so far.uint32 numSequencerTransactions =0;
// Cache the _nextQueueIndex storage variable to a temporary stack variable.// This is safe as long as nothing reads or writes to the storage variable// until it is updated by the temp variable.uint40 nextQueueIndex = _nextQueueIndex;
BatchContext memory curContext;
for (uint32 i =0; i < numContexts; i++) {
BatchContext memory nextContext = _getBatchContext(i);
// Now we can update our current context.
curContext = nextContext;
// Process sequencer transactions first.
numSequencerTransactions +=uint32(curContext.numSequencedTransactions);
// Now process any subsequent queue transactions.
nextQueueIndex +=uint40(curContext.numSubsequentQueueTransactions);
}
require(
nextQueueIndex <= queueElements.length,
"Attempted to append more elements than are available in the queue."
);
// Generate the required metadata that we need to append this batchuint40 numQueuedTransactions = totalElementsToAppend - numSequencerTransactions;
uint40 blockTimestamp;
uint40 blockNumber;
if (curContext.numSubsequentQueueTransactions ==0) {
// The last element is a sequencer tx, therefore pull timestamp and block number from// the last context.
blockTimestamp =uint40(curContext.timestamp);
blockNumber =uint40(curContext.blockNumber);
} else {
// The last element is a queue tx, therefore pull timestamp and block number from the// queue element.// curContext.numSubsequentQueueTransactions > 0 which means that we've processed at// least one queue element. We increment nextQueueIndex after processing each queue// element, so the index of the last element we processed is nextQueueIndex - 1.
Lib_OVMCodec.QueueElement memory lastElement = queueElements[nextQueueIndex -1];
blockTimestamp = lastElement.timestamp;
blockNumber = lastElement.blockNumber;
}
// Cache the previous blockhash to ensure all transaction data can be retrieved efficiently.
_appendBatch(
blockhash(block.number-1),
totalElementsToAppend,
numQueuedTransactions,
blockTimestamp,
blockNumber
);
emit SequencerBatchAppended(
nextQueueIndex - numQueuedTransactions,
numQueuedTransactions,
getTotalElements()
);
// Update the _nextQueueIndex storage variable.
_nextQueueIndex = nextQueueIndex;
}
/**********************
* Internal Functions *
**********************//**
* Returns the BatchContext located at a particular index.
* @param _index The index of the BatchContext
* @return The BatchContext at the specified index.
*/function_getBatchContext(uint256 _index) internalpurereturns (BatchContext memory) {
uint256 contextPtr =15+ _index * BATCH_CONTEXT_SIZE;
uint256 numSequencedTransactions;
uint256 numSubsequentQueueTransactions;
uint256 ctxTimestamp;
uint256 ctxBlockNumber;
assembly {
numSequencedTransactions :=shr(232, calldataload(contextPtr))
numSubsequentQueueTransactions :=shr(232, calldataload(add(contextPtr, 3)))
ctxTimestamp :=shr(216, calldataload(add(contextPtr, 6)))
ctxBlockNumber :=shr(216, calldataload(add(contextPtr, 11)))
}
return
BatchContext({
numSequencedTransactions: numSequencedTransactions,
numSubsequentQueueTransactions: numSubsequentQueueTransactions,
timestamp: ctxTimestamp,
blockNumber: ctxBlockNumber
});
}
/**
* Parses the batch context from the extra data.
* @return Total number of elements submitted.
* @return Index of the next queue element.
*/function_getBatchExtraData()
internalviewreturns (uint40,
uint40,
uint40,
uint40)
{
bytes27 extraData = batches().getGlobalMetadata();
uint40 totalElements;
uint40 nextQueueIndex;
uint40 lastTimestamp;
uint40 lastBlockNumber;
// solhint-disable max-line-lengthassembly {
extraData :=shr(40, extraData)
totalElements :=and(
extraData,
0x000000000000000000000000000000000000000000000000000000FFFFFFFFFF
)
nextQueueIndex :=shr(
40,
and(extraData, 0x00000000000000000000000000000000000000000000FFFFFFFFFF0000000000)
)
lastTimestamp :=shr(
80,
and(extraData, 0x0000000000000000000000000000000000FFFFFFFFFF00000000000000000000)
)
lastBlockNumber :=shr(
120,
and(extraData, 0x000000000000000000000000FFFFFFFFFF000000000000000000000000000000)
)
}
// solhint-enable max-line-lengthreturn (totalElements, nextQueueIndex, lastTimestamp, lastBlockNumber);
}
/**
* Encodes the batch context for the extra data.
* @param _totalElements Total number of elements submitted.
* @param _nextQueueIdx Index of the next queue element.
* @param _timestamp Timestamp for the last batch.
* @param _blockNumber Block number of the last batch.
* @return Encoded batch context.
*/function_makeBatchExtraData(uint40 _totalElements,
uint40 _nextQueueIdx,
uint40 _timestamp,
uint40 _blockNumber
) internalpurereturns (bytes27) {
bytes27 extraData;
assembly {
extraData := _totalElements
extraData :=or(extraData, shl(40, _nextQueueIdx))
extraData :=or(extraData, shl(80, _timestamp))
extraData :=or(extraData, shl(120, _blockNumber))
extraData :=shl(40, extraData)
}
return extraData;
}
/**
* Inserts a batch into the chain of batches.
* @param _transactionRoot Root of the transaction tree for this batch.
* @param _batchSize Number of elements in the batch.
* @param _numQueuedTransactions Number of queue transactions in the batch.
* @param _timestamp The latest batch timestamp.
* @param _blockNumber The latest batch blockNumber.
*/function_appendBatch(bytes32 _transactionRoot,
uint256 _batchSize,
uint256 _numQueuedTransactions,
uint40 _timestamp,
uint40 _blockNumber
) internal{
IChainStorageContainer batchesRef = batches();
(uint40 totalElements, uint40 nextQueueIndex, , ) = _getBatchExtraData();
Lib_OVMCodec.ChainBatchHeader memory header = Lib_OVMCodec.ChainBatchHeader({
batchIndex: batchesRef.length(),
batchRoot: _transactionRoot,
batchSize: _batchSize,
prevTotalElements: totalElements,
extraData: hex""
});
emit TransactionBatchAppended(
header.batchIndex,
header.batchRoot,
header.batchSize,
header.prevTotalElements,
header.extraData
);
bytes32 batchHeaderHash = Lib_OVMCodec.hashBatchHeader(header);
bytes27 latestBatchContext = _makeBatchExtraData(
totalElements +uint40(header.batchSize),
nextQueueIndex +uint40(_numQueuedTransactions),
_timestamp,
_blockNumber
);
batchesRef.push(batchHeaderHash, latestBatchContext);
}
}
Código Fuente del Contrato
Archivo 3 de 13: Context.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Código Fuente del Contrato
Archivo 4 de 13: ICanonicalTransactionChain.sol
// SPDX-License-Identifier: MITpragmasolidity >0.5.0 <0.9.0;/* Library Imports */import { Lib_OVMCodec } from"../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */import { IChainStorageContainer } from"./IChainStorageContainer.sol";
/**
* @title ICanonicalTransactionChain
*/interfaceICanonicalTransactionChain{
/**********
* Events *
**********/eventL2GasParamsUpdated(uint256 l2GasDiscountDivisor,
uint256 enqueueGasCost,
uint256 enqueueL2GasPrepaid
);
eventTransactionEnqueued(addressindexed _l1TxOrigin,
addressindexed _target,
uint256 _gasLimit,
bytes _data,
uint256indexed _queueIndex,
uint256 _timestamp
);
eventQueueBatchAppended(uint256 _startingQueueIndex,
uint256 _numQueueElements,
uint256 _totalElements
);
eventSequencerBatchAppended(uint256 _startingQueueIndex,
uint256 _numQueueElements,
uint256 _totalElements
);
eventTransactionBatchAppended(uint256indexed _batchIndex,
bytes32 _batchRoot,
uint256 _batchSize,
uint256 _prevTotalElements,
bytes _extraData
);
/***********
* Structs *
***********/structBatchContext {
uint256 numSequencedTransactions;
uint256 numSubsequentQueueTransactions;
uint256 timestamp;
uint256 blockNumber;
}
/*******************************
* Authorized Setter Functions *
*******************************//**
* Allows the Burn Admin to update the parameters which determine the amount of gas to burn.
* The value of enqueueL2GasPrepaid is immediately updated as well.
*/functionsetGasParams(uint256 _l2GasDiscountDivisor, uint256 _enqueueGasCost) external;
/********************
* Public Functions *
********************//**
* Accesses the batch storage container.
* @return Reference to the batch storage container.
*/functionbatches() externalviewreturns (IChainStorageContainer);
/**
* Retrieves the total number of elements submitted.
* @return _totalElements Total submitted elements.
*/functiongetTotalElements() externalviewreturns (uint256 _totalElements);
/**
* Retrieves the total number of batches submitted.
* @return _totalBatches Total submitted batches.
*/functiongetTotalBatches() externalviewreturns (uint256 _totalBatches);
/**
* Returns the index of the next element to be enqueued.
* @return Index for the next queue element.
*/functiongetNextQueueIndex() externalviewreturns (uint40);
/**
* Gets the queue element at a particular index.
* @param _index Index of the queue element to access.
* @return _element Queue element at the given index.
*/functiongetQueueElement(uint256 _index)
externalviewreturns (Lib_OVMCodec.QueueElement memory _element);
/**
* Returns the timestamp of the last transaction.
* @return Timestamp for the last transaction.
*/functiongetLastTimestamp() externalviewreturns (uint40);
/**
* Returns the blocknumber of the last transaction.
* @return Blocknumber for the last transaction.
*/functiongetLastBlockNumber() externalviewreturns (uint40);
/**
* Get the number of queue elements which have not yet been included.
* @return Number of pending queue elements.
*/functiongetNumPendingQueueElements() externalviewreturns (uint40);
/**
* Retrieves the length of the queue, including
* both pending and canonical transactions.
* @return Length of the queue.
*/functiongetQueueLength() externalviewreturns (uint40);
/**
* Adds a transaction to the queue.
* @param _target Target contract to send the transaction to.
* @param _gasLimit Gas limit for the given transaction.
* @param _data Transaction data.
*/functionenqueue(address _target,
uint256 _gasLimit,
bytesmemory _data
) external;
/**
* Allows the sequencer to append a batch of transactions.
* @dev This function uses a custom encoding scheme for efficiency reasons.
* .param _shouldStartAtElement Specific batch we expect to start appending to.
* .param _totalElementsToAppend Total number of batch elements we expect to append.
* .param _contexts Array of batch contexts.
* .param _transactionDataFields Array of raw transaction data.
*/functionappendSequencerBatch(// uint40 _shouldStartAtElement,// uint24 _totalElementsToAppend,// BatchContext[] _contexts,// bytes[] _transactionDataFields) external;
}
Código Fuente del Contrato
Archivo 5 de 13: IChainStorageContainer.sol
// SPDX-License-Identifier: MITpragmasolidity >0.5.0 <0.9.0;/**
* @title IChainStorageContainer
*/interfaceIChainStorageContainer{
/********************
* Public Functions *
********************//**
* Sets the container's global metadata field. We're using `bytes27` here because we use five
* bytes to maintain the length of the underlying data structure, meaning we have an extra
* 27 bytes to store arbitrary data.
* @param _globalMetadata New global metadata to set.
*/functionsetGlobalMetadata(bytes27 _globalMetadata) external;
/**
* Retrieves the container's global metadata field.
* @return Container global metadata field.
*/functiongetGlobalMetadata() externalviewreturns (bytes27);
/**
* Retrieves the number of objects stored in the container.
* @return Number of objects in the container.
*/functionlength() externalviewreturns (uint256);
/**
* Pushes an object into the container.
* @param _object A 32 byte value to insert into the container.
*/functionpush(bytes32 _object) external;
/**
* Pushes an object into the container. Function allows setting the global metadata since
* we'll need to touch the "length" storage slot anyway, which also contains the global
* metadata (it's an optimization).
* @param _object A 32 byte value to insert into the container.
* @param _globalMetadata New global metadata for the container.
*/functionpush(bytes32 _object, bytes27 _globalMetadata) external;
/**
* Retrieves an object from the container.
* @param _index Index of the particular object to access.
* @return 32 byte object value.
*/functionget(uint256 _index) externalviewreturns (bytes32);
/**
* Removes all objects after and including a given index.
* @param _index Object index to delete from.
*/functiondeleteElementsAfterInclusive(uint256 _index) external;
/**
* Removes all objects after and including a given index. Also allows setting the global
* metadata field.
* @param _index Object index to delete from.
* @param _globalMetadata New global metadata for the container.
*/functiondeleteElementsAfterInclusive(uint256 _index, bytes27 _globalMetadata) external;
}
Código Fuente del Contrato
Archivo 6 de 13: Lib_AddressManager.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/* External Imports */import { Ownable } from"@openzeppelin/contracts/access/Ownable.sol";
/**
* @title Lib_AddressManager
*/contractLib_AddressManagerisOwnable{
/**********
* Events *
**********/eventAddressSet(stringindexed _name, address _newAddress, address _oldAddress);
/*************
* Variables *
*************/mapping(bytes32=>address) private addresses;
/********************
* Public Functions *
********************//**
* Changes the address associated with a particular name.
* @param _name String name to associate an address with.
* @param _address Address to associate with the name.
*/functionsetAddress(stringmemory _name, address _address) externalonlyOwner{
bytes32 nameHash = _getNameHash(_name);
address oldAddress = addresses[nameHash];
addresses[nameHash] = _address;
emit AddressSet(_name, _address, oldAddress);
}
/**
* Retrieves the address associated with a given name.
* @param _name Name to retrieve an address for.
* @return Address associated with the given name.
*/functiongetAddress(stringmemory _name) externalviewreturns (address) {
return addresses[_getNameHash(_name)];
}
/**********************
* Internal Functions *
**********************//**
* Computes the hash of a name.
* @param _name Name to compute a hash for.
* @return Hash of the given name.
*/function_getNameHash(stringmemory _name) internalpurereturns (bytes32) {
returnkeccak256(abi.encodePacked(_name));
}
}
Código Fuente del Contrato
Archivo 7 de 13: Lib_AddressResolver.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/* Library Imports */import { Lib_AddressManager } from"./Lib_AddressManager.sol";
/**
* @title Lib_AddressResolver
*/abstractcontractLib_AddressResolver{
/*************
* Variables *
*************/
Lib_AddressManager public libAddressManager;
/***************
* Constructor *
***************//**
* @param _libAddressManager Address of the Lib_AddressManager.
*/constructor(address _libAddressManager) {
libAddressManager = Lib_AddressManager(_libAddressManager);
}
/********************
* Public Functions *
********************//**
* Resolves the address associated with a given name.
* @param _name Name to resolve an address for.
* @return Address associated with the given name.
*/functionresolve(stringmemory _name) publicviewreturns (address) {
return libAddressManager.getAddress(_name);
}
}
Código Fuente del Contrato
Archivo 8 de 13: Lib_Bytes32Utils.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/**
* @title Lib_Byte32Utils
*/libraryLib_Bytes32Utils{
/**********************
* Internal Functions *
**********************//**
* Converts a bytes32 value to a boolean. Anything non-zero will be converted to "true."
* @param _in Input bytes32 value.
* @return Bytes32 as a boolean.
*/functiontoBool(bytes32 _in) internalpurereturns (bool) {
return _in !=0;
}
/**
* Converts a boolean to a bytes32 value.
* @param _in Input boolean value.
* @return Boolean as a bytes32.
*/functionfromBool(bool _in) internalpurereturns (bytes32) {
returnbytes32(uint256(_in ? 1 : 0));
}
/**
* Converts a bytes32 value to an address. Takes the *last* 20 bytes.
* @param _in Input bytes32 value.
* @return Bytes32 as an address.
*/functiontoAddress(bytes32 _in) internalpurereturns (address) {
returnaddress(uint160(uint256(_in)));
}
/**
* Converts an address to a bytes32.
* @param _in Input address value.
* @return Address as a bytes32.
*/functionfromAddress(address _in) internalpurereturns (bytes32) {
returnbytes32(uint256(uint160(_in)));
}
}
Código Fuente del Contrato
Archivo 9 de 13: Lib_BytesUtils.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/**
* @title Lib_BytesUtils
*/libraryLib_BytesUtils{
/**********************
* Internal Functions *
**********************/functionslice(bytesmemory _bytes,
uint256 _start,
uint256 _length
) internalpurereturns (bytesmemory) {
require(_length +31>= _length, "slice_overflow");
require(_start + _length >= _start, "slice_overflow");
require(_bytes.length>= _start + _length, "slice_outOfBounds");
bytesmemory tempBytes;
assembly {
switchiszero(_length)
case0 {
// 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 nowmstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length arraydefault {
tempBytes :=mload(0x40)
//zero out the 32 bytes slice we are about to return//we need to do it because Solidity does not garbage collectmstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
functionslice(bytesmemory _bytes, uint256 _start) internalpurereturns (bytesmemory) {
if (_start >= _bytes.length) {
returnbytes("");
}
return slice(_bytes, _start, _bytes.length- _start);
}
functiontoBytes32(bytesmemory _bytes) internalpurereturns (bytes32) {
if (_bytes.length<32) {
bytes32 ret;
assembly {
ret :=mload(add(_bytes, 32))
}
return ret;
}
returnabi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes
}
functiontoUint256(bytesmemory _bytes) internalpurereturns (uint256) {
returnuint256(toBytes32(_bytes));
}
functiontoNibbles(bytesmemory _bytes) internalpurereturns (bytesmemory) {
bytesmemory nibbles =newbytes(_bytes.length*2);
for (uint256 i =0; i < _bytes.length; i++) {
nibbles[i *2] = _bytes[i] >>4;
nibbles[i *2+1] =bytes1(uint8(_bytes[i]) %16);
}
return nibbles;
}
functionfromNibbles(bytesmemory _bytes) internalpurereturns (bytesmemory) {
bytesmemory ret =newbytes(_bytes.length/2);
for (uint256 i =0; i < ret.length; i++) {
ret[i] = (_bytes[i *2] <<4) | (_bytes[i *2+1]);
}
return ret;
}
functionequal(bytesmemory _bytes, bytesmemory _other) internalpurereturns (bool) {
returnkeccak256(_bytes) ==keccak256(_other);
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/**
* @title Lib_RLPReader
* @dev Adapted from "RLPReader" by Hamdi Allam (hamdi.allam97@gmail.com).
*/libraryLib_RLPReader{
/*************
* Constants *
*************/uint256internalconstant MAX_LIST_LENGTH =32;
/*********
* Enums *
*********/enumRLPItemType {
DATA_ITEM,
LIST_ITEM
}
/***********
* Structs *
***********/structRLPItem {
uint256 length;
uint256 ptr;
}
/**********************
* Internal Functions *
**********************//**
* Converts bytes to a reference to memory position and length.
* @param _in Input bytes to convert.
* @return Output memory reference.
*/functiontoRLPItem(bytesmemory _in) internalpurereturns (RLPItem memory) {
uint256 ptr;
assembly {
ptr :=add(_in, 32)
}
return RLPItem({ length: _in.length, ptr: ptr });
}
/**
* Reads an RLP list value into a list of RLP items.
* @param _in RLP list value.
* @return Decoded RLP list items.
*/functionreadList(RLPItem memory _in) internalpurereturns (RLPItem[] memory) {
(uint256 listOffset, , RLPItemType itemType) = _decodeLength(_in);
require(itemType == RLPItemType.LIST_ITEM, "Invalid RLP list value.");
// Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by// writing to the length. Since we can't know the number of RLP items without looping over// the entire input, we'd have to loop twice to accurately size this array. It's easier to// simply set a reasonable maximum list length and decrease the size before we finish.
RLPItem[] memory out =new RLPItem[](MAX_LIST_LENGTH);
uint256 itemCount =0;
uint256 offset = listOffset;
while (offset < _in.length) {
require(itemCount < MAX_LIST_LENGTH, "Provided RLP list exceeds max list length.");
(uint256 itemOffset, uint256 itemLength, ) = _decodeLength(
RLPItem({ length: _in.length- offset, ptr: _in.ptr + offset })
);
out[itemCount] = RLPItem({ length: itemLength + itemOffset, ptr: _in.ptr + offset });
itemCount +=1;
offset += itemOffset + itemLength;
}
// Decrease the array size to match the actual item count.assembly {
mstore(out, itemCount)
}
return out;
}
/**
* Reads an RLP list value into a list of RLP items.
* @param _in RLP list value.
* @return Decoded RLP list items.
*/functionreadList(bytesmemory _in) internalpurereturns (RLPItem[] memory) {
return readList(toRLPItem(_in));
}
/**
* Reads an RLP bytes value into bytes.
* @param _in RLP bytes value.
* @return Decoded bytes.
*/functionreadBytes(RLPItem memory _in) internalpurereturns (bytesmemory) {
(uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);
require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes value.");
return _copy(_in.ptr, itemOffset, itemLength);
}
/**
* Reads an RLP bytes value into bytes.
* @param _in RLP bytes value.
* @return Decoded bytes.
*/functionreadBytes(bytesmemory _in) internalpurereturns (bytesmemory) {
return readBytes(toRLPItem(_in));
}
/**
* Reads an RLP string value into a string.
* @param _in RLP string value.
* @return Decoded string.
*/functionreadString(RLPItem memory _in) internalpurereturns (stringmemory) {
returnstring(readBytes(_in));
}
/**
* Reads an RLP string value into a string.
* @param _in RLP string value.
* @return Decoded string.
*/functionreadString(bytesmemory _in) internalpurereturns (stringmemory) {
return readString(toRLPItem(_in));
}
/**
* Reads an RLP bytes32 value into a bytes32.
* @param _in RLP bytes32 value.
* @return Decoded bytes32.
*/functionreadBytes32(RLPItem memory _in) internalpurereturns (bytes32) {
require(_in.length<=33, "Invalid RLP bytes32 value.");
(uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in);
require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes32 value.");
uint256 ptr = _in.ptr + itemOffset;
bytes32 out;
assembly {
out :=mload(ptr)
// Shift the bytes over to match the item size.iflt(itemLength, 32) {
out :=div(out, exp(256, sub(32, itemLength)))
}
}
return out;
}
/**
* Reads an RLP bytes32 value into a bytes32.
* @param _in RLP bytes32 value.
* @return Decoded bytes32.
*/functionreadBytes32(bytesmemory _in) internalpurereturns (bytes32) {
return readBytes32(toRLPItem(_in));
}
/**
* Reads an RLP uint256 value into a uint256.
* @param _in RLP uint256 value.
* @return Decoded uint256.
*/functionreadUint256(RLPItem memory _in) internalpurereturns (uint256) {
returnuint256(readBytes32(_in));
}
/**
* Reads an RLP uint256 value into a uint256.
* @param _in RLP uint256 value.
* @return Decoded uint256.
*/functionreadUint256(bytesmemory _in) internalpurereturns (uint256) {
return readUint256(toRLPItem(_in));
}
/**
* Reads an RLP bool value into a bool.
* @param _in RLP bool value.
* @return Decoded bool.
*/functionreadBool(RLPItem memory _in) internalpurereturns (bool) {
require(_in.length==1, "Invalid RLP boolean value.");
uint256 ptr = _in.ptr;
uint256 out;
assembly {
out :=byte(0, mload(ptr))
}
require(out ==0|| out ==1, "Lib_RLPReader: Invalid RLP boolean value, must be 0 or 1");
return out !=0;
}
/**
* Reads an RLP bool value into a bool.
* @param _in RLP bool value.
* @return Decoded bool.
*/functionreadBool(bytesmemory _in) internalpurereturns (bool) {
return readBool(toRLPItem(_in));
}
/**
* Reads an RLP address value into a address.
* @param _in RLP address value.
* @return Decoded address.
*/functionreadAddress(RLPItem memory _in) internalpurereturns (address) {
if (_in.length==1) {
returnaddress(0);
}
require(_in.length==21, "Invalid RLP address value.");
returnaddress(uint160(readUint256(_in)));
}
/**
* Reads an RLP address value into a address.
* @param _in RLP address value.
* @return Decoded address.
*/functionreadAddress(bytesmemory _in) internalpurereturns (address) {
return readAddress(toRLPItem(_in));
}
/**
* Reads the raw bytes of an RLP item.
* @param _in RLP item to read.
* @return Raw RLP bytes.
*/functionreadRawBytes(RLPItem memory _in) internalpurereturns (bytesmemory) {
return _copy(_in);
}
/*********************
* Private Functions *
*********************//**
* Decodes the length of an RLP item.
* @param _in RLP item to decode.
* @return Offset of the encoded data.
* @return Length of the encoded data.
* @return RLP item type (LIST_ITEM or DATA_ITEM).
*/function_decodeLength(RLPItem memory _in)
privatepurereturns (uint256,
uint256,
RLPItemType
)
{
require(_in.length>0, "RLP item cannot be null.");
uint256 ptr = _in.ptr;
uint256 prefix;
assembly {
prefix :=byte(0, mload(ptr))
}
if (prefix <=0x7f) {
// Single byte.return (0, 1, RLPItemType.DATA_ITEM);
} elseif (prefix <=0xb7) {
// Short string.uint256 strLen = prefix -0x80;
require(_in.length> strLen, "Invalid RLP short string.");
return (1, strLen, RLPItemType.DATA_ITEM);
} elseif (prefix <=0xbf) {
// Long string.uint256 lenOfStrLen = prefix -0xb7;
require(_in.length> lenOfStrLen, "Invalid RLP long string length.");
uint256 strLen;
assembly {
// Pick out the string length.
strLen :=div(mload(add(ptr, 1)), exp(256, sub(32, lenOfStrLen)))
}
require(_in.length> lenOfStrLen + strLen, "Invalid RLP long string.");
return (1+ lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
} elseif (prefix <=0xf7) {
// Short list.uint256 listLen = prefix -0xc0;
require(_in.length> listLen, "Invalid RLP short list.");
return (1, listLen, RLPItemType.LIST_ITEM);
} else {
// Long list.uint256 lenOfListLen = prefix -0xf7;
require(_in.length> lenOfListLen, "Invalid RLP long list length.");
uint256 listLen;
assembly {
// Pick out the list length.
listLen :=div(mload(add(ptr, 1)), exp(256, sub(32, lenOfListLen)))
}
require(_in.length> lenOfListLen + listLen, "Invalid RLP long list.");
return (1+ lenOfListLen, listLen, RLPItemType.LIST_ITEM);
}
}
/**
* Copies the bytes from a memory location.
* @param _src Pointer to the location to read from.
* @param _offset Offset to start reading from.
* @param _length Number of bytes to read.
* @return Copied bytes.
*/function_copy(uint256 _src,
uint256 _offset,
uint256 _length
) privatepurereturns (bytesmemory) {
bytesmemory out =newbytes(_length);
if (out.length==0) {
return out;
}
uint256 src = _src + _offset;
uint256 dest;
assembly {
dest :=add(out, 32)
}
// Copy over as many complete words as we can.for (uint256 i =0; i < _length /32; i++) {
assembly {
mstore(dest, mload(src))
}
src +=32;
dest +=32;
}
// Pick out the remaining bytes.uint256 mask;
unchecked {
mask =256**(32- (_length %32)) -1;
}
assembly {
mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask)))
}
return out;
}
/**
* Copies an RLP item into bytes.
* @param _in RLP item to copy.
* @return Copied bytes.
*/function_copy(RLPItem memory _in) privatepurereturns (bytesmemory) {
return _copy(_in.ptr, 0, _in.length);
}
}
Código Fuente del Contrato
Archivo 12 de 13: Lib_RLPWriter.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.9;/**
* @title Lib_RLPWriter
* @author Bakaoh (with modifications)
*/libraryLib_RLPWriter{
/**********************
* Internal Functions *
**********************//**
* RLP encodes a byte string.
* @param _in The byte string to encode.
* @return The RLP encoded string in bytes.
*/functionwriteBytes(bytesmemory _in) internalpurereturns (bytesmemory) {
bytesmemory encoded;
if (_in.length==1&&uint8(_in[0]) <128) {
encoded = _in;
} else {
encoded =abi.encodePacked(_writeLength(_in.length, 128), _in);
}
return encoded;
}
/**
* RLP encodes a list of RLP encoded byte byte strings.
* @param _in The list of RLP encoded byte strings.
* @return The RLP encoded list of items in bytes.
*/functionwriteList(bytes[] memory _in) internalpurereturns (bytesmemory) {
bytesmemory list = _flatten(_in);
returnabi.encodePacked(_writeLength(list.length, 192), list);
}
/**
* RLP encodes a string.
* @param _in The string to encode.
* @return The RLP encoded string in bytes.
*/functionwriteString(stringmemory _in) internalpurereturns (bytesmemory) {
return writeBytes(bytes(_in));
}
/**
* RLP encodes an address.
* @param _in The address to encode.
* @return The RLP encoded address in bytes.
*/functionwriteAddress(address _in) internalpurereturns (bytesmemory) {
return writeBytes(abi.encodePacked(_in));
}
/**
* RLP encodes a uint.
* @param _in The uint256 to encode.
* @return The RLP encoded uint256 in bytes.
*/functionwriteUint(uint256 _in) internalpurereturns (bytesmemory) {
return writeBytes(_toBinary(_in));
}
/**
* RLP encodes a bool.
* @param _in The bool to encode.
* @return The RLP encoded bool in bytes.
*/functionwriteBool(bool _in) internalpurereturns (bytesmemory) {
bytesmemory encoded =newbytes(1);
encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80));
return encoded;
}
/*********************
* Private Functions *
*********************//**
* Encode the first byte, followed by the `len` in binary form if `length` is more than 55.
* @param _len The length of the string or the payload.
* @param _offset 128 if item is string, 192 if item is list.
* @return RLP encoded bytes.
*/function_writeLength(uint256 _len, uint256 _offset) privatepurereturns (bytesmemory) {
bytesmemory encoded;
if (_len <56) {
encoded =newbytes(1);
encoded[0] =bytes1(uint8(_len) +uint8(_offset));
} else {
uint256 lenLen;
uint256 i =1;
while (_len / i !=0) {
lenLen++;
i *=256;
}
encoded =newbytes(lenLen +1);
encoded[0] =bytes1(uint8(lenLen) +uint8(_offset) +55);
for (i =1; i <= lenLen; i++) {
encoded[i] =bytes1(uint8((_len / (256**(lenLen - i))) %256));
}
}
return encoded;
}
/**
* Encode integer in big endian binary form with no leading zeroes.
* @notice TODO: This should be optimized with assembly to save gas costs.
* @param _x The integer to encode.
* @return RLP encoded bytes.
*/function_toBinary(uint256 _x) privatepurereturns (bytesmemory) {
bytesmemory b =abi.encodePacked(_x);
uint256 i =0;
for (; i <32; i++) {
if (b[i] !=0) {
break;
}
}
bytesmemory res =newbytes(32- i);
for (uint256 j =0; j < res.length; j++) {
res[j] = b[i++];
}
return res;
}
/**
* Copies a piece of memory to another location.
* @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol.
* @param _dest Destination location.
* @param _src Source location.
* @param _len Length of memory to copy.
*/function_memcpy(uint256 _dest,
uint256 _src,
uint256 _len
) privatepure{
uint256 dest = _dest;
uint256 src = _src;
uint256 len = _len;
for (; len >=32; len -=32) {
assembly {
mstore(dest, mload(src))
}
dest +=32;
src +=32;
}
uint256 mask;
unchecked {
mask =256**(32- len) -1;
}
assembly {
let srcpart :=and(mload(src), not(mask))
let destpart :=and(mload(dest), mask)
mstore(dest, or(destpart, srcpart))
}
}
/**
* Flattens a list of byte strings into one byte string.
* @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol.
* @param _list List of byte strings to flatten.
* @return The flattened byte string.
*/function_flatten(bytes[] memory _list) privatepurereturns (bytesmemory) {
if (_list.length==0) {
returnnewbytes(0);
}
uint256 len;
uint256 i =0;
for (; i < _list.length; i++) {
len += _list[i].length;
}
bytesmemory flattened =newbytes(len);
uint256 flattenedPtr;
assembly {
flattenedPtr :=add(flattened, 0x20)
}
for (i =0; i < _list.length; i++) {
bytesmemory item = _list[i];
uint256 listPtr;
assembly {
listPtr :=add(item, 0x20)
}
_memcpy(flattenedPtr, listPtr, item.length);
flattenedPtr += _list[i].length;
}
return flattened;
}
}
Código Fuente del Contrato
Archivo 13 de 13: Ownable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_setOwner(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function_setOwner(address newOwner) private{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}