Hola a todos, thx @frangio for throwing the idea of using one proxy per call. I ended up implementing it and I have to say this looks much better and secure than using one single proxy contract.
Advantage: By having a mapping of nonces for ERC827 senders that execute extra calls and increments each time a call is executed successfully I can protect the proxies against replay attacks.
But the best thing so far is that now the ERC827 receiver can verify that the call that he is receiving form the proxy is actually submitted by the real msg.sender. Since the create2 address computation inputs are the token address, msg.sender, the contract to call and the nonce all this data is available when the ERC827Receiver receives the call.
PR in WT/ERC827 repo: https://github.com/windingtree/erc827/pull/18
ERC827 internal _call funciton that executes the call, creates a proxy for each call.
function _call(address _to, bytes memory _data) internal {
bytes32 salt = keccak256(abi.encodePacked(msg.sender, _to, nonces[msg.sender]));
address proxy = Create2.deploy(salt, proxyBytecode);
(bool success, bytes memory data) = address(proxy).call.value(msg.value)(
abi.encodeWithSelector(bytes4(keccak256("callContract(address,bytes)")), _to, _data)
);
require(success, "Call to external contract failed");
nonces[msg.sender].add(1);
}
ERC827 Receiver can verify that the call is coming from the original msg.sender. The salt used in ERC827 protects it against replay attacks and allows on chain verification.
function receiveVerifiedTokens(address sender, ERC827 token) public {
address proxy = Create2.computeAddress(
address(token),
keccak256(abi.encodePacked(sender, address(this), token.nonces(sender))),
token.proxyBytecode()
);
require(msg.sender == proxy, "ERC827Receiver: Sender invalid");
}
Also, the proxy cant end with token or eth balance, and it gets destroyed after the call.
Looking forward to your opinions @frangio @nventuro @abcoathup @k06a