DCIPs/assets/eip-5050/ERC5050State.sol

105 lines
3.2 KiB
Solidity

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import {IERC5050Sender, IERC5050Receiver, Action} from "./IERC5050.sol";
import {ActionsSet} from "./ActionsSet.sol";
contract ERC5050State is IERC5050Receiver {
using ActionsSet for ActionsSet.Set;
ActionsSet.Set private _receivableActions;
function onActionReceived(Action calldata action, uint256 nonce)
external
payable
virtual
override
onlyReceivableAction(action, nonce)
{
_onActionReceived(action, nonce);
}
function receivableActions() external view returns (string[] memory) {
return _receivableActions.names();
}
modifier onlyReceivableAction(Action calldata action, uint256 nonce) {
require(
_receivableActions.contains(action.selector),
"ERC5050: invalid action"
);
require(action.state == address(this), "ERC5050: invalid state");
require(
action.user == address(0) || action.user == tx.origin,
"ERC5050: invalid user"
);
address expectedSender = action.to._address;
if (expectedSender == address(0)) {
if (action.from._address != address(0)) {
expectedSender = action.from._address;
} else {
expectedSender = action.user;
}
}
require(msg.sender == expectedSender, "ERC5050: invalid sender");
// State contracts must validate the action with the `from` contract in
// the case of a 3-contract chain (`from`, `to` and `state`) all set to
// valid contract addresses.
if (
action.to._address.isContract() && action.from._address.isContract()
) {
uint256 actionHash = uint256(
keccak256(
abi.encodePacked(
action.selector,
action.user,
action.from._address,
action.from._tokenId,
action.to._address,
action.to._tokenId,
action.state,
action.data,
nonce
)
)
);
try
IERC5050Sender(action.from._address).isValid(actionHash, nonce)
returns (bool ok) {
require(ok, "ERC5050: action not validated");
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC5050: call to non ERC5050Sender");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
_;
}
function _onActionReceived(Action calldata action, uint256 nonce)
internal
virtual
{
emit ActionReceived(
action.selector,
action.user,
action.from._address,
action.from._tokenId,
action.to._address,
action.to._tokenId,
action.state,
action.data
);
}
function _registerReceivable(string memory action) internal {
_receivableActions.add(action);
}
}