Forráskód Böngészése

feat(target_chains/ethereum/sdk/solidity): add convertToUint method to the sdk (#1390)

* Moving convertToUint to utils

* pre-commit fix

* reversing OracleSwap example

* pre-commit]

* added test

* abi-gen

* Added solc to sdk

* resolved comments
Aditya Arora 1 éve
szülő
commit
77db9ee53b

+ 72 - 2
package-lock.json

@@ -59453,7 +59453,47 @@
       "devDependencies": {
         "abi_generator": "*",
         "prettier": "^2.7.1",
-        "prettier-plugin-solidity": "^1.0.0-rc.1"
+        "prettier-plugin-solidity": "^1.0.0-rc.1",
+        "solc": "^0.8.25"
+      }
+    },
+    "target_chains/ethereum/sdk/solidity/node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "target_chains/ethereum/sdk/solidity/node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "target_chains/ethereum/sdk/solidity/node_modules/solc": {
+      "version": "0.8.25",
+      "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.25.tgz",
+      "integrity": "sha512-7P0TF8gPeudl1Ko3RGkyY6XVCxe2SdD/qQhtns1vl3yAbK/PDifKDLHGtx1t7mX3LgR7ojV7Fg/Kc6Q9D2T8UQ==",
+      "dev": true,
+      "dependencies": {
+        "command-exists": "^1.2.8",
+        "commander": "^8.1.0",
+        "follow-redirects": "^1.12.1",
+        "js-sha3": "0.8.0",
+        "memorystream": "^0.3.1",
+        "semver": "^5.5.0",
+        "tmp": "0.0.33"
+      },
+      "bin": {
+        "solcjs": "solc.js"
+      },
+      "engines": {
+        "node": ">=10.0.0"
       }
     },
     "target_chains/solana/sdk/js/pyth_solana_receiver": {
@@ -71066,7 +71106,37 @@
       "requires": {
         "abi_generator": "*",
         "prettier": "^2.7.1",
-        "prettier-plugin-solidity": "^1.0.0-rc.1"
+        "prettier-plugin-solidity": "^1.0.0-rc.1",
+        "solc": "*"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "8.3.0",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+          "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+          "dev": true
+        },
+        "semver": {
+          "version": "5.7.2",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+          "dev": true
+        },
+        "solc": {
+          "version": "0.8.25",
+          "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.25.tgz",
+          "integrity": "sha512-7P0TF8gPeudl1Ko3RGkyY6XVCxe2SdD/qQhtns1vl3yAbK/PDifKDLHGtx1t7mX3LgR7ojV7Fg/Kc6Q9D2T8UQ==",
+          "dev": true,
+          "requires": {
+            "command-exists": "^1.2.8",
+            "commander": "^8.1.0",
+            "follow-redirects": "^1.12.1",
+            "js-sha3": "0.8.0",
+            "memorystream": "^0.3.1",
+            "semver": "^5.5.0",
+            "tmp": "0.0.33"
+          }
+        }
       }
     },
     "@pythnetwork/pyth-solana-receiver": {

+ 33 - 0
target_chains/ethereum/contracts/forge-test/utils/PythTestUtils.t.sol

@@ -12,6 +12,7 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
 import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
 import "@pythnetwork/pyth-sdk-solidity/IPythEvents.sol";
 import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
+import "@pythnetwork/pyth-sdk-solidity/PythUtils.sol";
 
 import "forge-std/Test.sol";
 import "./WormholeTestUtils.t.sol";
@@ -275,3 +276,35 @@ abstract contract PythTestUtils is Test, WormholeTestUtils, RandTestUtils {
         }
     }
 }
+
+contract PythUtilsTest is Test, WormholeTestUtils, PythTestUtils, IPythEvents {
+    function testConvertToUnit() public {
+        // Price can't be negative
+        vm.expectRevert();
+        PythUtils.convertToUint(-100, -5, 18);
+
+        // Exponent can't be positive
+        vm.expectRevert();
+        PythUtils.convertToUint(100, 5, 18);
+
+        // Price with 18 decimals and exponent -5
+        assertEq(
+            PythUtils.convertToUint(100, -5, 18),
+            1000000000000000 // 100 * 10^13
+        );
+
+        // Price with 9 decimals and exponent -2
+        assertEq(
+            PythUtils.convertToUint(100, -2, 9),
+            1000000000 // 100 * 10^7
+        );
+
+        // Price with 4 decimals and exponent -5
+        assertEq(PythUtils.convertToUint(100, -5, 4), 10);
+
+        // Price with 5 decimals and exponent -2
+        // @note: We will lose precision here as price is
+        // 0.00001 and we are targetDecimals is 2.
+        assertEq(PythUtils.convertToUint(100, -5, 2), 0);
+    }
+}

+ 34 - 0
target_chains/ethereum/sdk/solidity/PythUtils.sol

@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.0;
+
+library PythUtils {
+    /// @notice Converts a Pyth price to a uint256 with a target number of decimals
+    /// @param price The Pyth price
+    /// @param expo The Pyth price exponent
+    /// @param targetDecimals The target number of decimals
+    /// @return The price as a uint256
+    /// @dev Function will lose precision if targetDecimals is less than the Pyth price decimals.
+    /// This method will truncate any digits that cannot be represented by the targetDecimals.
+    /// e.g. If the price is 0.000123 and the targetDecimals is 2, the result will be 0
+    function convertToUint(
+        int64 price,
+        int32 expo,
+        uint8 targetDecimals
+    ) public pure returns (uint256) {
+        if (price < 0 || expo > 0 || expo < -255) {
+            revert();
+        }
+
+        uint8 priceDecimals = uint8(uint32(-1 * expo));
+
+        if (targetDecimals >= priceDecimals) {
+            return
+                uint(uint64(price)) *
+                10 ** uint32(targetDecimals - priceDecimals);
+        } else {
+            return
+                uint(uint64(price)) /
+                10 ** uint32(priceDecimals - targetDecimals);
+        }
+    }
+}

+ 31 - 0
target_chains/ethereum/sdk/solidity/abis/PythUtils.json

@@ -0,0 +1,31 @@
+[
+  {
+    "inputs": [
+      {
+        "internalType": "int64",
+        "name": "price",
+        "type": "int64"
+      },
+      {
+        "internalType": "int32",
+        "name": "expo",
+        "type": "int32"
+      },
+      {
+        "internalType": "uint8",
+        "name": "targetDecimals",
+        "type": "uint8"
+      }
+    ],
+    "name": "convertToUint",
+    "outputs": [
+      {
+        "internalType": "uint256",
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "stateMutability": "pure",
+    "type": "function"
+  }
+]

+ 3 - 2
target_chains/ethereum/sdk/solidity/package.json

@@ -9,7 +9,7 @@
   },
   "scripts": {
     "format": "npx prettier --write .",
-    "generate-abi": "npx generate-abis IPyth IPythEvents AbstractPyth MockPyth PythErrors",
+    "generate-abi": "npx generate-abis IPyth IPythEvents AbstractPyth MockPyth PythErrors PythUtils",
     "check-abi": "git diff --exit-code abis",
     "build": "solcjs --bin MockPyth.sol --base-path . -o build/"
   },
@@ -25,8 +25,9 @@
   },
   "homepage": "https://github.com/pyth-network/pyth-crosschain/tree/main/target_chains/ethereum/sdk/solidity",
   "devDependencies": {
+    "abi_generator": "*",
     "prettier": "^2.7.1",
     "prettier-plugin-solidity": "^1.0.0-rc.1",
-    "abi_generator": "*"
+    "solc": "^0.8.25"
   }
 }