diff --git a/contracts/TenGransAbstractToken.sol b/contracts/TenGransAbstractToken.sol index 39f9c09..c385954 100644 --- a/contracts/TenGransAbstractToken.sol +++ b/contracts/TenGransAbstractToken.sol @@ -8,12 +8,16 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import "erc-payable-token/contracts/token/ERC1363/IERC1363.sol"; +import "erc-payable-token/contracts/token/ERC1363/IERC1363Spender.sol"; +import "erc-payable-token/contracts/token/ERC1363/IERC1363Receiver.sol"; + +abstract contract AbstractGrans is IERC1363, ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint { + using Address for address; + + constructor() ERC20("10Grans", "GRANS") ERC20Permit("10Grans") {} -abstract contract AbstractGrans is ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint { - constructor() ERC20("10Grans", "GRANS") ERC20Permit("10Grans") { - - } - function pause() public onlyOwner { _pause(); } @@ -22,6 +26,14 @@ abstract contract AbstractGrans is ERC20, ERC20Burnable, Pausable, Ownable, ERC2 _unpause(); } + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165) returns (bool) { + return interfaceId == type(IERC1363).interfaceId; + } + + function _beforeTokenTransfer(address from, address to, uint256 amount) internal override whenNotPaused { super._beforeTokenTransfer(from, to, amount); } @@ -40,11 +52,151 @@ abstract contract AbstractGrans is ERC20, ERC20Burnable, Pausable, Ownable, ERC2 super._burn(account, amount); } - function balanceOf(address account) public view virtual override(ERC20) returns (uint256) { + function balanceOf(address account) public view virtual override(ERC20, IERC20) returns (uint256) { return ERC20.balanceOf(account); } - function transferFrom(address sender, address recipient, uint256 amount) public virtual override(ERC20) returns (bool) { + function transferFrom(address sender, address recipient, uint256 amount) public virtual override(ERC20, IERC20) returns (bool) { return ERC20.transferFrom(sender, recipient, amount); } + + /** + * @dev Transfer tokens to a specified address and then execute a callback on `to`. + * @param to The address to transfer to. + * @param amount The amount to be transferred. + * @return A boolean that indicates if the operation was successful. + */ + function transferAndCall(address to, uint256 amount) public virtual override returns (bool) { + return transferAndCall(to, amount, ""); + } + + /** + * @dev Transfer tokens to a specified address and then execute a callback on `to`. + * @param to The address to transfer to + * @param amount The amount to be transferred + * @param data Additional data with no specified format + * @return A boolean that indicates if the operation was successful. + */ + function transferAndCall(address to, uint256 amount, bytes memory data) public virtual override returns (bool) { + transfer(to, amount); + require(_checkOnTransferReceived(_msgSender(), to, amount, data), "ERC1363: receiver returned wrong data"); + return true; + } + + /** + * @dev Transfer tokens from one address to another and then execute a callback on `to`. + * @param from The address which you want to send tokens from + * @param to The address which you want to transfer to + * @param amount The amount of tokens to be transferred + * @return A boolean that indicates if the operation was successful. + */ + function transferFromAndCall(address from, address to, uint256 amount) public virtual override returns (bool) { + return transferFromAndCall(from, to, amount, ""); + } + + /** + * @dev Transfer tokens from one address to another and then execute a callback on `to`. + * @param from The address which you want to send tokens from + * @param to The address which you want to transfer to + * @param amount The amount of tokens to be transferred + * @param data Additional data with no specified format + * @return A boolean that indicates if the operation was successful. + */ + function transferFromAndCall( + address from, + address to, + uint256 amount, + bytes memory data + ) public virtual override returns (bool) { + transferFrom(from, to, amount); + require(_checkOnTransferReceived(from, to, amount, data), "ERC1363: receiver returned wrong data"); + return true; + } + + /** + * @dev Approve spender to transfer tokens and then execute a callback on `spender`. + * @param spender The address allowed to transfer to + * @param amount The amount allowed to be transferred + * @return A boolean that indicates if the operation was successful. + */ + function approveAndCall(address spender, uint256 amount) public virtual override returns (bool) { + return approveAndCall(spender, amount, ""); + } + + /** + * @dev Approve spender to transfer tokens and then execute a callback on `spender`. + * @param spender The address allowed to transfer to. + * @param amount The amount allowed to be transferred. + * @param data Additional data with no specified format. + * @return A boolean that indicates if the operation was successful. + */ + function approveAndCall(address spender, uint256 amount, bytes memory data) public virtual override returns (bool) { + approve(spender, amount); + require(_checkOnApprovalReceived(spender, amount, data), "ERC1363: spender returned wrong data"); + return true; + } + + /** + * @dev Internal function to invoke {IERC1363Receiver-onTransferReceived} on a target address. + * The call is not executed if the target address is not a contract. + * @param sender address Representing the previous owner of the given token amount + * @param recipient address Target address that will receive the tokens + * @param amount uint256 The amount mount of tokens to be transferred + * @param data bytes Optional data to send along with the call + * @return whether the call correctly returned the expected magic value + */ + function _checkOnTransferReceived( + address sender, + address recipient, + uint256 amount, + bytes memory data + ) internal virtual returns (bool) { + if (!recipient.isContract()) { + revert("ERC1363: transfer to non contract address"); + } + + try IERC1363Receiver(recipient).onTransferReceived(_msgSender(), sender, amount, data) returns (bytes4 retval) { + return retval == IERC1363Receiver.onTransferReceived.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC1363: transfer to non ERC1363Receiver implementer"); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } + + /** + * @dev Internal function to invoke {IERC1363Receiver-onApprovalReceived} on a target address. + * The call is not executed if the target address is not a contract. + * @param spender address The address which will spend the funds + * @param amount uint256 The amount of tokens to be spent + * @param data bytes Optional data to send along with the call + * @return whether the call correctly returned the expected magic value + */ + function _checkOnApprovalReceived( + address spender, + uint256 amount, + bytes memory data + ) internal virtual returns (bool) { + if (!spender.isContract()) { + revert("ERC1363: approve a non contract address"); + } + + try IERC1363Spender(spender).onApprovalReceived(_msgSender(), amount, data) returns (bytes4 retval) { + return retval == IERC1363Spender.onApprovalReceived.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert("ERC1363: approve a non ERC1363Spender implementer"); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } } diff --git a/hardhat.config.ts b/hardhat.config.ts index c4ef851..5dabb1f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,8 +1,17 @@ import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; +import "hardhat-contract-sizer"; const config: HardhatUserConfig = { - solidity: "0.8.19", + solidity: { + version: "0.8.19", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + } }; export default config; diff --git a/package-lock.json b/package-lock.json index ab4ef9d..3fda1ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,14 +7,16 @@ "": { "name": "10grans-ng", "version": "1.0.0", - "license": "ISC", + "license": "MIT", "dependencies": { "@arbitrum/token-bridge-contracts": "^1.0.0-beta.0", - "@openzeppelin/contracts": "^4.9.3" + "@openzeppelin/contracts": "^4.9.3", + "erc-payable-token": "^4.9.3" }, "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^3.0.0", "hardhat": "^2.17.1", + "hardhat-contract-sizer": "^2.10.0", "prettier": "^3.0.1", "prettier-plugin-solidity": "^1.1.3" } @@ -100,6 +102,16 @@ "case": "^1.6.3" } }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -4150,6 +4162,14 @@ "node": ">=6" } }, + "node_modules/erc-payable-token": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/erc-payable-token/-/erc-payable-token-4.9.3.tgz", + "integrity": "sha512-hAkSyH+TfXvbTyrlcOpGhzQkSTgrRWkAktOhRUL2vY2sztYr3twjxhPydaK6CI4uf9+nk6SpujiRrPqxoDOd/w==", + "dependencies": { + "@openzeppelin/contracts": "4.9.3" + } + }, "node_modules/es-abstract": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", @@ -5814,6 +5834,105 @@ } } }, + "node_modules/hardhat-contract-sizer": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz", + "integrity": "sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "cli-table3": "^0.6.0", + "strip-ansi": "^6.0.0" + }, + "peerDependencies": { + "hardhat": "^2.0.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/hardhat-contract-sizer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/hardhat-gas-reporter": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", diff --git a/package.json b/package.json index 0614cfd..3259aec 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^3.0.0", "hardhat": "^2.17.1", + "hardhat-contract-sizer": "^2.10.0", "prettier": "^3.0.1", "prettier-plugin-solidity": "^1.1.3" }, "dependencies": { "@arbitrum/token-bridge-contracts": "^1.0.0-beta.0", - "@openzeppelin/contracts": "^4.9.3" + "@openzeppelin/contracts": "^4.9.3", + "erc-payable-token": "^4.9.3" } }