From 3d021cffa3a6ee7a69ffbfc36782ff061aeef0ac Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Fri, 11 Mar 2022 14:59:16 +0100 Subject: [PATCH] explorer: add Pyth instruction support (#23551) --- explorer/package-lock.json | 409 +++++++++++---- explorer/package.json | 1 + .../pyth/AddMappingDetailsCard.tsx | 60 +++ .../instruction/pyth/AddPriceDetailsCard.tsx | 70 +++ .../pyth/AddProductDetailsCard.tsx | 60 +++ .../pyth/AggregatePriceDetailsCard.tsx | 53 ++ .../pyth/BasePublisherOperationCard.tsx | 55 ++ .../pyth/InitMappingDetailsCard.tsx | 53 ++ .../instruction/pyth/InitPriceDetailsCard.tsx | 63 +++ .../instruction/pyth/PythDetailsCard.tsx | 123 +++++ .../pyth/SetMinPublishersDetailsCard.tsx | 58 +++ .../pyth/UpdatePriceDetailsCard.tsx | 73 +++ .../pyth/UpdateProductDetailsCard.tsx | 82 +++ .../components/instruction/pyth/program.ts | 482 ++++++++++++++++++ .../src/components/instruction/pyth/types.ts | 13 + .../transaction/InstructionsSection.tsx | 4 + 16 files changed, 1547 insertions(+), 112 deletions(-) create mode 100644 explorer/src/components/instruction/pyth/AddMappingDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/AddPriceDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/AddProductDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/AggregatePriceDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/BasePublisherOperationCard.tsx create mode 100644 explorer/src/components/instruction/pyth/InitMappingDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/InitPriceDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/PythDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/SetMinPublishersDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/UpdatePriceDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/UpdateProductDetailsCard.tsx create mode 100644 explorer/src/components/instruction/pyth/program.ts create mode 100644 explorer/src/components/instruction/pyth/types.ts diff --git a/explorer/package-lock.json b/explorer/package-lock.json index ab2321369d..b7f22fa74e 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -17,6 +17,7 @@ "@project-serum/serum": "^0.13.61", "@react-hook/debounce": "^4.0.0", "@sentry/react": "^6.16.1", + "@solana/buffer-layout": "^3.0.0", "@solana/spl-token-registry": "^0.2.1143", "@solana/web3.js": "^1.31.0", "@testing-library/jest-dom": "^5.16.1", @@ -10297,24 +10298,30 @@ } }, "node_modules/es-abstract": { - "version": "1.18.0-next.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", - "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.1", - "object-inspect": "^1.9.0", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.3", - "string.prototype.trimstart": "^1.0.3" + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -12526,6 +12533,21 @@ "node": ">=6" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -12696,6 +12718,14 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", @@ -12710,9 +12740,23 @@ } }, "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { "node": ">= 0.4" }, @@ -13556,6 +13600,17 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -13567,15 +13622,30 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "node_modules/is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", "engines": { "node": ">= 0.4" }, @@ -13763,6 +13833,20 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -13826,11 +13910,12 @@ "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" }, "node_modules/is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -13860,6 +13945,14 @@ "node": ">=6" } }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -13869,9 +13962,12 @@ } }, "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -13898,6 +13994,17 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -19001,9 +19108,9 @@ } }, "node_modules/object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -23864,11 +23971,11 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" }, "funding": { @@ -23876,11 +23983,11 @@ } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" }, "funding": { @@ -24924,6 +25031,20 @@ "node": "*" } }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -25259,30 +25380,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/util.promisify/node_modules/es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/util/node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -26604,6 +26701,21 @@ "which": "bin/which" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -35124,24 +35236,30 @@ } }, "es-abstract": { - "version": "1.18.0-next.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", - "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.1", - "object-inspect": "^1.9.0", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.3", - "string.prototype.trimstart": "^1.0.3" + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" } }, "es-to-primitive": { @@ -36818,6 +36936,15 @@ "pump": "^3.0.0" } }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -36947,6 +37074,11 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, "has-cors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", @@ -36958,9 +37090,17 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } }, "has-value": { "version": "1.0.0", @@ -37637,6 +37777,14 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -37645,15 +37793,24 @@ "binary-extensions": "^2.0.0" } }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" }, "is-capitalized": { "version": "1.0.0", @@ -37782,6 +37939,14 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -37827,11 +37992,12 @@ "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "requires": { - "has-symbols": "^1.0.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-regexp": { @@ -37849,15 +38015,23 @@ "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-symbol": { "version": "1.0.3", @@ -37872,6 +38046,14 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -41775,9 +41957,9 @@ } }, "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" }, "object-is": { "version": "1.1.5", @@ -45657,20 +45839,20 @@ } }, "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, @@ -46462,6 +46644,17 @@ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -46717,26 +46910,6 @@ "es-abstract": "^1.17.2", "has-symbols": "^1.0.1", "object.getownpropertydescriptors": "^2.1.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "utila": { @@ -47807,6 +47980,18 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", diff --git a/explorer/package.json b/explorer/package.json index 3744e28dc1..3b00882c23 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -12,6 +12,7 @@ "@project-serum/serum": "^0.13.61", "@react-hook/debounce": "^4.0.0", "@sentry/react": "^6.16.1", + "@solana/buffer-layout": "^3.0.0", "@solana/spl-token-registry": "^0.2.1143", "@solana/web3.js": "^1.31.0", "@testing-library/jest-dom": "^5.16.1", diff --git a/explorer/src/components/instruction/pyth/AddMappingDetailsCard.tsx b/explorer/src/components/instruction/pyth/AddMappingDetailsCard.tsx new file mode 100644 index 0000000000..412071c056 --- /dev/null +++ b/explorer/src/components/instruction/pyth/AddMappingDetailsCard.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { AddMappingParams } from "./program"; + +export default function AddMappingDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AddMappingParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Mapping Account + +
+ + + + + Next Mapping Account + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/AddPriceDetailsCard.tsx b/explorer/src/components/instruction/pyth/AddPriceDetailsCard.tsx new file mode 100644 index 0000000000..ac3484efa8 --- /dev/null +++ b/explorer/src/components/instruction/pyth/AddPriceDetailsCard.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { AddPriceParams, PriceType } from "./program"; + +export default function AddPriceDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AddPriceParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Product Account + +
+ + + + + Price Account + +
+ + + + + Exponent + {info.exponent} + + + + Price Type + {PriceType[info.priceType]} + + + ); +} diff --git a/explorer/src/components/instruction/pyth/AddProductDetailsCard.tsx b/explorer/src/components/instruction/pyth/AddProductDetailsCard.tsx new file mode 100644 index 0000000000..bbbfa51ad7 --- /dev/null +++ b/explorer/src/components/instruction/pyth/AddProductDetailsCard.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { AddProductParams } from "./program"; + +export default function AddProductDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AddProductParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Mapping Account + +
+ + + + + Product Account + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/AggregatePriceDetailsCard.tsx b/explorer/src/components/instruction/pyth/AggregatePriceDetailsCard.tsx new file mode 100644 index 0000000000..4450de28b9 --- /dev/null +++ b/explorer/src/components/instruction/pyth/AggregatePriceDetailsCard.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { AggregatePriceParams } from "./program"; + +export default function AggregatePriceDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AggregatePriceParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Price Account + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/BasePublisherOperationCard.tsx b/explorer/src/components/instruction/pyth/BasePublisherOperationCard.tsx new file mode 100644 index 0000000000..a8751a1f4a --- /dev/null +++ b/explorer/src/components/instruction/pyth/BasePublisherOperationCard.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { BasePublisherOperationParams } from "./program"; + +export default function BasePublisherOperationCard({ + ix, + index, + result, + operationName, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + operationName: string; + info: BasePublisherOperationParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Price Account + +
+ + + + + Publisher + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/InitMappingDetailsCard.tsx b/explorer/src/components/instruction/pyth/InitMappingDetailsCard.tsx new file mode 100644 index 0000000000..cc2d4727fa --- /dev/null +++ b/explorer/src/components/instruction/pyth/InitMappingDetailsCard.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { InitMappingParams } from "./program"; + +export default function InitMappingDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: InitMappingParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Mapping Account + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/InitPriceDetailsCard.tsx b/explorer/src/components/instruction/pyth/InitPriceDetailsCard.tsx new file mode 100644 index 0000000000..dadb8a495b --- /dev/null +++ b/explorer/src/components/instruction/pyth/InitPriceDetailsCard.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { InitPriceParams, PriceType } from "./program"; + +export default function InitPriceDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: InitPriceParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Price Account + +
+ + + + + Exponent + {info.exponent} + + + + Price Type + {PriceType[info.priceType]} + + + ); +} diff --git a/explorer/src/components/instruction/pyth/PythDetailsCard.tsx b/explorer/src/components/instruction/pyth/PythDetailsCard.tsx new file mode 100644 index 0000000000..a51352f67b --- /dev/null +++ b/explorer/src/components/instruction/pyth/PythDetailsCard.tsx @@ -0,0 +1,123 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { useCluster } from "providers/cluster"; +import { reportError } from "utils/sentry"; +import { InstructionCard } from "../InstructionCard"; +import { PythInstruction } from "./program"; +import UpdatePriceDetailsCard from "./UpdatePriceDetailsCard"; +import BasePublisherOperationCard from "./BasePublisherOperationCard"; +import AddProductDetailsCard from "./AddProductDetailsCard"; +import AddPriceDetailsCard from "./AddPriceDetailsCard"; +import UpdateProductDetailsCard from "./UpdateProductDetailsCard"; +import InitMappingDetailsCard from "./InitMappingDetailsCard"; +import AddMappingDetailsCard from "./AddMappingDetailsCard"; +import AggregatePriceDetailsCard from "./AggregatePriceDetailsCard"; +import InitPriceDetailsCard from "./InitPriceDetailsCard"; + +export function PythDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + signature: string; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { url } = useCluster(); + const { ix, index, result, signature, innerCards, childIndex } = props; + + try { + let ixType = PythInstruction.decodeInstructionType(ix); + + switch (ixType) { + case "InitMapping": + return ( + + ); + case "AddMapping": + return ( + + ); + case "AddProduct": + return ( + + ); + case "UpdateProduct": + return ( + + ); + case "AddPrice": + return ( + + ); + case "AddPublisher": + return ( + + ); + case "DeletePublisher": + return ( + + ); + case "UpdatePrice": + return ( + + ); + case "AggregatePrice": + return ( + + ); + case "InitPrice": + return ( + + ); + } + } catch (error) { + reportError(error, { + url: url, + signature: signature, + }); + } + + return ( + + ); +} diff --git a/explorer/src/components/instruction/pyth/SetMinPublishersDetailsCard.tsx b/explorer/src/components/instruction/pyth/SetMinPublishersDetailsCard.tsx new file mode 100644 index 0000000000..fa5522e12c --- /dev/null +++ b/explorer/src/components/instruction/pyth/SetMinPublishersDetailsCard.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { SetMinPublishersParams } from "./program"; + +export default function SetMinPublishersDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: SetMinPublishersParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Price Account + +
+ + + + + Min Publishers + {info.minPublishers} + + + ); +} diff --git a/explorer/src/components/instruction/pyth/UpdatePriceDetailsCard.tsx b/explorer/src/components/instruction/pyth/UpdatePriceDetailsCard.tsx new file mode 100644 index 0000000000..3ef00e0a87 --- /dev/null +++ b/explorer/src/components/instruction/pyth/UpdatePriceDetailsCard.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { TradingStatus, UpdatePriceParams } from "./program"; + +export default function UpdatePriceDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: UpdatePriceParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + return ( + + + Program + +
+ + + + + Publisher + +
+ + + + + Price Account + +
+ + + + + Status + {TradingStatus[info.status]} + + + + Price + {info.price} + + + + Conf + {info.conf} + + + + Publish Slot + {info.publishSlot} + + + ); +} diff --git a/explorer/src/components/instruction/pyth/UpdateProductDetailsCard.tsx b/explorer/src/components/instruction/pyth/UpdateProductDetailsCard.tsx new file mode 100644 index 0000000000..a1f4066b5f --- /dev/null +++ b/explorer/src/components/instruction/pyth/UpdateProductDetailsCard.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { Copyable } from "components/common/Copyable"; +import { InstructionCard } from "../InstructionCard"; +import { UpdateProductParams } from "./program"; + +export default function UpdateProductDetailsCard({ + ix, + index, + result, + info, + innerCards, + childIndex, +}: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: UpdateProductParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const attrsJSON = JSON.stringify( + Object.fromEntries(info.attributes), + null, + 2 + ); + + function Content() { + return ( + +
{attrsJSON}
+
+ ); + } + + return ( + + + Program + +
+ + + + + Funding Account + +
+ + + + + Product Account + +
+ + + + + + Attributes (JSON) + + +
+ +
+
+ +
+ + + + ); +} diff --git a/explorer/src/components/instruction/pyth/program.ts b/explorer/src/components/instruction/pyth/program.ts new file mode 100644 index 0000000000..0e1478f8ab --- /dev/null +++ b/explorer/src/components/instruction/pyth/program.ts @@ -0,0 +1,482 @@ +import * as BufferLayout from "@solana/buffer-layout"; +import { + InstructionType, + PublicKey, + TransactionInstruction, +} from "@solana/web3.js"; +import { Layout, uint8ArrayToBuffer } from "@solana/buffer-layout"; + +/** + * An enumeration of valid PythInstructionTypes + */ +export type PythInstructionType = + | "InitMapping" + | "AddMapping" + | "AddProduct" + | "UpdateProduct" + | "AddPrice" + | "AddPublisher" + | "DeletePublisher" + | "UpdatePrice" + | "AggregatePrice" + | "InitPrice" + | "InitTest" + | "UpdateTest" + | "SetMinPublishers"; + +export function headerLayout(property: string = "header") { + return BufferLayout.struct( + [BufferLayout.u32("version"), BufferLayout.u32("type")], + property + ); +} + +function decodeData(type: InstructionType, buffer: Buffer): any { + let data; + try { + data = type.layout.decode(buffer); + } catch (err) { + throw new Error("invalid instruction; " + err); + } + + if (data.header.type !== type.index) { + throw new Error( + `invalid instruction; instruction index mismatch ${data.header.type} != ${type.index}` + ); + } + + return data; +} + +/** + * An uint8 length-prefixed UTF-8 string. + */ +class LPString extends Layout { + getSpan(b: Uint8Array, offset?: number): number { + return 1 + b[offset || 0]; + } + + decode(b: Uint8Array, offset?: number): string { + if (offset === undefined) { + offset = 0; + } + return uint8ArrayToBuffer(b) + .slice(offset + 1, offset + b[offset] + 1) + .toString("utf-8"); + } +} + +/** + * A list that fills up all the available space with its elements. + */ +class GreedyList extends Layout { + private element: Layout; + + constructor(element: Layout, property?: string) { + super(-1, property); + this.element = element; + } + + getSpan(b: Uint8Array, offset?: number): number { + return b.length - (offset || 0); + } + + decode(b: Uint8Array, offset?: number): string[] { + if (offset === undefined) { + offset = 0; + } + const strs = []; + while (offset < b.length) { + strs.push(this.element.decode(b, offset)); + offset += this.element.getSpan(b, offset); + } + return strs; + } +} + +/** + * An enumeration of valid Pyth instruction layouts + * @internal + */ +export const PYTH_INSTRUCTION_LAYOUTS: { + [type in PythInstructionType]: InstructionType; +} = Object.freeze({ + InitMapping: { + index: 0, + layout: BufferLayout.struct([headerLayout()]), + }, + AddMapping: { + index: 1, + layout: BufferLayout.struct([headerLayout()]), + }, + AddProduct: { + index: 2, + layout: BufferLayout.struct([headerLayout()]), + }, + UpdateProduct: { + index: 3, + layout: BufferLayout.struct([ + headerLayout(), + new GreedyList( + BufferLayout.struct([ + new LPString(-1, "key"), + new LPString(-1, "value"), + ]), + "attributes" + ), + ]), + }, + AddPrice: { + index: 4, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.s32("exponent"), + BufferLayout.u32("priceType"), + ]), + }, + AddPublisher: { + index: 5, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.blob(32, "publisherPubkey"), + ]), + }, + DeletePublisher: { + index: 6, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.blob(32, "publisherPubkey"), + ]), + }, + UpdatePrice: { + index: 7, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.u32("status"), + BufferLayout.u32("unused1"), + BufferLayout.ns64("price"), + BufferLayout.nu64("conf"), + BufferLayout.nu64("publishSlot"), + ]), + }, + AggregatePrice: { + index: 8, + layout: BufferLayout.struct([headerLayout()]), + }, + InitPrice: { + index: 9, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.s32("exponent"), + BufferLayout.u32("priceType"), + ]), + }, + InitTest: { + index: 10, + layout: BufferLayout.struct([headerLayout()]), + }, + UpdateTest: { + index: 11, + layout: BufferLayout.struct([headerLayout()]), + }, + SetMinPublishers: { + index: 12, + layout: BufferLayout.struct([ + headerLayout(), + BufferLayout.u8("minPublishers"), + BufferLayout.blob(3, "unused1"), + ]), + }, +}); + +export enum PriceType { + Unknown = 0, + Price, +} + +export enum TradingStatus { + Unknown = 0, + Trading, + Halted, + Auction, +} + +export type InitMappingParams = { + fundingPubkey: PublicKey; + mappingPubkey: PublicKey; +}; + +export type AddMappingParams = { + fundingPubkey: PublicKey; + mappingPubkey: PublicKey; + nextMappingPubkey: PublicKey; +}; + +export type AddProductParams = { + fundingPubkey: PublicKey; + mappingPubkey: PublicKey; + productPubkey: PublicKey; +}; + +export type UpdateProductParams = { + fundingPubkey: PublicKey; + productPubkey: PublicKey; + attributes: Map; +}; + +export type AddPriceParams = { + fundingPubkey: PublicKey; + productPubkey: PublicKey; + pricePubkey: PublicKey; + exponent: number; + priceType: PriceType; +}; + +export type BasePublisherOperationParams = { + signerPubkey: PublicKey; + pricePubkey: PublicKey; + publisherPubkey: PublicKey; +}; + +export type UpdatePriceParams = { + publisherPubkey: PublicKey; + pricePubkey: PublicKey; + status: TradingStatus; + price: number; + conf: number; + publishSlot: number; +}; + +export type AggregatePriceParams = { + fundingPubkey: PublicKey; + pricePubkey: PublicKey; +}; + +export type InitPriceParams = { + fundingPubkey: PublicKey; + pricePubkey: PublicKey; + exponent: number; + priceType: PriceType; +}; + +export type SetMinPublishersParams = { + fundingPubkey: PublicKey; + pricePubkey: PublicKey; + minPublishers: number; +}; + +/** + * Pyth Instruction class + */ +export class PythInstruction { + /** + * Decode a Pyth instruction and retrieve the instruction type. + */ + static decodeInstructionType( + instruction: TransactionInstruction + ): PythInstructionType { + const header = headerLayout().decode(instruction.data); + if (header.version !== 2) { + throw new Error(`Unsupported Pyth version: ${header.version}`); + } + const typeIndex = header.type; + + let type: PythInstructionType | undefined; + for (const [ixType, layout] of Object.entries(PYTH_INSTRUCTION_LAYOUTS)) { + if (layout.index === typeIndex) { + type = ixType as PythInstructionType; + break; + } + } + + if (!type) { + throw new Error("Instruction type incorrect; not a PythInstruction"); + } + + return type; + } + + /** + * Decode an "init mapping" instruction and retrieve the instruction params. + */ + static decodeInitMapping( + instruction: TransactionInstruction + ): InitMappingParams { + decodeData(PYTH_INSTRUCTION_LAYOUTS.InitMapping, instruction.data); + return { + fundingPubkey: instruction.keys[0].pubkey, + mappingPubkey: instruction.keys[1].pubkey, + }; + } + + /** + * Decode an "add mapping" instruction and retrieve the instruction params. + */ + static decodeAddMapping( + instruction: TransactionInstruction + ): AddMappingParams { + decodeData(PYTH_INSTRUCTION_LAYOUTS.AddMapping, instruction.data); + return { + fundingPubkey: instruction.keys[0].pubkey, + mappingPubkey: instruction.keys[1].pubkey, + nextMappingPubkey: instruction.keys[2].pubkey, + }; + } + + /** + * Decode an "add product" instruction and retrieve the instruction params. + */ + static decodeAddProduct( + instruction: TransactionInstruction + ): AddProductParams { + decodeData(PYTH_INSTRUCTION_LAYOUTS.AddProduct, instruction.data); + return { + fundingPubkey: instruction.keys[0].pubkey, + mappingPubkey: instruction.keys[1].pubkey, + productPubkey: instruction.keys[2].pubkey, + }; + } + + /** + * Decode an "add product" instruction and retrieve the instruction params. + */ + static decodeUpdateProduct( + instruction: TransactionInstruction + ): UpdateProductParams { + const { attributes } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.UpdateProduct, + instruction.data + ); + return { + fundingPubkey: instruction.keys[0].pubkey, + productPubkey: instruction.keys[1].pubkey, + attributes: new Map( + attributes.map((kv: { key: string; value: string }) => [ + kv.key, + kv.value, + ]) + ), + }; + } + + /** + * Decode an "add price" instruction and retrieve the instruction params. + */ + static decodeAddPrice(instruction: TransactionInstruction): AddPriceParams { + const { exponent, priceType } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.AddPrice, + instruction.data + ); + return { + fundingPubkey: instruction.keys[0].pubkey, + productPubkey: instruction.keys[1].pubkey, + pricePubkey: instruction.keys[2].pubkey, + exponent, + priceType, + }; + } + + /** + * Decode an "add publisher" instruction and retrieve the instruction params. + */ + static decodeAddPublisher( + instruction: TransactionInstruction + ): BasePublisherOperationParams { + const { publisherPubkey } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.AddPublisher, + instruction.data + ); + + return { + signerPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + publisherPubkey: PublicKey.decode(publisherPubkey), + }; + } + + /** + * Decode an "delete publisher" instruction and retrieve the instruction params. + */ + static decodeDeletePublisher( + instruction: TransactionInstruction + ): BasePublisherOperationParams { + const { publisherPubkey } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.DeletePublisher, + instruction.data + ); + + return { + signerPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + publisherPubkey: PublicKey.decode(publisherPubkey), + }; + } + + /** + * Decode an "update price" instruction and retrieve the instruction params. + */ + static decodeUpdatePrice( + instruction: TransactionInstruction + ): UpdatePriceParams { + const { status, price, conf, publishSlot } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.UpdatePrice, + instruction.data + ); + + return { + publisherPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + status, + price, + conf, + publishSlot, + }; + } + + /** + * Decode an "aggregate price" instruction and retrieve the instruction params. + */ + static decodeAggregatePrice( + instruction: TransactionInstruction + ): AggregatePriceParams { + decodeData(PYTH_INSTRUCTION_LAYOUTS.AggregatePrice, instruction.data); + + return { + fundingPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + }; + } + + /** + * Decode an "init price" instruction and retrieve the instruction params. + */ + static decodeInitPrice(instruction: TransactionInstruction): InitPriceParams { + const { exponent, priceType } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.InitPrice, + instruction.data + ); + return { + fundingPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + exponent, + priceType, + }; + } + + /** + * Decode an "set min publishers" instruction and retrieve the instruction params. + */ + static decodeSetMinPublishers( + instruction: TransactionInstruction + ): SetMinPublishersParams { + const { minPublishers } = decodeData( + PYTH_INSTRUCTION_LAYOUTS.SetMinPublishers, + instruction.data + ); + return { + fundingPubkey: instruction.keys[0].pubkey, + pricePubkey: instruction.keys[1].pubkey, + minPublishers, + }; + } +} diff --git a/explorer/src/components/instruction/pyth/types.ts b/explorer/src/components/instruction/pyth/types.ts new file mode 100644 index 0000000000..399f82edc2 --- /dev/null +++ b/explorer/src/components/instruction/pyth/types.ts @@ -0,0 +1,13 @@ +import { TransactionInstruction } from "@solana/web3.js"; + +export const PROGRAM_IDS: string[] = [ + "gSbePebfvPy7tRqimPoVecS2UsBvYv46ynrzWocc92s", // devnet + "8tfDNiaEyrV6Q1U4DEXrEigs9DoDtkugzFbybENEbCDz", // testnet + "FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH", // mainnet +]; + +export function isPythInstruction( + instruction: TransactionInstruction +): boolean { + return PROGRAM_IDS.includes(instruction.programId.toBase58()); +} diff --git a/explorer/src/components/transaction/InstructionsSection.tsx b/explorer/src/components/transaction/InstructionsSection.tsx index 92f7510880..ce813c8809 100644 --- a/explorer/src/components/transaction/InstructionsSection.tsx +++ b/explorer/src/components/transaction/InstructionsSection.tsx @@ -41,6 +41,8 @@ import { isWormholeInstruction } from "components/instruction/wormhole/types"; import { AssociatedTokenDetailsCard } from "components/instruction/AssociatedTokenDetailsCard"; import { isMangoInstruction } from "components/instruction/mango/types"; import { MangoDetailsCard } from "components/instruction/MangoDetails"; +import { isPythInstruction } from "components/instruction/pyth/types"; +import { PythDetailsCard } from "components/instruction/pyth/PythDetailsCard"; export type InstructionDetailsProps = { tx: ParsedTransaction; @@ -222,6 +224,8 @@ function renderInstructionCard({ return ; } else if (isWormholeInstruction(transactionIx)) { return ; + } else if (isPythInstruction(transactionIx)) { + return ; } else { return ; }