mirror of
https://github.com/Instadapp/dsa-periphery-contract.git
synced 2024-07-29 22:27:13 +00:00
add: target contracts for Connext connector
This commit is contained in:
parent
c8b06468da
commit
e115e596fb
116
contracts/InstadappAdapter.sol
Normal file
116
contracts/InstadappAdapter.sol
Normal file
|
@ -0,0 +1,116 @@
|
|||
// 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(
|
||||
"Sig(CastData cast,bytes32 salt, uint256 deadline)CastData(string[] _targetNames,bytes[] _datas,address _origin)"
|
||||
);
|
||||
|
||||
/// 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.
|
||||
function hash(CastData memory castData) public pure returns (bytes32) {
|
||||
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);
|
||||
}
|
||||
}
|
95
contracts/InstadappTarget.sol
Normal file
95
contracts/InstadappTarget.sol
Normal file
|
@ -0,0 +1,95 @@
|
|||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {IConnext} from "@connext/interfaces/core/IConnext.sol";
|
||||
import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {InstadappAdapter} from "./InstadappAdapter.sol";
|
||||
|
||||
/// @title InstadappTarget
|
||||
/// @author Connext
|
||||
/// @notice You can use this contract for cross-chain casting via dsa address
|
||||
/// @dev This contract is used to receive funds from Connext
|
||||
/// and forward them to Instadapp DSA via authCast function, In case of failure,
|
||||
/// funds are forwarded to fallback address defined by the user under callData.
|
||||
/// @custom:experimental This is an experimental contract.
|
||||
contract InstadappTarget is IXReceiver, InstadappAdapter {
|
||||
using SafeERC20 for IERC20;
|
||||
/// Storage
|
||||
/// @dev This is the address of the Connext contract.
|
||||
IConnext public connext;
|
||||
|
||||
/// Events
|
||||
/// @dev This event is emitted when the authCast function is called.
|
||||
event AuthCast(bytes32 transferId, address dsaAddress, address auth, bool success, bytes returnedData);
|
||||
|
||||
/// Modifiers
|
||||
/// @dev This modifier is used to ensure that only the Connext contract can call the function.
|
||||
modifier onlyConnext() {
|
||||
require(msg.sender == address(connext), "Caller must be Connext");
|
||||
_;
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
/// @param _connext The address of the Connext contract.
|
||||
constructor(address _connext) {
|
||||
connext = IConnext(_connext);
|
||||
}
|
||||
|
||||
/// Public functions
|
||||
/// @dev This function is used to receive funds from Connext and forward them to DSA.
|
||||
/// Then it forwards the call to authCast function.
|
||||
/// @param _amount The amount of funds that will be received.
|
||||
/// @param _asset The address of the asset that will be received.
|
||||
/// @param _transferId The id of the transfer.
|
||||
/// @param _callData The data that will be sent to the targets.
|
||||
function xReceive(
|
||||
bytes32 _transferId,
|
||||
uint256 _amount,
|
||||
address _asset,
|
||||
address,
|
||||
uint32,
|
||||
bytes memory _callData
|
||||
) external onlyConnext returns (bytes memory) {
|
||||
// Decode signed calldata
|
||||
// dsaAddress: address of DSA contract
|
||||
// auth: address of Authority, which whitelisted at dsaContract.
|
||||
// signature: signature is signed by the auth includes the castData with salt.
|
||||
// castData: CastData required for execution at destination
|
||||
// salt: salt for Signature Replay Protection, which is unique to each signature signed by auth.
|
||||
// deadline: deadline for the cast to be valid
|
||||
(
|
||||
address dsaAddress,
|
||||
address auth,
|
||||
bytes memory signature,
|
||||
CastData memory _castData,
|
||||
bytes32 _salt,
|
||||
uint256 deadline
|
||||
) = abi.decode(_callData, (address, address, bytes, CastData, bytes32, uint256));
|
||||
|
||||
// verify the dsaAddress
|
||||
require(dsaAddress != address(0), "!invalidFallback");
|
||||
|
||||
// transfer funds to this dsaAddress
|
||||
SafeERC20.safeTransfer(IERC20(_asset), dsaAddress, _amount);
|
||||
|
||||
// forward call to AuthCast
|
||||
// calling via encodeWithSignature as alternative to try/catch
|
||||
(bool success, bytes memory returnedData) = address(this).call(
|
||||
abi.encodeWithSignature(
|
||||
"authCast(address,address,bytes,CastData,bytes32, uint256)",
|
||||
dsaAddress,
|
||||
auth,
|
||||
signature,
|
||||
_castData,
|
||||
_salt,
|
||||
deadline
|
||||
)
|
||||
);
|
||||
|
||||
emit AuthCast(_transferId, dsaAddress, auth, success, returnedData);
|
||||
|
||||
return returnedData;
|
||||
}
|
||||
}
|
12
contracts/interfaces/IDSA.sol
Normal file
12
contracts/interfaces/IDSA.sol
Normal file
|
@ -0,0 +1,12 @@
|
|||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
interface IDSA {
|
||||
function cast(
|
||||
string[] calldata _targetNames,
|
||||
bytes[] calldata _datas,
|
||||
address _origin
|
||||
) external payable returns (bytes32);
|
||||
|
||||
function isAuth(address user) external view returns (bool);
|
||||
}
|
Loading…
Reference in New Issue
Block a user