Skip to content

Instantly share code, notes, and snippets.

@skozin
Last active February 3, 2023 14:02
Show Gist options
  • Save skozin/60e6ec7e9c56f34a7410db1cd11ffa34 to your computer and use it in GitHub Desktop.
Save skozin/60e6ec7e9c56f34a7410db1cd11ffa34 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
interface IMultiLocate {
function a() external view returns (address);
function b() external view returns (address);
function c() external view returns (address);
function d() external view returns (address);
function e() external view returns (address);
function f() external view returns (address);
function locate(bytes calldata selectors) external view returns (address[] memory addresses);
}
contract MultiLocate is IMultiLocate {
address public immutable a = 0xaAaabbbBAAaABbbbAAAaBbbbaaaABbBbaAaaBbBb;
address public immutable b = 0x1111Eeee1111eeEe1111EEee1111EeeE1111eeee;
address public immutable c = 0x2222FFff2222FfFF2222fFFF2222fFFf2222FFfF;
address public immutable d = 0x3333aAAa3333aAaa3333AaAa3333AAAa3333aAAa;
address public immutable e = 0x4444BbBb4444bbbb4444bbbB4444BbBb4444bBbB;
address public immutable f = 0x5555cCCC5555CCCC5555Cccc5555CCcc5555ccCC;
function getSelectors() external pure returns (bytes memory) {
return abi.encodePacked(
IMultiLocate.a.selector,
IMultiLocate.b.selector,
IMultiLocate.c.selector,
IMultiLocate.d.selector,
IMultiLocate.e.selector,
IMultiLocate.f.selector
);
}
function locate(bytes calldata selectors) external view returns (address[] memory addresses) {
require(selectors.length % 4 == 0);
uint256 numAddresses = selectors.length / 4;
address self = address(this);
assembly {
// allocate addresses array and selector memory
addresses := mload(0x40)
mstore(addresses, numAddresses)
// store offset for the 4-byte selector value
let selOffset := add(add(addresses, mul(numAddresses, 32)), 32)
// update free memory pointer
mstore(0x40, add(selOffset, 4))
// loop over selectors and call fns
let cdOffset := selectors.offset
let addrOffset := add(addresses, 32)
let postEndCdOffset := add(cdOffset, selectors.length)
for {} lt(cdOffset, postEndCdOffset) {} {
calldatacopy(selOffset, cdOffset, 4)
let result := staticcall(1000, self, selOffset, 4, addrOffset, 32)
cdOffset := add(cdOffset, 4)
addrOffset := add(addrOffset, 32)
}
}
}
function locate2() external view returns (address[] memory addresses) {
addresses = new address[](6);
addresses[0] = a;
addresses[1] = b;
addresses[2] = c;
addresses[3] = d;
addresses[4] = e;
addresses[5] = f;
}
}
contract Consumer {
event Addr(address a, address b, address c, address d, address e, address f);
IMultiLocate _locator;
constructor(address locator) {
_locator = IMultiLocate(locator);
}
function consume1() external {
address[] memory addr = _locator.locate(abi.encodePacked(
IMultiLocate.a.selector,
IMultiLocate.b.selector,
IMultiLocate.c.selector,
IMultiLocate.d.selector,
IMultiLocate.e.selector,
IMultiLocate.f.selector
));
emit Addr(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
}
function _getLocator() internal view returns (IMultiLocate) {
return IMultiLocate(_locator);
}
function consume2() external {
// emulate the proxy loading impl on each call
emit Addr(_getLocator().a(), _getLocator().b(), _getLocator().c(), _getLocator().d(), _getLocator().e(), _getLocator().f());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment