Instead you could create a function in contract A
that when called will return a list of holders that have > 100 balance.
But finally, the implementation you settle on is really going to depend on the scale of your data. E.g. if there are many holders, but only a few of them will have > 100 balance, it might make sense to keep track of them in an EnumerableSet called e.g. richHolders
, which you would only update when an account’s balance crosses the 100 line in either direction. You could then have contract A
expose richHolders
to contract B
.
On the other hand, if richHolders
is going to be very large, B.receive
is going to be very heavy on gas. A preferable pattern would be something like this:
pragma solidity 0.8.15;
contract A {
mapping(address => uint256) public balances;
}
contract B {
A immutable private _a;
constructor(A a) {
_a = a;
}
uint256 num = 0;
struct ReceiveCall {
address msgSender;
uint256 msgValue;
mapping(address => bool) didSomething;
}
mapping(uint256 => ReceiveCall) private _receiveCalls;
receive() external payable {
ReceiveCall storage thisReceiveCall = _receiveCalls[num++];
thisReceiveCall.msgSender = msg.sender;
thisReceiveCall.msgValue = msg.value;
}
function doSomething(uint256 receiveCallNum) external {
require(_a.balances(msg.sender) > 100);
ReceiveCall storage receiveCall = _receiveCalls[receiveCallNum];
require(!receiveCall.didSomething[msg.sender]);
// do something with previous receive data
receiveCall.didSomething[msg.sender] = true;
}
}
In this way, receive
is kept light, and then rich holders can call and pay for doSomething
themselves.
P.S. In my suggested solution, the list of rich holders might change between the time receive
is called and the time the individual rich holders call doSomething
. So the behaviour is different than yours. But maybe you can find a way to reuse the concept of devising a pull solution instead of a push solution, rather than reusing the exact code itself. This was the intention of my answer.