2023-04-28 04:08:30 +00:00
|
|
|
// SPDX-License-Identifier: UNLICENSED
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
|
|
|
|
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
|
|
|
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
|
|
|
|
|
|
|
|
import {IDSA} from "./interfaces/IDSA.sol";
|
|
|
|
|
|
|
|
/// @title InstadappAdapter
|
|
|
|
/// @author Connext
|
|
|
|
/// @notice This contract is inherited by InstadappTarget, it includes the logic to verify signatures
|
|
|
|
/// and execute the calls.
|
|
|
|
/// @dev This contract is not meant to be used directly, it is meant to be inherited by other contracts.
|
|
|
|
/// @custom:experimental This is an experimental contract.
|
|
|
|
contract InstadappAdapter is EIP712 {
|
|
|
|
/// Structs
|
|
|
|
/// @dev This struct is used to encode the data for InstadappTarget.cast function.
|
|
|
|
/// @param _targetNames The names of the targets that will be called.
|
|
|
|
/// @param _datas The data that will be sent to the targets.
|
|
|
|
/// @param _origin The address that will be used as the origin of the call.
|
|
|
|
struct CastData {
|
|
|
|
string[] _targetNames;
|
|
|
|
bytes[] _datas;
|
|
|
|
address _origin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @dev This struct is used to encode the data that is signed by the auth address.
|
|
|
|
/// The signature is then verified by the verify function.
|
|
|
|
struct Sig {
|
|
|
|
CastData castData;
|
|
|
|
bytes32 salt;
|
|
|
|
uint256 deadline;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Storage
|
|
|
|
/// @dev This mapping is used to prevent replay attacks.
|
|
|
|
mapping(bytes32 => bool) private sigReplayProtection;
|
|
|
|
|
|
|
|
/// Constants
|
|
|
|
/// @dev This is the typehash for the CastData struct.
|
|
|
|
bytes32 public constant CASTDATA_TYPEHASH =
|
|
|
|
keccak256("CastData(string[] _targetNames,bytes[] _datas,address _origin)");
|
|
|
|
|
|
|
|
/// @dev This is the typehash for the Sig struct.
|
|
|
|
bytes32 public constant SIG_TYPEHASH =
|
|
|
|
keccak256(
|
2023-05-01 05:37:53 +00:00
|
|
|
"Sig(CastData cast,bytes32 salt,uint256 deadline)CastData(string[] _targetNames,bytes[] _datas,address _origin)"
|
2023-04-28 04:08:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
/// Constructor
|
|
|
|
constructor() EIP712("InstaTargetAuth", "1") {}
|
|
|
|
|
|
|
|
/// Public functions
|
|
|
|
/// @dev This function is used to verify the signature.
|
|
|
|
/// @param auth The address of the auth.
|
|
|
|
/// @param signature The signature of the auth.
|
|
|
|
/// @param castData The data that will be sent to the targets.
|
|
|
|
/// @param salt The salt that will be used to prevent replay attacks.
|
|
|
|
/// @param deadline The deadline that will be used to prevent replay attacks.
|
|
|
|
/// @return boolean that indicates if the signature is valid.
|
|
|
|
function verify(
|
|
|
|
address auth,
|
|
|
|
bytes memory signature,
|
|
|
|
CastData memory castData,
|
|
|
|
bytes32 salt,
|
|
|
|
uint256 deadline
|
|
|
|
) public view returns (bool) {
|
|
|
|
bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(SIG_TYPEHASH, hash(castData), salt, deadline)));
|
|
|
|
address signer = ECDSA.recover(digest, signature);
|
|
|
|
return signer == auth;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @dev This function is used to hash the CastData struct.
|
|
|
|
/// @param castData The data that will be sent to the targets.
|
|
|
|
/// @return bytes32 that is the hash of the CastData struct.
|
2023-05-03 20:58:33 +00:00
|
|
|
function getcastDataSigDigest(CastData memory castData) public pure returns (bytes32) {
|
2023-04-28 04:08:30 +00:00
|
|
|
return keccak256(abi.encode(CASTDATA_TYPEHASH, castData._targetNames, castData._datas, castData._origin));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Internal functions
|
|
|
|
/// @dev This function is used to forward the call to dsa.cast function.
|
|
|
|
/// Cast the call is forwarded, the signature is verified and the salt is stored in the sigReplayProtection mapping.
|
|
|
|
/// @param dsaAddress The address of the DSA.
|
|
|
|
/// @param auth The address of the auth.
|
|
|
|
/// @param signature The signature by the auth. This signature is used to verify the SIG data.
|
|
|
|
/// @param castData The data that will be sent to the targets.
|
|
|
|
/// @param salt The salt that will be used to prevent replay attacks.
|
|
|
|
/// @param deadline The deadline that will be used to prevent replay attacks.
|
|
|
|
function authCast(
|
|
|
|
address dsaAddress,
|
|
|
|
address auth,
|
|
|
|
bytes memory signature,
|
|
|
|
CastData memory castData,
|
|
|
|
bytes32 salt,
|
|
|
|
uint256 deadline
|
|
|
|
) internal {
|
|
|
|
IDSA dsa = IDSA(dsaAddress);
|
|
|
|
// check if Auth is valid, and included in the DSA
|
|
|
|
require(dsa.isAuth(auth), "Invalid Auth");
|
|
|
|
|
|
|
|
// check if signature is not replayed
|
|
|
|
require(!sigReplayProtection[salt], "Replay Attack");
|
|
|
|
|
|
|
|
// check if signature is not expired
|
|
|
|
require(block.timestamp <= deadline, "Signature Expired");
|
|
|
|
|
|
|
|
// check if signature is valid, and not replayed
|
|
|
|
require(verify(auth, signature, castData, salt, deadline), "Invalid signature");
|
|
|
|
|
|
|
|
// Signature Replay Protection
|
|
|
|
sigReplayProtection[salt] = true;
|
|
|
|
|
|
|
|
// Cast the call
|
|
|
|
dsa.cast(castData._targetNames, castData._datas, castData._origin);
|
|
|
|
}
|
|
|
|
}
|