Skip to content

Instantly share code, notes, and snippets.

@light-fury
Created June 8, 2023 13:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save light-fury/35db5e308702cf3b8e472a825bfbe018 to your computer and use it in GitHub Desktop.
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=
// 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); } }
// 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