From 2c80fdd020c24b85c1d75f6889cbbad7aa6416fb Mon Sep 17 00:00:00 2001 From: 0xverin <104152026+0xverin@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:35:15 +0800 Subject: [PATCH] Fix the issue of handling decimals. (#2886) * add StringShift library * fix assembleAssertions * fix StringShift * rollback function type * fix comments * fix comment --- .../contracts/libraries/StringShift.sol | 119 ++++++++++++++++++ .../TokenHoldingAmount.sol | 8 +- 2 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/StringShift.sol diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/StringShift.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/StringShift.sol new file mode 100644 index 0000000000..ce4b70d940 --- /dev/null +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/libraries/StringShift.sol @@ -0,0 +1,119 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.0; +import "../openzeppelin/Strings.sol"; +import "../openzeppelin/math/Math.sol"; +library StringShift { + /** + * @dev Converts a uint256 input to a string and shifts the decimal point to the left by the specified number of places. + * @param value The uint256 parameter to be processed. + * @param decimal The number of decimal places to shift. + * @return The processed string. + */ + function toShiftedString( + uint256 value, + uint256 decimal + ) internal pure returns (string memory) { + // Convert uint256 to string + + if (value == 0) { + return "0"; + } else { + string memory str = Strings.toString(value); + + // Calculate the position to insert the decimal point + uint256 len = bytes(str).length; + uint256 digit = Math.log10(decimal); + + if (len <= digit) { + // If the length is less than or equal to the number of digits, pad with leading zeros and add '0.' + string memory leadingZeros = new string(digit - len); + for (uint256 i = 0; i < digit - len; i++) { + leadingZeros = string(abi.encodePacked("0", leadingZeros)); + } + str = string(abi.encodePacked("0.", leadingZeros, str)); + } else { + // Otherwise, insert the decimal point at the correct position + str = string( + abi.encodePacked( + substring(str, 0, len - digit), + ".", + substring(str, len - digit, len) + ) + ); + } + + // Remove trailing zeros after the decimal point + str = removeTrailingZeros(str); + + return str; + } + } + + /** + * @dev Extracts a substring from a given string. + * @param str The original string. + * @param start The starting position of the original string. + * @param end The ending position of the original string. + * @return The extracted substring. + */ + function substring( + string memory str, + uint256 start, + uint256 end + ) internal pure returns (string memory) { + bytes memory strBytes = bytes(str); + bytes memory result = new bytes(end - start); + for (uint256 i = start; i < end; i++) { + result[i - start] = strBytes[i]; + } + return string(result); + } + + /** + * @dev Removes trailing zeros after the decimal point in a string. + * @param str The input string. + * @return The processed string with trailing zeros removed. + */ + function removeTrailingZeros( + string memory str + ) internal pure returns (string memory) { + bytes memory strBytes = bytes(str); + uint256 len = strBytes.length; + + // Traverse from the end to find the position of the first non-zero character + uint256 newLen = len; + while (newLen > 0 && strBytes[newLen - 1] == "0") { + newLen--; + } + + // If the last character is a decimal point, remove it as well + if (newLen > 0 && strBytes[newLen - 1] == ".") { + newLen--; + } + + // Create a new byte array and copy the content + bytes memory trimmedBytes = new bytes(newLen); + for (uint256 i = 0; i < newLen; i++) { + trimmedBytes[i] = strBytes[i]; + } + + return string(trimmedBytes); + } +} diff --git a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol index 971a771fac..f0c62403df 100644 --- a/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol +++ b/tee-worker/litentry/core/assertion-build/src/dynamic/contracts/token_holding_amount/TokenHoldingAmount.sol @@ -23,6 +23,7 @@ import "../libraries/AssertionLogic.sol"; import "../libraries/Identities.sol"; import "../DynamicAssertion.sol"; import "./Constants.sol"; +import "../libraries/StringShift.sol"; abstract contract TokenHoldingAmount is DynamicAssertion { mapping(string => string) internal tokenNames; @@ -157,7 +158,7 @@ abstract contract TokenHoldingAmount is DynamicAssertion { 1, variable, AssertionLogic.Op.GreaterEq, - Strings.toString(min / Constants.decimals_factor) + StringShift.toShiftedString(min, Constants.decimals_factor) ); if (max > 0) { AssertionLogic.andOp( @@ -165,7 +166,10 @@ abstract contract TokenHoldingAmount is DynamicAssertion { 2, variable, AssertionLogic.Op.LessThan, - Strings.toString(uint256(max) / Constants.decimals_factor) + StringShift.toShiftedString( + uint256(max), + Constants.decimals_factor + ) ); }