tornado-contracts/contracts/Classic/PermitTornado.sol

147 lines
5.2 KiB
Solidity
Raw Normal View History

2024-10-13 13:25:29 +03:00
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ISignatureTransfer } from './interfaces/ISignatureTransfer.sol';
import { IVerifier, IHasher, IERC20, SafeERC20, ERC20Tornado } from './ERC20Tornado.sol';
/**
* Extends ERC20Tornado with ERC-2612 and TornadoProxyLight Router contract support
*
* For new chains deploy permit2 contract first using the tool (present at foundry https://github.com/Arachnid/deterministic-deployment-proxy)
*/
contract PermitTornado is ERC20Tornado {
using SafeERC20 for IERC20;
2024-10-22 14:37:10 +03:00
struct PermitCommitments {
address instance;
bytes32 commitmentsHash;
}
bytes public constant COMMITMENT_TYPE = "PermitCommitments(address instance,bytes32 commitmentsHash)";
bytes32 public constant COMMITMENT_TYPEHASH = keccak256(bytes(COMMITMENT_TYPE));
string public constant WITNESS_TYPE_STRING = string(abi.encodePacked("PermitCommitments witness)", COMMITMENT_TYPE, "TokenPermissions(address token,uint256 amount)"));
2024-10-13 13:25:29 +03:00
// https://docs.uniswap.org/contracts/v3/reference/deployments/ethereum-deployments
ISignatureTransfer public constant permit2 = ISignatureTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);
2024-10-22 14:37:10 +03:00
mapping(bytes32 => address) public verifiedCommitments;
event VerifiedCommitment(bytes32 indexed commitment, address owner);
2024-10-13 13:25:29 +03:00
address public immutable tornadoProxyLight;
constructor(
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight,
IERC20 _token,
address _tornadoProxyLight
) ERC20Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight, _token) {
tornadoProxyLight = _tornadoProxyLight;
}
function _processDeposit() internal virtual override {
if (msg.sender == tornadoProxyLight) {
token.safeTransferFrom(tx.origin, address(this), denomination);
return;
}
super._processDeposit();
}
2024-10-22 14:37:10 +03:00
function depositCommitment(
bytes32 _commitment,
address owner
) external nonReentrant {
require(verifiedCommitments[_commitment] == owner, "Unapproved commitment");
2024-10-13 13:25:29 +03:00
require(!commitments[_commitment], "The commitment has been submitted");
uint32 insertedIndex = _insert(_commitment);
commitments[_commitment] = true;
emit Deposit(_commitment, insertedIndex, block.timestamp);
}
2024-10-22 14:37:10 +03:00
function pushVerifiedCommitments(bytes32[] memory _commitments, address owner) internal {
for (uint256 i = 0; i < _commitments.length; i++) {
bytes32 _commitment = _commitments[i];
// Prevent losing money with wrong commitment
require(verifiedCommitments[_commitment] == address(0), "The commitment has been verified");
require(!commitments[_commitment], "The commitment has been submitted");
verifiedCommitments[_commitment] = owner;
emit VerifiedCommitment(_commitment, owner);
}
}
/**
* Verify commitments from signature as a part of deposit verification (to prevent MEV).
* We use deadline to verify commitment because they occupy the same byte size and using bigger value wouldn't revert on most wallets.
* If any wallet rejects signing signature should use permit2 with typed data instead.
*/
function permitCommitments(
2024-10-13 13:25:29 +03:00
address owner,
2024-10-22 14:37:10 +03:00
bytes32[] memory _commitments,
2024-10-13 13:25:29 +03:00
uint8 v,
bytes32 r,
bytes32 s
) external nonReentrant {
2024-10-22 14:37:10 +03:00
uint256 amount = denomination * _commitments.length;
bytes32 commitmentsHash = keccak256(abi.encodePacked(_commitments));
token.permit(owner, address(this), amount, uint256(commitmentsHash), v, r, s);
token.safeTransferFrom(owner, address(this), amount);
2024-10-13 13:25:29 +03:00
2024-10-22 14:37:10 +03:00
pushVerifiedCommitments(_commitments, owner);
2024-10-13 13:25:29 +03:00
}
2024-10-22 14:37:10 +03:00
function witness(PermitCommitments memory permitData) public pure returns (bytes32) {
return keccak256(
abi.encode(COMMITMENT_TYPEHASH, permitData.instance, permitData.commitmentsHash)
);
}
/**
* https://docs.uniswap.org/contracts/permit2/reference/signature-transfer#batch-permitwitnesstransferfrom
*/
function permit2Commitments(
2024-10-13 13:25:29 +03:00
address owner,
2024-10-22 14:37:10 +03:00
bytes32[] memory _commitments,
uint256 nonce,
uint256 deadline,
2024-10-13 13:25:29 +03:00
bytes calldata _signature
) external nonReentrant {
2024-10-22 14:37:10 +03:00
uint256 amount = denomination * _commitments.length;
bytes32 commitmentsHash = keccak256(abi.encodePacked(_commitments));
permit2.permitWitnessTransferFrom(
ISignatureTransfer.PermitTransferFrom({
permitted: ISignatureTransfer.TokenPermissions({
token: address(token),
amount: amount
}),
nonce: nonce,
deadline: deadline
}),
2024-10-13 13:25:29 +03:00
ISignatureTransfer.SignatureTransferDetails({
to: address(this),
2024-10-22 14:37:10 +03:00
requestedAmount: amount
2024-10-13 13:25:29 +03:00
}),
owner,
2024-10-22 14:37:10 +03:00
witness(PermitCommitments({
instance: address(this),
commitmentsHash: commitmentsHash
})),
WITNESS_TYPE_STRING,
2024-10-13 13:25:29 +03:00
_signature
);
2024-10-22 14:37:10 +03:00
pushVerifiedCommitments(_commitments, owner);
2024-10-13 13:25:29 +03:00
}
}