105 lines
3.2 KiB
Solidity
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);
|
||
|
}
|
||
|
}
|