diff --git a/explorer/package-lock.json b/explorer/package-lock.json index 7428b94a7..4f99b5104 100644 --- a/explorer/package-lock.json +++ b/explorer/package-lock.json @@ -5,8 +5,10 @@ "requires": true, "packages": { "": { + "name": "explorer", "version": "0.1.0", "dependencies": { + "@blockworks-foundation/mango-client": "^3.0.12", "@bonfida/bot": "^0.5.3", "@metamask/jazzicon": "^2.0.0", "@project-serum/serum": "^0.13.58", @@ -1306,6 +1308,308 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@blockworks-foundation/mango-client": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@blockworks-foundation/mango-client/-/mango-client-3.0.12.tgz", + "integrity": "sha512-gDwn2feXPMuyxb+mQXXJfAlyVLojnCifPbvxfRNikV4nswHQmPVQ1GhLzqAYXxqxANBiRlDB3uHISnnQp0Xqcw==", + "dependencies": { + "@project-serum/serum": "0.13.55", + "@project-serum/sol-wallet-adapter": "^0.2.0", + "@solana/spl-token": "^0.1.6", + "@solana/web3.js": "1.21.0", + "big.js": "^6.1.1", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.1.0", + "borsh": "git+https://github.com/defactojob/borsh-js.git#field-mapper", + "buffer-layout": "^1.2.1", + "yargs": "^17.0.1" + }, + "engines": { + "node": ">=14.15.3" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@babel/runtime": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", + "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@project-serum/serum": { + "version": "0.13.55", + "resolved": "https://registry.npmjs.org/@project-serum/serum/-/serum-0.13.55.tgz", + "integrity": "sha512-SPQ4NsuNbBJO3mLGnTYbjt47WCXoNIcW2C9xv0gNXyG62dxgONsAEEgErKv1gT34hWCMPsXFSpatnX6ppriq7w==", + "dependencies": { + "@project-serum/anchor": "^0.11.1", + "@solana/spl-token": "^0.1.6", + "@solana/web3.js": "^1.21.0", + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@project-serum/sol-wallet-adapter": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.5.tgz", + "integrity": "sha512-Y0XHe+FXXJ7P8XZtx3luAlatO0ge2LdrZUCmqMSzJf+K+fko+qTYIBSUuWwO7y/O4brIXVReR1mEUvF6QKDF2w==", + "dependencies": { + "bs58": "^4.0.1", + "eventemitter3": "^4.0.7" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.5.0" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@solana/spl-token": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.8.tgz", + "integrity": "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==", + "dependencies": { + "@babel/runtime": "^7.10.5", + "@solana/web3.js": "^1.21.0", + "bn.js": "^5.1.0", + "buffer": "6.0.3", + "buffer-layout": "^1.2.0", + "dotenv": "10.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@solana/spl-token/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@solana/web3.js": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.21.0.tgz", + "integrity": "sha512-x1NXlF92tEjxuTxS0u4n9JV17UKk0Dn2L+qSWGvKOb4iWhzApDj6wicJsrGdSbGdxnZ7eciQ/SNn3zB4ydUllA==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@solana/buffer-layout": "^3.0.0", + "bn.js": "^5.0.0", + "borsh": "^0.4.0", + "bs58": "^4.0.1", + "buffer": "6.0.1", + "crypto-hash": "^1.2.2", + "jayson": "^3.4.4", + "js-sha3": "^0.8.0", + "node-fetch": "^2.6.1", + "rpc-websockets": "^7.4.2", + "secp256k1": "^4.0.2", + "superstruct": "^0.14.2", + "tweetnacl": "^1.0.0" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@solana/web3.js/node_modules/borsh": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz", + "integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==", + "dependencies": { + "@types/bn.js": "^4.11.5", + "bn.js": "^5.0.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@blockworks-foundation/mango-client/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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/big.js": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.1.1.tgz", + "integrity": "sha512-1vObw81a8ylZO5ePrtMay0n018TcftpTA5HFKDaSuiUDBo8biRBtjIobw60OpwuvrGk+FsxKamqN4cnmj/eXdg==", + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/borsh": { + "version": "0.3.1", + "resolved": "git+ssh://git@github.com/defactojob/borsh-js.git#33a0d24af281112c0a48efb3fa503f3212443de9", + "license": "Apache-2.0", + "dependencies": { + "@types/bn.js": "^4.11.5", + "bn.js": "^5.0.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@blockworks-foundation/mango-client/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@blockworks-foundation/mango-client/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==" + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@blockworks-foundation/mango-client/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/@bonfida/bot": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@bonfida/bot/-/bot-0.5.3.tgz", @@ -5924,6 +6228,18 @@ "node": "*" } }, + "node_modules/bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.3.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -6220,9 +6536,9 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" }, "node_modules/buffer-layout": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.0.tgz", - "integrity": "sha512-iiyRoho/ERzBUv6kFvfsrLNgTlVwOkqQcSQN7WrO3Y+c5SeuEhCn6+y1KwhM0V3ndptF5mI/RI44zkw0qcR5Jg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", + "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==", "engines": { "node": ">=4.5" } @@ -26030,6 +26346,238 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "@blockworks-foundation/mango-client": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@blockworks-foundation/mango-client/-/mango-client-3.0.12.tgz", + "integrity": "sha512-gDwn2feXPMuyxb+mQXXJfAlyVLojnCifPbvxfRNikV4nswHQmPVQ1GhLzqAYXxqxANBiRlDB3uHISnnQp0Xqcw==", + "requires": { + "@project-serum/serum": "0.13.55", + "@project-serum/sol-wallet-adapter": "^0.2.0", + "@solana/spl-token": "^0.1.6", + "@solana/web3.js": "1.21.0", + "big.js": "^6.1.1", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.1.0", + "borsh": "git+https://github.com/defactojob/borsh-js.git#field-mapper", + "buffer-layout": "^1.2.1", + "yargs": "^17.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", + "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@project-serum/serum": { + "version": "0.13.55", + "resolved": "https://registry.npmjs.org/@project-serum/serum/-/serum-0.13.55.tgz", + "integrity": "sha512-SPQ4NsuNbBJO3mLGnTYbjt47WCXoNIcW2C9xv0gNXyG62dxgONsAEEgErKv1gT34hWCMPsXFSpatnX6ppriq7w==", + "requires": { + "@project-serum/anchor": "^0.11.1", + "@solana/spl-token": "^0.1.6", + "@solana/web3.js": "^1.21.0", + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + } + }, + "@project-serum/sol-wallet-adapter": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@project-serum/sol-wallet-adapter/-/sol-wallet-adapter-0.2.5.tgz", + "integrity": "sha512-Y0XHe+FXXJ7P8XZtx3luAlatO0ge2LdrZUCmqMSzJf+K+fko+qTYIBSUuWwO7y/O4brIXVReR1mEUvF6QKDF2w==", + "requires": { + "bs58": "^4.0.1", + "eventemitter3": "^4.0.7" + } + }, + "@solana/spl-token": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.1.8.tgz", + "integrity": "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ==", + "requires": { + "@babel/runtime": "^7.10.5", + "@solana/web3.js": "^1.21.0", + "bn.js": "^5.1.0", + "buffer": "6.0.3", + "buffer-layout": "^1.2.0", + "dotenv": "10.0.0" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + } + } + }, + "@solana/web3.js": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.21.0.tgz", + "integrity": "sha512-x1NXlF92tEjxuTxS0u4n9JV17UKk0Dn2L+qSWGvKOb4iWhzApDj6wicJsrGdSbGdxnZ7eciQ/SNn3zB4ydUllA==", + "requires": { + "@babel/runtime": "^7.12.5", + "@solana/buffer-layout": "^3.0.0", + "bn.js": "^5.0.0", + "borsh": "^0.4.0", + "bs58": "^4.0.1", + "buffer": "6.0.1", + "crypto-hash": "^1.2.2", + "jayson": "^3.4.4", + "js-sha3": "^0.8.0", + "node-fetch": "^2.6.1", + "rpc-websockets": "^7.4.2", + "secp256k1": "^4.0.2", + "superstruct": "^0.14.2", + "tweetnacl": "^1.0.0" + }, + "dependencies": { + "borsh": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.4.0.tgz", + "integrity": "sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==", + "requires": { + "@types/bn.js": "^4.11.5", + "bn.js": "^5.0.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + } + } + }, + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "big.js": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.1.1.tgz", + "integrity": "sha512-1vObw81a8ylZO5ePrtMay0n018TcftpTA5HFKDaSuiUDBo8biRBtjIobw60OpwuvrGk+FsxKamqN4cnmj/eXdg==" + }, + "borsh": { + "version": "git+ssh://git@github.com/defactojob/borsh-js.git#33a0d24af281112c0a48efb3fa503f3212443de9", + "from": "borsh@git+https://github.com/defactojob/borsh-js.git#field-mapper", + "requires": { + "@types/bn.js": "^4.11.5", + "bn.js": "^5.0.0", + "bs58": "^4.0.0", + "text-encoding-utf-8": "^1.0.2" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "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==" + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, "@bonfida/bot": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/@bonfida/bot/-/bot-0.5.3.tgz", @@ -29875,6 +30423,14 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" }, + "bigint-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz", + "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==", + "requires": { + "bindings": "^1.3.0" + } + }, "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -30150,9 +30706,9 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" }, "buffer-layout": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.0.tgz", - "integrity": "sha512-iiyRoho/ERzBUv6kFvfsrLNgTlVwOkqQcSQN7WrO3Y+c5SeuEhCn6+y1KwhM0V3ndptF5mI/RI44zkw0qcR5Jg==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", + "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==" }, "buffer-xor": { "version": "1.0.3", diff --git a/explorer/package.json b/explorer/package.json index 364c803b9..2ce29d355 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@blockworks-foundation/mango-client": "^3.0.12", "@bonfida/bot": "^0.5.3", "@metamask/jazzicon": "^2.0.0", "@project-serum/serum": "^0.13.58", diff --git a/explorer/src/components/account/TokenHistoryCard.tsx b/explorer/src/components/account/TokenHistoryCard.tsx index d8e2310fd..bb504aefe 100644 --- a/explorer/src/components/account/TokenHistoryCard.tsx +++ b/explorer/src/components/account/TokenHistoryCard.tsx @@ -51,6 +51,7 @@ import { useQuery } from "utils/url"; import { TokenInfoMap } from "@solana/spl-token-registry"; import { useTokenRegistry } from "providers/mints/token-registry"; import { getTokenProgramInstructionName } from "utils/instruction"; +import { isMangoInstruction, parseMangoInstructionTitle } from "components/instruction/mango/types"; const TRUNCATE_TOKEN_LENGTH = 10; const ALL_TOKENS = ""; @@ -502,6 +503,16 @@ const TokenTransactionRow = React.memo( reportError(error, { signature: tx.signature }); return undefined; } + } else if ( + transactionInstruction && + isMangoInstruction(transactionInstruction) + ) { + try { + name = parseMangoInstructionTitle(transactionInstruction); + } catch (error) { + reportError(error, { signature: tx.signature }); + return undefined; + } } else { if ( ix.accounts.findIndex((account) => diff --git a/explorer/src/components/instruction/MangoDetails.tsx b/explorer/src/components/instruction/MangoDetails.tsx new file mode 100644 index 000000000..0e0c76a4d --- /dev/null +++ b/explorer/src/components/instruction/MangoDetails.tsx @@ -0,0 +1,161 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { useCluster } from "providers/cluster"; +import { reportError } from "utils/sentry"; +import { InstructionCard } from "./InstructionCard"; +import { AddOracleDetailsCard } from "./mango/AddOracleDetailsCard"; +import { AddPerpMarketDetailsCard } from "./mango/AddPerpMarketDetailsCard"; +import { AddSpotMarketDetailsCard } from "./mango/AddSpotMarketDetailsCard"; +import { CancelPerpOrderDetailsCard } from "./mango/CancelPerpOrderDetailsCard"; +import { CancelSpotOrderDetailsCard } from "./mango/CancelSpotOrderDetailsCard"; +import { ChangePerpMarketParamsDetailsCard } from "./mango/ChangePerpMarketParamsDetailsCard"; +import { ConsumeEventsDetailsCard } from "./mango/ConsumeEventsDetailsCard"; +import { GenericMngoAccountDetailsCard } from "./mango/GenericMngoAccountDetailsCard"; +import { GenericPerpMngoDetailsCard } from "./mango/GenericPerpMngoDetailsCard"; +import { GenericSpotMngoDetailsCard } from "./mango/GenericSpotMngoDetailsCard"; +import { PlacePerpOrderDetailsCard } from "./mango/PlacePerpOrderDetailsCard"; +import { PlaceSpotOrderDetailsCard } from "./mango/PlaceSpotOrderDetailsCard"; +import { + decodeAddPerpMarket, + decodeAddSpotMarket, + decodeCancelPerpOrder, + decodeCancelSpotOrder, + decodeChangePerpMarketParams, + decodePlacePerpOrder, + decodePlaceSpotOrder, + parseMangoInstructionTitle, +} from "./mango/types"; + +export function MangoDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + signature: string; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, signature, innerCards, childIndex } = props; + + const { url } = useCluster(); + + let title; + try { + title = parseMangoInstructionTitle(ix); + + switch (title) { + case "InitMangoAccount": + return ( + + ); + case "Deposit": + return ( + + ); + case "Withdraw": + return ( + + ); + case "InitSpotOpenOrders": + return ( + + ); + case "PlaceSpotOrder": + return ( + + ); + case "CancelSpotOrder": + return ( + + ); + case "AddPerpMarket": + return ( + + ); + case "PlacePerpOrder": + return ( + + ); + case "ConsumeEvents": + return ; + case "CancelPerpOrder": + return ( + + ); + case "SettleFunds": + return ( + + ); + case "RedeemMngo": + return ( + + ); + case "ChangePerpMarketParams": + return ( + + ); + case "AddOracle": + return ; + case "AddSpotMarket": + return ( + + ); + } + } catch (error) { + reportError(error, { + url: url, + signature: signature, + }); + } + + return ( + + ); +} diff --git a/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx b/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx new file mode 100644 index 000000000..f3d90ab0a --- /dev/null +++ b/explorer/src/components/instruction/mango/AddOracleDetailsCard.tsx @@ -0,0 +1,23 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { InstructionCard } from "../InstructionCard"; + +export function AddOracleDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, innerCards, childIndex } = props; + + return ( + + ); +} diff --git a/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx b/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx new file mode 100644 index 000000000..2aebf7de0 --- /dev/null +++ b/explorer/src/components/instruction/mango/AddPerpMarketDetailsCard.tsx @@ -0,0 +1,76 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import moment from "moment"; +import { InstructionCard } from "../InstructionCard"; +import { AddPerpMarket } from "./types"; + +export function AddPerpMarketDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AddPerpMarket; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + + return ( + + + Market index + {info.marketIndex} + + + Maintenance leverage + {info.maintLeverage} + + + Initial leverage + {info.initLeverage} + + + Liquidation fee + {info.liquidationFee} + + + Maker fee + {info.makerFee} + + + Taker fee + {info.takerFee} + + + Base lot size + {info.baseLotSize} + + + Quote lot size + {info.quoteLotSize} + + + Rate + {info.rate} + + + Max depth bps + {info.maxDepthBps} + + + + MNGO per{" "} + {moment.duration(info.targetPeriodLength, "seconds").humanize()} + + + {info.mngoPerPeriod} {} + + + + ); +} diff --git a/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx b/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx new file mode 100644 index 000000000..7bc7f6f2a --- /dev/null +++ b/explorer/src/components/instruction/mango/AddSpotMarketDetailsCard.tsx @@ -0,0 +1,62 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { InstructionCard } from "../InstructionCard"; +import { AddSpotMarket, spotMarketFromIndex } from "./types"; + +export function AddSpotMarketDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: AddSpotMarket; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + + return ( + + {spotMarketFromIndex(ix, info.marketIndex) !== "UNKNOWN" && ( + + Market + + {spotMarketFromIndex(ix, info.marketIndex)} + + + )} + + Market index + {info.marketIndex} + + + Maint leverage + {info.maintLeverage} + + + Init leverage + {info.initLeverage} + + + Liquidation fee + {info.liquidationFee} + + + Optimal util + {info.optimalUtil} + + + Optimal rate + {info.optimalRate} + + + Max rate + {info.maxRate} + + + ); +} diff --git a/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx new file mode 100644 index 000000000..b1d10c55e --- /dev/null +++ b/explorer/src/components/instruction/mango/CancelPerpOrderDetailsCard.tsx @@ -0,0 +1,58 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { CancelPerpOrder, getPerpMarketFromInstruction } from "./types"; + +export function CancelPerpOrderDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: CancelPerpOrder; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + const mangoAccount = ix.keys[1]; + const perpMarketAccountMeta = ix.keys[3]; + const mangoPerpMarketConfig = getPerpMarketFromInstruction( + ix, + perpMarketAccountMeta + ); + + return ( + + + Mango account + +
+ + + + {mangoPerpMarketConfig !== undefined && ( + + Perp market + {mangoPerpMarketConfig.name} + + )} + + + Perp market address + +
+ + + + + Order Id + {info.orderId} + + + ); +} diff --git a/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx new file mode 100644 index 000000000..8688b77c8 --- /dev/null +++ b/explorer/src/components/instruction/mango/CancelSpotOrderDetailsCard.tsx @@ -0,0 +1,58 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { CancelSpotOrder, getSpotMarketFromInstruction } from "./types"; + +export function CancelSpotOrderDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: CancelSpotOrder; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + const mangoAccount = ix.keys[2]; + const spotMarketAccountMeta = ix.keys[4]; + const mangoSpotMarketConfig = getSpotMarketFromInstruction( + ix, + spotMarketAccountMeta + ); + + return ( + + + Mango account + +
+ + + + {mangoSpotMarketConfig !== undefined && ( + + Spot market + {mangoSpotMarketConfig.name} + + )} + + + Spot market address + +
+ + + + + Order Id + {info.orderId} + + + ); +} diff --git a/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx b/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx new file mode 100644 index 000000000..6ea4e7d86 --- /dev/null +++ b/explorer/src/components/instruction/mango/ChangePerpMarketParamsDetailsCard.tsx @@ -0,0 +1,122 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import moment from "moment"; +import { useCluster } from "providers/cluster"; +import { useEffect, useState } from "react"; +import { InstructionCard } from "../InstructionCard"; +import { + ChangePerpMarketParams, + getPerpMarketFromInstruction, + getPerpMarketFromPerpMarketConfig, +} from "./types"; + +export function ChangePerpMarketParamsDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: ChangePerpMarketParams; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + + const perpMarketAccountMeta = ix.keys[1]; + const mangoPerpMarketConfig = getPerpMarketFromInstruction( + ix, + perpMarketAccountMeta + ); + + const cluster = useCluster(); + const [targetPeriodLength, setTargetPeriodLength] = useState( + null + ); + useEffect(() => { + async function getTargetPeriodLength() { + if (mangoPerpMarketConfig === undefined) { + return; + } + const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig( + cluster.url, + mangoPerpMarketConfig + ); + + setTargetPeriodLength( + mangoPerpMarket.liquidityMiningInfo.targetPeriodLength.toNumber() + ); + } + + getTargetPeriodLength(); + }, [cluster.url, mangoPerpMarketConfig]); + + return ( + + {info.initLeverageOption && ( + + Init leverage + {info.initLeverage} + + )} + {info.liquidationFeeOption && ( + + Liquidation fee + {info.liquidationFee} + + )} + {info.maintLeverageOption && ( + + Maint leverage + {info.maintLeverage} + + )} + {info.makerFeeOption && ( + + Maker fee + {info.makerFee} + + )} + {info.mngoPerPeriodOption && ( + + + MNGO per{" "} + {targetPeriodLength !== null && + moment.duration(targetPeriodLength, "seconds").humanize()} + + + {info.mngoPerPeriod} {} + + + )} + + {info.maxDepthBpsOption && ( + + Max depth bps + {info.maxDepthBps} + + )} + {info.rateOption && ( + + Rate + {info.rate} + + )} + {info.takerFeeOption && ( + + Taker fee + {info.takerFee} + + )} + {info.targetPeriodLengthOption && ( + + Target period length + {info.targetPeriodLength} + + )} + + ); +} diff --git a/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx b/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx new file mode 100644 index 000000000..a97c2ba8c --- /dev/null +++ b/explorer/src/components/instruction/mango/ConsumeEventsDetailsCard.tsx @@ -0,0 +1,45 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { getPerpMarketFromInstruction } from "./types"; + +export function ConsumeEventsDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, innerCards, childIndex } = props; + + const perpMarketAccountMeta = ix.keys[2]; + const mangoPerpMarketConfig = getPerpMarketFromInstruction( + ix, + perpMarketAccountMeta + ); + + return ( + + {mangoPerpMarketConfig !== undefined && ( + + Perp market + {mangoPerpMarketConfig.name} + + )} + + + Perp market address + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx new file mode 100644 index 000000000..f291b72bf --- /dev/null +++ b/explorer/src/components/instruction/mango/GenericMngoAccountDetailsCard.tsx @@ -0,0 +1,42 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; + +export function GenericMngoAccountDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + mangoAccountKeyLocation: number; + title: String; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { + ix, + index, + result, + mangoAccountKeyLocation, + title, + innerCards, + childIndex, + } = props; + const mangoAccount = ix.keys[mangoAccountKeyLocation]; + + return ( + + + Mango account + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx new file mode 100644 index 000000000..5d4dc6eda --- /dev/null +++ b/explorer/src/components/instruction/mango/GenericPerpMngoDetailsCard.tsx @@ -0,0 +1,64 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { getPerpMarketFromInstruction } from "./types"; + +export function GenericPerpMngoDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + mangoAccountKeyLocation: number; + perpMarketKeyLocation: number; + title: String; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { + ix, + index, + result, + mangoAccountKeyLocation, + perpMarketKeyLocation, + title, + innerCards, + childIndex, + } = props; + const mangoAccount = ix.keys[mangoAccountKeyLocation]; + const perpMarketAccountMeta = ix.keys[perpMarketKeyLocation]; + const mangoPerpMarketConfig = getPerpMarketFromInstruction( + ix, + perpMarketAccountMeta + ); + + return ( + + + Mango account + +
+ + + + {mangoPerpMarketConfig !== undefined && ( + + Perp market + {mangoPerpMarketConfig.name} + + )} + + + Perp market address + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx b/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx new file mode 100644 index 000000000..df33a9b38 --- /dev/null +++ b/explorer/src/components/instruction/mango/GenericSpotMngoDetailsCard.tsx @@ -0,0 +1,64 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import { Address } from "components/common/Address"; +import { InstructionCard } from "../InstructionCard"; +import { getSpotMarketFromInstruction } from "./types"; + +export function GenericSpotMngoDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + accountKeyLocation: number; + spotMarketkeyLocation: number; + title: String; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { + ix, + index, + result, + accountKeyLocation, + spotMarketkeyLocation, + title, + innerCards, + childIndex, + } = props; + const mangoAccount = ix.keys[accountKeyLocation]; + const spotMarketAccountMeta = ix.keys[spotMarketkeyLocation]; + const mangoSpotMarketConfig = getSpotMarketFromInstruction( + ix, + spotMarketAccountMeta + ); + + return ( + + + Mango account + +
+ + + + {mangoSpotMarketConfig !== undefined && ( + + Spot market + {mangoSpotMarketConfig.name} + + )} + + + Spot market address + +
+ + + + ); +} diff --git a/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx new file mode 100644 index 000000000..ece6ceb66 --- /dev/null +++ b/explorer/src/components/instruction/mango/PlacePerpOrderDetailsCard.tsx @@ -0,0 +1,118 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import BN from "bn.js"; +import { Address } from "components/common/Address"; +import { useCluster } from "providers/cluster"; +import { useEffect, useState } from "react"; +import { InstructionCard } from "../InstructionCard"; +import { + getPerpMarketFromInstruction, + getPerpMarketFromPerpMarketConfig, + OrderLotDetails, + PlacePerpOrder, +} from "./types"; + +export function PlacePerpOrderDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: PlacePerpOrder; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + const mangoAccount = ix.keys[1]; + const perpMarketAccountMeta = ix.keys[4]; + const mangoPerpMarketConfig = getPerpMarketFromInstruction( + ix, + perpMarketAccountMeta + ); + + const cluster = useCluster(); + const [orderLotDetails, setOrderLotDetails] = + useState(null); + useEffect(() => { + async function getOrderLotDetails() { + if (mangoPerpMarketConfig === undefined) { + return; + } + const mangoPerpMarket = await getPerpMarketFromPerpMarketConfig( + cluster.url, + mangoPerpMarketConfig + ); + const maxBaseQuantity = mangoPerpMarket.baseLotsToNumber( + new BN(info.quantity.toString()) + ); + const limitPrice = mangoPerpMarket.priceLotsToNumber( + new BN(info.price.toString()) + ); + setOrderLotDetails({ + price: limitPrice, + size: maxBaseQuantity, + } as OrderLotDetails); + } + getOrderLotDetails(); + }, [cluster.url, info.quantity, info.price, mangoPerpMarketConfig]); + + return ( + + + Mango account + + {" "} +
+ + + + {mangoPerpMarketConfig !== undefined && ( + + Perp market + {mangoPerpMarketConfig.name} + + )} + + + Perp market address + +
+ + + + {info.clientOrderId !== "0" && ( + + Client order Id + {info.clientOrderId} + + )} + + + Order type + {info.orderType} + + + side + {info.side} + + + {orderLotDetails !== null && ( + + price + {orderLotDetails?.price} USDC + + )} + + {orderLotDetails !== null && ( + + quantity + {orderLotDetails?.size} + + )} + + ); +} diff --git a/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx b/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx new file mode 100644 index 000000000..aeac88645 --- /dev/null +++ b/explorer/src/components/instruction/mango/PlaceSpotOrderDetailsCard.tsx @@ -0,0 +1,130 @@ +import { SignatureResult, TransactionInstruction } from "@solana/web3.js"; +import BN from "bn.js"; +import { Address } from "components/common/Address"; +import { useCluster } from "providers/cluster"; +import { useEffect, useState } from "react"; +import { InstructionCard } from "../InstructionCard"; +import { + getSpotMarketFromInstruction, + getSpotMarketFromSpotMarketConfig, + OrderLotDetails, + PlaceSpotOrder, +} from "./types"; + +export function PlaceSpotOrderDetailsCard(props: { + ix: TransactionInstruction; + index: number; + result: SignatureResult; + info: PlaceSpotOrder; + innerCards?: JSX.Element[]; + childIndex?: number; +}) { + const { ix, index, result, info, innerCards, childIndex } = props; + const mangoAccount = ix.keys[1]; + const spotMarketAccountMeta = ix.keys[5]; + const mangoSpotMarketConfig = getSpotMarketFromInstruction( + ix, + spotMarketAccountMeta + ); + + const cluster = useCluster(); + const [orderLotDetails, setOrderLotDetails] = + useState(null); + useEffect(() => { + async function getOrderLotDetails() { + if (mangoSpotMarketConfig === undefined) { + return; + } + const mangoSpotMarket = await getSpotMarketFromSpotMarketConfig( + ix.programId, + cluster.url, + mangoSpotMarketConfig + ); + if (mangoSpotMarket === undefined) { + return; + } + const maxBaseQuantity = mangoSpotMarket.baseSizeLotsToNumber( + new BN(info.maxBaseQuantity.toString()) + ); + const limitPrice = mangoSpotMarket.priceLotsToNumber( + new BN(info.limitPrice.toString()) + ); + setOrderLotDetails({ + price: limitPrice, + size: maxBaseQuantity, + } as OrderLotDetails); + } + getOrderLotDetails(); + }, [ + cluster.url, + info.maxBaseQuantity, + info.limitPrice, + ix.programId, + mangoSpotMarketConfig, + ]); + + return ( + + + Mango account + + {" "} +
+ + + + {mangoSpotMarketConfig !== undefined && ( + + Spot market + {mangoSpotMarketConfig.name} + + )} + + + Spot market address + +
+ + + + + Order type + {info.orderType} + + + {info.clientId !== "0" && ( + + Client Id + {info.clientId} + + )} + + + Side + {info.side} + + + {orderLotDetails !== null && ( + + Limit price + {/* todo fix price */} + {orderLotDetails?.price} USDC + + )} + + {orderLotDetails !== null && ( + + Size + {orderLotDetails?.size} + + )} + + ); +} diff --git a/explorer/src/components/instruction/mango/types.ts b/explorer/src/components/instruction/mango/types.ts new file mode 100644 index 000000000..9f31e6a7f --- /dev/null +++ b/explorer/src/components/instruction/mango/types.ts @@ -0,0 +1,430 @@ +import { + Config, + GroupConfig, + MangoInstructionLayout, + PerpMarket, + PerpMarketConfig, + PerpMarketLayout, + SpotMarketConfig, +} from "@blockworks-foundation/mango-client"; +import { Market } from "@project-serum/serum"; +import { + AccountInfo, + AccountMeta, + Connection, + PublicKey, + TransactionInstruction, +} from "@solana/web3.js"; + +// note: mainnet.1 suffices since its a superset of mainnet.0 +const mangoGroups = Config.ids().groups.filter( + (group) => group.name !== "mainnet.0" +); + +// caching of account info's by public keys +let accountInfoCache: Record | null>> = {}; +function getAccountInfo( + clusterUrl: string, + publicKey: PublicKey +): Promise | null> { + if (publicKey.toBase58() in accountInfoCache) { + return accountInfoCache[publicKey.toBase58()]; + } + const connection = new Connection(clusterUrl); + const accountInfoPromise = connection.getAccountInfo(publicKey); + accountInfoCache[publicKey.toBase58()] = accountInfoPromise; + return accountInfoPromise; +} + +function findGroupConfig(programId: PublicKey): GroupConfig | undefined { + const filtered = mangoGroups.filter((group) => + group.mangoProgramId.equals(programId) + ); + if (filtered.length) { + return filtered[0]; + } +} + +export const isMangoInstruction = (instruction: TransactionInstruction) => { + return mangoGroups + .map((group) => group.mangoProgramId.toBase58()) + .includes(instruction.programId.toBase58()); +}; + +export const INSTRUCTION_LOOKUP: { [key: number]: string } = { + 0: "InitMangoGroup", + 1: "InitMangoAccount", + 2: "Deposit", + 3: "Withdraw", + 4: "AddSpotMarket", + 5: "AddToBasket", + 6: "Borrow", + 7: "CachePrices", + 8: "CacheRootBanks", + 9: "PlaceSpotOrder", + 10: "AddOracle", + 11: "AddPerpMarket", + 12: "PlacePerpOrder", + 13: "CancelPerpOrderByClientId", + 14: "CancelPerpOrder", + 15: "ConsumeEvents", + 16: "CachePerpMarkets", + 17: "UpdateFunding", + 18: "SetOracle", + 19: "SettleFunds", + 20: "CancelSpotOrder", + 21: "UpdateRootBank", + 22: "SettlePnl", + 23: "SettleBorrow", + 24: "ForceCancelSpotOrders", + 25: "ForceCancelPerpOrders", + 26: "LiquidateTokenAndToken", + 27: "LiquidateTokenAndPerp", + 28: "LiquidatePerpMarket", + 29: "SettleFees", + 30: "ResolvePerpBankruptcy", + 31: "ResolveTokenBankruptcy", + 32: "InitSpotOpenOrders", + 33: "RedeemMngo", + 34: "AddMangoAccountInfo", + 35: "DepositMsrm", + 36: "WithdrawMsrm", + 37: "ChangePerpMarketParams", +}; + +export const parseMangoInstructionTitle = ( + instruction: TransactionInstruction +): string => { + const code = instruction.data[0]; + + if (!(code in INSTRUCTION_LOOKUP)) { + throw new Error(`Unrecognized Mango instruction code: ${code}`); + } + return INSTRUCTION_LOOKUP[code]; +}; + +export type Deposit = { + quantity: number; +}; + +export const decodeDeposit = (ix: TransactionInstruction): Deposit => { + const decoded = MangoInstructionLayout.decode(ix.data); + const deposit: Deposit = { + quantity: decoded.Deposit.quantity.toNumber(), + }; + return deposit; +}; + +export type AddToBasket = { + marketIndex: number; +}; + +export const decodeAddToBasket = (ix: TransactionInstruction): AddToBasket => { + const decoded = MangoInstructionLayout.decode(ix.data); + const addToBasket: AddToBasket = { + marketIndex: decoded.AddToBasket.marketIndex.toNumber(), + }; + return addToBasket; +}; + +export type Withdraw = { + quantity: number; + allowBorrow: String; +}; + +export const decodeWithdraw = (ix: TransactionInstruction): Withdraw => { + const decoded = MangoInstructionLayout.decode(ix.data); + const withdraw: Withdraw = { + quantity: decoded.Withdraw.quantity.toNumber(), + allowBorrow: decoded.Withdraw.allowBorrow.toString(), + }; + return withdraw; +}; + +export type PlaceSpotOrder = { + side: String; + limitPrice: number; + maxBaseQuantity: number; + maxQuoteQuantity: number; + selfTradeBehavior: String; + orderType: String; + clientId: String; + limit: String; +}; + +export const decodePlaceSpotOrder = ( + ix: TransactionInstruction +): PlaceSpotOrder => { + const decoded = MangoInstructionLayout.decode(ix.data); + const placeSpotOrder: PlaceSpotOrder = { + side: decoded.PlaceSpotOrder.side.toString(), + limitPrice: decoded.PlaceSpotOrder.limitPrice.toNumber(), + maxBaseQuantity: decoded.PlaceSpotOrder.maxBaseQuantity.toNumber(), + maxQuoteQuantity: decoded.PlaceSpotOrder.maxQuoteQuantity.toNumber(), + selfTradeBehavior: decoded.PlaceSpotOrder.selfTradeBehavior, + orderType: decoded.PlaceSpotOrder.orderType.toString(), + clientId: decoded.PlaceSpotOrder.clientId.toString(), + limit: decoded.PlaceSpotOrder.limit.toString(), + }; + + return placeSpotOrder; +}; + +export type CancelSpotOrder = { + orderId: String; + side: String; +}; + +export const decodeCancelSpotOrder = ( + ix: TransactionInstruction +): CancelSpotOrder => { + const decoded = MangoInstructionLayout.decode(ix.data); + const cancelSpotOrder: CancelSpotOrder = { + orderId: decoded.CancelSpotOrder.orderId.toString(), + side: decoded.CancelSpotOrder.side.toString(), + }; + return cancelSpotOrder; +}; + +export type PlacePerpOrder = { + price: number; + quantity: number; + clientOrderId: String; + side: String; + orderType: String; +}; +export const decodePlacePerpOrder = ( + ix: TransactionInstruction +): PlacePerpOrder => { + const decoded = MangoInstructionLayout.decode(ix.data); + const placePerpOrder: PlacePerpOrder = { + price: decoded.PlacePerpOrder.price.toNumber(), + quantity: decoded.PlacePerpOrder.quantity.toNumber(), + clientOrderId: decoded.PlacePerpOrder.clientOrderId.toString(), + side: decoded.PlacePerpOrder.side.toString(), + orderType: decoded.PlacePerpOrder.orderType.toString(), + }; + + return placePerpOrder; +}; + +export type CancelPerpOrder = { + orderId: String; + invalidIdOk: String; +}; + +export const decodeCancelPerpOrder = ( + ix: TransactionInstruction +): CancelPerpOrder => { + const decoded = MangoInstructionLayout.decode(ix.data); + const cancelPerpOrder: CancelPerpOrder = { + orderId: decoded.CancelPerpOrder.orderId.toString(), + invalidIdOk: decoded.CancelPerpOrder.invalidIdOk.toString(), + }; + return cancelPerpOrder; +}; + +export type ChangePerpMarketParams = { + maintLeverageOption: Boolean; + maintLeverage: number; + initLeverageOption: Boolean; + initLeverage: number; + liquidationFeeOption: Boolean; + liquidationFee: number; + makerFeeOption: Boolean; + makerFee: number; + takerFeeOption: Boolean; + takerFee: number; + rateOption: Boolean; + rate: number; + maxDepthBpsOption: Boolean; + maxDepthBps: number; + targetPeriodLengthOption: Boolean; + targetPeriodLength: number; + mngoPerPeriodOption: Boolean; + mngoPerPeriod: number; +}; + +export const decodeChangePerpMarketParams = ( + ix: TransactionInstruction +): ChangePerpMarketParams => { + const decoded = MangoInstructionLayout.decode(ix.data); + const changePerpMarketParams: ChangePerpMarketParams = { + maintLeverageOption: decoded.ChangePerpMarketParams.maintLeverageOption, + maintLeverage: decoded.ChangePerpMarketParams.maintLeverage.toString(), + initLeverageOption: decoded.ChangePerpMarketParams.initLeverageOption, + initLeverage: decoded.ChangePerpMarketParams.initLeverage.toString(), + liquidationFeeOption: decoded.ChangePerpMarketParams.liquidationFeeOption, + liquidationFee: decoded.ChangePerpMarketParams.liquidationFee.toString(), + makerFeeOption: decoded.ChangePerpMarketParams.makerFeeOption, + makerFee: decoded.ChangePerpMarketParams.makerFee.toString(), + takerFeeOption: decoded.ChangePerpMarketParams.takerFeeOption, + takerFee: decoded.ChangePerpMarketParams.takerFee.toString(), + rateOption: decoded.ChangePerpMarketParams.rateOption, + rate: decoded.ChangePerpMarketParams.rate.toString(), + maxDepthBpsOption: decoded.ChangePerpMarketParams.maxDepthBpsOption, + maxDepthBps: decoded.ChangePerpMarketParams.maxDepthBps.toString(), + targetPeriodLengthOption: + decoded.ChangePerpMarketParams.targetPeriodLengthOption, + targetPeriodLength: + decoded.ChangePerpMarketParams.targetPeriodLength.toString(), + mngoPerPeriodOption: decoded.ChangePerpMarketParams.mngoPerPeriodOption, + mngoPerPeriod: decoded.ChangePerpMarketParams.mngoPerPeriod.toString(), + }; + return changePerpMarketParams; +}; + +export type AddSpotMarket = { + marketIndex: number; + maintLeverage: number; + initLeverage: number; + liquidationFee: number; + optimalUtil: number; + optimalRate: number; + maxRate: number; +}; + +export const decodeAddSpotMarket = ( + ix: TransactionInstruction +): AddSpotMarket => { + const decoded = MangoInstructionLayout.decode(ix.data); + const addSpotMarket: AddSpotMarket = { + marketIndex: decoded.AddSpotMarket.marketIndex.toNumber(), + maintLeverage: decoded.AddSpotMarket.maintLeverage.toNumber(), + initLeverage: decoded.AddSpotMarket.initLeverage.toNumber(), + liquidationFee: decoded.AddSpotMarket.liquidationFee.toNumber(), + optimalUtil: decoded.AddSpotMarket.optimalUtil.toNumber(), + optimalRate: decoded.AddSpotMarket.optimalRate.toNumber(), + maxRate: decoded.AddSpotMarket.maxRate.toNumber(), + }; + return addSpotMarket; +}; + +export type AddPerpMarket = { + marketIndex: number; + maintLeverage: number; + initLeverage: number; + liquidationFee: number; + makerFee: number; + takerFee: number; + baseLotSize: number; + quoteLotSize: number; + rate: number; + maxDepthBps: number; + targetPeriodLength: number; + mngoPerPeriod: number; +}; + +export const decodeAddPerpMarket = ( + ix: TransactionInstruction +): AddPerpMarket => { + const decoded = MangoInstructionLayout.decode(ix.data); + const addPerpMarket: AddPerpMarket = { + marketIndex: decoded.AddPerpMarket.marketIndex.toNumber(), + maintLeverage: decoded.AddPerpMarket.maintLeverage.toNumber(), + initLeverage: decoded.AddPerpMarket.initLeverage.toNumber(), + liquidationFee: decoded.AddPerpMarket.liquidationFee.toNumber(), + makerFee: decoded.AddPerpMarket.makerFee.toNumber(), + takerFee: decoded.AddPerpMarket.takerFee.toNumber(), + baseLotSize: decoded.AddPerpMarket.baseLotSize.toNumber(), + quoteLotSize: decoded.AddPerpMarket.quoteLotSize.toNumber(), + rate: decoded.AddPerpMarket.rate.toNumber(), + maxDepthBps: decoded.AddPerpMarket.maxDepthBps.toNumber(), + targetPeriodLength: decoded.AddPerpMarket.targetPeriodLength.toNumber(), + mngoPerPeriod: decoded.AddPerpMarket.mngoPerPeriod.toNumber(), + }; + return addPerpMarket; +}; + +export type OrderLotDetails = { + price: number; + size: number; +}; + +//// + +export function logAllKeys(keys: AccountMeta[]) { + keys.map((key) => console.log(key.pubkey.toBase58())); +} + +export function getSpotMarketFromInstruction( + ix: TransactionInstruction, + spotMarket: AccountMeta +): SpotMarketConfig | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const spotMarketConfigs = groupConfig.spotMarkets.filter((mangoSpotMarket) => + spotMarket.pubkey.equals(mangoSpotMarket.publicKey) + ); + if (spotMarketConfigs.length) { + return spotMarketConfigs[0]; + } +} + +export async function getSpotMarketFromSpotMarketConfig( + programId: PublicKey, + clusterUrl: string, + mangoSpotMarketConfig: SpotMarketConfig +): Promise { + const connection = new Connection(clusterUrl); + const groupConfig = findGroupConfig(programId); + if (groupConfig === undefined) { + return; + } + return await Market.load( + connection, + mangoSpotMarketConfig.publicKey, + undefined, + groupConfig.serumProgramId + ); +} + +export function getPerpMarketFromInstruction( + ix: TransactionInstruction, + perpMarket: AccountMeta +): PerpMarketConfig | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const perpMarketConfigs = groupConfig.perpMarkets.filter((mangoPerpMarket) => + perpMarket.pubkey.equals(mangoPerpMarket.publicKey) + ); + if (perpMarketConfigs.length) { + return perpMarketConfigs[0]; + } +} + +export async function getPerpMarketFromPerpMarketConfig( + clusterUrl: string, + mangoPerpMarketConfig: PerpMarketConfig +): Promise { + const acc = await getAccountInfo(clusterUrl, mangoPerpMarketConfig.publicKey); + const decoded = PerpMarketLayout.decode(acc?.data); + + return new PerpMarket( + mangoPerpMarketConfig.publicKey, + mangoPerpMarketConfig.baseDecimals, + mangoPerpMarketConfig.quoteDecimals, + decoded + ); +} + +export function spotMarketFromIndex( + ix: TransactionInstruction, + marketIndex: number +): String | undefined { + const groupConfig = findGroupConfig(ix.programId); + if (groupConfig === undefined) { + return; + } + const spotMarketConfigs = groupConfig.spotMarkets.filter( + (spotMarketConfig) => spotMarketConfig.marketIndex === marketIndex + ); + if (!spotMarketConfigs.length) { + return "UNKNOWN"; + } + return spotMarketConfigs[0].name; +} diff --git a/explorer/src/components/transaction/InstructionsSection.tsx b/explorer/src/components/transaction/InstructionsSection.tsx index 12d0cb0f2..68a79fec5 100644 --- a/explorer/src/components/transaction/InstructionsSection.tsx +++ b/explorer/src/components/transaction/InstructionsSection.tsx @@ -39,6 +39,8 @@ import { BpfUpgradeableLoaderDetailsCard } from "components/instruction/bpf-upgr import { VoteDetailsCard } from "components/instruction/vote/VoteDetailsCard"; 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"; export type InstructionDetailsProps = { tx: ParsedTransaction; @@ -208,6 +210,8 @@ function renderInstructionCard({ if (isBonfidaBotInstruction(transactionIx)) { return ; + } else if (isMangoInstruction(transactionIx)) { + return ; } else if (isSerumInstruction(transactionIx)) { return ; } else if (isTokenSwapInstruction(transactionIx)) { diff --git a/explorer/src/utils/tx.ts b/explorer/src/utils/tx.ts index a5177287f..54bfb1dd1 100644 --- a/explorer/src/utils/tx.ts +++ b/explorer/src/utils/tx.ts @@ -50,6 +50,7 @@ export enum PROGRAM_NAMES { SERUM_1 = "Serum Program v1", SERUM_2 = "Serum Program v2", SERUM_3 = "Serum Program v3", + MANGO_3 = "Mango Program v3", } const ALL_CLUSTERS = [ @@ -90,6 +91,7 @@ export const PROGRAM_DEPLOYMENTS = { [PROGRAM_NAMES.SERUM_1]: MAINNET_ONLY, [PROGRAM_NAMES.SERUM_2]: MAINNET_ONLY, [PROGRAM_NAMES.SERUM_3]: MAINNET_ONLY, + [PROGRAM_NAMES.MANGO_3]: MAINNET_ONLY, } as const; export const PROGRAM_NAME_BY_ID = { @@ -121,6 +123,7 @@ export const PROGRAM_NAME_BY_ID = { BJ3jrUzddfuSrZHXSCxMUUQsjKEyLmuuyZebkcaFp2fg: PROGRAM_NAMES.SERUM_1, EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o: PROGRAM_NAMES.SERUM_2, "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin": PROGRAM_NAMES.SERUM_3, + mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68: PROGRAM_NAMES.MANGO_3, } as const; export type LoaderName = typeof LOADER_IDS[keyof typeof LOADER_IDS];