Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CCIP-2465 enumerableMap for addressToBytes Mapping #14012

Merged
merged 6 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/.changeset/nice-planets-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/contracts': minor
---

EnumerableMap Library for an Address to Bytes mapping
28 changes: 18 additions & 10 deletions contracts/gas-snapshots/shared.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,29 @@ CallWithExactGas__callWithExactGasSafeReturnData:test_NoContractReverts() (gas:
CallWithExactGas__callWithExactGasSafeReturnData:test_NoGasForCallExactCheckReverts() (gas: 16139)
CallWithExactGas__callWithExactGasSafeReturnData:test_NotEnoughGasForCallReverts() (gas: 16547)
CallWithExactGas__callWithExactGasSafeReturnData:test_callWithExactGasSafeReturnData_ThrowOOGError_Revert() (gas: 36752)
EnumerableMapAddresses_at:testAtSuccess() (gas: 95001)
EnumerableMapAddresses_at:testBytes32AtSuccess() (gas: 94770)
EnumerableMapAddresses_at:testAtSuccess() (gas: 95086)
EnumerableMapAddresses_at:testBytes32AtSuccess() (gas: 94877)
EnumerableMapAddresses_contains:testBytes32ContainsSuccess() (gas: 93518)
EnumerableMapAddresses_contains:testContainsSuccess() (gas: 93696)
EnumerableMapAddresses_get:testBytes32GetSuccess() (gas: 94249)
EnumerableMapAddresses_get:testGetSuccess() (gas: 94436)
EnumerableMapAddresses_get_errorMessage:testGetErrorMessageSuccess() (gas: 94477)
EnumerableMapAddresses_length:testBytes32LengthSuccess() (gas: 72404)
EnumerableMapAddresses_length:testLengthSuccess() (gas: 72582)
EnumerableMapAddresses_remove:testBytes32RemoveSuccess() (gas: 73408)
EnumerableMapAddresses_get:testBytes32GetSuccess() (gas: 94278)
EnumerableMapAddresses_get:testGetSuccess() (gas: 94453)
EnumerableMapAddresses_get_errorMessage:testGetErrorMessageSuccess() (gas: 94489)
EnumerableMapAddresses_length:testBytes32LengthSuccess() (gas: 72445)
EnumerableMapAddresses_length:testLengthSuccess() (gas: 72640)
EnumerableMapAddresses_remove:testBytes32RemoveSuccess() (gas: 73462)
EnumerableMapAddresses_remove:testRemoveSuccess() (gas: 73686)
EnumerableMapAddresses_set:testBytes32SetSuccess() (gas: 94496)
EnumerableMapAddresses_set:testSetSuccess() (gas: 94685)
EnumerableMapAddresses_tryGet:testBytes32TryGetSuccess() (gas: 94604)
EnumerableMapAddresses_tryGet:testTryGetSuccess() (gas: 94864)
EnumerableMapAddresses_tryGet:testBytes32TryGetSuccess() (gas: 94622)
EnumerableMapAddresses_tryGet:testTryGetSuccess() (gas: 94893)
EnumerableMapAddresses_at:testBytesAtSuccess() (gas: 96564)
EnumerableMapAddresses_contains:testBytesContainsSuccess() (gas: 94012)
EnumerableMapAddresses_get:testBytesGetSuccess() (gas: 95879)
EnumerableMapAddresses_get_errorMessage:testBytesGetErrorMessageSuccess() (gas: 95878)
EnumerableMapAddresses_length:testBytesLengthSuccess() (gas: 73011)
EnumerableMapAddresses_remove:testBytesRemoveSuccess() (gas: 74249)
EnumerableMapAddresses_set:testBytesSetSuccess() (gas: 95428)
EnumerableMapAddresses_tryGet:testBytesTryGetSuccess() (gas: 96279)
OpStackBurnMintERC677_constructor:testConstructorSuccess() (gas: 1743649)
OpStackBurnMintERC677_interfaceCompatibility:testBurnCompatibility() (gas: 298649)
OpStackBurnMintERC677_interfaceCompatibility:testMintCompatibility() (gas: 137957)
Expand Down
89 changes: 87 additions & 2 deletions contracts/src/v0.8/shared/enumerable/EnumerableMapAddresses.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// SPDX-License-Identifier: MIT
/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */
pragma solidity ^0.8.0;

import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol";
import {EnumerableMapBytes32} from "./EnumerableMapBytes32.sol";

// TODO: the lib can be replaced with OZ v5.1 post-upgrade, which has AddressToAddressMap and AddressToBytes32Map
library EnumerableMapAddresses {
using EnumerableMap for EnumerableMap.UintToAddressMap;
using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map;
using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap;

struct AddressToAddressMap {
EnumerableMap.UintToAddressMap _inner;
Expand Down Expand Up @@ -57,8 +60,6 @@ library EnumerableMapAddresses {
return map._inner.get(uint256(uint160(key)), errorMessage);
}

// AddressToBytes32Map

struct AddressToBytes32Map {
EnumerableMap.Bytes32ToBytes32Map _inner;
}
Expand Down Expand Up @@ -137,4 +138,88 @@ library EnumerableMapAddresses {
function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
return map._inner.get(bytes32(uint256(uint160(key))));
}

struct AddressToBytesMap {
EnumerableMapBytes32.Bytes32ToBytesMap _inner;
}

/**
* @dev Sets the value for `key` in the map. Returns true if the key was added to the map, that is if it was not already present.
* @param map The map where the value will be set
* @param key The key to set the value for
* @param value The value to set for the key
* @return bool indicating whether the key was added to the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(AddressToBytesMap storage map, address key, bytes memory value) internal returns (bool) {
return map._inner.set(bytes32(uint256(uint160(key))), value);
}

/**
* @dev Removes the value for `key` in the map. Returns true if the key was removed from the map, that is if it was present.
* @param map The map where the value will be removed
* @param key The key to remove the value for
* @return bool indicating whether the key was removed from the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(AddressToBytesMap storage map, address key) internal returns (bool) {
return map._inner.remove(bytes32(uint256(uint160(key))));
}

/**
* @dev Checks if the map contains the `key`. Returns true if the key is in the map.
* @param map The map to check for the presence of the key
* @param key The key to check for presence in the map
* @return bool indicating whether the key is in the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(AddressToBytesMap storage map, address key) internal view returns (bool) {
return map._inner.contains(bytes32(uint256(uint160(key))));
}

/**
* @dev Returns the number of elements in the map.
* @param map The map to check the length of
* @return uint256 indicating the number of elements in the map
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(AddressToBytesMap storage map) internal view returns (uint256) {
return map._inner.length();
}

/**
* @dev Returns the element stored at position `index` in the map. Note that there are no guarantees on the ordering of values inside the array, and it may change when more values are added or removed.
* @param map The map to retrieve the element from
* @param index The index to retrieve the element at
* @return address The key of the element at the specified index
* @return bytes The value of the element at the specified index
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(AddressToBytesMap storage map, uint256 index) internal view returns (address, bytes memory) {
(bytes32 key, bytes memory value) = map._inner.at(index);
return (address(uint160(uint256(key))), value);
}

/**
* @dev Tries to return the value associated with `key`. Does not revert if `key` is not in the map.
* @param map The map to retrieve the value from
* @param key The key to retrieve the value for
* @return bool indicating whether the key was in the map
* @return bytes The value associated with the key
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(AddressToBytesMap storage map, address key) internal view returns (bool, bytes memory) {
return map._inner.tryGet(bytes32(uint256(uint160(key))));
}

/**
* @dev Returns the value associated with `key`.
* @param map The map to retrieve the value from
* @param key The key to retrieve the value for
* @return bytes The value associated with the key
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(AddressToBytesMap storage map, address key) internal view returns (bytes memory) {
return map._inner.get(bytes32(uint256(uint160(key))));
}
}
136 changes: 136 additions & 0 deletions contracts/src/v0.8/shared/enumerable/EnumerableMapBytes32.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: MIT
/* solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore */
pragma solidity ^0.8.0;

import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol";

/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMapBytes32 for EnumerableMapBytes32.Bytes32ToBytesMap;
*
* // Declare a set state variable
* EnumerableMapBytes32.Bytes32ToBytesMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `bytes32 -> bytes` (`Bytes32ToBytes`)
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean up an EnumerableMapBytes32, you should remove all elements one by one.
* ====
*/
library EnumerableMapBytes32 {
using EnumerableSet for EnumerableSet.Bytes32Set;

error NonexistentKeyError();

struct Bytes32ToBytesMap {
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes) _values;
}

/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function set(Bytes32ToBytesMap storage map, bytes32 key, bytes memory value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}

/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function remove(Bytes32ToBytesMap storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}

/**
* @dev Returns true if the key is in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function contains(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}

/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function length(Bytes32ToBytesMap storage map) internal view returns (uint256) {
return map._keys.length();
}

/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function at(Bytes32ToBytesMap storage map, uint256 index) internal view returns (bytes32, bytes memory) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}

/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function tryGet(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bool, bytes memory) {
bytes memory value = map._values[key];
if (value.length == 0) {
return (contains(map, key), bytes(""));
} else {
return (true, value);
}
}

/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function get(Bytes32ToBytesMap storage map, bytes32 key) internal view returns (bytes memory) {
bytes memory value = map._values[key];
if (value.length == 0 && !contains(map, key)) {
revert NonexistentKeyError();
}
return value;
}
}
Loading
Loading