Mango v3 integration (#19437)

* mango v3 integration

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* more instructions InitMangoAccount, Deposit, Withdraw, InitSpotOpenOrders, ConsumeEvents

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* add support for addPerpMarket

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* code review

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* make group config handling generic

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* code review

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>

* fix issue where rpc calls would be done infinitely - basically react useeffect (when to change) was not narrow enough + added some caching

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2021-09-04 00:01:29 +02:00 committed by GitHub
parent 1828579580
commit f7a5c0301a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2034 additions and 6 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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) =>

View File

@ -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 (
<GenericMngoAccountDetailsCard
mangoAccountKeyLocation={1}
title={title}
{...props}
/>
);
case "Deposit":
return (
<GenericMngoAccountDetailsCard
mangoAccountKeyLocation={1}
title={title}
{...props}
/>
);
case "Withdraw":
return (
<GenericMngoAccountDetailsCard
mangoAccountKeyLocation={1}
title={title}
{...props}
/>
);
case "InitSpotOpenOrders":
return (
<GenericMngoAccountDetailsCard
mangoAccountKeyLocation={1}
title={title}
{...props}
/>
);
case "PlaceSpotOrder":
return (
<PlaceSpotOrderDetailsCard
info={decodePlaceSpotOrder(ix)}
{...props}
/>
);
case "CancelSpotOrder":
return (
<CancelSpotOrderDetailsCard
info={decodeCancelSpotOrder(ix)}
{...props}
/>
);
case "AddPerpMarket":
return (
<AddPerpMarketDetailsCard info={decodeAddPerpMarket(ix)} {...props} />
);
case "PlacePerpOrder":
return (
<PlacePerpOrderDetailsCard
info={decodePlacePerpOrder(ix)}
{...props}
/>
);
case "ConsumeEvents":
return <ConsumeEventsDetailsCard {...props} />;
case "CancelPerpOrder":
return (
<CancelPerpOrderDetailsCard
info={decodeCancelPerpOrder(ix)}
{...props}
/>
);
case "SettleFunds":
return (
<GenericSpotMngoDetailsCard
accountKeyLocation={2}
spotMarketkeyLocation={5}
title={title}
{...props}
/>
);
case "RedeemMngo":
return (
<GenericPerpMngoDetailsCard
mangoAccountKeyLocation={3}
perpMarketKeyLocation={4}
title={title}
{...props}
/>
);
case "ChangePerpMarketParams":
return (
<ChangePerpMarketParamsDetailsCard
info={decodeChangePerpMarketParams(ix)}
{...props}
/>
);
case "AddOracle":
return <AddOracleDetailsCard {...props} />;
case "AddSpotMarket":
return (
<AddSpotMarketDetailsCard info={decodeAddSpotMarket(ix)} {...props} />
);
}
} catch (error) {
reportError(error, {
url: url,
signature: signature,
});
}
return (
<InstructionCard
ix={ix}
index={index}
result={result}
title={`Mango: ${title || "Unknown"}`}
innerCards={innerCards}
childIndex={childIndex}
defaultRaw
/>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: AddOracle"
innerCards={innerCards}
childIndex={childIndex}
></InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: AddPerpMarket"
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Market index</td>
<td className="text-lg-right">{info.marketIndex}</td>
</tr>
<tr>
<td>Maintenance leverage</td>
<td className="text-lg-right">{info.maintLeverage}</td>
</tr>
<tr>
<td>Initial leverage</td>
<td className="text-lg-right">{info.initLeverage}</td>
</tr>
<tr>
<td>Liquidation fee</td>
<td className="text-lg-right">{info.liquidationFee}</td>
</tr>
<tr>
<td>Maker fee</td>
<td className="text-lg-right">{info.makerFee}</td>
</tr>
<tr>
<td>Taker fee</td>
<td className="text-lg-right">{info.takerFee}</td>
</tr>
<tr>
<td>Base lot size</td>
<td className="text-lg-right">{info.baseLotSize}</td>
</tr>
<tr>
<td>Quote lot size</td>
<td className="text-lg-right">{info.quoteLotSize}</td>
</tr>
<tr>
<td>Rate</td>
<td className="text-lg-right">{info.rate}</td>
</tr>
<tr>
<td>Max depth bps</td>
<td className="text-lg-right">{info.maxDepthBps}</td>
</tr>
<tr>
<td>
MNGO per{" "}
{moment.duration(info.targetPeriodLength, "seconds").humanize()}
</td>
<td className="text-lg-right">
{info.mngoPerPeriod} {}
</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: AddSpotMarket"
innerCards={innerCards}
childIndex={childIndex}
>
{spotMarketFromIndex(ix, info.marketIndex) !== "UNKNOWN" && (
<tr>
<td>Market</td>
<td className="text-lg-right">
{spotMarketFromIndex(ix, info.marketIndex)}
</td>
</tr>
)}
<tr>
<td>Market index</td>
<td className="text-lg-right">{info.marketIndex}</td>
</tr>
<tr>
<td>Maint leverage</td>
<td className="text-lg-right">{info.maintLeverage}</td>
</tr>
<tr>
<td>Init leverage</td>
<td className="text-lg-right">{info.initLeverage}</td>
</tr>
<tr>
<td>Liquidation fee</td>
<td className="text-lg-right">{info.liquidationFee}</td>
</tr>
<tr>
<td>Optimal util</td>
<td className="text-lg-right">{info.optimalUtil}</td>
</tr>
<tr>
<td>Optimal rate</td>
<td className="text-lg-right">{info.optimalRate}</td>
</tr>
<tr>
<td>Max rate</td>
<td className="text-lg-right">{info.maxRate}</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: CancelPerpOrder"
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoPerpMarketConfig !== undefined && (
<tr>
<td>Perp market</td>
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Perp market address</td>
<td>
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
<tr>
<td>Order Id</td>
<td className="text-lg-right">{info.orderId}</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: CancelSpotOrder"
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoSpotMarketConfig !== undefined && (
<tr>
<td>Spot market</td>
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Spot market address</td>
<td>
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
<tr>
<td>Order Id</td>
<td className="text-lg-right">{info.orderId}</td>
</tr>
</InstructionCard>
);
}

View File

@ -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<number | null>(
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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: ChangePerpMarketParams"
innerCards={innerCards}
childIndex={childIndex}
>
{info.initLeverageOption && (
<tr>
<td>Init leverage</td>
<td className="text-lg-right">{info.initLeverage}</td>
</tr>
)}
{info.liquidationFeeOption && (
<tr>
<td>Liquidation fee</td>
<td className="text-lg-right">{info.liquidationFee}</td>
</tr>
)}
{info.maintLeverageOption && (
<tr>
<td>Maint leverage</td>
<td className="text-lg-right">{info.maintLeverage}</td>
</tr>
)}
{info.makerFeeOption && (
<tr>
<td>Maker fee</td>
<td className="text-lg-right">{info.makerFee}</td>
</tr>
)}
{info.mngoPerPeriodOption && (
<tr>
<td>
MNGO per{" "}
{targetPeriodLength !== null &&
moment.duration(targetPeriodLength, "seconds").humanize()}
</td>
<td className="text-lg-right">
{info.mngoPerPeriod} {}
</td>
</tr>
)}
{info.maxDepthBpsOption && (
<tr>
<td>Max depth bps</td>
<td className="text-lg-right">{info.maxDepthBps}</td>
</tr>
)}
{info.rateOption && (
<tr>
<td>Rate</td>
<td className="text-lg-right">{info.rate}</td>
</tr>
)}
{info.takerFeeOption && (
<tr>
<td>Taker fee</td>
<td className="text-lg-right">{info.takerFee}</td>
</tr>
)}
{info.targetPeriodLengthOption && (
<tr>
<td>Target period length</td>
<td className="text-lg-right">{info.targetPeriodLength}</td>
</tr>
)}
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title={"Mango: ConsumeEvents"}
innerCards={innerCards}
childIndex={childIndex}
>
{mangoPerpMarketConfig !== undefined && (
<tr>
<td>Perp market</td>
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Perp market address</td>
<td>
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title={"Mango: " + title}
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title={"Mango: " + title}
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoPerpMarketConfig !== undefined && (
<tr>
<td>Perp market</td>
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Perp market address</td>
<td>
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
</InstructionCard>
);
}

View File

@ -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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title={"Mango: " + title}
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoSpotMarketConfig !== undefined && (
<tr>
<td>Spot market</td>
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Spot market address</td>
<td>
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
</InstructionCard>
);
}

View File

@ -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<OrderLotDetails | null>(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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: PlacePerpOrder"
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
{" "}
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoPerpMarketConfig !== undefined && (
<tr>
<td>Perp market</td>
<td className="text-lg-right">{mangoPerpMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Perp market address</td>
<td>
<Address pubkey={perpMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
{info.clientOrderId !== "0" && (
<tr>
<td>Client order Id</td>
<td className="text-lg-right">{info.clientOrderId}</td>
</tr>
)}
<tr>
<td>Order type</td>
<td className="text-lg-right">{info.orderType}</td>
</tr>
<tr>
<td>side</td>
<td className="text-lg-right">{info.side}</td>
</tr>
{orderLotDetails !== null && (
<tr>
<td>price</td>
<td className="text-lg-right">{orderLotDetails?.price} USDC</td>
</tr>
)}
{orderLotDetails !== null && (
<tr>
<td>quantity</td>
<td className="text-lg-right">{orderLotDetails?.size}</td>
</tr>
)}
</InstructionCard>
);
}

View File

@ -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<OrderLotDetails | null>(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 (
<InstructionCard
ix={ix}
index={index}
result={result}
title="Mango: PlaceSpotOrder"
innerCards={innerCards}
childIndex={childIndex}
>
<tr>
<td>Mango account</td>
<td>
{" "}
<Address pubkey={mangoAccount.pubkey} alignRight link />
</td>
</tr>
{mangoSpotMarketConfig !== undefined && (
<tr>
<td>Spot market</td>
<td className="text-lg-right">{mangoSpotMarketConfig.name}</td>
</tr>
)}
<tr>
<td>Spot market address</td>
<td>
<Address pubkey={spotMarketAccountMeta.pubkey} alignRight link />
</td>
</tr>
<tr>
<td>Order type</td>
<td className="text-lg-right">{info.orderType}</td>
</tr>
{info.clientId !== "0" && (
<tr>
<td>Client Id</td>
<td className="text-lg-right">{info.clientId}</td>
</tr>
)}
<tr>
<td>Side</td>
<td className="text-lg-right">{info.side}</td>
</tr>
{orderLotDetails !== null && (
<tr>
<td>Limit price</td>
{/* todo fix price */}
<td className="text-lg-right">{orderLotDetails?.price} USDC</td>
</tr>
)}
{orderLotDetails !== null && (
<tr>
<td>Size</td>
<td className="text-lg-right">{orderLotDetails?.size}</td>
</tr>
)}
</InstructionCard>
);
}

View File

@ -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<string, Promise<AccountInfo<Buffer> | null>> = {};
function getAccountInfo(
clusterUrl: string,
publicKey: PublicKey
): Promise<AccountInfo<Buffer> | 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<Market | undefined> {
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<PerpMarket> {
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;
}

View File

@ -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 <BonfidaBotDetailsCard key={key} {...props} />;
} else if (isMangoInstruction(transactionIx)) {
return <MangoDetailsCard key={key} {...props} />;
} else if (isSerumInstruction(transactionIx)) {
return <SerumDetailsCard key={key} {...props} />;
} else if (isTokenSwapInstruction(transactionIx)) {

View File

@ -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];