Created
June 8, 2023 13:21
-
-
Save light-fury/35db5e308702cf3b8e472a825bfbe018 to your computer and use it in GitHub Desktop.
Created using tron-ide: Realtime Tron Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at http://tronide.io/#version=soljson_v0.8.18+commit.f18bedf.js&optimize=true&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract CustomERC20 is ERC20, ERC20Burnable, Pausable, Ownable { uint8 private immutable _decimals; constructor(string memory name, string memory symbol, uint256 initialSupply, uint8 decimals_) ERC20(name, symbol) { if (initialSupply > 0) { _mint(msg.sender, initialSupply); } _decimals = decimals_; } function decimals() public view virtual override returns (uint8) { return _decimals; } function pause() public onlyOwner { _pause(); } function unpause() public onlyOwner { _unpause(); } function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function revoke(address from, uint256 amount) public onlyOwner { _burn(from, amount); } function _beforeTokenTransfer(address from, address to, uint256 amount) internal override { require(!paused() || owner() == _msgSender(), "Paused or not owner"); super._beforeTokenTransfer(from, to, amount); } } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol"; // Uncomment this line to use console.log // import "hardhat/console.sol"; contract PaymentRouter is Initializable, ContextUpgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; event Route(address indexed sender, address indexed destination, uint amount, string indexed metadata); event VerifierUpdated(address indexed prevVerifier, address indexed newVerifier); address private verifier; bytes32 private _CACHED_DOMAIN_SEPARATOR; uint256 private _CACHED_CHAIN_ID; address private _CACHED_THIS; bytes32 private _HASHED_NAME; bytes32 private _HASHED_VERSION; bytes32 private _TYPE_HASH; bytes32 private ROUTEINFO_TYPEHASH; // RouteInfo struct RouteInfo { address sender; address token; address[] destinations; uint256[] amounts; string metadata; } function initialize(address _verifier) public initializer { __Ownable_init(); __ReentrancyGuard_init(); __Context_init(); verifier = _verifier; _HASHED_NAME = keccak256(bytes("Payment Router V1")); _HASHED_VERSION = keccak256(bytes("1")); _CACHED_CHAIN_ID = block.chainid; _CACHED_THIS = address(this); ROUTEINFO_TYPEHASH = keccak256( "RouteInfo(address sender,address token,address[] destinations,uint256[] amounts,string metadata)" ); _TYPE_HASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } // Payment Recover Functions receive() external payable { } function withdraw() external onlyOwner { address payable to = payable(_msgSender()); to.transfer(address(this).balance); } function recoverERC20(address tokenAddress) external onlyOwner { IERC20(tokenAddress).transfer(_msgSender(), IERC20(tokenAddress).balanceOf(address(this))); } // Update Admin Settings function updateVerifier(address newVerifier) external onlyOwner { emit VerifierUpdated(verifier, newVerifier); verifier = newVerifier; } // The main function to route transactions. function route( address token, address[] calldata destinations, uint[] calldata amounts, string calldata metadata, bytes memory signature ) external payable nonReentrant returns(bool) { require(destinations.length == amounts.length, "PaymentRouter: Wrong params"); RouteInfo memory routeInfo = RouteInfo( _msgSender(), token, destinations, amounts, metadata ); require(validateRouteSignature(routeInfo, signature), "PaymentRouter: Invalid signature"); uint count = amounts.length; uint totalSum; for (uint i; i < count; ++i) { totalSum += amounts[i]; } require(totalSum > 0, "PaymentRouter: Zero payment"); uint originBalance; IERC20 targetToken = IERC20(token); if (token == address(0)) { originBalance = address(this).balance; require(msg.value == totalSum, "PaymentRouter: Insufficient funds"); } else { originBalance = targetToken.balanceOf(address(this)); targetToken.safeTransferFrom(_msgSender(), address(this), totalSum); uint addedBalance = targetToken.balanceOf(address(this)); require(addedBalance == originBalance + totalSum, "PaymentRouter: Wrong token contract"); } _execute(token, destinations, amounts, metadata); uint finalBalance; if (token == address(0)) { finalBalance = address(this).balance; } else { finalBalance = targetToken.balanceOf(address(this)); } require(finalBalance + totalSum >= originBalance, "PaymentRouter: Payment failed"); return true; } /* * @dev Validate the payment route info signed by the owner. */ function validateRouteSignature( RouteInfo memory routeInfo, bytes memory signature ) public view returns (bool) { bytes32 structHash = keccak256( abi.encode( ROUTEINFO_TYPEHASH, routeInfo.sender, routeInfo.token, keccak256(abi.encodePacked(routeInfo.destinations)), keccak256(abi.encodePacked(routeInfo.amounts)), keccak256(abi.encodePacked(routeInfo.metadata)) ) ); address recoveredVerifierAddress = ECDSAUpgradeable.recover( ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash), signature ); return recoveredVerifierAddress == verifier; } // Executes plugins in the given order. function _execute( address token, address[] calldata destinations, uint[] calldata amounts, string calldata metadata ) internal { uint count = amounts.length; if (token == address(0)) { for (uint256 index; index < count; ++index) { (bool success, ) = payable(destinations[index]).call{value: amounts[index]}(new bytes(0)); require(success, "PaymentRouter: Failed to send funds"); emit Route(_msgSender(), destinations[index], amounts[index], metadata); } } else { IERC20 targetToken = IERC20(token); for (uint256 index; index < count; ++index) { targetToken.safeTransfer(destinations[index], amounts[index]); emit Route(_msgSender(), destinations[index], amounts[index], metadata); } } } // Proof Generation & Validation Functions function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) { return _CACHED_DOMAIN_SEPARATOR; } else { return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); } } function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash ) private view returns (bytes32) { return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); } } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment