├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky └── pre-commit ├── .prettierignore ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── .yarnrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── jest.config.ts ├── package.json ├── src ├── api │ ├── api.ts │ ├── index.ts │ ├── type.ts │ ├── url.ts │ └── utils.ts ├── common │ ├── accountInfo.ts │ ├── bignumber.ts │ ├── constant.ts │ ├── date.ts │ ├── error.ts │ ├── fee.ts │ ├── fractionUtil.ts │ ├── index.ts │ ├── json-file.ts │ ├── lodash.ts │ ├── logger.ts │ ├── owner.ts │ ├── pda.ts │ ├── programId.ts │ ├── pubKey.ts │ ├── transfer.ts │ ├── txTool │ │ ├── lookupTable.ts │ │ ├── txTool.ts │ │ ├── txType.ts │ │ └── txUtils.ts │ └── utility.ts ├── index.ts ├── marshmallow │ ├── buffer-layout.ts │ └── index.ts ├── module │ ├── amount.ts │ ├── currency.ts │ ├── formatter.ts │ ├── fraction.ts │ ├── index.ts │ ├── percent.ts │ ├── price.ts │ └── token.ts ├── raydium │ ├── account │ │ ├── account.ts │ │ ├── index.ts │ │ ├── instruction.ts │ │ ├── layout.ts │ │ ├── types.ts │ │ └── util.ts │ ├── clmm │ │ ├── clmm.ts │ │ ├── index.ts │ │ ├── instrument.ts │ │ ├── layout.ts │ │ ├── type.ts │ │ └── utils │ │ │ ├── constants.ts │ │ │ ├── math.ts │ │ │ ├── pda.ts │ │ │ ├── pool.ts │ │ │ ├── position.ts │ │ │ ├── tick.ts │ │ │ ├── tickQuery.ts │ │ │ ├── tickarrayBitmap.ts │ │ │ └── util.ts │ ├── cpmm │ │ ├── cpmm.ts │ │ ├── curve │ │ │ ├── calculator.ts │ │ │ ├── constantProduct.ts │ │ │ └── fee.ts │ │ ├── index.ts │ │ ├── instruction.ts │ │ ├── layout.ts │ │ ├── pda.ts │ │ └── type.ts │ ├── farm │ │ ├── config.ts │ │ ├── farm.ts │ │ ├── index.ts │ │ ├── instruction.ts │ │ ├── layout.ts │ │ ├── pda.ts │ │ ├── type.ts │ │ └── util.ts │ ├── ido │ │ ├── ido.ts │ │ ├── index.ts │ │ ├── instruction.ts │ │ ├── layout.ts │ │ └── type.ts │ ├── index.ts │ ├── launchpad │ │ ├── curve │ │ │ ├── constantProductCurve.ts │ │ │ ├── curve.ts │ │ │ ├── curveBase.ts │ │ │ ├── fixedPriceCurve.ts │ │ │ ├── func.ts │ │ │ └── linearPriceCurve.ts │ │ ├── index.ts │ │ ├── instrument.ts │ │ ├── launchpad.ts │ │ ├── layout.ts │ │ ├── pda.ts │ │ └── type.ts │ ├── liquidity │ │ ├── constant.ts │ │ ├── index.ts │ │ ├── instruction.ts │ │ ├── layout.ts │ │ ├── liquidity.ts │ │ ├── serum.ts │ │ ├── stable.ts │ │ ├── type.ts │ │ └── utils.ts │ ├── marketV2 │ │ ├── createMarket.ts │ │ ├── index.ts │ │ ├── instrument.ts │ │ └── layout.ts │ ├── moduleBase.ts │ ├── raydium.ts │ ├── serum │ │ ├── id.ts │ │ ├── index.ts │ │ ├── layout.ts │ │ ├── serum.ts │ │ └── type.ts │ ├── token │ │ ├── constant.ts │ │ ├── index.ts │ │ ├── layout.ts │ │ ├── token.ts │ │ ├── type.ts │ │ └── utils.ts │ ├── tradeV2 │ │ ├── index.ts │ │ ├── instrument.ts │ │ ├── trade.ts │ │ └── type.ts │ ├── type.ts │ └── utils1216 │ │ ├── index.ts │ │ └── utils1216.ts └── solana │ ├── index.ts │ └── type.ts ├── test └── init.ts ├── tsconfig.json ├── typedoc.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/preset-env", { "targets": { "node": "current" } }], "@babel/preset-typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 80 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.json] 17 | insert_final_newline = false 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | dist/* 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | plugins: ["@typescript-eslint", "eslint-plugin-tsdoc"], 4 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 5 | env: { 6 | es6: true, 7 | browser: true, 8 | jest: true, 9 | node: true, 10 | }, 11 | rules: { 12 | "tsdoc/syntax": "off", 13 | "tsdoc-malformed-inline-tag": "off", 14 | "@typescript-eslint/no-explicit-any": 0, 15 | "object-shorthand": ["error", "always"], 16 | "@typescript-eslint/explicit-function-return-type": ["warn"], 17 | "@typescript-eslint/no-non-null-assertion": "off", 18 | "no-inner-declarations": ["warn"], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Microbundle cache 58 | .rpt2_cache/ 59 | .rts2_cache_cjs/ 60 | .rts2_cache_es/ 61 | .rts2_cache_umd/ 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # parcel-bundler cache (https://parceljs.org/) 73 | .cache 74 | .parcel-cache 75 | 76 | # Next.js build output 77 | .next 78 | out 79 | 80 | # Nuxt.js build / generate output 81 | .nuxt 82 | dist 83 | 84 | # Gatsby files 85 | .cache/ 86 | # Comment in the public line in if your project uses Gatsby and not Next.js 87 | # https://nextjs.org/blog/next-9-1#public-directory-support 88 | # public 89 | 90 | # vuepress build output 91 | .vuepress/dist 92 | 93 | # Serverless directories 94 | .serverless/ 95 | 96 | # FuseBox cache 97 | .fusebox/ 98 | 99 | # DynamoDB Local files 100 | .dynamodb/ 101 | 102 | # TernJS port file 103 | .tern-port 104 | 105 | # Stores VSCode versions used for testing VSCode extensions 106 | .vscode-test 107 | 108 | # yarn v2 109 | .yarn/cache 110 | .yarn/unplugged 111 | .yarn/build-state.yml 112 | .yarn/install-state.gz 113 | .pnp.* 114 | 115 | # misc 116 | .DS_Store 117 | lib/ 118 | .coverage/ 119 | docs/ 120 | test/local/ 121 | test/data/ 122 | 123 | example/node_modules/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint 5 | yarn build 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "eslintIntegration": true, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { 9 | "parser": "typescript" 10 | } 11 | } 12 | ], 13 | "printWidth": 120, 14 | "semi": true, 15 | "singleQuote": false, 16 | "tabWidth": 2, 17 | "trailingComma": "all", 18 | "useTabs": false 19 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | // prettier 4 | "esbenp.prettier-vscode", 5 | // control prettier format flow 6 | "rohit-gohri.format-code-action", 7 | // import sorter 8 | "dozerg.tsimportsorter", 9 | // colorful comments 10 | "aaron-bond.better-comments", 11 | // ts comments automate 12 | "salbert.comment-ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.tabSize": 2, 5 | // format flow 6 | "editor.codeActionsOnSave": ["source.formatDocument", "source.organizeImports.sortImports"], 7 | "eslint.validate": ["typescript"], 8 | // https://marketplace.visualstudio.com/items?itemName=dozerg.tsimportsorter 9 | "tsImportSorter.configuration.groupRules": [ 10 | ["^@\\w", "^\\w"], 11 | ["^@/"], 12 | ["^\\.\\."], 13 | ["^\\."], 14 | { "flags": "scripts" } 15 | ], 16 | "tsImportSorter.configuration.keepUnused": [".*"], 17 | "tsImportSorter.configuration.wrappingStyle": { 18 | "maxNamesPerWrappedLine": 0, 19 | "maxBindingNamesPerLine": 0, 20 | "maxDefaultAndBindingNamesPerLine": 0 21 | }, 22 | // aaron-bond.better-comments 23 | "better-comments.multilineComments": true, 24 | "better-comments.tags": [ 25 | { 26 | "tag": "!", 27 | "color": "#FF2D00", 28 | "strikethrough": false, 29 | "underline": false, 30 | "backgroundColor": "transparent", 31 | "bold": false, 32 | "italic": false 33 | }, 34 | { 35 | "tag": "?", 36 | "color": "#3498DB", 37 | "strikethrough": false, 38 | "underline": false, 39 | "backgroundColor": "transparent", 40 | "bold": false, 41 | "italic": false 42 | }, 43 | { 44 | "tag": "//", 45 | "color": "#474747", 46 | "strikethrough": true, 47 | "underline": false, 48 | "backgroundColor": "transparent", 49 | "bold": false, 50 | "italic": false 51 | }, 52 | { 53 | "tag": "todo", 54 | "color": "#FF8C00", 55 | "strikethrough": false, 56 | "underline": false, 57 | "backgroundColor": "transparent", 58 | "bold": false, 59 | "italic": false 60 | }, 61 | { 62 | "tag": "*", 63 | "color": "#98C379", 64 | "strikethrough": false, 65 | "underline": false, 66 | "backgroundColor": "transparent", 67 | "bold": false, 68 | "italic": false 69 | } 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | https://registry.yarnpkg.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raydium SDK 2 | 3 | [npm-image]: https://img.shields.io/npm/v/@raydium-io/raydium-sdk-v2.svg?style=flat 4 | [npm-url]: https://www.npmjs.com/package/@raydium-io/raydium-sdk-v2 5 | 6 | [![npm][npm-image]][npm-url] 7 | 8 | An SDK for building applications on top of Raydium. 9 | 10 | ## Usage Guide 11 | 12 | ### Installation 13 | 14 | ``` 15 | $ yarn add @raydium-io/raydium-sdk-v2 16 | ``` 17 | 18 | ## SDK method Demo 19 | 20 | [SDK V2 Demo Repo](https://github.com/raydium-io/raydium-sdk-V2-demo) 21 | 22 | ## SDK local test 23 | 24 | ``` 25 | $ yarn dev {directory} 26 | 27 | e.g. yarn dev test/init.ts 28 | ``` 29 | 30 | ## Features 31 | 32 | ### Initialization 33 | 34 | ```javascript 35 | import { Raydium } from "@raydium-io/raydium-sdk"; 36 | const raydium = await Raydium.load({ 37 | connection, 38 | owner, // key pair or publicKey, if you run a node process, provide keyPair 39 | signAllTransactions, // optional - provide sign functions provided by @solana/wallet-adapter-react 40 | tokenAccounts, // optional, if dapp handle it by self can provide to sdk 41 | tokenAccountRowInfos, // optional, if dapp handle it by self can provide to sdk 42 | disableLoadToken: false, // default is false, if you don't need token info, set to true 43 | }); 44 | ``` 45 | 46 | #### how to transform token account data 47 | 48 | ```javascript 49 | import { parseTokenAccountResp } from "@raydium-io/raydium-sdk"; 50 | 51 | const solAccountResp = await connection.getAccountInfo(owner.publicKey); 52 | const tokenAccountResp = await connection.getTokenAccountsByOwner(owner.publicKey, { programId: TOKEN_PROGRAM_ID }); 53 | const token2022Req = await connection.getTokenAccountsByOwner(owner.publicKey, { programId: TOKEN_2022_PROGRAM_ID }); 54 | const tokenAccountData = parseTokenAccountResp({ 55 | owner: owner.publicKey, 56 | solAccountResp, 57 | tokenAccountResp: { 58 | context: tokenAccountResp.context, 59 | value: [...tokenAccountResp.value, ...token2022Req.value], 60 | }, 61 | }); 62 | ``` 63 | 64 | #### data after initialization 65 | 66 | ``` 67 | # token 68 | raydium.token.tokenList 69 | raydium.token.tokenMap 70 | raydium.token.mintGroup 71 | 72 | 73 | # token account 74 | raydium.account.tokenAccounts 75 | raydium.account.tokenAccountRawInfos 76 | ``` 77 | 78 | #### Api methods (https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/api/api.ts) 79 | 80 | - fetch raydium default mint list (mainnet only) 81 | 82 | ```javascript 83 | const data = await raydium.api.getTokenList(); 84 | ``` 85 | 86 | - fetch mints recognizable by raydium 87 | 88 | ```javascript 89 | const data = await raydium.api.getTokenInfo([ 90 | "So11111111111111111111111111111111111111112", 91 | "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R", 92 | ]); 93 | ``` 94 | 95 | - fetch pool list (mainnet only) 96 | available fetch params defined here: https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/api/type.ts#L249 97 | 98 | ```javascript 99 | const data = await raydium.api.getPoolList({}); 100 | ``` 101 | 102 | - fetch poolInfo by id (mainnet only) 103 | 104 | ```javascript 105 | // ids: join pool ids by comma(,) 106 | const data = await raydium.api.fetchPoolById({ 107 | ids: "AVs9TA4nWDzfPJE9gGVNJMVhcQy3V9PGazuz33BfG2RA,8sLbNZoA1cfnvMJLPfp98ZLAnFSYCFApfJKMbiXNLwxj", 108 | }); 109 | ``` 110 | 111 | - fetch pool list by mints (mainnet only) 112 | 113 | ```javascript 114 | const data = await raydium.api.fetchPoolByMints({ 115 | mint1: "So11111111111111111111111111111111111111112", 116 | mint2: "4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R", // optional, 117 | // extra params: https://github.com/raydium-io/raydium-sdk-V2/blob/master/src/api/type.ts#L249 118 | }); 119 | ``` 120 | 121 | - fetch farmInfo by id (mainnet only) 122 | 123 | ```javascript 124 | // ids: join farm ids by comma(,) 125 | const data = await raydium.api.fetchFarmInfoById({ 126 | ids: "4EwbZo8BZXP5313z5A2H11MRBP15M5n6YxfmkjXESKAW,HUDr9BDaAGqi37xbQHzxCyXvfMCKPTPNF8g9c9bPu1Fu", 127 | }); 128 | ``` 129 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@raydium-io/raydium-sdk-v2", 3 | "version": "0.1.138-alpha", 4 | "description": "An SDK for building applications on top of Raydium.", 5 | "license": "GPL-3.0", 6 | "main": "./lib/index.js", 7 | "module": "./lib/index.mjs", 8 | "types": "./lib/index.d.ts", 9 | "files": [ 10 | "./lib", 11 | "./src" 12 | ], 13 | "repository": "https://github.com/raydium-io/raydium-sdk-V2", 14 | "publicConfig": { 15 | "registry": "https://registry.yarnpkg.com", 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "raydium", 20 | "solana" 21 | ], 22 | "lint-staged": { 23 | "{src,test,misc}/**/*.ts": [ 24 | "yarn lint" 25 | ] 26 | }, 27 | "scripts": { 28 | "lint": "eslint src/**/*.ts", 29 | "test": "jest", 30 | "build-docs": "typedoc", 31 | "build-docs-watch": "typedoc --watch", 32 | "build-dist": "rm -rf dist && mkdir dist && yarn build-docs", 33 | "build-lib": "rm -rf lib && tsup src --format cjs,esm --dts --sourcemap --no-splitting --minify -d lib --silent", 34 | "build-all": "yarn build-dist && yarn build-lib", 35 | "build": "yarn build-lib", 36 | "push": "yarn build && yarn publish", 37 | "prepare": "husky install", 38 | "dev": "ts-node -r tsconfig-paths/register", 39 | "watch": "rm -rf lib && tsup src --watch --format cjs,esm --dts --sourcemap --no-splitting --minify -d lib" 40 | }, 41 | "dependencies": { 42 | "@solana/buffer-layout": "^4.0.1", 43 | "@solana/spl-token": "^0.4.8", 44 | "@solana/web3.js": "^1.95.3", 45 | "axios": "^1.1.3", 46 | "big.js": "^6.2.1", 47 | "bn.js": "^5.2.1", 48 | "dayjs": "^1.11.5", 49 | "decimal.js-light": "^2.5.1", 50 | "jsonfile": "^6.1.0", 51 | "lodash": "^4.17.21", 52 | "toformat": "^2.0.0", 53 | "tsconfig-paths": "^4.2.0" 54 | }, 55 | "devDependencies": { 56 | "@babel/core": "^7.18.0", 57 | "@babel/preset-env": "^7.18.0", 58 | "@babel/preset-typescript": "^7.17.12", 59 | "@tsconfig/node14": "^1.0.3", 60 | "@types/big.js": "^6.1.3", 61 | "@types/bn.js": "^5.1.1", 62 | "@types/jest": "^27.5.1", 63 | "@types/lodash": "^4.14.182", 64 | "@types/node": "^17.0.35", 65 | "@typescript-eslint/eslint-plugin": "^5.25.0", 66 | "@typescript-eslint/parser": "^5.25.0", 67 | "babel-jest": "^27.5.1", 68 | "eslint": "^8.15.0", 69 | "eslint-plugin-tsdoc": "^0.2.16", 70 | "husky": "^7.0.4", 71 | "jest": "^27.5.1", 72 | "lint-staged": "^12.4.1", 73 | "prettier": "^2.6.2", 74 | "ts-jest": "^27.0.7", 75 | "ts-jest-resolver": "^2.0.0", 76 | "ts-node": "^10.7.0", 77 | "tsup": "^5.12.8", 78 | "typedoc": "^0.22.15", 79 | "typescript": "^4.7.3", 80 | "yarn": "^1.22.19" 81 | } 82 | } -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | export * from "./type"; 3 | export * from "./url"; 4 | export * from "./utils"; 5 | -------------------------------------------------------------------------------- /src/api/url.ts: -------------------------------------------------------------------------------- 1 | export const API_URLS = { 2 | BASE_HOST: "https://api-v3.raydium.io", 3 | OWNER_BASE_HOST: "https://owner-v1.raydium.io", 4 | SERVICE_BASE_HOST: "https://service.raydium.io", 5 | MONITOR_BASE_HOST: "https://monitor.raydium.io", 6 | SERVICE_1_BASE_HOST: "https://service-v1.raydium.io", 7 | 8 | SEND_TRANSACTION: "/send-transaction", 9 | FARM_ARP: "/main/farm/info", 10 | FARM_ARP_LINE: "/main/farm-apr-tv", 11 | 12 | CLMM_CONFIG: "/main/clmm-config", 13 | CPMM_CONFIG: "/main/cpmm-config", 14 | 15 | VERSION: "/main/version", 16 | 17 | // api v3 18 | CHECK_AVAILABILITY: "/v3/main/AvailabilityCheckAPI", 19 | RPCS: "/main/rpcs", 20 | INFO: "/main/info", 21 | STAKE_POOLS: "/main/stake-pools", 22 | CHAIN_TIME: "/main/chain-time", 23 | 24 | TOKEN_LIST: "/mint/list", 25 | MINT_INFO_ID: "/mint/ids", 26 | 27 | JUP_TOKEN_LIST: "https://lite-api.jup.ag/tokens/v1/tagged/verified", 28 | /** 29 | * poolType: {all, concentrated, standard, allFarm, concentratedFarm, standardFarm} 30 | * poolSortField: {liquidity | volume_24h / 7d / 30d | fee_24h / 7d / 30d | apr_24h / 7d / 30d} 31 | * sortType: {desc/asc} 32 | * page: number 33 | * pageSize: number 34 | */ 35 | POOL_LIST: "/pools/info/list", 36 | /** 37 | * ?ids=idList.join(',') 38 | */ 39 | POOL_SEARCH_BY_ID: "/pools/info/ids", 40 | /** 41 | * mint1/mint2: search pool by mint 42 | * poolSortField: {liquidity | volume_24h / 7d / 30d | fee_24h / 7d / 30d | apr_24h / 7d / 30d} 43 | * poolType: {all, concentrated, standard, allFarm, concentratedFarm, standardFarm} 44 | * sortType: {desc/asc} 45 | * page: number 46 | * pageSize: number 47 | */ 48 | POOL_SEARCH_MINT: "/pools/info/mint", 49 | /** ?lps=lpList.join(',') */ 50 | POOL_SEARCH_LP: "/pools/info/lps", 51 | /** ?ids=idList.join(',') */ 52 | POOL_KEY_BY_ID: "/pools/key/ids", 53 | /** ?id=string */ 54 | POOL_LIQUIDITY_LINE: "/pools/line/liquidity", 55 | POOL_POSITION_LINE: "/pools/line/position", 56 | 57 | FARM_INFO: "/farms/info/ids", 58 | /** ?lp=string&pageSize=100&page=number */ 59 | FARM_LP_INFO: "/farms/info/lp", 60 | FARM_KEYS: "/farms/key/ids", 61 | 62 | OWNER_CREATED_FARM: "/create-pool/{owner}", 63 | OWNER_IDO: "/main/ido/{owner}", 64 | OWNER_STAKE_FARMS: "/position/stake/{owner}", 65 | OWNER_LOCK_POSITION: "/position/clmm-lock/{owner}", 66 | IDO_KEYS: "/ido/key/ids", 67 | SWAP_HOST: "https://transaction-v1.raydium.io", 68 | SWAP_COMPUTE: "/compute/", 69 | SWAP_TX: "/transaction/", 70 | MINT_PRICE: "/mint/price", 71 | MIGRATE_CONFIG: "/main/migrate-lp", 72 | PRIORITY_FEE: "/main/auto-fee", 73 | 74 | CPMM_LOCK: "https://dynamic-ipfs.raydium.io/lock/cpmm/position", 75 | }; 76 | 77 | export const DEV_API_URLS = { 78 | ...API_URLS, 79 | }; 80 | 81 | export type API_URL_CONFIG = Partial; 82 | -------------------------------------------------------------------------------- /src/api/utils.ts: -------------------------------------------------------------------------------- 1 | export const SESSION_KEY = "ray_tab_hash"; 2 | export const STORAGE_KEY = "ray_req_hash"; 3 | 4 | export const getSessionKey = (): string => { 5 | if (typeof window === undefined) return ""; 6 | let key = sessionStorage.getItem(SESSION_KEY); 7 | 8 | // new a session key 9 | if (!key) { 10 | key = `ray-${Date.now()}`; 11 | sessionStorage.setItem(SESSION_KEY, key); 12 | } 13 | return key; 14 | }; 15 | 16 | export interface ResHistory { 17 | status: number; 18 | url: string; 19 | params?: any; 20 | data: any; 21 | logCount?: number; 22 | time: number; 23 | session: string; 24 | removeLastLog?: boolean; 25 | } 26 | 27 | export const updateReqHistory = async ({ 28 | logCount = 1000, 29 | removeLastLog, 30 | ...resData 31 | }: Omit): Promise => { 32 | if (typeof window === undefined) return new Promise((resolve) => resolve()); 33 | const data: ResHistory[] = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]").slice(0, logCount - 1); 34 | 35 | // means retry last save error 36 | if (removeLastLog) data.pop(); 37 | 38 | // if data > 1kb 39 | if (new Blob([JSON.stringify(resData.data)]).size > 1024) 40 | resData.data = JSON.stringify(resData.data).substring(0, 200) + "..."; 41 | data.unshift({ ...resData, time: Date.now(), session: getSessionKey() }); 42 | 43 | try { 44 | localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); 45 | } catch { 46 | // if retry failed, empty request data 47 | if (removeLastLog) { 48 | let success = false; 49 | const resStr = JSON.stringify(resData.data).substring(0, 100); 50 | data[0].data = resStr + (resStr.length > 100 ? "..." : ""); 51 | while (!success) { 52 | data.pop(); 53 | const resStr = JSON.stringify(resData.data).substring(0, 100); 54 | data[0].data = resStr + (resStr.length > 100 ? "..." : ""); 55 | try { 56 | localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); 57 | success = true; 58 | } catch { 59 | success = false; 60 | } 61 | } 62 | return new Promise((resolve) => resolve()); 63 | } 64 | return updateReqHistory({ 65 | ...resData, 66 | logCount, 67 | removeLastLog: true, 68 | }); 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /src/common/accountInfo.ts: -------------------------------------------------------------------------------- 1 | import { AccountInfo, Commitment, Connection, PublicKey } from "@solana/web3.js"; 2 | import { ReturnTypeFetchMultipleMintInfos } from "../raydium/type"; 3 | import { WSOLMint, chunkArray, solToWSol } from "./"; 4 | import { createLogger } from "./logger"; 5 | import { MINT_SIZE, TOKEN_PROGRAM_ID, getTransferFeeConfig, unpackMint } from "@solana/spl-token"; 6 | 7 | interface MultipleAccountsJsonRpcResponse { 8 | jsonrpc: string; 9 | id: string; 10 | error?: { 11 | code: number; 12 | message: string; 13 | }; 14 | result: { 15 | context: { slot: number }; 16 | value: { data: Array; executable: boolean; lamports: number; owner: string; rentEpoch: number }[]; 17 | }; 18 | } 19 | 20 | export interface GetMultipleAccountsInfoConfig { 21 | batchRequest?: boolean; 22 | commitment?: Commitment; 23 | chunkCount?: number; 24 | } 25 | 26 | const logger = createLogger("Raydium_accountInfo_util"); 27 | 28 | export async function getMultipleAccountsInfo( 29 | connection: Connection, 30 | publicKeys: PublicKey[], 31 | config?: GetMultipleAccountsInfoConfig, 32 | ): Promise<(AccountInfo | null)[]> { 33 | const { 34 | batchRequest, 35 | commitment = "confirmed", 36 | chunkCount = 100, 37 | } = { 38 | batchRequest: false, 39 | ...config, 40 | }; 41 | 42 | const chunkedKeys = chunkArray(publicKeys, chunkCount); 43 | let results: (AccountInfo | null)[][] = new Array(chunkedKeys.length).fill([]); 44 | 45 | if (batchRequest) { 46 | const batch = chunkedKeys.map((keys) => { 47 | const args = connection._buildArgs([keys.map((key) => key.toBase58())], commitment, "base64"); 48 | return { 49 | methodName: "getMultipleAccounts", 50 | args, 51 | }; 52 | }); 53 | 54 | const _batch = chunkArray(batch, 10); 55 | 56 | const unsafeResponse: MultipleAccountsJsonRpcResponse[] = await ( 57 | await Promise.all(_batch.map(async (i) => await (connection as any)._rpcBatchRequest(i))) 58 | ).flat(); 59 | results = unsafeResponse.map((unsafeRes: MultipleAccountsJsonRpcResponse) => { 60 | if (unsafeRes.error) 61 | logger.logWithError(`failed to get info for multiple accounts, RPC_ERROR, ${unsafeRes.error.message}`); 62 | 63 | return unsafeRes.result.value.map((accountInfo) => { 64 | if (accountInfo) { 65 | const { data, executable, lamports, owner, rentEpoch } = accountInfo; 66 | 67 | if (data.length !== 2 && data[1] !== "base64") logger.logWithError(`info must be base64 encoded, RPC_ERROR`); 68 | 69 | return { 70 | data: Buffer.from(data[0], "base64"), 71 | executable, 72 | lamports, 73 | owner: new PublicKey(owner), 74 | rentEpoch, 75 | }; 76 | } 77 | return null; 78 | }); 79 | }); 80 | } else { 81 | try { 82 | results = (await Promise.all( 83 | chunkedKeys.map((keys) => connection.getMultipleAccountsInfo(keys, commitment)), 84 | )) as (AccountInfo | null)[][]; 85 | } catch (error) { 86 | if (error instanceof Error) { 87 | logger.logWithError(`failed to get info for multiple accounts, RPC_ERROR, ${error.message}`); 88 | } 89 | } 90 | } 91 | 92 | return results.flat(); 93 | } 94 | 95 | export async function getMultipleAccountsInfoWithCustomFlags( 96 | connection: Connection, 97 | publicKeysWithCustomFlag: T[], 98 | config?: GetMultipleAccountsInfoConfig, 99 | ): Promise<({ accountInfo: AccountInfo | null } & T)[]> { 100 | const multipleAccountsInfo = await getMultipleAccountsInfo( 101 | connection, 102 | publicKeysWithCustomFlag.map((o) => o.pubkey), 103 | config, 104 | ); 105 | 106 | return publicKeysWithCustomFlag.map((o, idx) => ({ ...o, accountInfo: multipleAccountsInfo[idx] })); 107 | } 108 | 109 | export enum AccountType { 110 | Uninitialized, 111 | Mint, 112 | Account, 113 | } 114 | export const ACCOUNT_TYPE_SIZE = 1; 115 | 116 | export async function fetchMultipleMintInfos({ 117 | connection, 118 | mints, 119 | config, 120 | }: { 121 | connection: Connection; 122 | mints: PublicKey[]; 123 | config?: { batchRequest?: boolean }; 124 | }): Promise { 125 | if (mints.length === 0) return {}; 126 | const mintInfos = await getMultipleAccountsInfoWithCustomFlags( 127 | connection, 128 | mints.map((i) => ({ pubkey: solToWSol(i) })), 129 | config, 130 | ); 131 | 132 | const mintK: ReturnTypeFetchMultipleMintInfos = {}; 133 | for (const i of mintInfos) { 134 | if (!i.accountInfo || i.accountInfo.data.length < MINT_SIZE) { 135 | console.log("invalid mint account", i.pubkey.toBase58()); 136 | continue; 137 | } 138 | const t = unpackMint(i.pubkey, i.accountInfo, i.accountInfo?.owner); 139 | mintK[i.pubkey.toString()] = { 140 | ...t, 141 | programId: i.accountInfo?.owner || TOKEN_PROGRAM_ID, 142 | feeConfig: getTransferFeeConfig(t) ?? undefined, 143 | }; 144 | } 145 | mintK[PublicKey.default.toBase58()] = mintK[WSOLMint.toBase58()]; 146 | 147 | return mintK; 148 | } 149 | -------------------------------------------------------------------------------- /src/common/bignumber.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import Decimal from "decimal.js"; 3 | import { CurrencyAmount, TokenAmount } from "../module/amount"; 4 | import { Currency } from "../module/currency"; 5 | import { Fraction } from "../module/fraction"; 6 | import { Percent } from "../module/percent"; 7 | import { Price } from "../module/price"; 8 | import { Token } from "../module/token"; 9 | import { SplToken, TokenJson } from "../raydium/token/type"; 10 | import { ReplaceType } from "../raydium/type"; 11 | import { parseBigNumberish } from "./constant"; 12 | import { mul } from "./fractionUtil"; 13 | import { notInnerObject } from "./utility"; 14 | 15 | export const BN_ZERO = new BN(0); 16 | export const BN_ONE = new BN(1); 17 | export const BN_TWO = new BN(2); 18 | export const BN_THREE = new BN(3); 19 | export const BN_FIVE = new BN(5); 20 | export const BN_TEN = new BN(10); 21 | export const BN_100 = new BN(100); 22 | export const BN_1000 = new BN(1000); 23 | export const BN_10000 = new BN(10000); 24 | export type BigNumberish = BN | string | number | bigint; 25 | export type Numberish = number | string | bigint | Fraction | BN; 26 | 27 | export function tenExponential(shift: BigNumberish): BN { 28 | return BN_TEN.pow(parseBigNumberish(shift)); 29 | } 30 | 31 | /** 32 | * 33 | * @example 34 | * getIntInfo(0.34) => { numerator: '34', denominator: '100'} 35 | * getIntInfo('0.34') //=> { numerator: '34', denominator: '100'} 36 | */ 37 | export function parseNumberInfo(n: Numberish | undefined): { 38 | denominator: string; 39 | numerator: string; 40 | sign?: string; 41 | int?: string; 42 | dec?: string; 43 | } { 44 | if (n === undefined) return { denominator: "1", numerator: "0" }; 45 | if (n instanceof BN) { 46 | return { numerator: n.toString(), denominator: "1" }; 47 | } 48 | 49 | if (n instanceof Fraction) { 50 | return { denominator: n.denominator.toString(), numerator: n.numerator.toString() }; 51 | } 52 | 53 | const s = String(n); 54 | const [, sign = "", int = "", dec = ""] = s.replace(",", "").match(/(-?)(\d*)\.?(\d*)/) ?? []; 55 | const denominator = "1" + "0".repeat(dec.length); 56 | const numerator = sign + (int === "0" ? "" : int) + dec || "0"; 57 | return { denominator, numerator, sign, int, dec }; 58 | } 59 | 60 | // round up 61 | export function divCeil(a: BN, b: BN): BN { 62 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 63 | // @ts-ignore 64 | const dm = a.divmod(b); 65 | 66 | // Fast case - exact division 67 | if (dm.mod.isZero()) return dm.div; 68 | 69 | // Round up 70 | return dm.div.isNeg() ? dm.div.isubn(1) : dm.div.iaddn(1); 71 | } 72 | 73 | export function shakeFractionDecimal(n: Fraction): string { 74 | const [, sign = "", int = ""] = n.toFixed(2).match(/(-?)(\d*)\.?(\d*)/) ?? []; 75 | return `${sign}${int}`; 76 | } 77 | 78 | export function toBN(n: Numberish, decimal: BigNumberish = 0): BN { 79 | if (n instanceof BN) return n; 80 | return new BN(shakeFractionDecimal(toFraction(n).mul(BN_TEN.pow(new BN(String(decimal)))))); 81 | } 82 | 83 | export function toFraction(value: Numberish): Fraction { 84 | // to complete math format(may have decimal), not int 85 | if (value instanceof Percent) return new Fraction(value.numerator, value.denominator); 86 | 87 | if (value instanceof Price) return value.adjusted; 88 | 89 | // to complete math format(may have decimal), not BN 90 | if (value instanceof TokenAmount) 91 | try { 92 | return toFraction(value.toExact()); 93 | } catch { 94 | return new Fraction(BN_ZERO); 95 | } 96 | 97 | // do not ideal with other fraction value 98 | if (value instanceof Fraction) return value; 99 | 100 | // wrap to Fraction 101 | const n = String(value); 102 | const details = parseNumberInfo(n); 103 | return new Fraction(details.numerator, details.denominator); 104 | } 105 | 106 | export function ceilDiv(tokenAmount: BN, feeNumerator: BN, feeDenominator: BN): BN { 107 | return tokenAmount.mul(feeNumerator).add(feeDenominator).sub(new BN(1)).div(feeDenominator); 108 | } 109 | 110 | export function floorDiv(tokenAmount: BN, feeNumerator: BN, feeDenominator: BN): BN { 111 | return tokenAmount.mul(feeNumerator).div(feeDenominator); 112 | } 113 | 114 | /** 115 | * @example 116 | * toPercent(3.14) // => Percent { 314.00% } 117 | * toPercent(3.14, { alreadyDecimaled: true }) // => Percent {3.14%} 118 | */ 119 | export function toPercent( 120 | n: Numberish, 121 | options?: { /* usually used for backend data */ alreadyDecimaled?: boolean }, 122 | ): Percent { 123 | const { numerator, denominator } = parseNumberInfo(n); 124 | return new Percent(new BN(numerator), new BN(denominator).mul(options?.alreadyDecimaled ? new BN(100) : new BN(1))); 125 | } 126 | 127 | export function toTokenPrice(params: { 128 | token: TokenJson | Token | SplToken; 129 | numberPrice: Numberish; 130 | decimalDone?: boolean; 131 | }): Price { 132 | const { token, numberPrice, decimalDone } = params; 133 | const usdCurrency = new Token({ mint: "", decimals: 6, symbol: "usd", name: "usd", skipMint: true }); 134 | const { numerator, denominator } = parseNumberInfo(numberPrice); 135 | const parsedNumerator = decimalDone ? new BN(numerator).mul(BN_TEN.pow(new BN(token.decimals))) : numerator; 136 | const parsedDenominator = new BN(denominator).mul(BN_TEN.pow(new BN(usdCurrency.decimals))); 137 | 138 | return new Price({ 139 | baseToken: usdCurrency, 140 | denominator: parsedDenominator.toString(), 141 | quoteToken: new Token({ ...token, skipMint: true, mint: "" }), 142 | numerator: parsedNumerator.toString(), 143 | }); 144 | } 145 | 146 | export function toUsdCurrency(amount: Numberish): CurrencyAmount { 147 | const usdCurrency = new Currency({ decimals: 6, symbol: "usd", name: "usd" }); 148 | const amountBigNumber = toBN(mul(amount, 10 ** usdCurrency.decimals)!); 149 | return new CurrencyAmount(usdCurrency, amountBigNumber); 150 | } 151 | 152 | export function toTotalPrice(amount: Numberish | undefined, price: Price | undefined): CurrencyAmount { 153 | if (!price || !amount) return toUsdCurrency(0); 154 | return toUsdCurrency(mul(amount, price)!); 155 | } 156 | 157 | export function decimalToFraction(n: Decimal | undefined): Fraction | undefined { 158 | if (n == null) return undefined; 159 | const { numerator, denominator } = parseNumberInfo(n.toString()); 160 | return new Fraction(numerator, denominator); 161 | } 162 | 163 | export function isDecimal(val: unknown): boolean { 164 | return val instanceof Decimal; 165 | } 166 | 167 | export function recursivelyDecimalToFraction(info: T): ReplaceType { 168 | // @ts-expect-error no need type for inner code 169 | return isDecimal(info) 170 | ? decimalToFraction(info as any) 171 | : Array.isArray(info) 172 | ? info.map((k) => recursivelyDecimalToFraction(k)) 173 | : notInnerObject(info) 174 | ? Object.fromEntries(Object.entries(info as any).map(([k, v]) => [k, recursivelyDecimalToFraction(v)])) 175 | : info; 176 | } 177 | -------------------------------------------------------------------------------- /src/common/constant.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { BigNumberish } from "./bignumber"; 3 | import { createLogger } from "./logger"; 4 | 5 | export enum Rounding { 6 | ROUND_DOWN, 7 | ROUND_HALF_UP, 8 | ROUND_UP, 9 | } 10 | 11 | const MAX_SAFE = 0x1fffffffffffff; 12 | 13 | export function parseBigNumberish(value: BigNumberish): BN { 14 | const logger = createLogger("Raydium_parseBigNumberish"); 15 | // BN 16 | if (value instanceof BN) { 17 | return value; 18 | } 19 | 20 | if (typeof value === "string") { 21 | if (value.match(/^-?[0-9]+$/)) { 22 | return new BN(value); 23 | } 24 | logger.logWithError(`invalid BigNumberish string: ${value}`); 25 | } 26 | 27 | if (typeof value === "number") { 28 | if (value % 1) { 29 | logger.logWithError(`BigNumberish number underflow: ${value}`); 30 | } 31 | 32 | if (value >= MAX_SAFE || value <= -MAX_SAFE) { 33 | logger.logWithError(`BigNumberish number overflow: ${value}`); 34 | } 35 | 36 | return new BN(String(value)); 37 | } 38 | 39 | if (typeof value === "bigint") { 40 | return new BN(value.toString()); 41 | } 42 | logger.error(`invalid BigNumberish value: ${value}`); 43 | return new BN(0); // never reach, because logWithError will throw error 44 | } -------------------------------------------------------------------------------- /src/common/date.ts: -------------------------------------------------------------------------------- 1 | export type TimeStamp = string | number | Date; 2 | 3 | export const isNumber = (val): boolean => typeof val === "number"; 4 | export type DateParam = string | number | Date | undefined; 5 | 6 | export const getDate = (value?: DateParam): Date => (value ? new Date(value) : new Date()); 7 | export const getTime = (value?: DateParam): number => getDate(value).getTime(); 8 | 9 | /** A must be milliseconds */ 10 | export function isDateBefore(timestampA: TimeStamp, timestampB: TimeStamp, options?: { unit?: "ms" | "s" }): boolean { 11 | const realTimestampB = isNumber(timestampB) 12 | ? (timestampB as number) * (options?.unit === "s" ? 1000 : 1) 13 | : timestampB; 14 | return new Date(timestampA).getTime() <= realTimestampB; 15 | } 16 | 17 | /** A must be milliseconds */ 18 | export function isDateAfter(timestampA: TimeStamp, timestampB: TimeStamp, options?: { unit?: "ms" | "s" }): boolean { 19 | const realTimestampB = isNumber(timestampB) 20 | ? (timestampB as number) * (options?.unit === "s" ? 1000 : 1) 21 | : timestampB; 22 | return new Date(timestampA).getTime() > realTimestampB; 23 | } 24 | 25 | export function offsetDateTime( 26 | baseDate: DateParam, 27 | offset: { 28 | days?: number; 29 | hours?: number; 30 | minutes?: number; 31 | seconds?: number; 32 | milliseconds?: number; 33 | }, 34 | ): Date { 35 | const timestamp = getTime(baseDate); 36 | const offsetedTimestamp = 37 | timestamp + 38 | (offset.days ? offset.days * 24 * 60 * 60 * 1000 : 0) + 39 | (offset.hours ? offset.hours * 60 * 60 * 1000 : 0) + 40 | (offset.minutes ? offset.minutes * 60 * 1000 : 0) + 41 | (offset.seconds ? offset.seconds * 1000 : 0) + 42 | (offset.milliseconds ? offset.milliseconds : 0); 43 | return getDate(offsetedTimestamp); 44 | } 45 | -------------------------------------------------------------------------------- /src/common/error.ts: -------------------------------------------------------------------------------- 1 | export const EMPTY_OWNER = 2 | "please provide owner in load() initialization or you can set by calling raydium.setOwner(owner)"; 3 | 4 | export const EMPTY_CONNECTION = 5 | "please provide connection in load() initialization or set it by raydium.setConnection(connection)"; 6 | -------------------------------------------------------------------------------- /src/common/fee.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | export const FEE_RATE_DENOMINATOR_VALUE = new BN(1_000_000); 3 | -------------------------------------------------------------------------------- /src/common/fractionUtil.ts: -------------------------------------------------------------------------------- 1 | import { Fraction, Percent, Price, TokenAmount } from "../module"; 2 | 3 | import { BN_ZERO, Numberish, parseNumberInfo } from "./bignumber"; 4 | 5 | export default function toFraction(value: Numberish): Fraction { 6 | // to complete math format(may have decimal), not int 7 | if (value instanceof Percent) return new Fraction(value.numerator, value.denominator); 8 | 9 | if (value instanceof Price) return value.adjusted; 10 | 11 | // to complete math format(may have decimal), not BN 12 | if (value instanceof TokenAmount) 13 | try { 14 | return toFraction(value.toExact()); 15 | } catch { 16 | return new Fraction(BN_ZERO); 17 | } 18 | 19 | // do not ideal with other fraction value 20 | if (value instanceof Fraction) return value; 21 | 22 | // wrap to Fraction 23 | const n = String(value); 24 | const details = parseNumberInfo(n); 25 | return new Fraction(details.numerator, details.denominator); 26 | } 27 | 28 | export function toFractionWithDecimals(value: Numberish): { fr: Fraction; decimals?: number } { 29 | // to complete math format(may have decimal), not int 30 | if (value instanceof Percent) return { fr: new Fraction(value.numerator, value.denominator) }; 31 | 32 | if (value instanceof Price) return { fr: value.adjusted }; 33 | 34 | // to complete math format(may have decimal), not BN 35 | if (value instanceof TokenAmount) return { fr: toFraction(value.toExact()), decimals: value.token.decimals }; 36 | 37 | // do not ideal with other fraction value 38 | if (value instanceof Fraction) return { fr: value }; 39 | 40 | // wrap to Fraction 41 | const n = String(value); 42 | const details = parseNumberInfo(n); 43 | return { fr: new Fraction(details.numerator, details.denominator), decimals: details.dec?.length }; 44 | } 45 | 46 | export function lt(a: Numberish | undefined, b: Numberish | undefined): boolean { 47 | if (a == null || b == null) return false; 48 | const fa = toFraction(a); 49 | const fb = toFraction(b); 50 | fa.sub(fb).numerator; 51 | return fa.sub(fb).numerator.lt(BN_ZERO); 52 | } 53 | 54 | export function gt(a: Numberish | undefined, b: Numberish | undefined): boolean { 55 | if (a == null || b == null) return false; 56 | const fa = toFraction(a); 57 | const fb = toFraction(b); 58 | return fa.sub(fb).numerator.gt(BN_ZERO); 59 | } 60 | 61 | export function lte(a: Numberish | undefined, b: Numberish | undefined): boolean { 62 | if (a == null || b == null) return false; 63 | const fa = toFraction(a); 64 | const fb = toFraction(b); 65 | return fa.sub(fb).numerator.lte(BN_ZERO); 66 | } 67 | 68 | export function gte(a: Numberish | undefined, b: Numberish | undefined): boolean { 69 | if (a == null || b == null) return false; 70 | const fa = toFraction(a); 71 | const fb = toFraction(b); 72 | return fa.sub(fb).numerator.gte(BN_ZERO); 73 | } 74 | 75 | export function eq(a: Numberish | undefined, b: Numberish | undefined): boolean { 76 | if (a == null || b == null) return false; 77 | const fa = toFraction(a); 78 | const fb = toFraction(b); 79 | return fa.sub(fb).numerator.eq(BN_ZERO); 80 | } 81 | 82 | export function div(a: Numberish | undefined, b: Numberish | undefined): Fraction | undefined { 83 | if (a == null || b == null) return undefined; 84 | const fa = toFraction(a); 85 | const fb = toFraction(b); 86 | try { 87 | return fa.div(fb); // if fb is zero , operation will throw error 88 | } catch { 89 | return fa; 90 | } 91 | } 92 | 93 | export function sub(a: Numberish | undefined, b: Numberish | undefined): Fraction | undefined { 94 | if (a == null || b == null) return undefined; 95 | const fa = toFraction(a); 96 | const fb = toFraction(b); 97 | return fa.sub(fb); 98 | } 99 | 100 | export function isMeaningfulNumber(n: Numberish | undefined): n is Numberish { 101 | if (n == null) return false; 102 | return !eq(n, 0); 103 | } 104 | 105 | export function getMax(a: Numberish, b: Numberish): Numberish { 106 | return gt(b, a) ? b : a; 107 | } 108 | 109 | export function mul(a: Numberish | undefined, b: Numberish | undefined): Fraction | undefined { 110 | if (a == null || b == null) return undefined; 111 | const fa = toFraction(a); 112 | const fb = toFraction(b); 113 | return fa.mul(fb); 114 | } 115 | 116 | export function add(a: Numberish | undefined, b: Numberish | undefined): Fraction | undefined { 117 | if (a == null || b == null) return undefined; 118 | const fa = toFraction(a); 119 | const fb = toFraction(b); 120 | return fa.add(fb); 121 | } 122 | -------------------------------------------------------------------------------- /src/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./accountInfo"; 2 | export * from "./bignumber"; 3 | export * from "./constant"; 4 | export * from "./date"; 5 | export * from "./fractionUtil"; 6 | export * from "./lodash"; 7 | export * from "./logger"; 8 | export * from "./owner"; 9 | export * from "./pda"; 10 | export * from "./programId"; 11 | export * from "./pubKey"; 12 | export * from "./transfer"; 13 | export * from "./txTool/lookupTable"; 14 | export * from "./txTool/txTool"; 15 | export * from "./txTool/txType"; 16 | export * from "./txTool/txUtils"; 17 | export * from "./utility"; 18 | export * from "./fee"; 19 | -------------------------------------------------------------------------------- /src/common/json-file.ts: -------------------------------------------------------------------------------- 1 | export interface JsonFileMetaData { 2 | readonly name: string; 3 | readonly timestamp: string; 4 | readonly version: { 5 | major: number; 6 | minor: number; 7 | patch: number; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /src/common/lodash.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * https://youmightnotneed.com/lodash/ 3 | */ 4 | 5 | export function chunkArray(arr: T[], chunkSize = 1, cache: T[][] = []): T[][] { 6 | const tmp = [...arr]; 7 | if (chunkSize <= 0) return cache; 8 | while (tmp.length) cache.push(tmp.splice(0, chunkSize)); 9 | return cache; 10 | } 11 | 12 | export function intersection(arr: T[], ...args: T[][]): T[] { 13 | return arr.filter((item) => args.every((arr) => arr.includes(item))); 14 | } 15 | 16 | export function xor(arr: T[], ...args: T[][]): T[] { 17 | return arr.filter((item) => args.every((arr) => !arr.includes(item))); 18 | } 19 | 20 | export function uniq(arr: T[]): T[] { 21 | return [...new Set(arr)]; 22 | } 23 | -------------------------------------------------------------------------------- /src/common/logger.ts: -------------------------------------------------------------------------------- 1 | import { get, set } from "lodash"; 2 | 3 | export type ModuleName = "Common.Api"; 4 | 5 | export enum LogLevel { 6 | Error, 7 | Warning, 8 | Info, 9 | Debug, 10 | } 11 | export class Logger { 12 | private logLevel: LogLevel; 13 | private name: string; 14 | constructor(params: { name: string; logLevel?: LogLevel }) { 15 | this.logLevel = params.logLevel !== undefined ? params.logLevel : LogLevel.Error; 16 | this.name = params.name; 17 | } 18 | 19 | set level(logLevel: LogLevel) { 20 | this.logLevel = logLevel; 21 | } 22 | get time(): string { 23 | return Date.now().toString(); 24 | } 25 | get moduleName(): string { 26 | return this.name; 27 | } 28 | 29 | private isLogLevel(level: LogLevel): boolean { 30 | return level <= this.logLevel; 31 | } 32 | 33 | public error(...props): Logger { 34 | if (!this.isLogLevel(LogLevel.Error)) return this; 35 | console.error(this.time, this.name, "sdk logger error", ...props); 36 | return this; 37 | } 38 | 39 | public logWithError(...props): Logger { 40 | // this.error(...props) 41 | const msg = props.map((arg) => (typeof arg === "object" ? JSON.stringify(arg) : arg)).join(", "); 42 | throw new Error(msg); 43 | } 44 | 45 | public warning(...props): Logger { 46 | if (!this.isLogLevel(LogLevel.Warning)) return this; 47 | console.warn(this.time, this.name, "sdk logger warning", ...props); 48 | return this; 49 | } 50 | 51 | public info(...props): Logger { 52 | if (!this.isLogLevel(LogLevel.Info)) return this; 53 | console.info(this.time, this.name, "sdk logger info", ...props); 54 | return this; 55 | } 56 | 57 | public debug(...props): Logger { 58 | if (!this.isLogLevel(LogLevel.Debug)) return this; 59 | console.debug(this.time, this.name, "sdk logger debug", ...props); 60 | return this; 61 | } 62 | } 63 | 64 | const moduleLoggers: { [key in ModuleName]?: Logger } = {}; 65 | const moduleLevels: { [key in ModuleName]?: LogLevel } = {}; 66 | 67 | export function createLogger(moduleName: string): Logger { 68 | let logger = get(moduleLoggers, moduleName); 69 | if (!logger) { 70 | // default level is error 71 | const logLevel = get(moduleLevels, moduleName); 72 | 73 | logger = new Logger({ name: moduleName, logLevel }); 74 | set(moduleLoggers, moduleName, logger); 75 | } 76 | 77 | return logger; 78 | } 79 | 80 | export function setLoggerLevel(moduleName: string, level: LogLevel): void { 81 | set(moduleLevels, moduleName, level); 82 | 83 | const logger = get(moduleLoggers, moduleName); 84 | if (logger) logger.level = level; 85 | } 86 | -------------------------------------------------------------------------------- /src/common/owner.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey, Signer } from "@solana/web3.js"; 2 | 3 | type _Owner = Keypair | PublicKey; 4 | 5 | export class Owner { 6 | private readonly _owner: _Owner; 7 | 8 | constructor(owner: _Owner) { 9 | this._owner = owner; 10 | } 11 | 12 | get publicKey(): PublicKey { 13 | if (Owner.isKeyPair(this._owner)) { 14 | return this._owner.publicKey; 15 | } 16 | 17 | return this._owner; 18 | } 19 | 20 | get signer(): Signer | undefined { 21 | return Owner.isKeyPair(this._owner) ? this._owner : undefined; 22 | } 23 | 24 | get isKeyPair(): boolean { 25 | return Owner.isKeyPair(this._owner); 26 | } 27 | 28 | get isPublicKey(): boolean { 29 | return Owner.isPublicKey(this._owner); 30 | } 31 | 32 | static isKeyPair(owner: _Owner): owner is Keypair { 33 | return (owner as Keypair).secretKey !== undefined; 34 | } 35 | 36 | static isPublicKey(owner: _Owner): owner is PublicKey { 37 | return !Owner.isKeyPair(owner); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/common/pda.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 4 | import { findProgramAddress } from "./txTool/txUtils"; 5 | 6 | export function getATAAddress( 7 | owner: PublicKey, 8 | mint: PublicKey, 9 | programId?: PublicKey, 10 | ): { 11 | publicKey: PublicKey; 12 | nonce: number; 13 | } { 14 | return findProgramAddress( 15 | [owner.toBuffer(), (programId ?? TOKEN_PROGRAM_ID).toBuffer(), mint.toBuffer()], 16 | new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/common/programId.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | // raydium 4 | export const FARM_PROGRAM_ID_V3 = new PublicKey("EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q"); 5 | // temp fusion 6 | export const FARM_PROGRAM_ID_V4 = new PublicKey("CBuCnLe26faBpcBP2fktp4rp8abpcAnTWft6ZrP5Q4T"); 7 | // "fusion" 8 | export const FARM_PROGRAM_ID_V5 = new PublicKey("9KEPoZmtHUrBbhWN1v1KWLMkkvwY6WLtAVUCPRtRjP4z"); 9 | // echosystem 10 | export const FARM_PROGRAM_ID_V6 = new PublicKey("FarmqiPv5eAj3j1GMdMCMUGXqPUvmquZtMy86QH6rzhG"); 11 | 12 | export const UTIL1216 = new PublicKey("CLaimxFqjHzgTJtAGHU47NPhg6qrc5sCnpC4tBLyABQS"); 13 | 14 | export const OPEN_BOOK_PROGRAM = new PublicKey("srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"); 15 | export const SERUM_PROGRAM_ID_V3 = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); 16 | 17 | export const AMM_V4 = new PublicKey("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); 18 | export const AMM_STABLE = new PublicKey("5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h"); 19 | export const LIQUIDITY_POOL_PROGRAM_ID_V5_MODEL = new PublicKey("CDSr3ssLcRB6XYPJwAfFt18MZvEZp4LjHcvzBVZ45duo"); 20 | export const CLMM_PROGRAM_ID = new PublicKey("CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"); 21 | export const CLMM_LOCK_PROGRAM_ID = new PublicKey("LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"); 22 | export const CLMM_LOCK_AUTH_ID = new PublicKey("kN1kEznaF5Xbd8LYuqtEFcxzWSBk5Fv6ygX6SqEGJVy"); 23 | 24 | export const Router = new PublicKey("routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS"); 25 | export const FEE_DESTINATION_ID = new PublicKey("7YttLkHDoNj9wyDur5pM1ejNaAvT9X4eqaYcHQqtj2G5"); 26 | 27 | export const IDO_PROGRAM_ID_V1 = new PublicKey("6FJon3QE27qgPVggARueB22hLvoh22VzJpXv4rBEoSLF"); 28 | export const IDO_PROGRAM_ID_V2 = new PublicKey("CC12se5To1CdEuw7fDS27B7Geo5jJyL7t5UK2B44NgiH"); 29 | export const IDO_PROGRAM_ID_V3 = new PublicKey("9HzJyW1qZsEiSfMUf6L2jo3CcTKAyBmSyKdwQeYisHrC"); 30 | export const IDO_PROGRAM_ID_V4 = new PublicKey("DropEU8AvevN3UrXWXTMuz3rqnMczQVNjq3kcSdW2SQi"); 31 | 32 | export const CREATE_CPMM_POOL_PROGRAM = new PublicKey("CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"); 33 | export const CREATE_CPMM_POOL_AUTH = new PublicKey("GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL"); 34 | export const CREATE_CPMM_POOL_FEE_ACC = new PublicKey("DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"); 35 | 36 | export const DEV_CREATE_CPMM_POOL_PROGRAM = new PublicKey("CPMDWBwJDtYax9qW7AyRuVC19Cc4L4Vcy4n2BHAbHkCW"); 37 | export const DEV_CREATE_CPMM_POOL_AUTH = new PublicKey("7rQ1QFNosMkUCuh7Z7fPbTHvh73b68sQYdirycEzJVuw"); 38 | export const DEV_CREATE_CPMM_POOL_FEE_ACC = new PublicKey("G11FKBRaAkHAKuLCgLM6K6NUc9rTjPAznRCjZifrTQe2"); 39 | 40 | export const LOCK_CPMM_PROGRAM = new PublicKey("LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"); 41 | export const DEV_LOCK_CPMM_PROGRAM = new PublicKey("DLockwT7X7sxtLmGH9g5kmfcjaBtncdbUmi738m5bvQC"); 42 | 43 | export const LOCK_CPMM_AUTH = new PublicKey("3f7GcQFG397GAaEnv51zR6tsTVihYRydnydDD1cXekxH"); 44 | export const DEV_LOCK_CPMM_AUTH = new PublicKey("7AFUeLVRjBfzqK3tTGw8hN48KLQWSk6DTE8xprWdPqix"); 45 | 46 | export const LAUNCHPAD_PROGRAM = new PublicKey("LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"); 47 | export const LAUNCHPAD_AUTH = new PublicKey("WLHv2UAZm6z4KyaaELi5pjdbJh6RESMva1Rnn8pJVVh"); 48 | 49 | export const DEV_LAUNCHPAD_PROGRAM = new PublicKey("LanD8FpTBBvzZFXjTxsAoipkFsxPUCDB4qAqKxYDiNP"); 50 | export const DEV_LAUNCHPAD_AUTH = new PublicKey("HYNHiyKJ3gGVFvyxJAurK7qr7P2o5J9THmvCGMdULtpW"); 51 | 52 | export const IDO_ALL_PROGRAM = { 53 | IDO_PROGRAM_ID_V1, 54 | IDO_PROGRAM_ID_V2, 55 | IDO_PROGRAM_ID_V3, 56 | IDO_PROGRAM_ID_V4, 57 | }; 58 | 59 | export const ALL_PROGRAM_ID = { 60 | AMM_V4, 61 | AMM_STABLE, 62 | CLMM_PROGRAM_ID, 63 | CLMM_LOCK_PROGRAM_ID, 64 | CLMM_LOCK_AUTH_ID, 65 | 66 | FARM_PROGRAM_ID_V3, 67 | FARM_PROGRAM_ID_V5, 68 | FARM_PROGRAM_ID_V6, 69 | 70 | OPEN_BOOK_PROGRAM, 71 | SERUM_PROGRAM_ID_V3, 72 | 73 | UTIL1216, 74 | 75 | Router, 76 | 77 | CREATE_CPMM_POOL_PROGRAM, 78 | CREATE_CPMM_POOL_AUTH, 79 | CREATE_CPMM_POOL_FEE_ACC, 80 | 81 | LOCK_CPMM_PROGRAM, 82 | LOCK_CPMM_AUTH, 83 | 84 | LAUNCHPAD_PROGRAM, 85 | LAUNCHPAD_AUTH, 86 | }; 87 | 88 | export type ProgramIdConfig = Partial; 89 | 90 | export const DEVNET_PROGRAM_ID = { 91 | SERUM_MARKET: PublicKey.default, 92 | OPENBOOK_MARKET: new PublicKey("EoTcMgcDRTJVZDMZWBoU6rhYHZfkNTVEAfz3uUJRcYGj"), 93 | 94 | UTIL1216: PublicKey.default, 95 | 96 | FarmV3: new PublicKey("85BFyr98MbCUU9MVTEgzx1nbhWACbJqLzho6zd6DZcWL"), 97 | FarmV5: new PublicKey("EcLzTrNg9V7qhcdyXDe2qjtPkiGzDM2UbdRaeaadU5r2"), 98 | FarmV6: new PublicKey("Farm2hJLcqPtPg8M4rR6DMrsRNc5TPm5Cs4bVQrMe2T7"), 99 | 100 | AmmV4: new PublicKey("HWy1jotHpo6UqeQxx49dpYYdQB8wj9Qk9MdxwjLvDHB8"), 101 | AmmStable: new PublicKey("DDg4VmQaJV9ogWce7LpcjBA9bv22wRp5uaTPa5pGjijF"), 102 | 103 | CLMM: new PublicKey("devi51mZmdwUJGU9hjN27vEz64Gps7uUefqxg27EAtH"), 104 | CLMM_LOCK_PROGRAM_ID: new PublicKey("DLockwT7X7sxtLmGH9g5kmfcjaBtncdbUmi738m5bvQC"), 105 | CLMM_LOCK_AUTH_ID: new PublicKey("8qmHNvu2Kr2C7U8mJL4Vz1vTDxMhVuXKREwU7TNoaVEo"), 106 | 107 | Router: new PublicKey("BVChZ3XFEwTMUk1o9i3HAf91H6mFxSwa5X2wFAWhYPhU"), 108 | 109 | CREATE_CPMM_POOL_PROGRAM: DEV_CREATE_CPMM_POOL_PROGRAM, 110 | CREATE_CPMM_POOL_AUTH: DEV_CREATE_CPMM_POOL_AUTH, 111 | CREATE_CPMM_POOL_FEE_ACC: DEV_CREATE_CPMM_POOL_FEE_ACC, 112 | 113 | FEE_DESTINATION_ID: new PublicKey("3XMrhbv989VxAMi3DErLV9eJht1pHppW5LbKxe9fkEFR"), 114 | 115 | LOCK_CPMM_PROGRAM: DEV_LOCK_CPMM_PROGRAM, 116 | LCOK_CPMM_AUTH: DEV_LOCK_CPMM_AUTH, 117 | 118 | LAUNCHPAD_PROGRAM: DEV_LAUNCHPAD_PROGRAM, 119 | LAUNCHPAD_AUTH: DEV_LAUNCHPAD_AUTH, 120 | }; 121 | -------------------------------------------------------------------------------- /src/common/pubKey.ts: -------------------------------------------------------------------------------- 1 | import { AccountMeta, PublicKey, SystemProgram, SYSVAR_RENT_PUBKEY } from "@solana/web3.js"; 2 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 3 | 4 | interface AccountMetaProps { 5 | pubkey: PublicKey; 6 | isSigner?: boolean; 7 | isWritable?: boolean; 8 | } 9 | 10 | export function accountMeta({ pubkey, isSigner = false, isWritable = true }: AccountMetaProps): AccountMeta { 11 | return { 12 | pubkey, 13 | isWritable, 14 | isSigner, 15 | }; 16 | } 17 | 18 | export const commonSystemAccountMeta = [ 19 | accountMeta({ pubkey: TOKEN_PROGRAM_ID, isWritable: false }), 20 | accountMeta({ pubkey: SystemProgram.programId, isWritable: false }), 21 | accountMeta({ pubkey: SYSVAR_RENT_PUBKEY, isWritable: false }), 22 | ]; 23 | 24 | export type PublicKeyish = PublicKey | string; 25 | 26 | export function validateAndParsePublicKey({ 27 | publicKey: orgPubKey, 28 | transformSol, 29 | }: { 30 | publicKey: PublicKeyish; 31 | transformSol?: boolean; 32 | }): PublicKey { 33 | const publicKey = tryParsePublicKey(orgPubKey.toString()); 34 | 35 | if (publicKey instanceof PublicKey) { 36 | if (transformSol && publicKey.equals(SOLMint)) return WSOLMint; 37 | return publicKey; 38 | } 39 | 40 | if (transformSol && publicKey.toString() === SOLMint.toBase58()) return WSOLMint; 41 | 42 | if (typeof publicKey === "string") { 43 | if (publicKey === PublicKey.default.toBase58()) return PublicKey.default; 44 | try { 45 | const key = new PublicKey(publicKey); 46 | return key; 47 | } catch { 48 | throw new Error("invalid public key"); 49 | } 50 | } 51 | 52 | throw new Error("invalid public key"); 53 | } 54 | 55 | export function tryParsePublicKey(v: string): PublicKey | string { 56 | try { 57 | return new PublicKey(v); 58 | } catch (e) { 59 | return v; 60 | } 61 | } 62 | 63 | export const MEMO_PROGRAM_ID = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); 64 | export const MEMO_PROGRAM_ID2 = new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"); 65 | export const RENT_PROGRAM_ID = new PublicKey("SysvarRent111111111111111111111111111111111"); 66 | export const CLOCK_PROGRAM_ID = new PublicKey("SysvarC1ock11111111111111111111111111111111"); 67 | export const METADATA_PROGRAM_ID = new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"); 68 | export const INSTRUCTION_PROGRAM_ID = new PublicKey("Sysvar1nstructions1111111111111111111111111"); 69 | export const SYSTEM_PROGRAM_ID = SystemProgram.programId; 70 | 71 | export const RAYMint = new PublicKey("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R"); 72 | export const PAIMint = new PublicKey("Ea5SjE2Y6yvCeW5dYTn7PYMuW5ikXkvbGdcmSnXeaLjS"); 73 | export const SRMMint = new PublicKey("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); 74 | export const USDCMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); 75 | export const USDTMint = new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"); 76 | export const mSOLMint = new PublicKey("mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So"); 77 | export const stSOLMint = new PublicKey("7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj"); 78 | export const USDHMint = new PublicKey("USDH1SM1ojwWUga67PGrgFWUHibbjqMvuMaDkRJTgkX"); 79 | export const NRVMint = new PublicKey("NRVwhjBQiUPYtfDT5zRBVJajzFQHaBUNtC7SNVvqRFa"); 80 | export const ANAMint = new PublicKey("ANAxByE6G2WjFp7A4NqtWYXb3mgruyzZYg3spfxe6Lbo"); 81 | export const ETHMint = new PublicKey("7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"); 82 | export const WSOLMint = new PublicKey("So11111111111111111111111111111111111111112"); 83 | export const SOLMint = PublicKey.default; 84 | 85 | export function solToWSol(mint: PublicKeyish): PublicKey { 86 | return validateAndParsePublicKey({ publicKey: mint, transformSol: true }); 87 | } 88 | -------------------------------------------------------------------------------- /src/common/transfer.ts: -------------------------------------------------------------------------------- 1 | import { EpochInfo } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import { TransferFee, TransferFeeConfig } from "@solana/spl-token"; 4 | 5 | import { TransferFeeDataBaseType } from "../api/type"; 6 | import { GetTransferAmountFee } from "../raydium/type"; 7 | 8 | const POINT = 10_000; 9 | export function getTransferAmountFee( 10 | amount: BN, 11 | feeConfig: TransferFeeConfig | undefined, 12 | epochInfo: EpochInfo, 13 | addFee: boolean, 14 | ): GetTransferAmountFee { 15 | if (feeConfig === undefined) { 16 | return { 17 | amount, 18 | fee: undefined, 19 | expirationTime: undefined, 20 | }; 21 | } 22 | 23 | const nowFeeConfig: TransferFee = 24 | epochInfo.epoch < feeConfig.newerTransferFee.epoch ? feeConfig.olderTransferFee : feeConfig.newerTransferFee; 25 | const maxFee = new BN(nowFeeConfig.maximumFee.toString()); 26 | const expirationTime: number | undefined = 27 | epochInfo.epoch < feeConfig.newerTransferFee.epoch 28 | ? ((Number(feeConfig.newerTransferFee.epoch) * epochInfo.slotsInEpoch - epochInfo.absoluteSlot) * 400) / 1000 29 | : undefined; 30 | 31 | if (addFee) { 32 | if (nowFeeConfig.transferFeeBasisPoints === POINT) { 33 | const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString()); 34 | return { 35 | amount: amount.add(nowMaxFee), 36 | fee: nowMaxFee, 37 | expirationTime, 38 | }; 39 | } else { 40 | const _TAmount = BNDivCeil(amount.mul(new BN(POINT)), new BN(POINT - nowFeeConfig.transferFeeBasisPoints)); 41 | 42 | const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString()); 43 | const TAmount = _TAmount.sub(amount).gt(nowMaxFee) ? amount.add(nowMaxFee) : _TAmount; 44 | 45 | const _fee = BNDivCeil(TAmount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT)); 46 | const fee = _fee.gt(maxFee) ? maxFee : _fee; 47 | return { 48 | amount: TAmount, 49 | fee, 50 | expirationTime, 51 | }; 52 | } 53 | } else { 54 | const _fee = BNDivCeil(amount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT)); 55 | const fee = _fee.gt(maxFee) ? maxFee : _fee; 56 | 57 | return { 58 | amount, 59 | fee, 60 | expirationTime, 61 | }; 62 | } 63 | } 64 | 65 | export function getTransferAmountFeeV2( 66 | amount: BN, 67 | _feeConfig: TransferFeeDataBaseType | undefined, 68 | epochInfo: EpochInfo, 69 | addFee: boolean, 70 | ): GetTransferAmountFee { 71 | if (_feeConfig === undefined) { 72 | return { 73 | amount, 74 | fee: undefined, 75 | expirationTime: undefined, 76 | }; 77 | } 78 | const feeConfig = { 79 | ..._feeConfig, 80 | olderTransferFee: { 81 | epoch: BigInt(_feeConfig.olderTransferFee.epoch), 82 | maximumFee: BigInt(_feeConfig.olderTransferFee.maximumFee), 83 | transferFeeBasisPoints: _feeConfig.olderTransferFee.transferFeeBasisPoints, 84 | }, 85 | newerTransferFee: { 86 | epoch: BigInt(_feeConfig.newerTransferFee.epoch), 87 | maximumFee: BigInt(_feeConfig.newerTransferFee.maximumFee), 88 | transferFeeBasisPoints: _feeConfig.newerTransferFee.transferFeeBasisPoints, 89 | }, 90 | }; 91 | 92 | const nowFeeConfig: TransferFee = 93 | epochInfo.epoch < feeConfig.newerTransferFee.epoch ? feeConfig.olderTransferFee : feeConfig.newerTransferFee; 94 | const maxFee = new BN(nowFeeConfig.maximumFee.toString()); 95 | const expirationTime: number | undefined = 96 | epochInfo.epoch < feeConfig.newerTransferFee.epoch 97 | ? ((Number(feeConfig.newerTransferFee.epoch) * epochInfo.slotsInEpoch - epochInfo.absoluteSlot) * 400) / 1000 98 | : undefined; 99 | 100 | if (addFee) { 101 | if (nowFeeConfig.transferFeeBasisPoints === POINT) { 102 | const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString()); 103 | return { 104 | amount: amount.add(nowMaxFee), 105 | fee: nowMaxFee, 106 | expirationTime, 107 | }; 108 | } else { 109 | const _TAmount = BNDivCeil(amount.mul(new BN(POINT)), new BN(POINT - nowFeeConfig.transferFeeBasisPoints)); 110 | 111 | const nowMaxFee = new BN(nowFeeConfig.maximumFee.toString()); 112 | const TAmount = _TAmount.sub(amount).gt(nowMaxFee) ? amount.add(nowMaxFee) : _TAmount; 113 | 114 | const _fee = BNDivCeil(TAmount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT)); 115 | const fee = _fee.gt(maxFee) ? maxFee : _fee; 116 | return { 117 | amount: TAmount, 118 | fee, 119 | expirationTime, 120 | }; 121 | } 122 | } else { 123 | const _fee = BNDivCeil(amount.mul(new BN(nowFeeConfig.transferFeeBasisPoints)), new BN(POINT)); 124 | const fee = _fee.gt(maxFee) ? maxFee : _fee; 125 | 126 | return { 127 | amount, 128 | fee, 129 | expirationTime, 130 | }; 131 | } 132 | } 133 | 134 | export function minExpirationTime( 135 | expirationTime1: number | undefined, 136 | expirationTime2: number | undefined, 137 | ): number | undefined { 138 | if (expirationTime1 === undefined) return expirationTime2; 139 | if (expirationTime2 === undefined) return expirationTime1; 140 | 141 | return Math.min(expirationTime1, expirationTime2); 142 | } 143 | 144 | export function BNDivCeil(bn1: BN, bn2: BN): BN { 145 | const { div, mod } = bn1.divmod(bn2); 146 | 147 | if (mod.gt(new BN(0))) { 148 | return div.add(new BN(1)); 149 | } else { 150 | return div; 151 | } 152 | } 153 | 154 | export function ceilDivBN(amountA: BN, amountB: BN): BN { 155 | if (amountA.isZero()) return new BN(0); 156 | 157 | const quotient = amountA.div(amountB); 158 | 159 | if (quotient.isZero()) return new BN(1); 160 | 161 | const remainder = amountA.mod(amountB); 162 | if (remainder.gt(new BN(0))) { 163 | return quotient.add(new BN(1)); 164 | } 165 | return quotient; 166 | } 167 | -------------------------------------------------------------------------------- /src/common/txTool/lookupTable.ts: -------------------------------------------------------------------------------- 1 | import { Connection, PublicKey, AddressLookupTableAccount } from "@solana/web3.js"; 2 | import { getMultipleAccountsInfo } from "../accountInfo"; 3 | 4 | export interface CacheLTA { 5 | [key: string]: AddressLookupTableAccount; 6 | } 7 | 8 | export async function getMultipleLookupTableInfo({ 9 | connection, 10 | address, 11 | }: { 12 | connection: Connection; 13 | address: PublicKey[]; 14 | }): Promise { 15 | const dataInfos = await getMultipleAccountsInfo( 16 | connection, 17 | [...new Set(address.map((i) => i.toString()))].map((i) => new PublicKey(i)), 18 | ); 19 | 20 | const outDict: CacheLTA = {}; 21 | for (let i = 0; i < address.length; i++) { 22 | const info = dataInfos[i]; 23 | const key = address[i]; 24 | if (!info) continue; 25 | const lookupAccount = new AddressLookupTableAccount({ 26 | key, 27 | state: AddressLookupTableAccount.deserialize(info.data), 28 | }); 29 | outDict[key.toString()] = lookupAccount; 30 | LOOKUP_TABLE_CACHE[key.toString()] = lookupAccount; 31 | } 32 | 33 | return outDict; 34 | } 35 | 36 | export const LOOKUP_TABLE_CACHE: CacheLTA = { 37 | "AcL1Vo8oy1ULiavEcjSUcwfBSForXMudcZvDZy5nzJkU": new AddressLookupTableAccount({ 38 | key: new PublicKey("AcL1Vo8oy1ULiavEcjSUcwfBSForXMudcZvDZy5nzJkU"), 39 | state: AddressLookupTableAccount.deserialize( 40 | Buffer.from( 41 | 'AQAAAP//////////I1rcEwAAAAAvAQYwun9CU6c5Ikm2pAj+D9IEnCOR45nK+SFTGSdpd6J6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpBt324e51j94YQl285GzN2rYa/E2DuQ0n/r35KNihi/wFSlNQ+F3IgtYUpVZyeIopbd8eq6vQpgZ4iEky9O72oAVKU1qZKSEGTSTocWDaOHx8NbXdvJK7geQfqEBBBUSNBqfVFxksXFEhjMlMPUrxf1ja7gibof1E49vZigAAAAAGp9UXGMd0yShWY5hpHV62i164o5tLbVxzVVshAAAAAIyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZC3BlsePRfEU4nVJ/awTDzVi4bHMaoP21SbbRvAP4KUbIScv+6Yw2LHF/6K0ZjUPibbSWXCirYPGuuVl7zT789IUPLW4CpHr4JNCatp3ELXDLKMv6JJ+37le50lbBJ2LvBkX2T9y7AHdNGviJAqQNtlDUDCnauQRWybsLji6nPM8Qkw5asQRvCdB3MbX6IEBwytOrpM32l4jQygKG9TKgR0vZScQ2AsM/IHeQ7RajUkyhuZdc8SGiqQz/7H34torNR/Wir3sl0ruUrVxJWEZfUg+QLNAxxODdBi53/OP7Ioil1cqeBM9dtZC3FLov4yyxWRM/wcGStyJX/QfTnLBAHqkqWotPKVlShCVQqpP9W5W1rOao65IMk5QuQ2kMIOxzDMKAy2vjGSxQODgBz0QwGA+eP4ZjIjrIAQaXENv31QfLlOdXSRCkaybRniDHF4C8YcwhcvsqrOVuTP4B2Na+9wLdtrB31uz2rtlFI5kahdsnp/d1SrASDInYCtTYtdoke4kX+hoKWcEWM4Tle8pTUkUVv4BxS6fje/EzKBE4Qu/YsA/yfEEFGcr8Z57VKDw8uQzpiru7g4lvjnfapW62W030syevD8k07SGoxUHiuT/ai7gAHWWhDsVmg/C63ajgpkH7Sn3GdutArDTfyqOkdqv4/IPC/EFFy7mGkfDd2C57N5a/4jC+BbmJy7wQaSEZr0CQU88lPtUxIVvzGjC95b8Ooss2TqmkrayGKofkPMGQn7Ux+9lfwBSNfxwH8NgbpqC/7LNlV4I7nCvsXf3p+ohQk9NrAJb2KAFpUqEIJ9ZBV7BYDzHF/ORKYlgtvPnXjudZQ6CEo5OzUDaNIomTCCsvhD16TxJjsbgne1kGnQPCFSoaxUbq2V1bPMFQ3VYP6wDZ9bKStCFKx9A3tNbwZFC5ZGAN83MFK7XoTy+OmmcFEr6rLOjfSuTfPvHJkSVxW6Qllwkl67XcBi5v00u2gQsbu+38sp+rd5pA/LvyWj4P94ZGZwc1tE2P88xekCLcAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAn+HWRkdcPKyFFMnVwEoD7vnD0jCKFIU1sImubYCxNTSVzsKpaQX+fzNxrLAI3L14JQnJx/D6Uk2LADIHGqnGELzjEbkBDAlaM77NkXMPfqXNLSveCkWI7UEgNs31WEWB6XHSYI/v5DklHOb4QTtDOR804PVbi3fjloZeLR2F8d4FuZmMMO7ck3Fnkn2zEMG5gOmqsygb6PjTitArVl52NhcSznTxVnguaIJxiZkAnurDmn3MWR0PC2GLghp2KJqHCc6QQ85odeIjFHKOlRlJyeSXVJmL8vb1UgOzsbJPVP8p6zM4M3C1Sd7uWIHP33G42AP2Zg8ucn/n6meQjjD266JgCWdxZD6PXs9CsnIeL7SSG0/6lGb9xfP0ZcWkCXB/3hjxHYVXjra/GPOeXGk0fLLKjCbk+mgs2w6d2oCwimBipTzuoZ30GiI8ij8VRzD5CzMWtu2m21eDBIfjGAEo4pQeNNonKcqzV/cleX8ySZLOHsz8PtBCrLqF+VkLm9hOzIT+6i/nIf6keR4GWKMOD4AvqfpjHoD4DuhBpz8P28+DxkGrDXXr/nr20x291VPvcTU/b+b+o2kC9G0kcXeTlLjU6a2TQXWlZ4gBUdBl1jgT7mObSTpLblNiXZsLkbmVXZwvFKXua5cUKlWed/w30skmEUraTuQqtqr5fHZPW9n57EmeTif6LjHL2YJFZkQU+TrJmFzqzmF4/b8OwrPQAprl8mX3q4LUIdAS/a+11B6DWD1Xk2++Sn94dLC4xjkO4Wtlw8c4XuzciVbepHOmnoWzVu/0y3KCrLCSfQxQ3br8DJCoVzhgtPsS2nZZjsBGIZgnU0QpMv+2MnRsnKwdp1VsrCX84j/qvaZn4WhKunippgTbN2EUs0tPTP55Qfgj+nKmjtWW5IYs72FrEwJKYoNfsmqaF4o5pf4v9zgPwVwY/5I4XJKUL2L25m9kAQcW/K+H1RTFEUoj8Z4ajpOmAB/dG0COmCphVMW2CCMvnxhcGiSgPnpDuWu6qiJ7NG7ye5kvHgefgqPLeicspNJ5EpL3XiRNLM2tmJLI1awAwOyd6iHv0dCkMYRKaa6rcaZeYwmKCkckm0kM2JNmnmmAaBQQ7mwmIM0IMxX4f5W6j9PqZWcJxF7r17T/lQBAmcjoupRiJifbnXCNUv9GhpRF19WcBdeKbivRJVlGop6I2RS6lGImJ9udcI1S/0aGlEXX1ZwF14puK9ElWUainojZFYVHLHD6dIP2ESjqBzg3ol1/wB7+/ylGwd9LS7wSZ2A630CJSVKwH47K9P4bB8PEQP8BwjMFa7xQHOqZFP1XqaQ==', 42 | "base64", 43 | ), 44 | ), 45 | }), 46 | }; 47 | -------------------------------------------------------------------------------- /src/common/txTool/txType.ts: -------------------------------------------------------------------------------- 1 | export enum TxVersion { 2 | "V0", 3 | "LEGACY", 4 | } 5 | 6 | export const InstructionType = { 7 | CreateAccount: "CreateAccount", 8 | InitAccount: "InitAccount", 9 | CreateATA: "CreateATA", 10 | CloseAccount: "CloseAccount", 11 | TransferAmount: "TransferAmount", 12 | InitMint: "InitMint", 13 | MintTo: "MintTo", 14 | 15 | InitMarket: "InitMarket", // create market main ins 16 | Util1216OwnerClaim: "Util1216OwnerClaim", // owner claim token ins 17 | 18 | SetComputeUnitPrice: "SetComputeUnitPrice", 19 | SetComputeUnitLimit: "SetComputeUnitLimit", 20 | 21 | // CLMM 22 | ClmmCreatePool: "ClmmCreatePool", 23 | ClmmOpenPosition: "ClmmOpenPosition", 24 | ClmmIncreasePosition: "ClmmIncreasePosition", 25 | ClmmDecreasePosition: "ClmmDecreasePosition", 26 | ClmmClosePosition: "ClmmClosePosition", 27 | ClmmSwapBaseIn: "ClmmSwapBaseIn", 28 | ClmmSwapBaseOut: "ClmmSwapBaseOut", 29 | ClmmInitReward: "ClmmInitReward", 30 | ClmmSetReward: "ClmmSetReward", 31 | ClmmCollectReward: "ClmmCollectReward", 32 | ClmmLockPosition: "ClmmLockPosition", 33 | ClmmHarvestLockPosition: "ClmmHarvestLockPosition", 34 | 35 | AmmV4Swap: "AmmV4Swap", 36 | AmmV4AddLiquidity: "AmmV4AddLiquidity", 37 | AmmV4RemoveLiquidity: "AmmV4RemoveLiquidity", 38 | AmmV4SimulatePoolInfo: "AmmV4SimulatePoolInfo", 39 | AmmV4SwapBaseIn: "AmmV4SwapBaseIn", 40 | AmmV4SwapBaseOut: "AmmV4SwapBaseOut", 41 | AmmV4CreatePool: "AmmV4CreatePool", 42 | AmmV4InitPool: "AmmV4InitPool", 43 | 44 | AmmV5AddLiquidity: "AmmV5AddLiquidity", 45 | AmmV5RemoveLiquidity: "AmmV5RemoveLiquidity", 46 | AmmV5SimulatePoolInfo: "AmmV5SimulatePoolInfo", 47 | AmmV5SwapBaseIn: "AmmV5SwapBaseIn", 48 | AmmV5SwapBaseOut: "AmmV5SwapBaseOut", 49 | 50 | RouteSwap: "RouteSwap", 51 | RouteSwap1: "RouteSwap1", 52 | RouteSwap2: "RouteSwap2", 53 | 54 | FarmV3Deposit: "FarmV3Deposit", 55 | FarmV3Withdraw: "FarmV3Withdraw", 56 | FarmV3CreateLedger: "FarmV3CreateLedger", 57 | 58 | FarmV4Withdraw: "FarmV4Withdraw", 59 | 60 | FarmV5Deposit: "FarmV5Deposit", 61 | FarmV5Withdraw: "FarmV5Withdraw", 62 | FarmV5CreateLedger: "FarmV5CreateLedger", 63 | 64 | FarmV6Deposit: "FarmV6Deposit", 65 | FarmV6Withdraw: "FarmV6Withdraw", 66 | FarmV6Create: "FarmV6Create", 67 | FarmV6Restart: "FarmV6Restart", 68 | FarmV6CreatorAddReward: "FarmV6CreatorAddReward", 69 | FarmV6CreatorWithdraw: "FarmV6CreatorWithdraw", 70 | 71 | CpmmCreatePool: "CpmmCreatePool", 72 | CpmmAddLiquidity: "CpmmAddLiquidity", 73 | CpmmWithdrawLiquidity: "CpmmWithdrawLiquidity", 74 | CpmmSwapBaseIn: "CpmmSwapBaseIn", 75 | CpmmSwapBaseOut: "CpmmSwapBaseOut", 76 | 77 | CpmmLockLp: "CpmmLockLp", 78 | CpmmCollectLockFee: "CpmmCollectLockFee", 79 | TransferTip: "TransferTip", 80 | }; 81 | -------------------------------------------------------------------------------- /src/common/utility.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | 4 | import { Fraction, Percent, Price, Token, TokenAmount } from "../module"; 5 | import { ReplaceType } from "../raydium/type"; 6 | 7 | import { tryParsePublicKey } from "./pubKey"; 8 | 9 | export async function sleep(ms: number): Promise { 10 | new Promise((resolve) => setTimeout(resolve, ms)); 11 | } 12 | 13 | export function getTimestamp(): number { 14 | return new Date().getTime(); 15 | } 16 | 17 | export function notInnerObject(v: unknown): v is Record { 18 | return ( 19 | typeof v === "object" && 20 | v !== null && 21 | ![Token, TokenAmount, PublicKey, Fraction, BN, Price, Percent].some((o) => typeof o === "object" && v instanceof o) 22 | ); 23 | } 24 | 25 | export function jsonInfo2PoolKeys(jsonInfo: T): ReplaceType { 26 | // @ts-expect-error no need type for inner code 27 | return typeof jsonInfo === "string" 28 | ? tryParsePublicKey(jsonInfo) 29 | : Array.isArray(jsonInfo) 30 | ? jsonInfo.map((k) => jsonInfo2PoolKeys(k)) 31 | : notInnerObject(jsonInfo) 32 | ? Object.fromEntries(Object.entries(jsonInfo).map(([k, v]) => [k, jsonInfo2PoolKeys(v)])) 33 | : jsonInfo; 34 | } 35 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | export * from "./common"; 3 | export * from "./raydium"; 4 | export * from "./solana"; 5 | export * from "./module"; 6 | export * from "./marshmallow"; 7 | -------------------------------------------------------------------------------- /src/module/currency.ts: -------------------------------------------------------------------------------- 1 | import { SOL_INFO } from "../raydium/token/constant"; 2 | 3 | import { Token } from "./token"; 4 | 5 | interface CurrencyProps { 6 | decimals: number; 7 | symbol?: string; 8 | name?: string; 9 | } 10 | /** 11 | * A currency is any fungible financial instrument on Solana, including SOL and all SPL tokens. 12 | * The only instance of the base class `Currency` is SOL. 13 | */ 14 | export class Currency { 15 | public readonly symbol?: string; 16 | public readonly name?: string; 17 | public readonly decimals: number; 18 | 19 | /** 20 | * The only instance of the base class `Currency`. 21 | */ 22 | public static readonly SOL: Currency = new Currency(SOL_INFO); 23 | 24 | /** 25 | * Constructs an instance of the base class `Currency`. The only instance of the base class `Currency` is `Currency.SOL`. 26 | * @param decimals - decimals of the currency 27 | * @param symbol - symbol of the currency 28 | * @param name - name of the currency 29 | */ 30 | public constructor({ decimals, symbol = "UNKNOWN", name = "UNKNOWN" }: CurrencyProps) { 31 | this.decimals = decimals; 32 | this.symbol = symbol; 33 | this.name = name; 34 | } 35 | 36 | public equals(other: Currency): boolean { 37 | return this === other; 38 | } 39 | } 40 | 41 | /** 42 | * Compares two currencies for equality 43 | */ 44 | export function currencyEquals(currencyA: Currency, currencyB: Currency): boolean { 45 | if (currencyA instanceof Token && currencyB instanceof Token) { 46 | return currencyA.equals(currencyB); 47 | } else if (currencyA instanceof Token || currencyB instanceof Token) { 48 | return false; 49 | } else { 50 | return currencyA === currencyB; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/module/formatter.ts: -------------------------------------------------------------------------------- 1 | import Big, { BigConstructor, BigSource, RoundingMode } from "big.js"; 2 | import Decimal, { Config, Numeric } from "decimal.js-light"; 3 | import _toFarmat from "toformat"; 4 | 5 | type TakeStatic = { [P in keyof T]: T[P] }; 6 | interface FormatOptions { 7 | decimalSeparator?: string; 8 | groupSeparator?: string; 9 | groupSize?: number; 10 | fractionGroupSeparator?: string; 11 | fractionGroupSize?: number; 12 | } 13 | interface WrappedBigConstructor extends TakeStatic { 14 | new (value: BigSource): WrappedBig; 15 | (value: BigSource): WrappedBig; 16 | (): WrappedBigConstructor; 17 | 18 | format: FormatOptions; 19 | } 20 | export interface WrappedBig extends Big { 21 | add(n: BigSource): WrappedBig; 22 | abs(): WrappedBig; 23 | div(n: BigSource): WrappedBig; 24 | minus(n: BigSource): WrappedBig; 25 | mod(n: BigSource): WrappedBig; 26 | mul(n: BigSource): WrappedBig; 27 | plus(n: BigSource): WrappedBig; 28 | pow(exp: number): WrappedBig; 29 | round(dp?: number, rm?: RoundingMode): WrappedBig; 30 | sqrt(): WrappedBig; 31 | sub(n: BigSource): WrappedBig; 32 | times(n: BigSource): WrappedBig; 33 | toFormat(): string; 34 | toFormat(options: FormatOptions): string; 35 | toFormat(fractionLength: number): string; 36 | toFormat(fractionLength: number, options: FormatOptions): string; 37 | toFormat(fractionLength: number, missionUnknown: number): string; 38 | toFormat(fractionLength: number, missionUnknown: number, options: FormatOptions): string; 39 | } 40 | 41 | type DecimalConstructor = typeof Decimal; 42 | interface WrappedDecimalConstructor extends TakeStatic { 43 | new (value: Numeric): WrappedDecimal; 44 | clone(config?: Config): WrappedDecimalConstructor; 45 | config(config: Config): WrappedDecimal; 46 | set(config: Config): WrappedDecimal; 47 | format: FormatOptions; 48 | } 49 | export interface WrappedDecimal extends Decimal { 50 | absoluteValue(): WrappedDecimal; 51 | abs(): WrappedDecimal; 52 | dividedBy(y: Numeric): WrappedDecimal; 53 | div(y: Numeric): WrappedDecimal; 54 | dividedToIntegerBy(y: Numeric): WrappedDecimal; 55 | idiv(y: Numeric): WrappedDecimal; 56 | logarithm(base?: Numeric): WrappedDecimal; 57 | log(base?: Numeric): WrappedDecimal; 58 | minus(y: Numeric): WrappedDecimal; 59 | sub(y: Numeric): WrappedDecimal; 60 | modulo(y: Numeric): WrappedDecimal; 61 | mod(y: Numeric): WrappedDecimal; 62 | naturalExponetial(): WrappedDecimal; 63 | exp(): WrappedDecimal; 64 | naturalLogarithm(): WrappedDecimal; 65 | ln(): WrappedDecimal; 66 | negated(): WrappedDecimal; 67 | neg(): WrappedDecimal; 68 | plus(y: Numeric): WrappedDecimal; 69 | add(y: Numeric): WrappedDecimal; 70 | squareRoot(): WrappedDecimal; 71 | sqrt(): WrappedDecimal; 72 | times(y: Numeric): WrappedDecimal; 73 | mul(y: Numeric): WrappedDecimal; 74 | toWrappedDecimalPlaces(dp?: number, rm?: number): WrappedDecimal; 75 | todp(dp?: number, rm?: number): WrappedDecimal; 76 | toInteger(): WrappedDecimal; 77 | toint(): WrappedDecimal; 78 | toPower(y: Numeric): WrappedDecimal; 79 | pow(y: Numeric): WrappedDecimal; 80 | toSignificantDigits(sd?: number, rm?: number): WrappedDecimal; 81 | tosd(sd?: number, rm?: number): WrappedDecimal; 82 | toFormat(options: FormatOptions): string; 83 | toFormat(fractionLength: number): string; 84 | toFormat(fractionLength: number, options: FormatOptions): string; 85 | toFormat(fractionLength: number, missionUnknown: number): string; 86 | toFormat(fractionLength: number, missionUnknown: number, options: FormatOptions): string; 87 | } 88 | 89 | const toFormat: { 90 | (fn: BigConstructor): WrappedBigConstructor; 91 | (fn: DecimalConstructor): WrappedDecimalConstructor; 92 | } = _toFarmat; 93 | export default toFormat; 94 | -------------------------------------------------------------------------------- /src/module/fraction.ts: -------------------------------------------------------------------------------- 1 | import _Big from "big.js"; 2 | import BN from "bn.js"; 3 | import _Decimal from "decimal.js-light"; 4 | 5 | import { BigNumberish } from "../common/bignumber"; 6 | import { createLogger } from "../common/logger"; 7 | 8 | import { parseBigNumberish, Rounding } from "../common/constant"; 9 | import toFormat, { WrappedBig } from "./formatter"; 10 | 11 | const logger = createLogger("module/fraction"); 12 | 13 | const Big = toFormat(_Big); 14 | type Big = WrappedBig; 15 | 16 | const Decimal = toFormat(_Decimal); 17 | 18 | const toSignificantRounding = { 19 | [Rounding.ROUND_DOWN]: Decimal.ROUND_DOWN, 20 | [Rounding.ROUND_HALF_UP]: Decimal.ROUND_HALF_UP, 21 | [Rounding.ROUND_UP]: Decimal.ROUND_UP, 22 | }; 23 | 24 | const toFixedRounding = { 25 | [Rounding.ROUND_DOWN]: _Big.roundDown, 26 | [Rounding.ROUND_HALF_UP]: _Big.roundHalfUp, 27 | [Rounding.ROUND_UP]: _Big.roundUp, 28 | }; 29 | 30 | export class Fraction { 31 | public readonly numerator: BN; 32 | public readonly denominator: BN; 33 | 34 | public constructor(numerator: BigNumberish, denominator: BigNumberish = new BN(1)) { 35 | this.numerator = parseBigNumberish(numerator); 36 | this.denominator = parseBigNumberish(denominator); 37 | } 38 | 39 | public get quotient(): BN { 40 | return this.numerator.div(this.denominator); 41 | } 42 | 43 | public invert(): Fraction { 44 | return new Fraction(this.denominator, this.numerator); 45 | } 46 | 47 | public add(other: Fraction | BigNumberish): Fraction { 48 | const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigNumberish(other)); 49 | 50 | if (this.denominator.eq(otherParsed.denominator)) { 51 | return new Fraction(this.numerator.add(otherParsed.numerator), this.denominator); 52 | } 53 | 54 | return new Fraction( 55 | this.numerator.mul(otherParsed.denominator).add(otherParsed.numerator.mul(this.denominator)), 56 | this.denominator.mul(otherParsed.denominator), 57 | ); 58 | } 59 | 60 | public sub(other: Fraction | BigNumberish): Fraction { 61 | const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigNumberish(other)); 62 | 63 | if (this.denominator.eq(otherParsed.denominator)) { 64 | return new Fraction(this.numerator.sub(otherParsed.numerator), this.denominator); 65 | } 66 | 67 | return new Fraction( 68 | this.numerator.mul(otherParsed.denominator).sub(otherParsed.numerator.mul(this.denominator)), 69 | this.denominator.mul(otherParsed.denominator), 70 | ); 71 | } 72 | 73 | public mul(other: Fraction | BigNumberish): Fraction { 74 | const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigNumberish(other)); 75 | 76 | return new Fraction(this.numerator.mul(otherParsed.numerator), this.denominator.mul(otherParsed.denominator)); 77 | } 78 | 79 | public div(other: Fraction | BigNumberish): Fraction { 80 | const otherParsed = other instanceof Fraction ? other : new Fraction(parseBigNumberish(other)); 81 | 82 | return new Fraction(this.numerator.mul(otherParsed.denominator), this.denominator.mul(otherParsed.numerator)); 83 | } 84 | 85 | public toSignificant( 86 | significantDigits: number, 87 | format: object = { groupSeparator: "" }, 88 | rounding: Rounding = Rounding.ROUND_HALF_UP, 89 | ): string { 90 | if (!Number.isInteger(significantDigits)) logger.logWithError(`${significantDigits} is not an integer.`); 91 | if (significantDigits <= 0) logger.logWithError(`${significantDigits} is not positive.`); 92 | 93 | Decimal.set({ precision: significantDigits + 1, rounding: toSignificantRounding[rounding] }); 94 | const quotient = new Decimal(this.numerator.toString()) 95 | .div(this.denominator.toString()) 96 | .toSignificantDigits(significantDigits); 97 | return quotient.toFormat(quotient.decimalPlaces(), format); 98 | } 99 | 100 | public toFixed( 101 | decimalPlaces: number, 102 | format: object = { groupSeparator: "" }, 103 | rounding: Rounding = Rounding.ROUND_HALF_UP, 104 | ): string { 105 | if (!Number.isInteger(decimalPlaces)) logger.logWithError(`${decimalPlaces} is not an integer.`); 106 | if (decimalPlaces < 0) logger.logWithError(`${decimalPlaces} is negative.`); 107 | 108 | Big.DP = decimalPlaces; 109 | Big.RM = toFixedRounding[rounding] || 1; 110 | return new Big(this.numerator.toString()).div(this.denominator.toString()).toFormat(decimalPlaces, format); 111 | } 112 | 113 | public isZero(): boolean { 114 | return this.numerator.isZero(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/module/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./amount"; 2 | export * from "./currency"; 3 | export * from "./formatter"; 4 | export * from "./fraction"; 5 | export * from "./percent"; 6 | export * from "./price"; 7 | export * from "./token"; 8 | -------------------------------------------------------------------------------- /src/module/percent.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { Rounding } from "../common"; 3 | import { Fraction } from "./fraction"; 4 | 5 | export const _100_PERCENT = new Fraction(new BN(100)); 6 | 7 | export class Percent extends Fraction { 8 | public toSignificant(significantDigits = 5, format?: object, rounding?: Rounding): string { 9 | return this.mul(_100_PERCENT).toSignificant(significantDigits, format, rounding); 10 | } 11 | 12 | public toFixed(decimalPlaces = 2, format?: object, rounding?: Rounding): string { 13 | return this.mul(_100_PERCENT).toFixed(decimalPlaces, format, rounding); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/module/price.ts: -------------------------------------------------------------------------------- 1 | import { Rounding } from "../common"; 2 | import { BigNumberish, tenExponential } from "../common/bignumber"; 3 | import { createLogger } from "../common/logger"; 4 | 5 | import { Fraction } from "./fraction"; 6 | import { Token } from "./token"; 7 | 8 | const logger = createLogger("Raydium_price"); 9 | 10 | interface PriceProps { 11 | baseToken: Token; 12 | denominator: BigNumberish; 13 | quoteToken: Token; 14 | numerator: BigNumberish; 15 | } 16 | 17 | export class Price extends Fraction { 18 | public readonly baseToken: Token; // input i.e. denominator 19 | public readonly quoteToken: Token; // output i.e. numerator 20 | // used to adjust the raw fraction w/r/t the decimals of the {base,quote}Token 21 | public readonly scalar: Fraction; 22 | 23 | // denominator and numerator _must_ be raw, i.e. in the native representation 24 | public constructor(params: PriceProps) { 25 | const { baseToken, quoteToken, numerator, denominator } = params; 26 | super(numerator, denominator); 27 | 28 | this.baseToken = baseToken; 29 | this.quoteToken = quoteToken; 30 | this.scalar = new Fraction(tenExponential(baseToken.decimals), tenExponential(quoteToken.decimals)); 31 | } 32 | 33 | public get raw(): Fraction { 34 | return new Fraction(this.numerator, this.denominator); 35 | } 36 | 37 | public get adjusted(): Fraction { 38 | return super.mul(this.scalar); 39 | } 40 | 41 | public invert(): Price { 42 | return new Price({ 43 | baseToken: this.quoteToken, 44 | quoteToken: this.baseToken, 45 | denominator: this.numerator, 46 | numerator: this.denominator, 47 | }); 48 | } 49 | 50 | public mul(other: Price): Price { 51 | if (this.quoteToken !== other.baseToken) logger.logWithError("mul token not equals"); 52 | 53 | const fraction = super.mul(other); 54 | return new Price({ 55 | baseToken: this.baseToken, 56 | quoteToken: other.quoteToken, 57 | denominator: fraction.denominator, 58 | numerator: fraction.numerator, 59 | }); 60 | } 61 | 62 | public toSignificant(significantDigits = this.quoteToken.decimals, format?: object, rounding?: Rounding): string { 63 | return this.adjusted.toSignificant(significantDigits, format, rounding); 64 | } 65 | 66 | public toFixed(decimalPlaces = this.quoteToken.decimals, format?: object, rounding?: Rounding): string { 67 | return this.adjusted.toFixed(decimalPlaces, format, rounding); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/module/token.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { PublicKeyish, SOLMint, validateAndParsePublicKey } from "../common/pubKey"; 4 | import { TOKEN_WSOL } from "../raydium/token/constant"; 5 | 6 | /** 7 | * A token is any fungible financial instrument on Solana, including SOL and all SPL tokens. 8 | */ 9 | export interface TokenProps { 10 | mint: PublicKeyish; 11 | decimals: number; 12 | symbol?: string; 13 | name?: string; 14 | skipMint?: boolean; 15 | isToken2022?: boolean; 16 | } 17 | 18 | export class Token { 19 | public readonly symbol?: string; 20 | public readonly name?: string; 21 | public readonly decimals: number; 22 | public readonly isToken2022: boolean; 23 | 24 | public readonly mint: PublicKey; 25 | public static readonly WSOL: Token = new Token({ 26 | ...TOKEN_WSOL, 27 | mint: TOKEN_WSOL.address, 28 | }); 29 | 30 | /** 31 | * 32 | * @param mint - pass "sol" as mint will auto generate wsol token config 33 | */ 34 | public constructor({ mint, decimals, symbol, name, skipMint = false, isToken2022 = false }: TokenProps) { 35 | if (mint === SOLMint.toBase58() || (mint instanceof PublicKey && SOLMint.equals(mint))) { 36 | this.decimals = TOKEN_WSOL.decimals; 37 | this.symbol = TOKEN_WSOL.symbol; 38 | this.name = TOKEN_WSOL.name; 39 | this.mint = new PublicKey(TOKEN_WSOL.address); 40 | this.isToken2022 = false; 41 | return; 42 | } 43 | 44 | this.decimals = decimals; 45 | this.symbol = symbol || mint.toString().substring(0, 6); 46 | this.name = name || mint.toString().substring(0, 6); 47 | this.mint = skipMint ? PublicKey.default : validateAndParsePublicKey({ publicKey: mint }); 48 | this.isToken2022 = isToken2022; 49 | } 50 | 51 | public equals(other: Token): boolean { 52 | // short circuit on reference equality 53 | if (this === other) { 54 | return true; 55 | } 56 | return this.mint.equals(other.mint); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/raydium/account/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./layout"; 2 | export * from "./types"; 3 | export * from "./util"; 4 | export * from "./instruction"; 5 | -------------------------------------------------------------------------------- /src/raydium/account/instruction.ts: -------------------------------------------------------------------------------- 1 | import { Commitment, Connection, PublicKey, Signer, SystemProgram, TransactionInstruction } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import { BigNumberish, parseBigNumberish } from "@/common"; 4 | import { 5 | createCloseAccountInstruction, 6 | createInitializeAccountInstruction, 7 | createTransferInstruction, 8 | TOKEN_PROGRAM_ID, 9 | } from "@solana/spl-token"; 10 | import { AddInstructionParam } from "@/common/txTool/txTool"; 11 | import { InstructionType } from "@/common/txTool/txType"; 12 | import { TOKEN_WSOL } from "../token/constant"; 13 | import { generatePubKey } from "./util"; 14 | 15 | import { splAccountLayout } from "./layout"; 16 | 17 | export function initTokenAccountInstruction(params: { 18 | mint: PublicKey; 19 | tokenAccount: PublicKey; 20 | owner: PublicKey; 21 | programId?: PublicKey; 22 | }): TransactionInstruction { 23 | const { mint, tokenAccount, owner, programId = TOKEN_PROGRAM_ID } = params; 24 | return createInitializeAccountInstruction(tokenAccount, mint, owner, programId); 25 | } 26 | 27 | export function closeAccountInstruction(params: { 28 | tokenAccount: PublicKey; 29 | payer: PublicKey; 30 | multiSigners?: Signer[]; 31 | owner: PublicKey; 32 | programId?: PublicKey; 33 | }): TransactionInstruction { 34 | const { tokenAccount, payer, multiSigners = [], owner, programId = TOKEN_PROGRAM_ID } = params; 35 | return createCloseAccountInstruction(tokenAccount, payer, owner, multiSigners, programId); 36 | } 37 | 38 | interface CreateWSolTokenAccount { 39 | connection: Connection; 40 | payer: PublicKey; 41 | owner: PublicKey; 42 | amount: BigNumberish; 43 | commitment?: Commitment; 44 | skipCloseAccount?: boolean; 45 | } 46 | /** 47 | * WrappedNative account = wsol account 48 | */ 49 | export async function createWSolAccountInstructions(params: CreateWSolTokenAccount): Promise< 50 | AddInstructionParam & { 51 | addresses: { newAccount: PublicKey }; 52 | } 53 | > { 54 | const { connection, amount, commitment, payer, owner, skipCloseAccount } = params; 55 | 56 | const balanceNeeded = await connection.getMinimumBalanceForRentExemption(splAccountLayout.span, commitment); 57 | const lamports = parseBigNumberish(amount).add(new BN(balanceNeeded)); 58 | const newAccount = generatePubKey({ fromPublicKey: payer, programId: TOKEN_PROGRAM_ID }); 59 | 60 | return { 61 | addresses: { newAccount: newAccount.publicKey }, 62 | signers: [], 63 | instructions: [ 64 | SystemProgram.createAccountWithSeed({ 65 | fromPubkey: payer, 66 | basePubkey: payer, 67 | seed: newAccount.seed, 68 | newAccountPubkey: newAccount.publicKey, 69 | lamports: lamports.toNumber(), 70 | space: splAccountLayout.span, 71 | programId: TOKEN_PROGRAM_ID, 72 | }), 73 | initTokenAccountInstruction({ 74 | mint: new PublicKey(TOKEN_WSOL.address), 75 | tokenAccount: newAccount.publicKey, 76 | owner, 77 | programId: TOKEN_PROGRAM_ID, 78 | }), 79 | ], 80 | instructionTypes: [InstructionType.CreateAccount, InstructionType.InitAccount], 81 | endInstructionTypes: skipCloseAccount ? [] : [InstructionType.CloseAccount], 82 | endInstructions: skipCloseAccount 83 | ? [] 84 | : [ 85 | closeAccountInstruction({ 86 | tokenAccount: newAccount.publicKey, 87 | payer, 88 | owner, 89 | }), 90 | ], 91 | }; 92 | } 93 | 94 | export function makeTransferInstruction({ 95 | source, 96 | destination, 97 | owner, 98 | amount, 99 | multiSigners = [], 100 | tokenProgram = TOKEN_PROGRAM_ID, 101 | }: { 102 | source: PublicKey; 103 | destination: PublicKey; 104 | owner: PublicKey; 105 | amount: BigNumberish; 106 | multiSigners?: Signer[]; 107 | tokenProgram?: PublicKey; 108 | }): TransactionInstruction { 109 | return createTransferInstruction(source, destination, owner, BigInt(String(amount)), multiSigners, tokenProgram); 110 | } 111 | -------------------------------------------------------------------------------- /src/raydium/account/layout.ts: -------------------------------------------------------------------------------- 1 | import { publicKey, struct, u32, u64, u8 } from "../../marshmallow"; 2 | 3 | export const splAccountLayout = struct([ 4 | publicKey("mint"), 5 | publicKey("owner"), 6 | u64("amount"), 7 | u32("delegateOption"), 8 | publicKey("delegate"), 9 | u8("state"), 10 | u32("isNativeOption"), 11 | u64("isNative"), 12 | u64("delegatedAmount"), 13 | u32("closeAuthorityOption"), 14 | publicKey("closeAuthority"), 15 | ]); 16 | -------------------------------------------------------------------------------- /src/raydium/account/types.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | 4 | import { BigNumberish } from "../../common/bignumber"; 5 | import { GetStructureSchema } from "../../marshmallow"; 6 | 7 | import { splAccountLayout } from "./layout"; 8 | 9 | export type SplAccountLayout = typeof splAccountLayout; 10 | export type SplAccount = GetStructureSchema; 11 | export interface TokenAccountRaw { 12 | programId: PublicKey; 13 | pubkey: PublicKey; 14 | accountInfo: SplAccount; 15 | } 16 | 17 | export interface TokenAccount { 18 | publicKey?: PublicKey; 19 | mint: PublicKey; 20 | isAssociated?: boolean; 21 | amount: BN; 22 | isNative: boolean; 23 | programId: PublicKey; 24 | } 25 | 26 | export interface getCreatedTokenAccountParams { 27 | mint: PublicKey; 28 | config?: { associatedOnly?: boolean }; 29 | } 30 | 31 | export interface HandleTokenAccountParams { 32 | side: "in" | "out"; 33 | amount: BigNumberish; 34 | mint: PublicKey; 35 | programId?: PublicKey; 36 | tokenAccount?: PublicKey; 37 | payer?: PublicKey; 38 | bypassAssociatedCheck: boolean; 39 | skipCloseAccount?: boolean; 40 | checkCreateATAOwner?: boolean; 41 | } 42 | 43 | export interface GetOrCreateTokenAccountParams { 44 | mint: PublicKey; 45 | owner: PublicKey; 46 | createInfo?: { 47 | payer: PublicKey; 48 | amount?: BigNumberish; 49 | }; 50 | 51 | associatedOnly: boolean; 52 | notUseTokenAccount?: boolean; 53 | skipCloseAccount?: boolean; 54 | tokenProgram?: PublicKey | string; 55 | checkCreateATAOwner?: boolean; 56 | assignSeed?: string; 57 | } 58 | -------------------------------------------------------------------------------- /src/raydium/account/util.ts: -------------------------------------------------------------------------------- 1 | import { AccountInfo, GetProgramAccountsResponse, Keypair, PublicKey, RpcResponseAndContext } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import { createLogger, getATAAddress } from "../../common"; 4 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 5 | import { sha256 } from "@noble/hashes/sha256"; 6 | import { splAccountLayout } from "./layout"; 7 | import { TokenAccount, TokenAccountRaw } from "./types"; 8 | 9 | const logger = createLogger("Raydium_Util"); 10 | 11 | export interface ParseTokenAccount { 12 | owner: PublicKey; 13 | solAccountResp?: AccountInfo | null; 14 | tokenAccountResp: RpcResponseAndContext; 15 | } 16 | 17 | export function parseTokenAccountResp({ owner, solAccountResp, tokenAccountResp }: ParseTokenAccount): { 18 | tokenAccounts: TokenAccount[]; 19 | tokenAccountRawInfos: TokenAccountRaw[]; 20 | } { 21 | const tokenAccounts: TokenAccount[] = []; 22 | const tokenAccountRawInfos: TokenAccountRaw[] = []; 23 | 24 | for (const { pubkey, account } of tokenAccountResp.value) { 25 | const accountInfo = splAccountLayout.decode(account.data); 26 | const { mint, amount } = accountInfo; 27 | tokenAccounts.push({ 28 | publicKey: pubkey, 29 | mint, 30 | amount, 31 | isAssociated: getATAAddress(owner, mint, account.owner).publicKey.equals(pubkey), 32 | isNative: false, 33 | programId: account.owner, 34 | }); 35 | // todo programId should get from api 36 | tokenAccountRawInfos.push({ pubkey, accountInfo, programId: account.owner }); 37 | } 38 | 39 | if (solAccountResp) { 40 | tokenAccounts.push({ 41 | mint: PublicKey.default, 42 | amount: new BN(String(solAccountResp.lamports)), 43 | isNative: true, 44 | programId: solAccountResp.owner, 45 | }); 46 | } 47 | 48 | return { 49 | tokenAccounts, 50 | tokenAccountRawInfos, 51 | }; 52 | } 53 | 54 | export function generatePubKey({ 55 | fromPublicKey, 56 | programId = TOKEN_PROGRAM_ID, 57 | assignSeed, 58 | }: { 59 | fromPublicKey: PublicKey; 60 | programId: PublicKey; 61 | assignSeed?: string; 62 | }): { publicKey: PublicKey; seed: string } { 63 | const seed = assignSeed ? btoa(assignSeed).slice(0, 32) : Keypair.generate().publicKey.toBase58().slice(0, 32); 64 | const publicKey = createWithSeed(fromPublicKey, seed, programId); 65 | return { publicKey, seed }; 66 | } 67 | 68 | function createWithSeed(fromPublicKey: PublicKey, seed: string, programId: PublicKey): PublicKey { 69 | const buffer = Buffer.concat([fromPublicKey.toBuffer(), Buffer.from(seed), programId.toBuffer()]); 70 | const publicKeyBytes = sha256(buffer); 71 | return new PublicKey(publicKeyBytes); 72 | } 73 | -------------------------------------------------------------------------------- /src/raydium/clmm/index.ts: -------------------------------------------------------------------------------- 1 | export { Clmm } from "./clmm"; 2 | export * from "./type"; 3 | export * from "./layout"; 4 | export * from "./instrument"; 5 | export * from "./utils/constants"; 6 | export * from "./utils/math"; 7 | export * from "./utils/pda"; 8 | export * from "./utils/pool"; 9 | export * from "./utils/position"; 10 | export * from "./utils/tick"; 11 | export * from "./utils/tickQuery"; 12 | export * from "./utils/tickarrayBitmap"; 13 | export * from "./utils/util"; 14 | -------------------------------------------------------------------------------- /src/raydium/clmm/layout.ts: -------------------------------------------------------------------------------- 1 | import { blob, bool, i128, i64, publicKey, s32, seq, struct, u128, u16, u32, u64, u8 } from "../../marshmallow"; 2 | 3 | import { TICK_ARRAY_SIZE } from "./utils/tick"; 4 | import { EXTENSION_TICKARRAY_BITMAP_SIZE } from "./utils/tickarrayBitmap"; 5 | 6 | export const ClmmConfigLayout = struct([ 7 | blob(8), 8 | u8("bump"), 9 | u16("index"), 10 | publicKey(""), 11 | u32("protocolFeeRate"), 12 | u32("tradeFeeRate"), 13 | u16("tickSpacing"), 14 | seq(u64(), 8, ""), 15 | ]); 16 | 17 | export const ObservationLayout = struct([u32("blockTimestamp"), i64("tickCumulative"), seq(u64(), 4)]); 18 | export const ObservationInfoLayout = struct([ 19 | blob(8), 20 | bool("initialized"), 21 | u64("recentEpoch"), 22 | u16("observationIndex"), 23 | publicKey("poolId"), 24 | seq(ObservationLayout, 100, "observations"), 25 | seq(u64(), 4), 26 | ]); 27 | 28 | export const RewardInfo = struct([ 29 | u8("rewardState"), 30 | u64("openTime"), 31 | u64("endTime"), 32 | u64("lastUpdateTime"), 33 | u128("emissionsPerSecondX64"), 34 | u64("rewardTotalEmissioned"), 35 | u64("rewardClaimed"), 36 | publicKey("tokenMint"), 37 | publicKey("tokenVault"), 38 | publicKey("creator"), 39 | u128("rewardGrowthGlobalX64"), 40 | ]); 41 | export const PoolInfoLayout = struct([ 42 | blob(8), 43 | u8("bump"), 44 | publicKey("ammConfig"), 45 | publicKey("creator"), 46 | publicKey("mintA"), 47 | publicKey("mintB"), 48 | publicKey("vaultA"), 49 | publicKey("vaultB"), 50 | publicKey("observationId"), 51 | u8("mintDecimalsA"), 52 | u8("mintDecimalsB"), 53 | u16("tickSpacing"), 54 | u128("liquidity"), 55 | u128("sqrtPriceX64"), 56 | s32("tickCurrent"), 57 | u32(), 58 | u128("feeGrowthGlobalX64A"), 59 | u128("feeGrowthGlobalX64B"), 60 | u64("protocolFeesTokenA"), 61 | u64("protocolFeesTokenB"), 62 | 63 | u128("swapInAmountTokenA"), 64 | u128("swapOutAmountTokenB"), 65 | u128("swapInAmountTokenB"), 66 | u128("swapOutAmountTokenA"), 67 | 68 | u8("status"), 69 | 70 | seq(u8(), 7, ""), 71 | 72 | seq(RewardInfo, 3, "rewardInfos"), 73 | seq(u64(), 16, "tickArrayBitmap"), 74 | 75 | u64("totalFeesTokenA"), 76 | u64("totalFeesClaimedTokenA"), 77 | u64("totalFeesTokenB"), 78 | u64("totalFeesClaimedTokenB"), 79 | 80 | u64("fundFeesTokenA"), 81 | u64("fundFeesTokenB"), 82 | 83 | u64("startTime"), 84 | 85 | seq(u64(), 15 * 4 - 3, "padding"), 86 | ]); 87 | 88 | export const PositionRewardInfoLayout = struct([u128("growthInsideLastX64"), u64("rewardAmountOwed")]); 89 | export const PositionInfoLayout = struct([ 90 | blob(8), 91 | u8("bump"), 92 | publicKey("nftMint"), 93 | publicKey("poolId"), 94 | 95 | s32("tickLower"), 96 | s32("tickUpper"), 97 | u128("liquidity"), 98 | u128("feeGrowthInsideLastX64A"), 99 | u128("feeGrowthInsideLastX64B"), 100 | u64("tokenFeesOwedA"), 101 | u64("tokenFeesOwedB"), 102 | 103 | seq(PositionRewardInfoLayout, 3, "rewardInfos"), 104 | 105 | seq(u64(), 8, ""), 106 | ]); 107 | 108 | export type ClmmPositionLayout = ReturnType; 109 | 110 | export const ProtocolPositionLayout = struct([ 111 | blob(8), 112 | u8("bump"), 113 | publicKey("poolId"), 114 | s32("tickLowerIndex"), 115 | s32("tickUpperIndex"), 116 | u128("liquidity"), 117 | u128("feeGrowthInsideLastX64A"), 118 | u128("feeGrowthInsideLastX64B"), 119 | u64("tokenFeesOwedA"), 120 | u64("tokenFeesOwedB"), 121 | seq(u128(), 3, "rewardGrowthInside"), 122 | 123 | seq(u64(), 8, ""), 124 | ]); 125 | 126 | export const TickLayout = struct([ 127 | s32("tick"), 128 | i128("liquidityNet"), 129 | u128("liquidityGross"), 130 | u128("feeGrowthOutsideX64A"), 131 | u128("feeGrowthOutsideX64B"), 132 | seq(u128(), 3, "rewardGrowthsOutsideX64"), 133 | 134 | seq(u32(), 13, ""), 135 | ]); 136 | 137 | export const TickArrayLayout = struct([ 138 | blob(8), 139 | publicKey("poolId"), 140 | s32("startTickIndex"), 141 | seq(TickLayout, TICK_ARRAY_SIZE, "ticks"), 142 | u8("initializedTickCount"), 143 | 144 | seq(u8(), 115, ""), 145 | ]); 146 | 147 | export const OperationLayout = struct([blob(329), seq(publicKey(), 100, "whitelistMints")]); 148 | 149 | export const TickArrayBitmapExtensionLayout = struct([ 150 | blob(8), 151 | publicKey("poolId"), 152 | seq(seq(u64(), 8), EXTENSION_TICKARRAY_BITMAP_SIZE, "positiveTickArrayBitmap"), 153 | seq(seq(u64(), 8), EXTENSION_TICKARRAY_BITMAP_SIZE, "negativeTickArrayBitmap"), 154 | ]); 155 | 156 | export const LockPositionLayout = struct([ 157 | u64(), 158 | u8("bump"), 159 | publicKey("owner"), 160 | publicKey("poolId"), 161 | publicKey("positionId"), 162 | publicKey("nftAccount"), 163 | seq(u64(), 8), 164 | ]); 165 | 166 | export const LockClPositionLayoutV2 = struct([ 167 | blob(8), 168 | u8("bump"), 169 | publicKey("lockOwner"), 170 | publicKey("poolId"), 171 | publicKey("positionId"), 172 | publicKey("nftAccount"), 173 | publicKey("lockNftMint"), 174 | u64("recentEpoch"), 175 | seq(u64(), 8), 176 | ]); 177 | -------------------------------------------------------------------------------- /src/raydium/clmm/utils/constants.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | 3 | export const ZERO = new BN(0); 4 | export const ONE = new BN(1); 5 | export const NEGATIVE_ONE = new BN(-1); 6 | 7 | export const Q64 = new BN(1).shln(64); 8 | export const Q128 = new BN(1).shln(128); 9 | 10 | export const MaxU64 = Q64.sub(ONE); 11 | 12 | export const U64Resolution = 64; 13 | 14 | export const MaxUint128 = Q128.subn(1); 15 | 16 | export const MIN_TICK = -443636; 17 | export const MAX_TICK = -MIN_TICK; 18 | 19 | export const MIN_SQRT_PRICE_X64: BN = new BN("4295048016"); 20 | export const MAX_SQRT_PRICE_X64: BN = new BN("79226673521066979257578248091"); 21 | 22 | export const MIN_SQRT_PRICE_X64_ADD_ONE: BN = new BN("4295048017"); 23 | export const MAX_SQRT_PRICE_X64_SUB_ONE: BN = new BN("79226673521066979257578248090"); 24 | 25 | // export const MIN_TICK_ARRAY_START_INDEX = -307200; 26 | // export const MAX_TICK_ARRAY_START_INDEX = 306600; 27 | 28 | export const BIT_PRECISION = 16; 29 | export const LOG_B_2_X32 = "59543866431248"; 30 | export const LOG_B_P_ERR_MARGIN_LOWER_X64 = "184467440737095516"; 31 | export const LOG_B_P_ERR_MARGIN_UPPER_X64 = "15793534762490258745"; 32 | 33 | export const FEE_RATE_DENOMINATOR = new BN(10).pow(new BN(6)); 34 | 35 | export enum Fee { 36 | rate_500 = 500, // 500 / 10e6 = 0.0005 37 | rate_3000 = 3000, // 3000/ 10e6 = 0.003 38 | rate_10000 = 10000, // 10000 /10e6 = 0.01 39 | } 40 | export const TICK_SPACINGS: { [amount in Fee]: number } = { 41 | [Fee.rate_500]: 10, 42 | [Fee.rate_3000]: 60, 43 | [Fee.rate_10000]: 200, 44 | }; 45 | 46 | export const mockCreatePoolInfo = { 47 | version: 6, 48 | liquidity: ZERO, 49 | tickCurrent: 0, 50 | feeGrowthGlobalX64A: ZERO, 51 | feeGrowthGlobalX64B: ZERO, 52 | protocolFeesTokenA: ZERO, 53 | protocolFeesTokenB: ZERO, 54 | swapInAmountTokenA: ZERO, 55 | swapOutAmountTokenB: ZERO, 56 | swapInAmountTokenB: ZERO, 57 | swapOutAmountTokenA: ZERO, 58 | tickArrayBitmap: [], 59 | 60 | rewardInfos: [], 61 | 62 | day: { 63 | volume: 0, 64 | volumeFee: 0, 65 | feeA: 0, 66 | feeB: 0, 67 | feeApr: 0, 68 | rewardApr: { A: 0, B: 0, C: 0 }, 69 | apr: 0, 70 | priceMax: 0, 71 | priceMin: 0, 72 | }, 73 | week: { 74 | volume: 0, 75 | volumeFee: 0, 76 | feeA: 0, 77 | feeB: 0, 78 | feeApr: 0, 79 | rewardApr: { A: 0, B: 0, C: 0 }, 80 | apr: 0, 81 | priceMax: 0, 82 | priceMin: 0, 83 | }, 84 | month: { 85 | volume: 0, 86 | volumeFee: 0, 87 | feeA: 0, 88 | feeB: 0, 89 | feeApr: 0, 90 | rewardApr: { A: 0, B: 0, C: 0 }, 91 | apr: 0, 92 | priceMax: 0, 93 | priceMin: 0, 94 | }, 95 | tvl: 0, 96 | }; 97 | 98 | export const mockV3CreatePoolInfo = { 99 | tvl: 0, 100 | volumeQuote: 0, 101 | mintAmountA: 0, 102 | mintAmountB: 0, 103 | rewardDefaultInfos: [], 104 | farmUpcomingCount: 0, 105 | farmOngoingCount: 0, 106 | farmFinishedCount: 0, 107 | 108 | day: { 109 | volume: 0, 110 | volumeQuote: 0, 111 | volumeFee: 0, 112 | apr: 0, 113 | feeApr: 0, 114 | priceMin: 0, 115 | priceMax: 0, 116 | rewardApr: [0], 117 | }, 118 | week: { 119 | volume: 0, 120 | volumeQuote: 0, 121 | volumeFee: 0, 122 | apr: 0, 123 | feeApr: 0, 124 | priceMin: 0, 125 | priceMax: 0, 126 | rewardApr: [0], 127 | }, 128 | month: { 129 | volume: 0, 130 | volumeQuote: 0, 131 | volumeFee: 0, 132 | apr: 0, 133 | feeApr: 0, 134 | priceMin: 0, 135 | priceMax: 0, 136 | rewardApr: [0], 137 | }, 138 | pooltype: [], 139 | }; 140 | 141 | export const U64_IGNORE_RANGE = new BN("18446744073700000000"); 142 | -------------------------------------------------------------------------------- /src/raydium/clmm/utils/pda.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { findProgramAddress, METADATA_PROGRAM_ID } from "../../../common"; 4 | 5 | import { i32ToBytes, u16ToBytes } from "./util"; 6 | 7 | export const AMM_CONFIG_SEED = Buffer.from("amm_config", "utf8"); 8 | export const POOL_SEED = Buffer.from("pool", "utf8"); 9 | export const POOL_VAULT_SEED = Buffer.from("pool_vault", "utf8"); 10 | export const POOL_REWARD_VAULT_SEED = Buffer.from("pool_reward_vault", "utf8"); 11 | export const POSITION_SEED = Buffer.from("position", "utf8"); 12 | export const TICK_ARRAY_SEED = Buffer.from("tick_array", "utf8"); 13 | export const OPERATION_SEED = Buffer.from("operation", "utf8"); 14 | export const POOL_TICK_ARRAY_BITMAP_SEED = Buffer.from("pool_tick_array_bitmap_extension", "utf8"); 15 | export const OBSERVATION_SEED = Buffer.from("observation", "utf8"); 16 | 17 | export function getPdaAmmConfigId( 18 | programId: PublicKey, 19 | index: number, 20 | ): { 21 | publicKey: PublicKey; 22 | nonce: number; 23 | } { 24 | return findProgramAddress([AMM_CONFIG_SEED, u16ToBytes(index)], programId); 25 | } 26 | 27 | export function getPdaPoolId( 28 | programId: PublicKey, 29 | ammConfigId: PublicKey, 30 | mintA: PublicKey, 31 | mintB: PublicKey, 32 | ): { 33 | publicKey: PublicKey; 34 | nonce: number; 35 | } { 36 | return findProgramAddress([POOL_SEED, ammConfigId.toBuffer(), mintA.toBuffer(), mintB.toBuffer()], programId); 37 | } 38 | 39 | export function getPdaPoolVaultId( 40 | programId: PublicKey, 41 | poolId: PublicKey, 42 | vaultMint: PublicKey, 43 | ): { 44 | publicKey: PublicKey; 45 | nonce: number; 46 | } { 47 | return findProgramAddress([POOL_VAULT_SEED, poolId.toBuffer(), vaultMint.toBuffer()], programId); 48 | } 49 | 50 | export function getPdaPoolRewardVaulId( 51 | programId: PublicKey, 52 | poolId: PublicKey, 53 | rewardMint: PublicKey, 54 | ): { 55 | publicKey: PublicKey; 56 | nonce: number; 57 | } { 58 | return findProgramAddress([POOL_REWARD_VAULT_SEED, poolId.toBuffer(), rewardMint.toBuffer()], programId); 59 | } 60 | 61 | export function getPdaTickArrayAddress( 62 | programId: PublicKey, 63 | poolId: PublicKey, 64 | startIndex: number, 65 | ): { 66 | publicKey: PublicKey; 67 | nonce: number; 68 | } { 69 | return findProgramAddress([TICK_ARRAY_SEED, poolId.toBuffer(), i32ToBytes(startIndex)], programId); 70 | } 71 | 72 | export function getPdaProtocolPositionAddress( 73 | programId: PublicKey, 74 | poolId: PublicKey, 75 | tickLower: number, 76 | tickUpper: number, 77 | ): { 78 | publicKey: PublicKey; 79 | nonce: number; 80 | } { 81 | return findProgramAddress( 82 | [POSITION_SEED, poolId.toBuffer(), i32ToBytes(tickLower), i32ToBytes(tickUpper)], 83 | programId, 84 | ); 85 | } 86 | 87 | export function getPdaPersonalPositionAddress( 88 | programId: PublicKey, 89 | nftMint: PublicKey, 90 | ): { 91 | publicKey: PublicKey; 92 | nonce: number; 93 | } { 94 | return findProgramAddress([POSITION_SEED, nftMint.toBuffer()], programId); 95 | } 96 | 97 | export function getPdaMetadataKey(mint: PublicKey): { 98 | publicKey: PublicKey; 99 | nonce: number; 100 | } { 101 | return findProgramAddress( 102 | [Buffer.from("metadata", "utf8"), METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer()], 103 | METADATA_PROGRAM_ID, 104 | ); 105 | } 106 | 107 | export function getPdaOperationAccount(programId: PublicKey): { 108 | publicKey: PublicKey; 109 | nonce: number; 110 | } { 111 | return findProgramAddress([OPERATION_SEED], programId); 112 | } 113 | 114 | export function getPdaExBitmapAccount( 115 | programId: PublicKey, 116 | poolId: PublicKey, 117 | ): { 118 | publicKey: PublicKey; 119 | nonce: number; 120 | } { 121 | return findProgramAddress([POOL_TICK_ARRAY_BITMAP_SEED, poolId.toBuffer()], programId); 122 | } 123 | 124 | export function getPdaObservationAccount( 125 | programId: PublicKey, 126 | poolId: PublicKey, 127 | ): { 128 | publicKey: PublicKey; 129 | nonce: number; 130 | } { 131 | return findProgramAddress([OBSERVATION_SEED, poolId.toBuffer()], programId); 132 | } 133 | 134 | export const POOL_LOCK_ID_SEED = Buffer.from("locked_position", "utf8"); 135 | export function getPdaLockPositionId( 136 | programId: PublicKey, 137 | positionId: PublicKey, 138 | ): { 139 | publicKey: PublicKey; 140 | nonce: number; 141 | } { 142 | return findProgramAddress([POOL_LOCK_ID_SEED, positionId.toBuffer()], programId); 143 | } 144 | 145 | export function getPdaLockClPositionIdV2( 146 | programId: PublicKey, 147 | lockNftMint: PublicKey, 148 | ): { 149 | publicKey: PublicKey; 150 | nonce: number; 151 | } { 152 | return findProgramAddress([POOL_LOCK_ID_SEED, lockNftMint.toBuffer()], programId); 153 | } 154 | 155 | export const SUPPORT_MINT_SEED = Buffer.from("support_mint", "utf8"); 156 | export function getPdaMintExAccount( 157 | programId: PublicKey, 158 | mintAddress: PublicKey, 159 | ): { 160 | publicKey: PublicKey; 161 | nonce: number; 162 | } { 163 | return findProgramAddress([SUPPORT_MINT_SEED, mintAddress.toBuffer()], programId); 164 | } 165 | -------------------------------------------------------------------------------- /src/raydium/clmm/utils/util.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | export function u16ToBytes(num: number): Uint8Array { 3 | const arr = new ArrayBuffer(2); 4 | const view = new DataView(arr); 5 | view.setUint16(0, num, false); 6 | return new Uint8Array(arr); 7 | } 8 | 9 | export function i16ToBytes(num: number): Uint8Array { 10 | const arr = new ArrayBuffer(2); 11 | const view = new DataView(arr); 12 | view.setInt16(0, num, false); 13 | return new Uint8Array(arr); 14 | } 15 | 16 | export function u32ToBytes(num: number): Uint8Array { 17 | const arr = new ArrayBuffer(4); 18 | const view = new DataView(arr); 19 | view.setUint32(0, num, false); 20 | return new Uint8Array(arr); 21 | } 22 | 23 | export function i32ToBytes(num: number): Uint8Array { 24 | const arr = new ArrayBuffer(4); 25 | const view = new DataView(arr); 26 | view.setInt32(0, num, false); 27 | return new Uint8Array(arr); 28 | } 29 | 30 | export function leadingZeros(bitNum: number, data: BN): number { 31 | let i = 0; 32 | for (let j = bitNum - 1; j >= 0; j--) { 33 | if (!data.testn(j)) { 34 | i++; 35 | } else { 36 | break; 37 | } 38 | } 39 | return i; 40 | } 41 | 42 | export function trailingZeros(bitNum: number, data: BN) { 43 | let i = 0; 44 | for (let j = 0; j < bitNum; j++) { 45 | if (!data.testn(j)) { 46 | i++; 47 | } else { 48 | break; 49 | } 50 | } 51 | return i; 52 | } 53 | 54 | export function isZero(bitNum: number, data: BN): boolean { 55 | for (let i = 0; i < bitNum; i++) { 56 | if (data.testn(i)) return false; 57 | } 58 | return true; 59 | } 60 | 61 | export function mostSignificantBit(bitNum: number, data: BN): number | null { 62 | if (isZero(bitNum, data)) return null; 63 | else return leadingZeros(bitNum, data); 64 | } 65 | 66 | export function leastSignificantBit(bitNum: number, data: BN): number | null { 67 | if (isZero(bitNum, data)) return null; 68 | else return trailingZeros(bitNum, data); 69 | } 70 | -------------------------------------------------------------------------------- /src/raydium/cpmm/curve/calculator.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import Decimal from "decimal.js-light"; 4 | import { ApiV3Token } from "../../../api/type"; 5 | import { BNDivCeil } from "../../../common"; 6 | import { ConstantProductCurve } from "./constantProduct"; 7 | import { CpmmFee } from "./fee"; 8 | 9 | export enum RoundDirection { 10 | Floor, 11 | Ceiling, 12 | } 13 | 14 | export type SwapWithoutFeesResult = { destinationAmountSwapped: BN }; 15 | 16 | export type TradingTokenResult = { tokenAmount0: BN; tokenAmount1: BN }; 17 | 18 | export type SwapResult = { 19 | newSwapDestinationAmount: BN; 20 | sourceAmountSwapped: BN; 21 | destinationAmountSwapped: BN; 22 | tradeFee: BN; 23 | }; 24 | 25 | export class CurveCalculator { 26 | static validate_supply(tokenAmount0: BN, tokenAmount1: BN): void { 27 | if (tokenAmount0.isZero()) throw Error("tokenAmount0 is zero"); 28 | if (tokenAmount1.isZero()) throw Error("tokenAmount1 is zero"); 29 | } 30 | 31 | static swap(sourceAmount: BN, swapSourceAmount: BN, swapDestinationAmount: BN, tradeFeeRate: BN): SwapResult { 32 | const tradeFee = CpmmFee.tradingFee(sourceAmount, tradeFeeRate); 33 | 34 | const sourceAmountLessFees = sourceAmount.sub(tradeFee); 35 | 36 | const { destinationAmountSwapped } = ConstantProductCurve.swapWithoutFees( 37 | sourceAmountLessFees, 38 | swapSourceAmount, 39 | swapDestinationAmount, 40 | ); 41 | 42 | return { 43 | newSwapDestinationAmount: swapDestinationAmount.sub(destinationAmountSwapped), 44 | sourceAmountSwapped: sourceAmount, 45 | destinationAmountSwapped, 46 | tradeFee, 47 | }; 48 | } 49 | 50 | static swapBaseOut({ 51 | poolMintA, 52 | poolMintB, 53 | tradeFeeRate, 54 | baseReserve, 55 | quoteReserve, 56 | outputMint, 57 | outputAmount, 58 | }: { 59 | poolMintA: ApiV3Token; 60 | poolMintB: ApiV3Token; 61 | tradeFeeRate: BN; 62 | baseReserve: BN; 63 | quoteReserve: BN; 64 | outputMint: string | PublicKey; 65 | outputAmount: BN; 66 | }): { 67 | amountRealOut: BN; 68 | 69 | amountIn: BN; 70 | amountInWithoutFee: BN; 71 | 72 | tradeFee: BN; 73 | priceImpact: number; 74 | } { 75 | const [reserveInAmount, reserveOutAmount, reserveInDecimals, reserveOutDecimals, inputMint] = 76 | poolMintB.address === outputMint.toString() 77 | ? [baseReserve, quoteReserve, poolMintA.decimals, poolMintB.decimals, poolMintA.address] 78 | : [quoteReserve, baseReserve, poolMintB.decimals, poolMintA.decimals, poolMintB.address]; 79 | const currentPrice = new Decimal(reserveOutAmount.toString()) 80 | .div(10 ** reserveOutDecimals) 81 | .div(new Decimal(reserveInAmount.toString()).div(10 ** reserveInDecimals)); 82 | const amountRealOut = outputAmount.gte(reserveOutAmount) ? reserveOutAmount.sub(new BN(1)) : outputAmount; 83 | 84 | const denominator = reserveOutAmount.sub(amountRealOut); 85 | const amountInWithoutFee = BNDivCeil(reserveInAmount.mul(amountRealOut), denominator); 86 | const amountIn = BNDivCeil(amountInWithoutFee.mul(new BN(1_000_000)), new BN(1_000_000).sub(tradeFeeRate)); 87 | const fee = amountIn.sub(amountInWithoutFee); 88 | const executionPrice = new Decimal(amountRealOut.toString()) 89 | .div(10 ** reserveOutDecimals) 90 | .div(new Decimal(amountIn.toString()).div(10 ** reserveInDecimals)); 91 | const priceImpact = currentPrice.isZero() ? 0 : executionPrice.sub(currentPrice).div(currentPrice).abs().toNumber(); 92 | 93 | return { 94 | amountRealOut, 95 | 96 | amountIn, 97 | amountInWithoutFee, 98 | 99 | tradeFee: fee, 100 | priceImpact, 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/raydium/cpmm/curve/constantProduct.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { RoundDirection, SwapWithoutFeesResult, TradingTokenResult } from "./calculator"; 3 | 4 | function checkedRem(dividend: BN, divisor: BN): BN { 5 | if (divisor.isZero()) throw Error("divisor is zero"); 6 | 7 | const result = dividend.mod(divisor); 8 | return result; 9 | } 10 | 11 | function checkedCeilDiv(dividend: BN, rhs: BN): BN[] { 12 | if (rhs.isZero()) throw Error("rhs is zero"); 13 | 14 | let quotient = dividend.div(rhs); 15 | 16 | if (quotient.isZero()) throw Error("quotient is zero"); 17 | 18 | let remainder = checkedRem(dividend, rhs); 19 | 20 | if (remainder.gt(ZERO)) { 21 | quotient = quotient.add(new BN(1)); 22 | 23 | rhs = dividend.div(quotient); 24 | remainder = checkedRem(dividend, quotient); 25 | if (remainder.gt(ZERO)) { 26 | rhs = rhs.add(new BN(1)); 27 | } 28 | } 29 | return [quotient, rhs]; 30 | } 31 | 32 | const ZERO = new BN(0); 33 | 34 | export class ConstantProductCurve { 35 | static swapWithoutFees(sourceAmount: BN, swapSourceAmount: BN, swapDestinationAmount: BN): SwapWithoutFeesResult { 36 | const invariant = swapSourceAmount.mul(swapDestinationAmount); 37 | 38 | const newSwapSourceAmount = swapSourceAmount.add(sourceAmount); 39 | const [newSwapDestinationAmount] = checkedCeilDiv(invariant, newSwapSourceAmount); 40 | 41 | const destinationAmountSwapped = swapDestinationAmount.sub(newSwapDestinationAmount); 42 | if (destinationAmountSwapped.isZero()) throw Error("destinationAmountSwapped is zero"); 43 | 44 | return { 45 | destinationAmountSwapped, 46 | }; 47 | } 48 | 49 | static lpTokensToTradingTokens( 50 | lpTokenAmount: BN, 51 | lpTokenSupply: BN, 52 | swapTokenAmount0: BN, 53 | swapTokenAmount1: BN, 54 | roundDirection: RoundDirection, 55 | ): TradingTokenResult { 56 | let tokenAmount0 = lpTokenAmount.mul(swapTokenAmount0).div(lpTokenSupply); 57 | let tokenAmount1 = lpTokenAmount.mul(swapTokenAmount1).div(lpTokenSupply); 58 | 59 | if (roundDirection === RoundDirection.Floor) { 60 | return { tokenAmount0, tokenAmount1 }; 61 | } else if (roundDirection === RoundDirection.Ceiling) { 62 | const tokenRemainder0 = checkedRem(lpTokenAmount.mul(swapTokenAmount0), lpTokenSupply); 63 | 64 | if (tokenRemainder0.gt(ZERO) && tokenAmount0.gt(ZERO)) { 65 | tokenAmount0 = tokenAmount0.add(new BN(1)); 66 | } 67 | 68 | const token1Remainder = checkedRem(lpTokenAmount.mul(swapTokenAmount1), lpTokenSupply); 69 | 70 | if (token1Remainder.gt(ZERO) && tokenAmount1.gt(ZERO)) { 71 | tokenAmount1 = tokenAmount1.add(new BN(1)); 72 | } 73 | 74 | return { tokenAmount0, tokenAmount1 }; 75 | } 76 | throw Error("roundDirection value error"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/raydium/cpmm/curve/fee.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { ceilDiv, floorDiv } from "@/common/bignumber"; 3 | import { FEE_RATE_DENOMINATOR_VALUE } from "@/common/fee"; 4 | 5 | export class CpmmFee { 6 | static tradingFee(amount: BN, tradeFeeRate: BN): BN { 7 | return ceilDiv(amount, tradeFeeRate, FEE_RATE_DENOMINATOR_VALUE); 8 | } 9 | static protocolFee(amount: BN, protocolFeeRate: BN): BN { 10 | return floorDiv(amount, protocolFeeRate, FEE_RATE_DENOMINATOR_VALUE); 11 | } 12 | static fundFee(amount: BN, fundFeeRate: BN): BN { 13 | return floorDiv(amount, fundFeeRate, FEE_RATE_DENOMINATOR_VALUE); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/raydium/cpmm/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./layout"; 2 | export * from "./type"; 3 | export * from "./instruction"; 4 | export * from "./pda"; 5 | export * from "./instruction"; 6 | export * from "./curve/calculator"; 7 | export * from "./curve/constantProduct"; 8 | export * from "./curve/fee"; 9 | -------------------------------------------------------------------------------- /src/raydium/cpmm/layout.ts: -------------------------------------------------------------------------------- 1 | import { blob, bool, publicKey, seq, struct, u16, u64, u8, u128 } from "../../marshmallow"; 2 | 3 | export const CpmmConfigInfoLayout = struct([ 4 | blob(8), 5 | u8("bump"), 6 | bool("disableCreatePool"), 7 | u16("index"), 8 | u64("tradeFeeRate"), 9 | u64("protocolFeeRate"), 10 | u64("fundFeeRate"), 11 | u64("createPoolFee"), 12 | 13 | publicKey("protocolOwner"), 14 | publicKey("fundOwner"), 15 | seq(u64(), 16), 16 | ]); 17 | 18 | export const CpmmPoolInfoLayout = struct([ 19 | blob(8), 20 | 21 | publicKey("configId"), 22 | publicKey("poolCreator"), 23 | publicKey("vaultA"), 24 | publicKey("vaultB"), 25 | 26 | publicKey("mintLp"), 27 | publicKey("mintA"), 28 | publicKey("mintB"), 29 | 30 | publicKey("mintProgramA"), 31 | publicKey("mintProgramB"), 32 | 33 | publicKey("observationId"), 34 | 35 | u8("bump"), 36 | u8("status"), 37 | 38 | u8("lpDecimals"), 39 | u8("mintDecimalA"), 40 | u8("mintDecimalB"), 41 | 42 | u64("lpAmount"), 43 | u64("protocolFeesMintA"), 44 | u64("protocolFeesMintB"), 45 | u64("fundFeesMintA"), 46 | u64("fundFeesMintB"), 47 | u64("openTime"), 48 | 49 | seq(u64(), 32), 50 | ]); 51 | -------------------------------------------------------------------------------- /src/raydium/cpmm/pda.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { findProgramAddress } from "../../common/txTool/txUtils"; 3 | 4 | const AUTH_SEED = Buffer.from("vault_and_lp_mint_auth_seed", "utf8"); 5 | const AMM_CONFIG_SEED = Buffer.from("amm_config", "utf8"); 6 | const POOL_SEED = Buffer.from("pool", "utf8"); 7 | const POOL_LP_MINT_SEED = Buffer.from("pool_lp_mint", "utf8"); 8 | const POOL_VAULT_SEED = Buffer.from("pool_vault", "utf8"); 9 | const OBSERVATION_SEED = Buffer.from("observation", "utf8"); 10 | 11 | export function getPdaPoolAuthority(programId: PublicKey): { 12 | publicKey: PublicKey; 13 | nonce: number; 14 | } { 15 | return findProgramAddress([AUTH_SEED], programId); 16 | } 17 | 18 | export function getCpmmPdaAmmConfigId( 19 | programId: PublicKey, 20 | index: number, 21 | ): { 22 | publicKey: PublicKey; 23 | nonce: number; 24 | } { 25 | return findProgramAddress([AMM_CONFIG_SEED, u16ToBytes(index)], programId); 26 | } 27 | 28 | export function getCpmmPdaPoolId( 29 | programId: PublicKey, 30 | ammConfigId: PublicKey, 31 | mintA: PublicKey, 32 | mintB: PublicKey, 33 | ): { 34 | publicKey: PublicKey; 35 | nonce: number; 36 | } { 37 | return findProgramAddress([POOL_SEED, ammConfigId.toBuffer(), mintA.toBuffer(), mintB.toBuffer()], programId); 38 | } 39 | 40 | export function getPdaLpMint( 41 | programId: PublicKey, 42 | poolId: PublicKey, 43 | ): { 44 | publicKey: PublicKey; 45 | nonce: number; 46 | } { 47 | return findProgramAddress([POOL_LP_MINT_SEED, poolId.toBuffer()], programId); 48 | } 49 | 50 | export function getPdaVault( 51 | programId: PublicKey, 52 | poolId: PublicKey, 53 | mint: PublicKey, 54 | ): { 55 | publicKey: PublicKey; 56 | nonce: number; 57 | } { 58 | return findProgramAddress([POOL_VAULT_SEED, poolId.toBuffer(), mint.toBuffer()], programId); 59 | } 60 | 61 | export function getPdaObservationId( 62 | programId: PublicKey, 63 | poolId: PublicKey, 64 | ): { 65 | publicKey: PublicKey; 66 | nonce: number; 67 | } { 68 | return findProgramAddress([OBSERVATION_SEED, poolId.toBuffer()], programId); 69 | } 70 | 71 | function u16ToBytes(num: number): Uint8Array { 72 | const arr = new ArrayBuffer(2); 73 | const view = new DataView(arr); 74 | view.setUint16(0, num, false); 75 | return new Uint8Array(arr); 76 | } 77 | 78 | export function getCreatePoolKeys({ 79 | poolId: propPoolId, 80 | programId, 81 | configId, 82 | mintA, 83 | mintB, 84 | }: { 85 | poolId?: PublicKey; 86 | programId: PublicKey; 87 | configId: PublicKey; 88 | mintA: PublicKey; 89 | mintB: PublicKey; 90 | }): { 91 | poolId: PublicKey; 92 | configId: PublicKey; 93 | authority: PublicKey; 94 | lpMint: PublicKey; 95 | vaultA: PublicKey; 96 | vaultB: PublicKey; 97 | observationId: PublicKey; 98 | } { 99 | // const configId = getCpmmPdaAmmConfigId(programId, 0).publicKey; 100 | const authority = getPdaPoolAuthority(programId).publicKey; 101 | const poolId = propPoolId || getCpmmPdaPoolId(programId, configId, mintA, mintB).publicKey; 102 | const lpMint = getPdaLpMint(programId, poolId).publicKey; 103 | const vaultA = getPdaVault(programId, poolId, mintA).publicKey; 104 | const vaultB = getPdaVault(programId, poolId, mintB).publicKey; 105 | const observationId = getPdaObservationId(programId, poolId).publicKey; 106 | 107 | return { 108 | poolId, 109 | configId, 110 | authority, 111 | lpMint, 112 | vaultA, 113 | vaultB, 114 | observationId, 115 | }; 116 | } 117 | 118 | export const LOCK_LIQUIDITY_SEED = Buffer.from("locked_liquidity", "utf8"); 119 | 120 | export function getCpLockPda( 121 | programId: PublicKey, 122 | mint: PublicKey, 123 | ): { 124 | publicKey: PublicKey; 125 | nonce: number; 126 | } { 127 | return findProgramAddress([LOCK_LIQUIDITY_SEED, mint.toBuffer()], programId); 128 | } 129 | -------------------------------------------------------------------------------- /src/raydium/cpmm/type.ts: -------------------------------------------------------------------------------- 1 | import { EpochInfo, PublicKey } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import Decimal from "decimal.js"; 4 | import { ApiCpmmConfigInfo, ApiV3PoolInfoStandardItemCpmm, ApiV3Token, CpmmKeys } from "../../api/type"; 5 | import { TxVersion } from "../../common/txTool/txType"; 6 | import { Percent } from "../../module"; 7 | import { ComputeBudgetConfig, GetTransferAmountFee, TxTipConfig } from "../../raydium/type"; 8 | import { SwapResult } from "./curve/calculator"; 9 | import { CpmmPoolInfoLayout } from "./layout"; 10 | 11 | export interface CpmmConfigInfoInterface { 12 | bump: number; 13 | disableCreatePool: boolean; 14 | index: number; 15 | tradeFeeRate: BN; 16 | protocolFeeRate: BN; 17 | fundFeeRate: BN; 18 | createPoolFee: BN; 19 | 20 | protocolOwner: PublicKey; 21 | fundOwner: PublicKey; 22 | } 23 | 24 | export interface CpmmPoolInfoInterface { 25 | configId: PublicKey; 26 | poolCreator: PublicKey; 27 | vaultA: PublicKey; 28 | vaultB: PublicKey; 29 | 30 | mintLp: PublicKey; 31 | mintA: PublicKey; 32 | mintB: PublicKey; 33 | 34 | mintProgramA: PublicKey; 35 | mintProgramB: PublicKey; 36 | 37 | observationId: PublicKey; 38 | 39 | bump: number; 40 | status: number; 41 | 42 | lpDecimals: number; 43 | mintDecimalA: number; 44 | mintDecimalB: number; 45 | 46 | lpAmount: BN; 47 | protocolFeesMintA: BN; 48 | protocolFeesMintB: BN; 49 | fundFeesMintA: BN; 50 | fundFeesMintB: BN; 51 | openTime: BN; 52 | } 53 | 54 | export interface CreateCpmmPoolParam { 55 | poolId?: PublicKey; 56 | programId: PublicKey; 57 | poolFeeAccount: PublicKey; 58 | mintA: Pick; 59 | mintB: Pick; 60 | mintAAmount: BN; 61 | mintBAmount: BN; 62 | startTime: BN; 63 | feeConfig: ApiCpmmConfigInfo; 64 | 65 | associatedOnly: boolean; 66 | checkCreateATAOwner?: boolean; 67 | 68 | ownerInfo: { 69 | feePayer?: PublicKey; 70 | useSOLBalance?: boolean; // if has WSOL mint 71 | }; 72 | computeBudgetConfig?: ComputeBudgetConfig; 73 | txVersion?: T; 74 | txTipConfig?: TxTipConfig; 75 | feePayer?: PublicKey; 76 | } 77 | 78 | export interface CreateCpmmPoolAddress { 79 | poolId: PublicKey; 80 | configId: PublicKey; 81 | authority: PublicKey; 82 | lpMint: PublicKey; 83 | vaultA: PublicKey; 84 | vaultB: PublicKey; 85 | observationId: PublicKey; 86 | mintA: ApiV3Token; 87 | mintB: ApiV3Token; 88 | programId: PublicKey; 89 | poolFeeAccount: PublicKey; 90 | feeConfig: ApiCpmmConfigInfo; 91 | } 92 | 93 | export interface AddCpmmLiquidityParams { 94 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 95 | poolKeys?: CpmmKeys; 96 | payer?: PublicKey; 97 | inputAmount: BN; 98 | baseIn: boolean; 99 | slippage: Percent; 100 | config?: { 101 | bypassAssociatedCheck?: boolean; 102 | checkCreateATAOwner?: boolean; 103 | }; 104 | computeBudgetConfig?: ComputeBudgetConfig; 105 | txTipConfig?: TxTipConfig; 106 | txVersion?: T; 107 | computeResult?: { 108 | inputAmountFee: GetTransferAmountFee; 109 | anotherAmount: GetTransferAmountFee; 110 | maxAnotherAmount: GetTransferAmountFee; 111 | liquidity: BN; 112 | }; 113 | feePayer?: PublicKey; 114 | } 115 | 116 | export interface WithdrawCpmmLiquidityParams { 117 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 118 | poolKeys?: CpmmKeys; 119 | payer?: PublicKey; 120 | lpAmount: BN; 121 | slippage: Percent; 122 | computeBudgetConfig?: ComputeBudgetConfig; 123 | txTipConfig?: TxTipConfig; 124 | txVersion?: T; 125 | feePayer?: PublicKey; 126 | closeWsol?: boolean; 127 | } 128 | 129 | export interface CpmmSwapParams { 130 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 131 | poolKeys?: CpmmKeys; 132 | payer?: PublicKey; 133 | baseIn: boolean; 134 | fixedOut?: boolean; 135 | slippage?: number; 136 | swapResult: Pick; 137 | inputAmount: BN; 138 | 139 | config?: { 140 | bypassAssociatedCheck?: boolean; 141 | checkCreateATAOwner?: boolean; 142 | associatedOnly?: boolean; 143 | }; 144 | computeBudgetConfig?: ComputeBudgetConfig; 145 | txTipConfig?: TxTipConfig; 146 | txVersion?: T; 147 | feePayer?: PublicKey; 148 | } 149 | 150 | export interface ComputePairAmountParams { 151 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 152 | baseReserve: BN; 153 | quoteReserve: BN; 154 | amount: string | Decimal; 155 | slippage: Percent; 156 | epochInfo: EpochInfo; 157 | baseIn?: boolean; 158 | } 159 | 160 | export type CpmmRpcData = ReturnType & { 161 | baseReserve: BN; 162 | quoteReserve: BN; 163 | vaultAAmount: BN; 164 | vaultBAmount: BN; 165 | configInfo?: CpmmConfigInfoInterface; 166 | poolPrice: Decimal; 167 | programId: PublicKey; 168 | }; 169 | 170 | export type CpmmComputeData = { 171 | id: PublicKey; 172 | version: 7; 173 | configInfo: CpmmConfigInfoInterface; 174 | mintA: ApiV3Token; 175 | mintB: ApiV3Token; 176 | authority: PublicKey; 177 | } & Omit; 178 | 179 | export type CpmmLockExtInfo = { 180 | nftMint: PublicKey; 181 | nftAccount: PublicKey; 182 | metadataAccount: PublicKey; 183 | lockPda: PublicKey; 184 | userLpVault: PublicKey; 185 | lockLpVault: PublicKey; 186 | }; 187 | 188 | export interface LockCpmmLpParams { 189 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 190 | poolKeys?: CpmmKeys; 191 | lpAmount: BN; 192 | programId?: PublicKey; 193 | authProgram?: PublicKey; 194 | feePayer?: PublicKey; 195 | feeNftOwner?: PublicKey; 196 | withMetadata?: boolean; 197 | getEphemeralSigners?: (k: number) => any; 198 | computeBudgetConfig?: ComputeBudgetConfig; 199 | txTipConfig?: TxTipConfig; 200 | txVersion?: T; 201 | } 202 | 203 | export interface HarvestLockCpmmLpParams { 204 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 205 | poolKeys?: CpmmKeys; 206 | 207 | nftMint: PublicKey; 208 | lpFeeAmount: BN; 209 | 210 | programId?: PublicKey; 211 | authProgram?: PublicKey; 212 | clmmProgram?: PublicKey; 213 | 214 | cpmmProgram?: { 215 | programId?: PublicKey; 216 | authProgram?: PublicKey; 217 | }; 218 | 219 | feePayer?: PublicKey; 220 | 221 | withMetadata?: boolean; 222 | getEphemeralSigners?: (k: number) => any; 223 | computeBudgetConfig?: ComputeBudgetConfig; 224 | txTipConfig?: TxTipConfig; 225 | txVersion?: T; 226 | closeWsol?: boolean; 227 | } 228 | 229 | export interface CpmmLockNftBasicInfo { 230 | name: string; 231 | symbol: string; 232 | description: string; 233 | external_url: string; 234 | collection: { 235 | name: string; 236 | family: string; 237 | }; 238 | image: string; 239 | } 240 | 241 | export interface CpmmLockNftInfo extends CpmmLockNftBasicInfo { 242 | poolInfo: ApiV3PoolInfoStandardItemCpmm; 243 | positionInfo: { 244 | percentage: number; 245 | usdValue: number; 246 | unclaimedFee: { 247 | lp: number; 248 | amountA: number; 249 | amountB: number; 250 | useValue: number; 251 | }; 252 | }; 253 | } 254 | -------------------------------------------------------------------------------- /src/raydium/farm/config.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { ApiV3Token } from "../../api/type"; 4 | import { createLogger } from "../../common/logger"; 5 | import { FARM_PROGRAM_ID_V3, FARM_PROGRAM_ID_V4, FARM_PROGRAM_ID_V5, FARM_PROGRAM_ID_V6 } from "../../common/programId"; 6 | 7 | import { 8 | FarmLedgerLayout, 9 | farmLedgerLayoutV3_2, 10 | farmLedgerLayoutV5_2, 11 | farmLedgerLayoutV6_1, 12 | FarmStateLayout, 13 | farmStateV3Layout, 14 | farmStateV5Layout, 15 | farmStateV6Layout, 16 | } from "./layout"; 17 | 18 | const logger = createLogger("Raydium_farm_config"); 19 | 20 | export type FarmVersion = 3 | 4 | 5 | 6; 21 | export const FARM_LOCK_MINT = new PublicKey("4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R"); 22 | export const FARM_LOCK_VAULT = new PublicKey("FrspKwj8i3pNmKwXreTveC4fu7KL5ZbGeXdZBe2XViu1"); 23 | 24 | /* ================= index ================= */ 25 | // version => farm state layout 26 | export const FARM_VERSION_TO_STATE_LAYOUT: { 27 | [version in FarmVersion]?: FarmStateLayout; 28 | } = { 29 | 3: farmStateV3Layout, 30 | 5: farmStateV5Layout, 31 | 6: farmStateV6Layout, 32 | }; 33 | 34 | // version => farm ledger layout 35 | export const FARM_VERSION_TO_LEDGER_LAYOUT: { 36 | [version in FarmVersion]?: FarmLedgerLayout; 37 | } = { 38 | 3: farmLedgerLayoutV3_2, 39 | 5: farmLedgerLayoutV5_2, 40 | 6: farmLedgerLayoutV6_1, 41 | }; 42 | 43 | export const isValidFarmVersion = (version: number): boolean => [3, 4, 5, 6].indexOf(version) !== -1; 44 | 45 | export const validateFarmRewards = (params: { 46 | version: number; 47 | rewardInfos: { mint: ApiV3Token }[]; 48 | rewardTokenAccountsPublicKeys: PublicKey[]; 49 | }): (() => string | undefined) => { 50 | const { version, rewardInfos, rewardTokenAccountsPublicKeys } = params; 51 | 52 | const infoMsg = `rewardInfo:${JSON.stringify(rewardInfos)}, rewardAccount:${JSON.stringify( 53 | rewardTokenAccountsPublicKeys, 54 | )}`; 55 | 56 | const validator = { 57 | 3: (): string | undefined => { 58 | if (rewardInfos.length !== 1 || rewardTokenAccountsPublicKeys.length !== 1) { 59 | return `rewardInfos or rewardTokenAccounts lengths not equal 1: ${infoMsg}`; 60 | } 61 | }, 62 | 5: (): string | undefined => { 63 | if (rewardInfos.length !== rewardTokenAccountsPublicKeys.length) { 64 | return `rewardInfos and rewardTokenAccounts lengths not equal: ${infoMsg}`; 65 | } 66 | }, 67 | 6: (): string | undefined => { 68 | if (!rewardTokenAccountsPublicKeys.length || rewardInfos.length !== rewardTokenAccountsPublicKeys.length) { 69 | return `no rewardTokenAccounts or rewardInfos and rewardTokenAccounts lengths not equal: ${infoMsg}`; 70 | } 71 | }, 72 | }; 73 | 74 | return validator[version]?.(); 75 | }; 76 | 77 | export const poolTypeV6 = { "Standard SPL": 0, "Option tokens": 1 }; 78 | 79 | export const FARM_PROGRAM_TO_VERSION: Record = { 80 | [FARM_PROGRAM_ID_V3.toString()]: 3, 81 | [FARM_PROGRAM_ID_V4.toString()]: 4, 82 | [FARM_PROGRAM_ID_V5.toString()]: 5, 83 | [FARM_PROGRAM_ID_V6.toString()]: 6, 84 | }; 85 | -------------------------------------------------------------------------------- /src/raydium/farm/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./layout"; 2 | export * from "./type"; 3 | export * from "./util"; 4 | export * from "./config"; 5 | export * from "./instruction"; 6 | export * from "./pda"; 7 | -------------------------------------------------------------------------------- /src/raydium/farm/pda.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { findProgramAddress } from "../../common/txTool/txUtils"; 4 | 5 | export function getRegistrarAddress( 6 | programId: PublicKey, 7 | realm: PublicKey, 8 | communityTokenMint: PublicKey, 9 | ): { 10 | publicKey: PublicKey; 11 | nonce: number; 12 | } { 13 | return findProgramAddress( 14 | [realm.toBuffer(), Buffer.from("registrar", "utf8"), communityTokenMint.toBuffer()], 15 | programId, 16 | ); 17 | } 18 | 19 | export function getVotingTokenMint( 20 | programId: PublicKey, 21 | poolId: PublicKey, 22 | ): { 23 | publicKey: PublicKey; 24 | nonce: number; 25 | } { 26 | return findProgramAddress([poolId.toBuffer(), Buffer.from("voting_mint_seed", "utf8")], programId); 27 | } 28 | 29 | export function getVotingMintAuthority( 30 | programId: PublicKey, 31 | poolId: PublicKey, 32 | ): { 33 | publicKey: PublicKey; 34 | nonce: number; 35 | } { 36 | return findProgramAddress([poolId.toBuffer()], programId); 37 | } 38 | 39 | export function getVoterAddress( 40 | programId: PublicKey, 41 | registrar: PublicKey, 42 | authority: PublicKey, 43 | ): { 44 | publicKey: PublicKey; 45 | nonce: number; 46 | } { 47 | return findProgramAddress([registrar.toBuffer(), Buffer.from("voter", "utf8"), authority.toBuffer()], programId); 48 | } 49 | 50 | export function getVoterWeightRecordAddress( 51 | programId: PublicKey, 52 | registrar: PublicKey, 53 | authority: PublicKey, 54 | ): { 55 | publicKey: PublicKey; 56 | nonce: number; 57 | } { 58 | return findProgramAddress( 59 | [registrar.toBuffer(), Buffer.from("voter-weight-record", "utf8"), authority.toBuffer()], 60 | programId, 61 | ); 62 | } 63 | 64 | export function getTokenOwnerRecordAddress( 65 | programId: PublicKey, 66 | realm: PublicKey, 67 | governingTokenMint: PublicKey, 68 | governingTokenOwner: PublicKey, 69 | ): { 70 | publicKey: PublicKey; 71 | nonce: number; 72 | } { 73 | return findProgramAddress( 74 | [ 75 | Buffer.from("governance", "utf8"), 76 | realm.toBuffer(), 77 | governingTokenMint.toBuffer(), 78 | governingTokenOwner.toBuffer(), 79 | ], 80 | programId, 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /src/raydium/farm/type.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import { ApiV3PoolInfoStandardItem, ApiV3Token, FormatFarmInfoOut } from "../../api/type"; 4 | import { TxVersion } from "../../common"; 5 | import { BigNumberish } from "../../common/bignumber"; 6 | import { ComputeBudgetConfig, TxTipConfig } from "../../raydium/type"; 7 | import { poolTypeV6 } from "./config"; 8 | 9 | export type RewardType = keyof typeof poolTypeV6; 10 | export interface APIRewardInfo { 11 | rewardMint: string; 12 | rewardVault: string; 13 | rewardOpenTime: number; 14 | rewardEndTime: number; 15 | rewardPerSecond: string | number; 16 | rewardSender?: string; 17 | rewardType: string; 18 | } 19 | 20 | export interface RewardInfoWithKey { 21 | rewardMint: PublicKey; 22 | rewardVault: PublicKey; 23 | rewardOpenTime: number; 24 | rewardEndTime: number; 25 | rewardType: RewardType; 26 | rewardPerSecond: string | number; 27 | rewardSender?: PublicKey; 28 | } 29 | export interface FarmRewardInfo { 30 | mint: PublicKey; 31 | perSecond: string; 32 | openTime: number; 33 | endTime: number; 34 | rewardType: RewardType; 35 | } 36 | 37 | export interface FarmRewardInfoConfig { 38 | isSet: BN; 39 | rewardPerSecond: BN; 40 | rewardOpenTime: BN; 41 | rewardEndTime: BN; 42 | rewardType: BN; 43 | } 44 | 45 | export interface RewardInfoKey { 46 | rewardMint: PublicKey; 47 | rewardVault: PublicKey; 48 | userRewardToken: PublicKey; 49 | } 50 | 51 | export interface FarmPoolInfoV6 { 52 | version: number; 53 | programId: PublicKey; 54 | 55 | lpMint: PublicKey; 56 | 57 | rewardInfos: FarmRewardInfo[]; 58 | 59 | lockInfo: { 60 | lockMint: PublicKey; 61 | lockVault: PublicKey; 62 | }; 63 | } 64 | 65 | export interface CreateFarm { 66 | poolInfo: ApiV3PoolInfoStandardItem; 67 | rewardInfos: FarmRewardInfo[]; 68 | payer?: PublicKey; 69 | programId?: PublicKey; 70 | txVersion?: T; 71 | computeBudgetConfig?: ComputeBudgetConfig; 72 | txTipConfig?: TxTipConfig; 73 | feePayer?: PublicKey; 74 | } 75 | 76 | export interface CreateFarmExtInfo { 77 | farmId: PublicKey; 78 | farmAuthority: PublicKey; 79 | lpVault: PublicKey; 80 | lockUserAccount: PublicKey; 81 | nonce: number; 82 | } 83 | 84 | export interface UpdateFarmReward { 85 | farmInfo: FormatFarmInfoOut; 86 | newRewardInfo: FarmRewardInfo; 87 | payer?: PublicKey; 88 | txVersion?: T; 89 | computeBudgetConfig?: ComputeBudgetConfig; 90 | txTipConfig?: TxTipConfig; 91 | feePayer?: PublicKey; 92 | } 93 | 94 | export interface UpdateFarmRewards { 95 | farmInfo: FormatFarmInfoOut; 96 | newRewardInfos: FarmRewardInfo[]; 97 | payer?: PublicKey; 98 | txVersion?: T; 99 | computeBudgetConfig?: ComputeBudgetConfig; 100 | txTipConfig?: TxTipConfig; 101 | feePayer?: PublicKey; 102 | } 103 | 104 | export interface FarmDWParam { 105 | farmInfo: { 106 | id: string; 107 | programId: string; 108 | lpMint: ApiV3Token; 109 | rewardInfos: { mint: ApiV3Token }[]; 110 | }; 111 | amount: BigNumberish; 112 | feePayer?: PublicKey; 113 | useSOLBalance?: boolean; 114 | associatedOnly?: boolean; 115 | checkCreateATAOwner?: boolean; 116 | deposited?: BN; 117 | txVersion?: T; 118 | userAuxiliaryLedgers?: string[]; 119 | computeBudgetConfig?: ComputeBudgetConfig; 120 | txTipConfig?: TxTipConfig; 121 | } 122 | /* ================= pool keys ================= */ 123 | export type FarmPoolKeys = { 124 | readonly id: PublicKey; 125 | readonly lpMint: PublicKey; 126 | readonly version: number; 127 | readonly programId: PublicKey; 128 | readonly authority: PublicKey; 129 | readonly lpVault: PublicKey; 130 | readonly upcoming: boolean; 131 | readonly rewardInfos: ( 132 | | { 133 | readonly rewardMint: PublicKey; 134 | readonly rewardVault: PublicKey; 135 | } 136 | | { 137 | readonly rewardMint: PublicKey; 138 | readonly rewardVault: PublicKey; 139 | readonly rewardOpenTime: number; 140 | readonly rewardEndTime: number; 141 | readonly rewardPerSecond: number; 142 | readonly rewardType: RewardType; 143 | } 144 | )[]; 145 | }; 146 | -------------------------------------------------------------------------------- /src/raydium/ido/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ido"; 2 | export * from "./instruction"; 3 | export * from "./type"; 4 | export * from "./layout"; 5 | export { default } from "./ido"; 6 | -------------------------------------------------------------------------------- /src/raydium/ido/instruction.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey, SYSVAR_CLOCK_PUBKEY, TransactionInstruction } from "@solana/web3.js"; 2 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 3 | import { CLOCK_PROGRAM_ID, RENT_PROGRAM_ID, SYSTEM_PROGRAM_ID } from "@/common/pubKey"; 4 | import { claimLayout, purchaseLayout } from "./layout"; 5 | import { 6 | ClaimInstructionKeys, 7 | ClaimInstructionKeysV3, 8 | IdoClaimInstructionParams, 9 | PurchaseInstructionKeys, 10 | } from "./type"; 11 | 12 | export function makePurchaseInstruction({ 13 | programId, 14 | amount, 15 | instructionKeys, 16 | }: { 17 | programId: PublicKey; 18 | amount: string | number; 19 | instructionKeys: PurchaseInstructionKeys; 20 | }): TransactionInstruction { 21 | const keys = [ 22 | // system 23 | { pubkey: SYSTEM_PROGRAM_ID, isSigner: false, isWritable: false }, 24 | { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, 25 | { pubkey: RENT_PROGRAM_ID, isSigner: false, isWritable: false }, 26 | { pubkey: CLOCK_PROGRAM_ID, isSigner: false, isWritable: false }, 27 | // pubkeys 28 | ...Object.entries(instructionKeys).map(([name, pubkey]) => ({ 29 | pubkey, 30 | isSigner: name === "userOwner", 31 | isWritable: !["authority", "userOwner", "userIdoCheck", "userStakeInfo"].includes(name), 32 | })), 33 | ]; 34 | 35 | const data = Buffer.alloc(purchaseLayout.span); 36 | purchaseLayout.encode({ instruction: 1, amount: Number(amount) }, data); 37 | 38 | return new TransactionInstruction({ keys, programId, data }); 39 | } 40 | 41 | export function makeClaimInstruction( 42 | { programId }: { programId: PublicKey }, 43 | instructionKeys: Version extends "3" ? ClaimInstructionKeysV3 : ClaimInstructionKeys, 44 | ): TransactionInstruction { 45 | const keys = [ 46 | { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, 47 | { pubkey: CLOCK_PROGRAM_ID, isSigner: false, isWritable: false }, 48 | ...Object.entries(instructionKeys).map(([name, pubkey]) => ({ 49 | pubkey, 50 | isSigner: name === "userOwner", 51 | isWritable: !["authority", "userOwner"].includes(name), 52 | })), 53 | ]; 54 | 55 | const data = Buffer.alloc(claimLayout.span); 56 | claimLayout.encode({ instruction: 2 }, data); 57 | 58 | return new TransactionInstruction({ keys, programId, data }); 59 | } 60 | 61 | export function makeClaimInstructionV4(params: IdoClaimInstructionParams): TransactionInstruction { 62 | const { poolConfig, userKeys, side } = params; 63 | 64 | const tokenAccount = side === "base" ? userKeys.baseTokenAccount : userKeys.quoteTokenAccount; 65 | const vault = side === "base" ? poolConfig.baseVault : poolConfig.quoteVault; 66 | const data = Buffer.alloc(claimLayout.span); 67 | claimLayout.encode( 68 | { 69 | instruction: 2, 70 | }, 71 | data, 72 | ); 73 | 74 | const keys = [ 75 | { 76 | pubkey: TOKEN_PROGRAM_ID, 77 | isWritable: false, 78 | isSigner: false, 79 | }, 80 | { 81 | pubkey: SYSVAR_CLOCK_PUBKEY, 82 | isWritable: false, 83 | isSigner: false, 84 | }, 85 | // ido 86 | { 87 | pubkey: poolConfig.id, 88 | isWritable: true, 89 | isSigner: false, 90 | }, 91 | { 92 | pubkey: poolConfig.authority, 93 | isWritable: false, 94 | isSigner: false, 95 | }, 96 | { 97 | pubkey: vault, 98 | isWritable: true, 99 | isSigner: false, 100 | }, 101 | // user 102 | { 103 | pubkey: tokenAccount, 104 | isWritable: true, 105 | isSigner: false, 106 | }, 107 | { 108 | pubkey: userKeys.ledgerAccount, 109 | isWritable: true, 110 | isSigner: false, 111 | }, 112 | { 113 | pubkey: userKeys.owner, 114 | isWritable: false, 115 | isSigner: true, 116 | }, 117 | ]; 118 | 119 | return new TransactionInstruction({ 120 | programId: poolConfig.programId, 121 | keys, 122 | data, 123 | }); 124 | } 125 | -------------------------------------------------------------------------------- /src/raydium/ido/layout.ts: -------------------------------------------------------------------------------- 1 | import { nu64, struct, u8 } from "../../marshmallow"; 2 | 3 | export const purchaseLayout = struct([u8("instruction"), nu64("amount")]); 4 | export const claimLayout = struct([u8("instruction")]); 5 | -------------------------------------------------------------------------------- /src/raydium/ido/type.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { ApiV3Token } from "../../api/type"; 3 | 4 | export interface PurchaseInstructionKeys { 5 | // ido 6 | idoId: PublicKey; 7 | authority: PublicKey; 8 | poolQuoteTokenAccount: PublicKey; 9 | // user 10 | userQuoteTokenAccount: PublicKey; 11 | userIdoInfo: PublicKey; 12 | userStakeInfo?: PublicKey; 13 | userIdoCheck: PublicKey; 14 | userOwner: PublicKey; 15 | } 16 | 17 | export interface ClaimInstructionKeys { 18 | // ido 19 | idoId: PublicKey; 20 | authority: PublicKey; 21 | poolQuoteTokenAccount: PublicKey; 22 | poolBaseTokenAccount: PublicKey; 23 | // user 24 | userQuoteTokenAccount: PublicKey; 25 | userBaseTokenAccount: PublicKey; 26 | userIdoInfo: PublicKey; 27 | userOwner: PublicKey; 28 | } 29 | export interface ClaimInstructionKeysV3 { 30 | // ido 31 | idoId: PublicKey; 32 | authority: PublicKey; 33 | poolTokenAccount: PublicKey; // projectInfo.vault? 34 | // user 35 | userTokenAccount: PublicKey; // user token account.mint === projectInfo.mint? 36 | userIdoInfo: PublicKey; 37 | userOwner: PublicKey; 38 | } 39 | 40 | export type IdoVersion = 3; 41 | 42 | export type SnapshotVersion = 1; 43 | 44 | export interface IdoPoolConfig { 45 | id: PublicKey; 46 | 47 | // version: IdoVersion; 48 | programId: PublicKey; 49 | 50 | // snapshotVersion: SnapshotVersion; 51 | // snapshotProgramId: PublicKey; 52 | 53 | authority: PublicKey; 54 | // seedId: PublicKey; 55 | baseVault: PublicKey; 56 | quoteVault: PublicKey; 57 | baseToken: ApiV3Token; 58 | quoteToken: ApiV3Token; 59 | } 60 | 61 | export interface IdoUserKeys { 62 | baseTokenAccount: PublicKey; 63 | quoteTokenAccount: PublicKey; 64 | ledgerAccount: PublicKey; 65 | // snapshotAccount: PublicKey; 66 | owner: PublicKey; 67 | } 68 | 69 | export interface IdoClaimInstructionParams { 70 | poolConfig: IdoPoolConfig; 71 | userKeys: IdoUserKeys; 72 | side: "base" | "quote"; 73 | } 74 | -------------------------------------------------------------------------------- /src/raydium/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./raydium"; 2 | export * from "./account"; 3 | export * from "./farm"; 4 | export * from "./token"; 5 | export * from "./tradeV2"; 6 | export * from "./clmm"; 7 | export * from "./cpmm"; 8 | export * from "./serum"; 9 | export * from "./liquidity"; 10 | export * from "./utils1216"; 11 | export * from "./marketV2"; 12 | export * from "./ido"; 13 | export * from "./type"; 14 | export * from "./launchpad"; 15 | -------------------------------------------------------------------------------- /src/raydium/launchpad/curve/constantProductCurve.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import Decimal from "decimal.js"; 3 | import { ceilDivBN } from "@/common"; 4 | import { CurveBase, PoolBaseAmount } from "./curveBase"; 5 | import { Q64 } from "@/raydium/clmm"; 6 | import { LaunchpadPoolInfo } from "../type"; 7 | export { Q64 }; 8 | 9 | export class LaunchPadConstantProductCurve extends CurveBase { 10 | static getPoolInitPriceByPool({ 11 | poolInfo, 12 | decimalA, 13 | decimalB, 14 | }: { 15 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 16 | decimalA: number; 17 | decimalB: number; 18 | }): Decimal { 19 | return new Decimal(poolInfo.virtualB.toString()).div(poolInfo.virtualA.toString()).mul(10 ** (decimalA - decimalB)); 20 | } 21 | static getPoolInitPriceByInit({ 22 | a, 23 | b, 24 | decimalA, 25 | decimalB, 26 | }: { 27 | a: BN; 28 | b: BN; 29 | decimalA: number; 30 | decimalB: number; 31 | }): Decimal { 32 | return new Decimal(b.toString()).div(a.toString()).mul(10 ** (decimalA - decimalB)); 33 | } 34 | 35 | static getPoolPrice({ 36 | poolInfo, 37 | decimalA, 38 | decimalB, 39 | }: { 40 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 41 | decimalA: number; 42 | decimalB: number; 43 | }): Decimal { 44 | return new Decimal(poolInfo.virtualB.add(poolInfo.realB).toString()) 45 | .div(poolInfo.virtualA.sub(poolInfo.realA).toString()) 46 | .mul(10 ** (decimalA - decimalB)); 47 | } 48 | static getPoolEndPrice({ 49 | supply, 50 | totalSell, 51 | totalLockedAmount, 52 | totalFundRaising, 53 | migrateFee, 54 | decimalA, 55 | decimalB, 56 | }: { 57 | supply: BN; 58 | totalSell: BN; 59 | totalLockedAmount: BN; 60 | totalFundRaising: BN; 61 | migrateFee: BN; 62 | decimalA: number; 63 | decimalB: number; 64 | }): Decimal { 65 | return new Decimal(totalFundRaising.sub(migrateFee).toString()) 66 | .div(supply.sub(totalSell).sub(totalLockedAmount).toString()) 67 | .mul(10 ** (decimalA - decimalB)); 68 | } 69 | 70 | static getPoolEndPriceReal({ 71 | poolInfo, 72 | decimalA, 73 | decimalB, 74 | }: { 75 | poolInfo: LaunchpadPoolInfo; 76 | decimalA: number; 77 | decimalB: number; 78 | }): Decimal { 79 | const allSellToken = poolInfo.totalSellA.sub(poolInfo.realA); 80 | const buyAllTokenUseB = poolInfo.totalFundRaisingB.sub(poolInfo.realB); 81 | 82 | return new Decimal(poolInfo.virtualB.add(poolInfo.realB.add(buyAllTokenUseB)).toString()) 83 | .div(poolInfo.virtualA.sub(poolInfo.realA.add(allSellToken)).toString()) 84 | .mul(10 ** (decimalA - decimalB)); 85 | } 86 | 87 | static getInitParam({ 88 | supply, 89 | totalFundRaising, 90 | totalSell, 91 | totalLockedAmount, 92 | migrateFee, 93 | }: { 94 | supply: BN; 95 | totalSell: BN; 96 | totalLockedAmount: BN; 97 | totalFundRaising: BN; 98 | migrateFee: BN; 99 | }): { a: BN; b: BN; c: BN } { 100 | if (supply.lte(totalSell)) throw Error("supply need gt total sell"); 101 | const supplyMinusSellLocked = supply.sub(totalSell).sub(totalLockedAmount); 102 | if (supplyMinusSellLocked.lte(new BN(0))) throw Error("supplyMinusSellLocked <= 0"); 103 | 104 | const tfMinusMf = totalFundRaising.sub(migrateFee); 105 | if (tfMinusMf.lte(new BN(0))) throw Error("tfMinusMf <= 0"); 106 | 107 | // const migratePriceX64 = tfMinusMf.mul(Q64).div(supplyMinusSellLocked); 108 | 109 | const numerator = tfMinusMf.mul(totalSell).mul(totalSell).div(supplyMinusSellLocked); 110 | const denominator = tfMinusMf.mul(totalSell).div(supplyMinusSellLocked).sub(totalFundRaising); 111 | 112 | if (denominator.lt(new BN(0))) throw Error("supply/totalSell/totalLockedAmount diff too high"); 113 | 114 | const x0 = numerator.div(denominator); 115 | const y0 = totalFundRaising.mul(totalFundRaising).div(denominator); 116 | 117 | if (x0.lt(new BN(0)) || y0.lt(new BN(0))) throw Error("invalid input 0"); 118 | 119 | return { 120 | a: x0, 121 | b: y0, 122 | c: totalSell, 123 | }; 124 | } 125 | 126 | static buyExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 127 | return this.getAmountOut({ 128 | amountIn: amount, 129 | inputReserve: poolInfo.virtualB.add(poolInfo.realB), 130 | outputReserve: poolInfo.virtualA.sub(poolInfo.realA), 131 | }); 132 | } 133 | 134 | static buyExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 135 | return this.getAmountIn({ 136 | amountOut: amount, 137 | inputReserve: poolInfo.virtualB.add(poolInfo.realB), 138 | outputReserve: poolInfo.virtualA.sub(poolInfo.realA), 139 | }); 140 | } 141 | 142 | static sellExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 143 | return this.getAmountOut({ 144 | amountIn: amount, 145 | inputReserve: poolInfo.virtualA.sub(poolInfo.realA), 146 | outputReserve: poolInfo.virtualB.add(poolInfo.realB), 147 | }); 148 | } 149 | 150 | static sellExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 151 | return this.getAmountIn({ 152 | amountOut: amount, 153 | inputReserve: poolInfo.virtualA.sub(poolInfo.realA), 154 | outputReserve: poolInfo.virtualB.add(poolInfo.realB), 155 | }); 156 | } 157 | 158 | static getAmountOut({ 159 | amountIn, 160 | inputReserve, 161 | outputReserve, 162 | }: { 163 | amountIn: BN; 164 | inputReserve: BN; 165 | outputReserve: BN; 166 | }): BN { 167 | const numerator = amountIn.mul(outputReserve); 168 | const denominator = inputReserve.add(amountIn); 169 | const amountOut = numerator.div(denominator); 170 | return amountOut; 171 | } 172 | static getAmountIn({ 173 | amountOut, 174 | inputReserve, 175 | outputReserve, 176 | }: { 177 | amountOut: BN; 178 | inputReserve: BN; 179 | outputReserve: BN; 180 | }): BN { 181 | const numerator = inputReserve.mul(amountOut); 182 | const denominator = outputReserve.sub(amountOut); 183 | const amountIn = ceilDivBN(numerator, denominator); 184 | return amountIn; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/raydium/launchpad/curve/curveBase.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { LaunchpadPool } from "../layout"; 3 | import Decimal from "decimal.js"; 4 | import { LaunchpadPoolInfo } from "../type"; 5 | 6 | export interface PoolBaseAmount { 7 | virtualA: BN; 8 | virtualB: BN; 9 | realA: BN; 10 | realB: BN; 11 | } 12 | 13 | export class CurveBase { 14 | static getPoolInitPriceByPool({ 15 | poolInfo, 16 | decimalA, 17 | decimalB, 18 | }: { 19 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 20 | decimalA: number; 21 | decimalB: number; 22 | }): Decimal { 23 | throw Error(); 24 | } 25 | static getPoolInitPriceByInit({ 26 | a, 27 | b, 28 | decimalA, 29 | decimalB, 30 | }: { 31 | a: BN; 32 | b: BN; 33 | decimalA: number; 34 | decimalB: number; 35 | }): Decimal { 36 | throw Error(); 37 | } 38 | 39 | static getPoolPrice({ 40 | poolInfo, 41 | decimalA, 42 | decimalB, 43 | }: { 44 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 45 | decimalA: number; 46 | decimalB: number; 47 | }): Decimal { 48 | throw Error(); 49 | } 50 | static getPoolEndPrice({ 51 | supply, 52 | totalSell, 53 | totalLockedAmount, 54 | totalFundRaising, 55 | migrateFee, 56 | decimalA, 57 | decimalB, 58 | }: { 59 | supply: BN; 60 | totalSell: BN; 61 | totalLockedAmount: BN; 62 | totalFundRaising: BN; 63 | migrateFee: BN; 64 | decimalA: number; 65 | decimalB: number; 66 | }): Decimal { 67 | throw Error(); 68 | } 69 | 70 | static getPoolEndPriceReal({ 71 | poolInfo, 72 | decimalA, 73 | decimalB, 74 | }: { 75 | poolInfo: LaunchpadPoolInfo; 76 | decimalA: number; 77 | decimalB: number; 78 | }): Decimal { 79 | throw Error(); 80 | } 81 | 82 | static getInitParam({ 83 | supply, 84 | totalFundRaising, 85 | totalSell, 86 | totalLockedAmount, 87 | migrateFee, 88 | }: { 89 | supply: BN; 90 | totalSell: BN; 91 | totalLockedAmount: BN; 92 | totalFundRaising: BN; 93 | migrateFee: BN; 94 | }): { a: BN; b: BN; c: BN } { 95 | throw Error(); 96 | } 97 | 98 | static buyExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 99 | throw Error(); 100 | } 101 | 102 | static buyExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 103 | throw Error(); 104 | } 105 | 106 | static sellExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 107 | throw Error(); 108 | } 109 | static sellExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 110 | throw Error(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/raydium/launchpad/curve/fixedPriceCurve.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import Decimal from "decimal.js"; 3 | import { CurveBase, PoolBaseAmount } from "./curveBase"; 4 | import { LaunchpadPoolInfo } from "../type"; 5 | import { ceilDivBN } from "@/common"; 6 | 7 | export class FixedPriceCurve extends CurveBase { 8 | static getPoolInitPriceByPool({ 9 | poolInfo, 10 | decimalA, 11 | decimalB, 12 | }: { 13 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 14 | decimalA: number; 15 | decimalB: number; 16 | }): Decimal { 17 | return new Decimal(poolInfo.virtualB.toString()).div(poolInfo.virtualA.toString()).mul(10 ** (decimalA - decimalB)); 18 | } 19 | static getPoolInitPriceByInit({ 20 | a, 21 | b, 22 | decimalA, 23 | decimalB, 24 | }: { 25 | a: BN; 26 | b: BN; 27 | decimalA: number; 28 | decimalB: number; 29 | }): Decimal { 30 | return new Decimal(b.toString()).div(a.toString()).mul(10 ** (decimalA - decimalB)); 31 | } 32 | 33 | static getPoolPrice({ 34 | poolInfo, 35 | decimalA, 36 | decimalB, 37 | }: { 38 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 39 | decimalA: number; 40 | decimalB: number; 41 | }): Decimal { 42 | return new Decimal(poolInfo.virtualB.toString()).div(poolInfo.virtualA.toString()).mul(10 ** (decimalA - decimalB)); 43 | } 44 | static getPoolEndPrice({ 45 | supply, 46 | totalSell, 47 | totalLockedAmount, 48 | totalFundRaising, 49 | migrateFee, 50 | decimalA, 51 | decimalB, 52 | }: { 53 | supply: BN; 54 | totalSell: BN; 55 | totalLockedAmount: BN; 56 | totalFundRaising: BN; 57 | migrateFee: BN; 58 | decimalA: number; 59 | decimalB: number; 60 | }): Decimal { 61 | return new Decimal(totalFundRaising.sub(migrateFee).toString()) 62 | .div(supply.sub(totalSell).sub(totalLockedAmount).toString()) 63 | .mul(10 ** (decimalA - decimalB)); 64 | } 65 | 66 | static getPoolEndPriceReal({ 67 | poolInfo, 68 | decimalA, 69 | decimalB, 70 | }: { 71 | poolInfo: LaunchpadPoolInfo; 72 | decimalA: number; 73 | decimalB: number; 74 | }): Decimal { 75 | const allSellToken = poolInfo.totalSellA.sub(poolInfo.realA); 76 | const buyAllTokenUseB = poolInfo.totalFundRaisingB.sub(poolInfo.realB); 77 | 78 | return new Decimal(poolInfo.virtualB.add(poolInfo.realB).add(buyAllTokenUseB).toString()) 79 | .div(poolInfo.virtualA.sub(poolInfo.realA).add(allSellToken).toString()) 80 | .mul(10 ** (decimalA - decimalB)); 81 | } 82 | 83 | static getInitParam({ 84 | supply, 85 | totalFundRaising, 86 | totalSell, 87 | totalLockedAmount, 88 | migrateFee, 89 | }: { 90 | supply: BN; 91 | totalSell: BN; 92 | totalFundRaising: BN; 93 | totalLockedAmount: BN; 94 | migrateFee: BN; 95 | }): { a: BN; b: BN; c: BN } { 96 | const supplyMinusLocked = supply.sub(totalLockedAmount); 97 | 98 | if (supplyMinusLocked.lte(new BN(0))) throw Error("invalid input 1"); 99 | 100 | const denominator = new BN(2).mul(totalFundRaising).sub(migrateFee); 101 | const numerator = totalFundRaising.mul(supplyMinusLocked); 102 | const totalSellExpect = numerator.div(denominator); 103 | 104 | // if (!totalSell.eq(totalSellExpect)) throw Error("invalid input 2"); 105 | 106 | return { a: totalSellExpect, b: totalFundRaising, c: totalSellExpect }; 107 | } 108 | 109 | static buyExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 110 | return this.getAmountOut({ amountIn: amount, initInput: poolInfo.virtualB, initOutput: poolInfo.virtualA }); 111 | } 112 | 113 | static buyExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 114 | return this.getAmountIn({ amountOut: amount, initInput: poolInfo.virtualB, initOutput: poolInfo.virtualA }); 115 | } 116 | 117 | static sellExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 118 | return this.getAmountOut({ amountIn: amount, initInput: poolInfo.virtualA, initOutput: poolInfo.virtualB }); 119 | } 120 | 121 | static sellExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 122 | return this.getAmountIn({ amountOut: amount, initInput: poolInfo.virtualA, initOutput: poolInfo.virtualB }); 123 | } 124 | 125 | static getAmountOut({ amountIn, initInput, initOutput }: { amountIn: BN; initInput: BN; initOutput: BN }): BN { 126 | const numerator = initOutput.mul(amountIn); 127 | const amountOut = numerator.div(initInput); 128 | return amountOut; 129 | } 130 | 131 | static getAmountIn({ amountOut, initInput, initOutput }: { amountOut: BN; initInput: BN; initOutput: BN }): BN { 132 | const numerator = initInput.mul(amountOut); 133 | const amountIn = ceilDivBN(numerator, initOutput); 134 | return amountIn; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/raydium/launchpad/curve/func.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import Decimal from "decimal.js"; 3 | 4 | export class MathLaunch { 5 | static _Q64 = new Decimal(new BN(1).shln(64).toString()); 6 | 7 | static _multipler(decimals: number): Decimal { 8 | return new Decimal(10).pow(decimals); 9 | } 10 | 11 | static getPrice({ priceX64, decimalA, decimalB }: { priceX64: BN; decimalA: number; decimalB: number }): Decimal { 12 | const priceWithDecimals = new Decimal(priceX64.toString()).div(this._Q64); 13 | const price = priceWithDecimals.mul(this._multipler(decimalA)).div(this._multipler(decimalB)); 14 | 15 | return price; 16 | } 17 | 18 | static getPriceX64({ price, decimalA, decimalB }: { price: Decimal; decimalA: number; decimalB: number }): BN { 19 | const priceWithDecimals = price.mul(this._multipler(decimalB)).div(this._multipler(decimalA)); 20 | const priceX64 = new BN(priceWithDecimals.mul(this._Q64).toFixed(0)); 21 | return priceX64; 22 | } 23 | } 24 | 25 | export function checkPoolToAmm({ 26 | supply, 27 | totalFundRaisingB, 28 | totalLockedAmount, 29 | totalSellA, 30 | migrateType, 31 | decimalsA, 32 | }: { 33 | supply: BN; 34 | totalSellA: BN; 35 | totalLockedAmount: BN; 36 | totalFundRaisingB: BN; 37 | migrateType: "amm" | "cpmm"; 38 | decimalsA: number; 39 | }): boolean { 40 | const migrateAmountA = supply.sub(totalSellA).sub(totalLockedAmount); 41 | const liquidity = new BN(new Decimal(migrateAmountA.mul(totalFundRaisingB).toString()).sqrt().toFixed(0)); 42 | 43 | if (migrateType === "amm") { 44 | if (liquidity.gt(new BN(10).pow(new BN(decimalsA)))) return true; 45 | } else if (migrateType === "cpmm") { 46 | if (liquidity.gt(new BN(100))) return true; 47 | } else { 48 | throw Error("migrate type error"); 49 | } 50 | 51 | return false; 52 | } 53 | -------------------------------------------------------------------------------- /src/raydium/launchpad/curve/linearPriceCurve.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import Decimal from "decimal.js"; 3 | import { Q64 } from "./constantProductCurve"; 4 | import { MathLaunch } from "./func"; 5 | import { MaxU64 } from "@/raydium/clmm"; 6 | import { LaunchpadPoolInfo } from "../type"; 7 | import { CurveBase, PoolBaseAmount } from "./curveBase"; 8 | export class LinearPriceCurve extends CurveBase { 9 | static getPoolInitPriceByPool({ 10 | poolInfo, 11 | decimalA, 12 | decimalB, 13 | }: { 14 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 15 | decimalA: number; 16 | decimalB: number; 17 | }): Decimal { 18 | return new Decimal(0); 19 | } 20 | static getPoolInitPriceByInit({ 21 | a, 22 | b, 23 | decimalA, 24 | decimalB, 25 | }: { 26 | a: BN; 27 | b: BN; 28 | decimalA: number; 29 | decimalB: number; 30 | }): Decimal { 31 | return new Decimal(0); 32 | } 33 | 34 | static getPoolPrice({ 35 | poolInfo, 36 | decimalA, 37 | decimalB, 38 | }: { 39 | poolInfo: LaunchpadPoolInfo | PoolBaseAmount; 40 | decimalA: number; 41 | decimalB: number; 42 | }): Decimal { 43 | return new Decimal(poolInfo.virtualA.mul(poolInfo.realA).toString()) 44 | .div(MathLaunch._Q64) 45 | .mul(10 ** (decimalA - decimalB)); 46 | } 47 | static getPoolEndPrice({ 48 | supply, 49 | totalSell, 50 | totalLockedAmount, 51 | totalFundRaising, 52 | migrateFee, 53 | decimalA, 54 | decimalB, 55 | }: { 56 | supply: BN; 57 | totalSell: BN; 58 | totalLockedAmount: BN; 59 | totalFundRaising: BN; 60 | migrateFee: BN; 61 | decimalA: number; 62 | decimalB: number; 63 | }): Decimal { 64 | return new Decimal(totalFundRaising.sub(migrateFee).toString()) 65 | .div(supply.sub(totalSell).sub(totalLockedAmount).toString()) 66 | .mul(10 ** (decimalA - decimalB)); 67 | } 68 | 69 | static getPoolEndPriceReal({ 70 | poolInfo, 71 | decimalA, 72 | decimalB, 73 | }: { 74 | poolInfo: LaunchpadPoolInfo; 75 | decimalA: number; 76 | decimalB: number; 77 | }): Decimal { 78 | const allSellToken = poolInfo.totalSellA.sub(poolInfo.realA); 79 | const buyAllTokenUseB = poolInfo.totalFundRaisingB.sub(poolInfo.realB); 80 | 81 | return new Decimal(poolInfo.virtualB.add(poolInfo.realB).add(buyAllTokenUseB).toString()) 82 | .div(poolInfo.virtualA.sub(poolInfo.realA).add(allSellToken).toString()) 83 | .mul(10 ** (decimalA - decimalB)); 84 | } 85 | 86 | static getInitParam({ 87 | supply, 88 | totalFundRaising, 89 | totalSell, 90 | totalLockedAmount, 91 | migrateFee, 92 | }: { 93 | supply: BN; 94 | totalSell: BN; 95 | totalLockedAmount: BN; 96 | totalFundRaising: BN; 97 | migrateFee: BN; 98 | }): { a: BN; b: BN; c: BN } { 99 | const supplyMinusLocked = supply.sub(totalLockedAmount); 100 | if (supplyMinusLocked.lte(new BN(0))) throw Error("supplyMinusLocked need gt 0"); 101 | const denominator = totalFundRaising.mul(new BN(3)).sub(migrateFee); 102 | const numerator = totalFundRaising.mul(new BN(2)).mul(supplyMinusLocked); 103 | 104 | const totalSellExpect = numerator.div(denominator); 105 | 106 | // if (!totalSell.eq(totalSellExpect)) throw Error("invalid input"); 107 | 108 | const totalSellSquared = totalSellExpect.mul(totalSellExpect); 109 | const a = totalFundRaising.mul(new BN(2)).mul(Q64).div(totalSellSquared); 110 | 111 | if (!a.gt(new BN(0))) throw Error("a need gt 0"); 112 | 113 | if (!MaxU64.gt(a)) throw Error("a need lt u64 max"); 114 | 115 | return { a, b: new BN(0), c: totalSellExpect }; 116 | } 117 | 118 | static buyExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 119 | const newQuote = poolInfo.realB.add(amount); 120 | const termInsideSqrt = new BN(2).mul(newQuote).mul(Q64).div(poolInfo.virtualA); 121 | const sqrtTerm = new BN(new Decimal(termInsideSqrt.toString()).sqrt().toFixed(0)); 122 | const amountOut = sqrtTerm.sub(poolInfo.realA); 123 | 124 | return amountOut; 125 | } 126 | 127 | static buyExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 128 | const newBase = poolInfo.realA.add(amount); 129 | const newBaseSquared = newBase.mul(newBase); 130 | const { div: _newQuoteDiv, mod: _newQuoteMod } = poolInfo.virtualA.mul(newBaseSquared).divmod(new BN(2).mul(Q64)); 131 | const newQuote = _newQuoteMod.isZero() ? _newQuoteDiv : _newQuoteDiv.add(new BN(1)); 132 | return newQuote.sub(poolInfo.realB); 133 | } 134 | 135 | static sellExactIn({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 136 | const newBase = poolInfo.realA.sub(amount); 137 | const newBaseSquared = newBase.mul(newBase); 138 | 139 | const { div: _newQuoteDiv, mod: _newQuoteMod } = poolInfo.virtualA.mul(newBaseSquared).divmod(new BN(2).mul(Q64)); 140 | 141 | const newQuote = _newQuoteMod.isZero() ? _newQuoteDiv : _newQuoteDiv.add(new BN(1)); 142 | 143 | return poolInfo.realB.sub(newQuote); 144 | } 145 | 146 | static sellExactOut({ poolInfo, amount }: { poolInfo: LaunchpadPoolInfo | PoolBaseAmount; amount: BN }): BN { 147 | const newB = poolInfo.realB.sub(amount); 148 | const termInsideSqrt = new BN(2).mul(newB).mul(Q64).div(poolInfo.virtualA); 149 | 150 | const sqrtTerm = new BN(new Decimal(termInsideSqrt.toString()).sqrt().toFixed(0)); 151 | 152 | const amountIn = poolInfo.realA.sub(sqrtTerm); 153 | 154 | return amountIn; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/raydium/launchpad/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./instrument"; 2 | export * from "./pda"; 3 | export * from "./type"; 4 | export * from "./layout"; 5 | export * from "./curve/constantProductCurve"; 6 | export * from "./curve/curve"; 7 | export * from "./curve/curveBase"; 8 | export * from "./curve/fixedPriceCurve"; 9 | export * from "./curve/func"; 10 | export * from "./curve/linearPriceCurve"; 11 | export * from "./launchpad"; 12 | -------------------------------------------------------------------------------- /src/raydium/launchpad/layout.ts: -------------------------------------------------------------------------------- 1 | import { publicKey, seq, struct, u16, u64, u8 } from "../../marshmallow"; 2 | 3 | export const LaunchpadConfig = struct([ 4 | u64(), 5 | u64("epoch"), 6 | u8("curveType"), 7 | u16("index"), 8 | u64("migrateFee"), 9 | u64("tradeFeeRate"), 10 | u64("maxShareFeeRate"), 11 | u64("minSupplyA"), 12 | u64("maxLockRate"), 13 | u64("minSellRateA"), 14 | u64("minMigrateRateA"), 15 | u64("minFundRaisingB"), 16 | publicKey("mintB"), 17 | publicKey("protocolFeeOwner"), 18 | publicKey("migrateFeeOwner"), 19 | publicKey("migrateToAmmWallet"), 20 | publicKey("migrateToCpmmWallet"), 21 | seq(u64(), 16), 22 | ]); 23 | 24 | export const LaunchpadVestingSchedule = struct([ 25 | u64("totalLockedAmount"), 26 | u64("cliffPeriod"), 27 | u64("unlockPeriod"), 28 | u64("startTime"), 29 | u64("totalAllocatedShare"), 30 | ]); 31 | 32 | export const LaunchpadPool = struct([ 33 | u64(), 34 | u64("epoch"), 35 | u8("bump"), 36 | u8("status"), 37 | u8("mintDecimalsA"), 38 | u8("mintDecimalsB"), 39 | u8("migrateType"), 40 | 41 | u64("supply"), 42 | u64("totalSellA"), 43 | u64("virtualA"), 44 | u64("virtualB"), 45 | u64("realA"), 46 | u64("realB"), 47 | 48 | u64("totalFundRaisingB"), 49 | u64("protocolFee"), 50 | u64("platformFee"), 51 | u64("migrateFee"), 52 | 53 | LaunchpadVestingSchedule.replicate("vestingSchedule"), 54 | 55 | publicKey("configId"), 56 | publicKey("platformId"), 57 | publicKey("mintA"), 58 | publicKey("mintB"), 59 | publicKey("vaultA"), 60 | publicKey("vaultB"), 61 | 62 | publicKey("creator"), 63 | 64 | seq(u64(), 8), 65 | ]); 66 | 67 | export const LaunchpadVesting = struct([ 68 | u64(), 69 | u64("epoch"), 70 | publicKey("poolId"), 71 | publicKey("beneficiary"), 72 | u64("claimedAmount"), 73 | u64("tokenShareAmount"), 74 | seq(u64(), 8), 75 | ]); 76 | 77 | export const PlatformConfig = struct([ 78 | u64(), 79 | u64("epoch"), 80 | publicKey("platformClaimFeeWallet"), 81 | publicKey("platformLockNftWallet"), 82 | u64("platformScale"), 83 | u64("creatorScale"), 84 | u64("burnScale"), 85 | u64("feeRate"), 86 | seq(u8(), 64, "name"), 87 | seq(u8(), 256, "web"), 88 | seq(u8(), 256, "img"), 89 | publicKey('cpConfigId'), 90 | seq(u8(), 224), 91 | ]); 92 | -------------------------------------------------------------------------------- /src/raydium/launchpad/pda.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { findProgramAddress, ProgramAddress } from "@/common"; 3 | import { u16ToBytes } from "../clmm"; 4 | 5 | export const LAUNCHPAD_AUTH_SEED = Buffer.from("vault_auth_seed", "utf8"); 6 | export const LAUNCHPAD_CONFIG_SEED = Buffer.from("global_config", "utf8"); 7 | export const LAUNCHPAD_POOL_SEED = Buffer.from("pool", "utf8"); 8 | export const LAUNCHPAD_POOL_VAULT_SEED = Buffer.from("pool_vault", "utf8"); 9 | export const LAUNCHPAD_POOL_VESTING_SEED = Buffer.from("pool_vesting", "utf8"); 10 | export const LAUNCHPAD_POOL_PLATFORM_SEED = Buffer.from("platform_config", "utf8"); 11 | 12 | export function getPdaLaunchpadAuth(programId: PublicKey): ProgramAddress { 13 | return findProgramAddress([LAUNCHPAD_AUTH_SEED], programId); 14 | } 15 | 16 | export function getPdaLaunchpadConfigId( 17 | programId: PublicKey, 18 | mintB: PublicKey, 19 | curveType: number, 20 | index: number, 21 | ): ProgramAddress { 22 | return findProgramAddress( 23 | [LAUNCHPAD_CONFIG_SEED, mintB.toBuffer(), u8ToBytes(curveType), u16ToBytes(index)], 24 | programId, 25 | ); 26 | } 27 | 28 | export function getPdaLaunchpadPoolId(programId: PublicKey, mintA: PublicKey, mintB: PublicKey): ProgramAddress { 29 | return findProgramAddress([LAUNCHPAD_POOL_SEED, mintA.toBuffer(), mintB.toBuffer()], programId); 30 | } 31 | 32 | export function getPdaLaunchpadVaultId(programId: PublicKey, poolId: PublicKey, mint: PublicKey): ProgramAddress { 33 | return findProgramAddress([LAUNCHPAD_POOL_VAULT_SEED, poolId.toBuffer(), mint.toBuffer()], programId); 34 | } 35 | 36 | export function u8ToBytes(num: number): Uint8Array { 37 | const arr = new ArrayBuffer(1); 38 | const view = new DataView(arr); 39 | view.setUint8(0, num); 40 | return new Uint8Array(arr); 41 | } 42 | 43 | export function getPdaCpiEvent(programId: PublicKey): ProgramAddress { 44 | return findProgramAddress([Buffer.from("__event_authority", "utf8")], programId); 45 | } 46 | 47 | export function getPdaPlatformId(programId: PublicKey, platformAdminWallet: PublicKey): ProgramAddress { 48 | return findProgramAddress([LAUNCHPAD_POOL_PLATFORM_SEED, platformAdminWallet.toBuffer()], programId); 49 | } 50 | 51 | export function getPdaVestId(programId: PublicKey, poolId: PublicKey, owner: PublicKey): ProgramAddress { 52 | return findProgramAddress([LAUNCHPAD_POOL_VESTING_SEED, poolId.toBuffer(), owner.toBuffer()], programId); 53 | } 54 | -------------------------------------------------------------------------------- /src/raydium/launchpad/type.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey, Signer } from "@solana/web3.js"; 2 | import { ComputeBudgetConfig, TxTipConfig } from "../type"; 3 | import { TxVersion } from "@/common"; 4 | import BN from "bn.js"; 5 | import { LaunchpadPool, LaunchpadConfig, PlatformConfig } from "./layout"; 6 | 7 | export interface CreateLaunchPad { 8 | mintA: PublicKey; 9 | name: string; 10 | symbol: string; 11 | buyAmount: BN; 12 | platformId?: PublicKey; 13 | 14 | programId?: PublicKey; // default mainnet 15 | authProgramId?: PublicKey; // default mainnet 16 | decimals?: number; // default 6 17 | mintBDecimals?: number; // default 9 18 | curType?: number; // default 0 19 | configId: PublicKey; 20 | configInfo?: LaunchpadConfigInfo; 21 | 22 | minMintAAmount?: BN; // default calculated by realtime rpc data 23 | slippage?: BN; 24 | 25 | uri: string; 26 | migrateType: "amm" | "cpmm"; 27 | 28 | supply?: BN; 29 | totalSellA?: BN; 30 | totalFundRaisingB?: BN; 31 | totalLockedAmount?: BN; 32 | cliffPeriod?: BN; 33 | unlockPeriod?: BN; 34 | 35 | shareFeeRate?: BN; 36 | shareFeeReceiver?: PublicKey; 37 | platformFeeRate?: BN; // for preload usage 38 | 39 | createOnly?: boolean; 40 | 41 | computeBudgetConfig?: ComputeBudgetConfig; 42 | txTipConfig?: TxTipConfig; 43 | txVersion?: T; 44 | feePayer?: PublicKey; 45 | associatedOnly?: boolean; 46 | checkCreateATAOwner?: boolean; 47 | extraSigners?: Signer[]; 48 | } 49 | 50 | export interface BuyToken { 51 | mintA: PublicKey; 52 | buyAmount: BN; 53 | 54 | programId?: PublicKey; // default mainnet 55 | authProgramId?: PublicKey; // default mainnet 56 | mintB?: PublicKey; // default SOL 57 | poolInfo?: LaunchpadPoolInfo; // default calculated from mint 58 | minMintAAmount?: BN; // default calculated by realtime rpc data 59 | slippage?: BN; 60 | shareFeeRate?: BN; 61 | shareFeeReceiver?: PublicKey; 62 | 63 | configInfo?: LaunchpadConfigInfo; // for preload usage 64 | platformFeeRate?: BN; // for preload usage 65 | 66 | computeBudgetConfig?: ComputeBudgetConfig; 67 | txTipConfig?: TxTipConfig; 68 | txVersion?: T; 69 | feePayer?: PublicKey; 70 | associatedOnly?: boolean; 71 | checkCreateATAOwner?: boolean; 72 | } 73 | 74 | export interface SellToken { 75 | mintA: PublicKey; 76 | sellAmount: BN; 77 | slippage?: BN; 78 | 79 | programId?: PublicKey; // default mainnet 80 | authProgramId?: PublicKey; // default mainnet 81 | poolInfo?: LaunchpadPoolInfo; // default calculated from mint 82 | mintB?: PublicKey; // default SOL 83 | minAmountB?: BN; // default SOL decimals 9 84 | 85 | shareFeeRate?: BN; 86 | shareFeeReceiver?: PublicKey; 87 | 88 | configInfo?: LaunchpadConfigInfo; // for preload usage 89 | platformFeeRate?: BN; // for preload usage 90 | 91 | computeBudgetConfig?: ComputeBudgetConfig; 92 | txTipConfig?: TxTipConfig; 93 | txVersion?: T; 94 | feePayer?: PublicKey; 95 | associatedOnly?: boolean; 96 | checkCreateATAOwner?: boolean; 97 | } 98 | 99 | export interface CreatePlatform { 100 | programId?: PublicKey; 101 | 102 | platformAdmin: PublicKey; 103 | platformClaimFeeWallet: PublicKey; 104 | platformLockNftWallet: PublicKey; 105 | cpConfigId: PublicKey; 106 | 107 | migrateCpLockNftScale: { 108 | platformScale: BN; 109 | creatorScale: BN; 110 | burnScale: BN; 111 | }; 112 | 113 | feeRate: BN; 114 | name: string; 115 | web: string; 116 | img: string; 117 | 118 | computeBudgetConfig?: ComputeBudgetConfig; 119 | txTipConfig?: TxTipConfig; 120 | txVersion?: T; 121 | feePayer?: PublicKey; 122 | } 123 | 124 | export interface UpdatePlatform { 125 | programId?: PublicKey; 126 | 127 | platformAdmin: PublicKey; 128 | platformId?: PublicKey; 129 | 130 | updateInfo: 131 | | { type: "updateClaimFeeWallet"; value: PublicKey } 132 | | { type: "updateFeeRate"; value: BN } 133 | | { type: "updateName" | "updateImg" | "updateWeb"; value: string } 134 | | { type: "migrateCpLockNftScale"; value: { platformScale: BN; creatorScale: BN; burnScale: BN } } 135 | | { type: 'updateCpConfigId', value: PublicKey } 136 | | { 137 | type: 'updateAll', value: { 138 | platformClaimFeeWallet: PublicKey, 139 | platformLockNftWallet: PublicKey, 140 | cpConfigId: PublicKey, 141 | migrateCpLockNftScale: { 142 | platformScale: BN, 143 | creatorScale: BN, 144 | burnScale: BN, 145 | }, 146 | feeRate: BN, 147 | name: string, 148 | web: string, 149 | img: string, 150 | } 151 | }; 152 | 153 | computeBudgetConfig?: ComputeBudgetConfig; 154 | txTipConfig?: TxTipConfig; 155 | txVersion?: T; 156 | feePayer?: PublicKey; 157 | } 158 | 159 | export interface ClaimPlatformFee { 160 | programId?: PublicKey; 161 | authProgramId?: PublicKey; 162 | platformId: PublicKey; 163 | platformClaimFeeWallet: PublicKey; 164 | poolId: PublicKey; 165 | 166 | mintB?: PublicKey; 167 | vaultB?: PublicKey; 168 | mintBProgram?: PublicKey; 169 | 170 | computeBudgetConfig?: ComputeBudgetConfig; 171 | txTipConfig?: TxTipConfig; 172 | txVersion?: T; 173 | feePayer?: PublicKey; 174 | } 175 | 176 | export interface ClaimAllPlatformFee { 177 | programId?: PublicKey; 178 | authProgramId?: PublicKey; 179 | platformId: PublicKey; 180 | platformClaimFeeWallet: PublicKey; 181 | 182 | computeBudgetConfig?: ComputeBudgetConfig; 183 | txTipConfig?: TxTipConfig; 184 | txVersion?: T; 185 | feePayer?: PublicKey; 186 | } 187 | 188 | export interface CreateVesting { 189 | programId?: PublicKey; 190 | poolId: PublicKey; 191 | beneficiary: PublicKey; 192 | shareAmount: BN; 193 | 194 | computeBudgetConfig?: ComputeBudgetConfig; 195 | txTipConfig?: TxTipConfig; 196 | txVersion?: T; 197 | feePayer?: PublicKey; 198 | } 199 | 200 | export interface ClaimVesting { 201 | programId?: PublicKey; 202 | poolId: PublicKey; 203 | poolInfo?: LaunchpadPoolInfo; 204 | 205 | computeBudgetConfig?: ComputeBudgetConfig; 206 | txTipConfig?: TxTipConfig; 207 | txVersion?: T; 208 | feePayer?: PublicKey; 209 | 210 | associatedOnly?: boolean; 211 | checkCreateATAOwner?: boolean; 212 | } 213 | 214 | export type LaunchpadPoolInfo = ReturnType; 215 | export type LaunchpadConfigInfo = ReturnType; 216 | export type LaunchpadPlatformInfo = ReturnType; 217 | -------------------------------------------------------------------------------- /src/raydium/liquidity/constant.ts: -------------------------------------------------------------------------------- 1 | import BN from "bn.js"; 2 | import { SerumVersion } from "../serum"; 3 | 4 | export const LIQUIDITY_FEES_NUMERATOR = new BN(25); 5 | export const LIQUIDITY_FEES_DENOMINATOR = new BN(10000); 6 | 7 | // liquidity version => serum version 8 | export const LIQUIDITY_VERSION_TO_SERUM_VERSION: { 9 | [key in 4 | 5]?: SerumVersion; 10 | } = { 11 | 4: 3, 12 | 5: 3, 13 | }; 14 | -------------------------------------------------------------------------------- /src/raydium/liquidity/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constant"; 2 | export * from "./layout"; 3 | export * from "./type"; 4 | export * from "./utils"; 5 | export * from "./instruction"; 6 | export * from "./stable"; 7 | -------------------------------------------------------------------------------- /src/raydium/liquidity/layout.ts: -------------------------------------------------------------------------------- 1 | import { GetStructureSchema, publicKey, seq, struct, u128, u64, u8 } from "../../marshmallow"; 2 | 3 | export const fixedSwapInLayout = struct([u8("instruction"), u64("amountIn"), u64("minAmountOut")]); 4 | export const fixedSwapOutLayout = struct([u8("instruction"), u64("maxAmountIn"), u64("amountOut")]); 5 | 6 | export const createPoolV4Layout = struct([u8("instruction"), u8("nonce")]); 7 | export const initPoolLayout = struct([u8("instruction"), u8("nonce"), u64("startTime")]); 8 | /* ================= state layouts ================= */ 9 | export const liquidityStateV4Layout = struct([ 10 | u64("status"), 11 | u64("nonce"), 12 | u64("maxOrder"), 13 | u64("depth"), 14 | u64("baseDecimal"), 15 | u64("quoteDecimal"), 16 | u64("state"), 17 | u64("resetFlag"), 18 | u64("minSize"), 19 | u64("volMaxCutRatio"), 20 | u64("amountWaveRatio"), 21 | u64("baseLotSize"), 22 | u64("quoteLotSize"), 23 | u64("minPriceMultiplier"), 24 | u64("maxPriceMultiplier"), 25 | u64("systemDecimalValue"), 26 | u64("minSeparateNumerator"), 27 | u64("minSeparateDenominator"), 28 | u64("tradeFeeNumerator"), 29 | u64("tradeFeeDenominator"), 30 | u64("pnlNumerator"), 31 | u64("pnlDenominator"), 32 | u64("swapFeeNumerator"), 33 | u64("swapFeeDenominator"), 34 | u64("baseNeedTakePnl"), 35 | u64("quoteNeedTakePnl"), 36 | u64("quoteTotalPnl"), 37 | u64("baseTotalPnl"), 38 | u64("poolOpenTime"), 39 | u64("punishPcAmount"), 40 | u64("punishCoinAmount"), 41 | u64("orderbookToInitTime"), 42 | // u128('poolTotalDepositPc'), 43 | // u128('poolTotalDepositCoin'), 44 | u128("swapBaseInAmount"), 45 | u128("swapQuoteOutAmount"), 46 | u64("swapBase2QuoteFee"), 47 | u128("swapQuoteInAmount"), 48 | u128("swapBaseOutAmount"), 49 | u64("swapQuote2BaseFee"), 50 | // amm vault 51 | publicKey("baseVault"), 52 | publicKey("quoteVault"), 53 | // mint 54 | publicKey("baseMint"), 55 | publicKey("quoteMint"), 56 | publicKey("lpMint"), 57 | // market 58 | publicKey("openOrders"), 59 | publicKey("marketId"), 60 | publicKey("marketProgramId"), 61 | publicKey("targetOrders"), 62 | publicKey("withdrawQueue"), 63 | publicKey("lpVault"), 64 | publicKey("owner"), 65 | // true circulating supply without lock up 66 | u64("lpReserve"), 67 | seq(u64(), 3, "padding"), 68 | ]); 69 | 70 | export type LiquidityStateLayoutV4 = typeof liquidityStateV4Layout; 71 | export type LiquidityStateV4 = GetStructureSchema; 72 | 73 | export const liquidityStateV5Layout = struct([ 74 | u64("accountType"), 75 | u64("status"), 76 | u64("nonce"), 77 | u64("maxOrder"), 78 | u64("depth"), 79 | u64("baseDecimal"), 80 | u64("quoteDecimal"), 81 | u64("state"), 82 | u64("resetFlag"), 83 | u64("minSize"), 84 | u64("volMaxCutRatio"), 85 | u64("amountWaveRatio"), 86 | u64("baseLotSize"), 87 | u64("quoteLotSize"), 88 | u64("minPriceMultiplier"), 89 | u64("maxPriceMultiplier"), 90 | u64("systemDecimalsValue"), 91 | u64("abortTradeFactor"), 92 | u64("priceTickMultiplier"), 93 | u64("priceTick"), 94 | // Fees 95 | u64("minSeparateNumerator"), 96 | u64("minSeparateDenominator"), 97 | u64("tradeFeeNumerator"), 98 | u64("tradeFeeDenominator"), 99 | u64("pnlNumerator"), 100 | u64("pnlDenominator"), 101 | u64("swapFeeNumerator"), 102 | u64("swapFeeDenominator"), 103 | // OutPutData 104 | u64("baseNeedTakePnl"), 105 | u64("quoteNeedTakePnl"), 106 | u64("quoteTotalPnl"), 107 | u64("baseTotalPnl"), 108 | u64("poolOpenTime"), 109 | u64("punishPcAmount"), 110 | u64("punishCoinAmount"), 111 | u64("orderbookToInitTime"), 112 | u128("swapBaseInAmount"), 113 | u128("swapQuoteOutAmount"), 114 | u128("swapQuoteInAmount"), 115 | u128("swapBaseOutAmount"), 116 | u64("swapQuote2BaseFee"), 117 | u64("swapBase2QuoteFee"), 118 | 119 | publicKey("baseVault"), 120 | publicKey("quoteVault"), 121 | publicKey("baseMint"), 122 | publicKey("quoteMint"), 123 | publicKey("lpMint"), 124 | 125 | publicKey("modelDataAccount"), 126 | publicKey("openOrders"), 127 | publicKey("marketId"), 128 | publicKey("marketProgramId"), 129 | publicKey("targetOrders"), 130 | publicKey("owner"), 131 | seq(u64(), 64, "padding"), 132 | ]); 133 | 134 | export const addLiquidityLayout = struct([ 135 | u8("instruction"), 136 | u64("baseAmountIn"), 137 | u64("quoteAmountIn"), 138 | u64("fixedSide"), 139 | u64("otherAmountMin"), 140 | ]); 141 | 142 | export const removeLiquidityLayout = struct([ 143 | u8("instruction"), 144 | u64("lpAmount"), 145 | u64("baseAmountMin"), 146 | u64("quoteAmountMin"), 147 | ]); 148 | 149 | export type LiquidityStateLayoutV5 = typeof liquidityStateV5Layout; 150 | export type LiquidityStateV5 = GetStructureSchema; 151 | 152 | export type LiquidityState = LiquidityStateV4 | LiquidityStateV5; 153 | export type LiquidityStateLayout = LiquidityStateLayoutV4 | LiquidityStateLayoutV5; 154 | 155 | /* ================= index ================= */ 156 | // version => liquidity state layout 157 | export const LIQUIDITY_VERSION_TO_STATE_LAYOUT: { 158 | [version: number]: LiquidityStateLayout; 159 | } = { 160 | 4: liquidityStateV4Layout, 161 | 5: liquidityStateV5Layout, 162 | }; 163 | export const createPoolFeeLayout = struct([u64("fee")]); 164 | -------------------------------------------------------------------------------- /src/raydium/liquidity/serum.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { createLogger } from "../../common/logger"; 3 | import { SerumVersion } from "../serum"; 4 | import { LIQUIDITY_VERSION_TO_SERUM_VERSION } from "./constant"; 5 | 6 | const logger = createLogger("Raydium_liquidity_serum"); 7 | 8 | export function getSerumVersion(version: number): SerumVersion { 9 | const serumVersion = LIQUIDITY_VERSION_TO_SERUM_VERSION[version]; 10 | if (!serumVersion) logger.logWithError("invalid version", "version", version); 11 | 12 | return serumVersion; 13 | } 14 | 15 | export function getSerumAssociatedAuthority({ programId, marketId }: { programId: PublicKey; marketId: PublicKey }): { 16 | publicKey: PublicKey; 17 | nonce: number; 18 | } { 19 | const seeds = [marketId.toBuffer()]; 20 | 21 | let nonce = 0; 22 | let publicKey: PublicKey; 23 | 24 | while (nonce < 100) { 25 | try { 26 | const seedsWithNonce = seeds.concat(Buffer.from([nonce]), Buffer.alloc(7)); 27 | publicKey = PublicKey.createProgramAddressSync(seedsWithNonce, programId); 28 | } catch (err) { 29 | if (err instanceof TypeError) { 30 | throw err; 31 | } 32 | nonce++; 33 | continue; 34 | } 35 | return { publicKey, nonce }; 36 | } 37 | 38 | logger.logWithError("unable to find a viable program address nonce", "params", { 39 | programId, 40 | marketId, 41 | }); 42 | throw new Error("unable to find a viable program address nonce"); 43 | } 44 | -------------------------------------------------------------------------------- /src/raydium/marketV2/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createMarket"; 2 | export * from "./instrument"; 3 | export * from "./layout"; 4 | export { default } from "./createMarket"; 5 | -------------------------------------------------------------------------------- /src/raydium/marketV2/layout.ts: -------------------------------------------------------------------------------- 1 | import { blob, publicKey, struct, u64, WideBits } from "../../marshmallow"; 2 | 3 | function accountFlagsLayout(property = "accountFlags"): WideBits { 4 | const ACCOUNT_FLAGS_LAYOUT = new WideBits(property); 5 | ACCOUNT_FLAGS_LAYOUT.addBoolean("initialized"); 6 | ACCOUNT_FLAGS_LAYOUT.addBoolean("market"); 7 | ACCOUNT_FLAGS_LAYOUT.addBoolean("openOrders"); 8 | ACCOUNT_FLAGS_LAYOUT.addBoolean("requestQueue"); 9 | ACCOUNT_FLAGS_LAYOUT.addBoolean("eventQueue"); 10 | ACCOUNT_FLAGS_LAYOUT.addBoolean("bids"); 11 | ACCOUNT_FLAGS_LAYOUT.addBoolean("asks"); 12 | return ACCOUNT_FLAGS_LAYOUT; 13 | } 14 | 15 | export const MARKET_STATE_LAYOUT_V2 = struct([ 16 | blob(5), 17 | accountFlagsLayout("accountFlags"), 18 | publicKey("ownAddress"), 19 | u64("vaultSignerNonce"), 20 | publicKey("baseMint"), 21 | publicKey("quoteMint"), 22 | publicKey("baseVault"), 23 | u64("baseDepositsTotal"), 24 | u64("baseFeesAccrued"), 25 | publicKey("quoteVault"), 26 | u64("quoteDepositsTotal"), 27 | u64("quoteFeesAccrued"), 28 | u64("quoteDustThreshold"), 29 | publicKey("requestQueue"), 30 | publicKey("eventQueue"), 31 | publicKey("bids"), 32 | publicKey("asks"), 33 | u64("baseLotSize"), 34 | u64("quoteLotSize"), 35 | u64("feeRateBps"), 36 | u64("referrerRebatesAccrued"), 37 | blob(7), 38 | ]); 39 | -------------------------------------------------------------------------------- /src/raydium/moduleBase.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { createLogger, Logger } from "../common/logger"; 4 | import { TxBuilder } from "../common/txTool/txTool"; 5 | 6 | import { Raydium } from "./"; 7 | 8 | export interface ModuleBaseProps { 9 | scope: Raydium; 10 | moduleName: string; 11 | } 12 | 13 | const joinMsg = (...args: (string | number | Record)[]): string => 14 | args 15 | .map((arg) => { 16 | try { 17 | return typeof arg === "object" ? JSON.stringify(arg) : arg; 18 | } catch { 19 | return arg; 20 | } 21 | }) 22 | .join(", "); 23 | export default class ModuleBase { 24 | public scope: Raydium; 25 | private disabled = false; 26 | protected logger: Logger; 27 | 28 | constructor({ scope, moduleName }: ModuleBaseProps) { 29 | this.scope = scope; 30 | this.logger = createLogger(moduleName); 31 | } 32 | 33 | protected createTxBuilder(feePayer?: PublicKey): TxBuilder { 34 | this.scope.checkOwner(); 35 | return new TxBuilder({ 36 | connection: this.scope.connection, 37 | feePayer: feePayer || this.scope.ownerPubKey, 38 | cluster: this.scope.cluster, 39 | owner: this.scope.owner, 40 | blockhashCommitment: this.scope.blockhashCommitment, 41 | loopMultiTxStatus: this.scope.loopMultiTxStatus, 42 | api: this.scope.api, 43 | signAllTransactions: this.scope.signAllTransactions, 44 | }); 45 | } 46 | 47 | public logDebug(...args: (string | number | Record)[]): void { 48 | this.logger.debug(joinMsg(args)); 49 | } 50 | 51 | public logInfo(...args: (string | number | Record)[]): void { 52 | this.logger.info(joinMsg(args)); 53 | } 54 | 55 | public logAndCreateError(...args: (string | number | Record)[]): void { 56 | const message = joinMsg(args); 57 | throw new Error(message); 58 | } 59 | 60 | public checkDisabled(): void { 61 | if (this.disabled || !this.scope) this.logAndCreateError("module not working"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/raydium/serum/id.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { SERUM_PROGRAM_ID_V3 } from "../../common/programId"; 3 | import { SerumVersion } from "./type"; 4 | 5 | // serum program id string => serum version 6 | export const SERUM_PROGRAMID_TO_VERSION: { 7 | [key: string]: SerumVersion; 8 | } = { 9 | [SERUM_PROGRAM_ID_V3.toBase58()]: 3, 10 | }; 11 | 12 | // serum version => serum program id 13 | export const SERUM_VERSION_TO_PROGRAMID: { [key in SerumVersion]?: PublicKey } & { 14 | [K: number]: PublicKey; 15 | } = { 16 | 3: SERUM_PROGRAM_ID_V3, 17 | }; 18 | -------------------------------------------------------------------------------- /src/raydium/serum/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./id"; 2 | export * from "./layout"; 3 | export * from "./serum"; 4 | export * from "./type"; 5 | -------------------------------------------------------------------------------- /src/raydium/serum/layout.ts: -------------------------------------------------------------------------------- 1 | import { blob, GetStructureSchema, publicKey, struct, u64 } from "../../marshmallow"; 2 | 3 | /* ================= state layouts ================= */ 4 | export const MARKET_STATE_LAYOUT_V3 = struct([ 5 | blob(5), 6 | 7 | blob(8), // accountFlagsLayout('accountFlags'), 8 | 9 | publicKey("ownAddress"), 10 | 11 | u64("vaultSignerNonce"), 12 | 13 | publicKey("baseMint"), 14 | publicKey("quoteMint"), 15 | 16 | publicKey("baseVault"), 17 | u64("baseDepositsTotal"), 18 | u64("baseFeesAccrued"), 19 | 20 | publicKey("quoteVault"), 21 | u64("quoteDepositsTotal"), 22 | u64("quoteFeesAccrued"), 23 | 24 | u64("quoteDustThreshold"), 25 | 26 | publicKey("requestQueue"), 27 | publicKey("eventQueue"), 28 | 29 | publicKey("bids"), 30 | publicKey("asks"), 31 | 32 | u64("baseLotSize"), 33 | u64("quoteLotSize"), 34 | 35 | u64("feeRateBps"), 36 | 37 | u64("referrerRebatesAccrued"), 38 | 39 | blob(7), 40 | ]); 41 | 42 | export type MarketStateLayoutV3 = typeof MARKET_STATE_LAYOUT_V3; 43 | export type MarketStateLayout = MarketStateLayoutV3; 44 | 45 | export type MarketStateV3 = GetStructureSchema; 46 | export type MarketState = MarketStateV3; 47 | 48 | /* ================= index ================= */ 49 | // version => market state layout 50 | export const MARKET_VERSION_TO_STATE_LAYOUT: { 51 | [version: number]: MarketStateLayout; 52 | } = { 53 | 3: MARKET_STATE_LAYOUT_V3, 54 | }; 55 | -------------------------------------------------------------------------------- /src/raydium/serum/serum.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | 3 | import { createLogger } from "../../common/logger"; 4 | 5 | import { SERUM_PROGRAMID_TO_VERSION, SERUM_VERSION_TO_PROGRAMID } from "./id"; 6 | import { MARKET_VERSION_TO_STATE_LAYOUT, MarketStateLayout } from "./layout"; 7 | 8 | const logger = createLogger("Serum"); 9 | 10 | export class Market { 11 | /* ================= get version and program id ================= */ 12 | static getProgramId(version: number): PublicKey { 13 | const programId = SERUM_VERSION_TO_PROGRAMID[version]; 14 | if (!programId) logger.logWithError("invalid version", "version", version); 15 | 16 | return programId; 17 | } 18 | 19 | static getVersion(programId: PublicKey): number { 20 | const programIdString = programId.toBase58(); 21 | 22 | const version = SERUM_PROGRAMID_TO_VERSION[programIdString]; 23 | if (!version) logger.logWithError("invalid program id", "programId", programIdString); 24 | 25 | return version; 26 | } 27 | 28 | /* ================= get layout ================= */ 29 | static getStateLayout(version: number): MarketStateLayout { 30 | const STATE_LAYOUT = MARKET_VERSION_TO_STATE_LAYOUT[version]; 31 | if (!STATE_LAYOUT) logger.logWithError(!!STATE_LAYOUT, "invalid version", "version", version); 32 | 33 | return STATE_LAYOUT; 34 | } 35 | 36 | static getLayouts(version: number): { state: MarketStateLayout } { 37 | return { state: this.getStateLayout(version) }; 38 | } 39 | 40 | /* ================= get key ================= */ 41 | static getAssociatedAuthority({ programId, marketId }: { programId: PublicKey; marketId: PublicKey }): { 42 | publicKey: PublicKey; 43 | nonce: number; 44 | } { 45 | const seeds = [marketId.toBuffer()]; 46 | 47 | let nonce = 0; 48 | let publicKey: PublicKey; 49 | 50 | while (nonce < 100) { 51 | try { 52 | const seedsWithNonce = seeds.concat(Buffer.from([nonce]), Buffer.alloc(7)); 53 | publicKey = PublicKey.createProgramAddressSync(seedsWithNonce, programId); 54 | } catch (err) { 55 | if (err instanceof TypeError) { 56 | throw err; 57 | } 58 | nonce++; 59 | continue; 60 | } 61 | return { publicKey, nonce }; 62 | } 63 | 64 | logger.logWithError("unable to find a viable program address nonce", "params", { 65 | programId, 66 | marketId, 67 | }); 68 | 69 | return { publicKey: PublicKey.default, nonce }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/raydium/serum/type.ts: -------------------------------------------------------------------------------- 1 | // 1 | 2 | 3 2 | export type SerumVersion = 3; 3 | -------------------------------------------------------------------------------- /src/raydium/token/constant.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; 3 | import { TokenInfo } from "./type"; 4 | 5 | export const SOL_INFO: TokenInfo = { 6 | chainId: 101, 7 | address: PublicKey.default.toBase58(), 8 | programId: TOKEN_PROGRAM_ID.toBase58(), 9 | decimals: 9, 10 | symbol: "SOL", 11 | name: "solana", 12 | logoURI: `https://img-v1.raydium.io/icon/So11111111111111111111111111111111111111112.png`, 13 | tags: [], 14 | priority: 2, 15 | type: "raydium", 16 | extensions: { 17 | coingeckoId: "solana", 18 | }, 19 | }; 20 | 21 | export const TOKEN_WSOL: TokenInfo = { 22 | chainId: 101, 23 | address: "So11111111111111111111111111111111111111112", 24 | programId: TOKEN_PROGRAM_ID.toBase58(), 25 | decimals: 9, 26 | symbol: "WSOL", 27 | name: "Wrapped SOL", 28 | logoURI: `https://img-v1.raydium.io/icon/So11111111111111111111111111111111111111112.png`, 29 | tags: [], 30 | priority: 2, 31 | type: "raydium", 32 | extensions: { 33 | coingeckoId: "solana", 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /src/raydium/token/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constant"; 2 | export * from "./layout"; 3 | export * from "./type"; 4 | export * from "./utils"; 5 | -------------------------------------------------------------------------------- /src/raydium/token/layout.ts: -------------------------------------------------------------------------------- 1 | import { publicKey, struct, u32, u64, u8 } from "../../marshmallow"; 2 | 3 | export const SPL_MINT_LAYOUT = struct([ 4 | u32("mintAuthorityOption"), 5 | publicKey("mintAuthority"), 6 | u64("supply"), 7 | u8("decimals"), 8 | u8("isInitialized"), 9 | u32("freezeAuthorityOption"), 10 | publicKey("freezeAuthority"), 11 | ]); 12 | 13 | export type SplMintLayout = typeof SPL_MINT_LAYOUT; 14 | -------------------------------------------------------------------------------- /src/raydium/token/token.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey } from "@solana/web3.js"; 2 | import { MintLayout, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; 3 | 4 | import { ApiV3Token, JupTokenType } from "@/api/type"; 5 | import ModuleBase, { ModuleBaseProps } from "../moduleBase"; 6 | import { LoadParams } from "../type"; 7 | 8 | import { SOL_INFO } from "./constant"; 9 | import { TokenInfo } from "./type"; 10 | 11 | export default class TokenModule extends ModuleBase { 12 | private _tokenList: TokenInfo[] = []; 13 | private _tokenMap: Map = new Map(); 14 | private _blackTokenMap: Set = new Set(); 15 | private _mintGroup: { official: Set; jup: Set; extra: Set } = { 16 | official: new Set(), 17 | jup: new Set(), 18 | extra: new Set(), 19 | }; 20 | private _whiteMap: Set = new Set(); 21 | private _extraTokenList: TokenInfo[] = []; 22 | 23 | constructor(params: ModuleBaseProps) { 24 | super(params); 25 | } 26 | 27 | public async load(params?: LoadParams & { type?: JupTokenType }): Promise { 28 | this.checkDisabled(); 29 | const { forceUpdate = false, type = JupTokenType.Strict } = params || {}; 30 | const { mintList, blacklist, whiteList } = await this.scope.fetchV3TokenList(forceUpdate); 31 | const jup = await this.scope.fetchJupTokenList(forceUpdate); 32 | // reset all data 33 | this._tokenList = []; 34 | this._tokenMap = new Map(); 35 | this._blackTokenMap = new Set(blacklist); 36 | this._mintGroup = { official: new Set(), jup: new Set(), extra: new Set() }; 37 | this._whiteMap = new Set(whiteList); 38 | 39 | this._tokenMap.set(SOL_INFO.address, SOL_INFO); 40 | this._mintGroup.official.add(SOL_INFO.address); 41 | 42 | mintList.forEach((token) => { 43 | if (this._blackTokenMap.has(token.address)) return; 44 | this._tokenMap.set(token.address, { 45 | ...token, 46 | type: "raydium", 47 | priority: 2, 48 | programId: 49 | token.programId ?? 50 | (token.tags.includes("token-2022") ? TOKEN_2022_PROGRAM_ID.toBase58() : TOKEN_PROGRAM_ID.toBase58()), 51 | }); 52 | this._mintGroup.official.add(token.address); 53 | }); 54 | 55 | jup.forEach((token) => { 56 | if (this._blackTokenMap.has(token.address) || this._tokenMap.has(token.address)) return; 57 | this._tokenMap.set(token.address, { 58 | ...token, 59 | type: "jupiter", 60 | priority: 1, 61 | programId: 62 | token.programId ?? 63 | (token.tags.includes("token-2022") ? TOKEN_2022_PROGRAM_ID.toBase58() : TOKEN_PROGRAM_ID.toBase58()), 64 | tags: token.freezeAuthority ? [...(token.tags || []), "hasFreeze"] : token.tags, 65 | }); 66 | this._mintGroup.jup.add(token.address); 67 | }); 68 | 69 | this._extraTokenList.forEach((token) => { 70 | if (this._blackTokenMap.has(token.address) || this._tokenMap.has(token.address)) return; 71 | this._tokenMap.set(token.address, { 72 | ...token, 73 | type: "extra", 74 | priority: 1, 75 | programId: 76 | token.programId || token.tags.includes("token-2022") 77 | ? TOKEN_2022_PROGRAM_ID.toBase58() 78 | : TOKEN_PROGRAM_ID.toBase58(), 79 | }); 80 | this._mintGroup.extra.add(token.address); 81 | }); 82 | 83 | this._tokenList = Array.from(this._tokenMap).map((data) => data[1]); 84 | } 85 | 86 | get tokenList(): TokenInfo[] { 87 | return this._tokenList; 88 | } 89 | get tokenMap(): Map { 90 | return this._tokenMap; 91 | } 92 | get blackTokenMap(): Set { 93 | return this._blackTokenMap; 94 | } 95 | get mintGroup(): { official: Set; jup: Set } { 96 | return this._mintGroup; 97 | } 98 | get whiteListMap(): Set { 99 | return this._whiteMap; 100 | } 101 | 102 | /** === util functions === */ 103 | 104 | public async getTokenInfo(mint: string | PublicKey): Promise { 105 | if (!mint) throw new Error("please input mint"); 106 | const mintStr = mint.toString(); 107 | const info = this._tokenMap.get(mintStr); 108 | if (info) return info; 109 | if (mintStr.toLocaleUpperCase() === "SOL") return SOL_INFO; 110 | 111 | const apiTokenInfo = (await this.scope.api.getTokenInfo([mintStr]))[0]; 112 | if (apiTokenInfo) { 113 | this._mintGroup.extra.add(mintStr); 114 | this._tokenMap.set(mintStr, { ...apiTokenInfo, priority: 2 }); 115 | return apiTokenInfo; 116 | } 117 | 118 | const onlineInfo = await this.scope.connection.getAccountInfo(new PublicKey(mintStr)); 119 | if (!onlineInfo) throw new Error(`mint address not found: ${mintStr}`); 120 | const data = MintLayout.decode(onlineInfo.data); 121 | const mintSymbol = mintStr.toString().substring(0, 6); 122 | const fullInfo = { 123 | chainId: 101, 124 | address: mintStr, 125 | programId: onlineInfo.owner.toBase58(), 126 | logoURI: "", 127 | symbol: mintSymbol, 128 | name: mintSymbol, 129 | decimals: data.decimals, 130 | tags: [], 131 | extensions: {}, 132 | priority: 0, 133 | type: "unknown", 134 | }; 135 | this._mintGroup.extra.add(mintStr); 136 | this._tokenMap.set(mintStr, fullInfo); 137 | return fullInfo; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/raydium/token/type.ts: -------------------------------------------------------------------------------- 1 | import { ApiV3Token } from "../../api/type"; 2 | import { Token, TokenProps } from "../../module/token"; 3 | 4 | export type TokenInfo = ApiV3Token & { 5 | priority: number; 6 | userAdded?: boolean; 7 | type?: string; 8 | }; 9 | 10 | export interface TokenJson { 11 | symbol: string; 12 | name: string; 13 | mint: string; 14 | decimals: number; 15 | extensions: { 16 | coingeckoId?: string; 17 | }; 18 | icon: string; 19 | hasFreeze?: boolean; 20 | } 21 | 22 | export type SplToken = TokenProps & { 23 | icon: string; 24 | id: string; 25 | extensions: { 26 | [key in "coingeckoId" | "website" | "whitepaper"]?: string; 27 | }; 28 | userAdded?: boolean; // only if token is added by user 29 | }; 30 | 31 | export type LpToken = Token & { 32 | isLp: true; 33 | base: SplToken; 34 | quote: SplToken; 35 | icon: string; 36 | /** mint. for ``*/ 37 | id: string; 38 | extensions: { 39 | [key in "coingeckoId" | "website" | "whitepaper"]?: string; 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /src/raydium/token/utils.ts: -------------------------------------------------------------------------------- 1 | import { Connection, PublicKey } from "@solana/web3.js"; 2 | import { MintLayout, RawMint, TOKEN_PROGRAM_ID, TransferFeeConfigLayout } from "@solana/spl-token"; 3 | import { BigNumberish } from "@/common/bignumber"; 4 | import { Token, TokenAmount } from "../../module"; 5 | import { SOL_INFO, TOKEN_WSOL } from "./constant"; 6 | import { TokenInfo } from "./type"; 7 | 8 | import { ApiV3Token } from "../../api"; 9 | import { solToWSol } from "@/common"; 10 | 11 | export const parseTokenInfo = async ({ 12 | connection, 13 | mint, 14 | }: { 15 | connection: Connection; 16 | mint: PublicKey | string; 17 | }): Promise => { 18 | const accountData = await connection.getAccountInfo(new PublicKey(mint)); 19 | if (!accountData || accountData.data.length !== MintLayout.span) return; 20 | const tokenInfo = MintLayout.decode(accountData.data); 21 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 22 | //@ts-ignore 23 | return tokenInfo; 24 | }; 25 | 26 | export const toTokenInfo = ({ 27 | mint, 28 | decimals, 29 | programId = TOKEN_PROGRAM_ID, 30 | logoURI = "", 31 | priority = 3, 32 | }: { 33 | mint: PublicKey; 34 | decimals: number; 35 | programId?: PublicKey | string; 36 | priority?: number; 37 | logoURI?: string; 38 | }): TokenInfo => { 39 | const pubStr = mint.toBase58().substring(0, 6); 40 | return { 41 | address: mint.toBase58(), 42 | decimals, 43 | symbol: pubStr, 44 | logoURI, 45 | extensions: {}, 46 | chainId: 101, 47 | programId: programId.toString(), 48 | name: pubStr, 49 | tags: [], 50 | priority, 51 | }; 52 | }; 53 | 54 | export const toToken = (props: Omit): Token => 55 | new Token({ 56 | mint: props.address, 57 | decimals: props.decimals, 58 | symbol: props.symbol, 59 | name: props.name, 60 | }); 61 | 62 | export const toTokenAmount = ({ 63 | amount, 64 | isRaw, 65 | name, 66 | ...props 67 | }: Omit & { 68 | amount: BigNumberish; 69 | isRaw?: boolean; 70 | name?: string; 71 | }): TokenAmount => 72 | new TokenAmount( 73 | new Token({ 74 | mint: solToWSol(props.address).toBase58(), 75 | decimals: props.decimals, 76 | symbol: props.symbol, 77 | name, 78 | }), 79 | amount, 80 | isRaw, 81 | name, 82 | ); 83 | 84 | export function solToWSolToken(token: T): T { 85 | if (token.address === SOL_INFO.address) return TOKEN_WSOL as T; 86 | return token; 87 | } 88 | 89 | export function wSolToSolToken(token: T): T { 90 | if (token.address === TOKEN_WSOL.address) return SOL_INFO as T; 91 | return token; 92 | } 93 | 94 | export const toApiV3Token = ({ 95 | address, 96 | programId, 97 | decimals, 98 | ...props 99 | }: { 100 | address: string; 101 | programId: string; 102 | decimals: number; 103 | } & Partial): ApiV3Token => ({ 104 | chainId: 101, 105 | address: solToWSol(address).toBase58(), 106 | programId, 107 | logoURI: "", 108 | symbol: "", 109 | name: "", 110 | decimals, 111 | tags: [], 112 | extensions: props.extensions || {}, 113 | ...props, 114 | }); 115 | 116 | export const toFeeConfig = ( 117 | config?: ReturnType | undefined | null, 118 | ): ApiV3Token["extensions"]["feeConfig"] | undefined => 119 | config 120 | ? { 121 | ...config, 122 | transferFeeConfigAuthority: config.transferFeeConfigAuthority.toBase58(), 123 | withdrawWithheldAuthority: config.withdrawWithheldAuthority.toBase58(), 124 | withheldAmount: config.withheldAmount.toString(), 125 | olderTransferFee: { 126 | ...config.olderTransferFee, 127 | epoch: config.olderTransferFee.epoch.toString(), 128 | maximumFee: config.olderTransferFee.maximumFee.toString(), 129 | }, 130 | newerTransferFee: { 131 | ...config.newerTransferFee, 132 | epoch: config.newerTransferFee.epoch.toString(), 133 | maximumFee: config.newerTransferFee.maximumFee.toString(), 134 | }, 135 | } 136 | : undefined; 137 | -------------------------------------------------------------------------------- /src/raydium/tradeV2/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./trade"; 2 | export * from "./instrument"; 3 | export * from "./type"; 4 | -------------------------------------------------------------------------------- /src/raydium/tradeV2/type.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, PublicKey, Signer, Transaction, TransactionInstruction } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import Decimal from "decimal.js"; 4 | import { ApiV3PoolInfoItem, PoolKeys } from "../../api/type"; 5 | import { Token, TokenAmount } from "../../module"; 6 | import { ComputeClmmPoolInfo } from "../../raydium/clmm/type"; 7 | import { ComputeAmountOutParam } from "../../raydium/liquidity/type"; 8 | import { CpmmComputeData } from "../cpmm"; 9 | import { TransferAmountFee } from "../type"; 10 | 11 | export interface ComputeAmountOutAmmLayout { 12 | amountIn: TransferAmountFee; 13 | amountOut: TransferAmountFee; 14 | minAmountOut: TransferAmountFee; 15 | currentPrice: Decimal | undefined; 16 | executionPrice: Decimal | null; 17 | priceImpact: Decimal; 18 | fee: TokenAmount[]; 19 | routeType: "amm"; 20 | poolInfoList: ComputePoolType[]; 21 | remainingAccounts: PublicKey[][]; 22 | poolReady: boolean; 23 | poolType: "CLMM" | "CPMM" | "STABLE" | undefined; 24 | 25 | feeConfig?: { 26 | feeAmount: BN; 27 | feeAccount: PublicKey; 28 | }; 29 | 30 | expirationTime: number | undefined; 31 | 32 | allTrade: boolean; 33 | slippage: number; 34 | clmmExPriceX64: (BN | undefined)[]; 35 | } 36 | export interface ComputeAmountOutRouteLayout { 37 | amountIn: TransferAmountFee; 38 | amountOut: TransferAmountFee; 39 | minAmountOut: TransferAmountFee; 40 | currentPrice: Decimal | undefined; 41 | executionPrice: Decimal | null; 42 | priceImpact: Decimal; 43 | fee: TokenAmount[]; 44 | routeType: "route"; 45 | poolInfoList: ComputePoolType[]; 46 | remainingAccounts: (PublicKey[] | undefined)[]; 47 | minMiddleAmountFee: TokenAmount | undefined; 48 | middleToken: Token; 49 | poolReady: boolean; 50 | poolType: (string | undefined)[]; 51 | 52 | feeConfig?: { 53 | feeAmount: BN; 54 | feeAccount: PublicKey; 55 | }; 56 | 57 | expirationTime: number | undefined; 58 | allTrade: boolean; 59 | slippage: number; 60 | clmmExPriceX64: (BN | undefined)[]; 61 | } 62 | 63 | export type ComputeAmountOutLayout = ComputeAmountOutAmmLayout | ComputeAmountOutRouteLayout; 64 | 65 | export type MakeSwapInstructionParam = { 66 | ownerInfo: { 67 | wallet: PublicKey; 68 | // tokenAccountA: PublicKey 69 | // tokenAccountB: PublicKey 70 | 71 | sourceToken: PublicKey; 72 | routeToken?: PublicKey; 73 | destinationToken: PublicKey; 74 | userPdaAccount?: PublicKey; 75 | }; 76 | 77 | inputMint: PublicKey; 78 | routeProgram: PublicKey; 79 | 80 | // ComputeAmountOutAmmLayout | ComputeAmountOutRouteLayout; 81 | swapInfo: 82 | | ( 83 | | (Omit & { 84 | poolKey: PoolKeys[]; 85 | poolInfo: ComputePoolType[]; 86 | }) 87 | | (Omit & { 88 | poolKey: PoolKeys[]; 89 | poolInfo: ComputePoolType[]; 90 | }) 91 | ) & { 92 | outputMint: PublicKey; 93 | }; 94 | }; 95 | 96 | export interface PoolAccountInfoV4 { 97 | ammId: string; 98 | status: BN; 99 | baseDecimals: number; 100 | quoteDecimals: number; 101 | lpDecimals: number; 102 | baseReserve: BN; 103 | quoteReserve: BN; 104 | lpSupply: BN; 105 | startTime: BN; 106 | } 107 | 108 | export interface ReturnTypeFetchMultipleInfo { 109 | [ammId: string]: ComputeAmountOutParam["poolInfo"]; 110 | } 111 | export type ReturnTypeGetAddLiquidityDefaultPool = ApiV3PoolInfoItem | undefined; 112 | export interface ReturnTypeMakeSwapInstruction { 113 | signers: (Keypair | Signer)[]; 114 | instructions: TransactionInstruction[]; 115 | instructionTypes: string[]; 116 | address: { [key: string]: PublicKey }; 117 | lookupTableAddress: string[]; 118 | } 119 | export interface ReturnTypeMakeSwapTransaction { 120 | transactions: { 121 | transaction: Transaction; 122 | signer: (Keypair | Signer)[]; 123 | }[]; 124 | address: { [key: string]: PublicKey }; 125 | } 126 | 127 | export type BasicPoolInfo = { 128 | id: PublicKey; 129 | version: number; 130 | mintA: PublicKey; 131 | mintB: PublicKey; 132 | }; 133 | 134 | export type RoutePathType = { 135 | [routeMint: string]: { 136 | skipMintCheck?: boolean; 137 | mintProgram: PublicKey; 138 | in: BasicPoolInfo[]; 139 | out: BasicPoolInfo[]; 140 | mDecimals: number; 141 | }; 142 | }; 143 | 144 | export interface ReturnTypeGetAllRoute { 145 | directPath: BasicPoolInfo[]; 146 | addLiquidityPools: BasicPoolInfo[]; 147 | routePathDict: RoutePathType; 148 | needSimulate: BasicPoolInfo[]; 149 | needTickArray: BasicPoolInfo[]; 150 | cpmmPoolList: BasicPoolInfo[]; 151 | } 152 | 153 | export type ComputePoolType = ComputeAmountOutParam["poolInfo"] | ComputeClmmPoolInfo | CpmmComputeData; 154 | export type ComputeRoutePathType = { 155 | [routeMint: string]: { 156 | skipMintCheck?: boolean; 157 | mintProgram: PublicKey; 158 | in: ComputePoolType[]; 159 | out: ComputePoolType[]; 160 | mDecimals: number; 161 | }; 162 | }; 163 | -------------------------------------------------------------------------------- /src/raydium/type.ts: -------------------------------------------------------------------------------- 1 | import { PublicKey, Signer, Transaction, TransactionInstruction, VersionedTransaction, Keypair } from "@solana/web3.js"; 2 | import BN from "bn.js"; 3 | import { getTransferFeeConfig, Mint } from "@solana/spl-token"; 4 | import { MultiTxExecuteParam, TxBuilder } from "../common/txTool/txTool"; 5 | import { TokenAmount } from "../module/amount"; 6 | 7 | export interface ReturnTypeMakeInstructions> { 8 | signers: (Signer | Keypair)[]; 9 | instructions: TransactionInstruction[]; 10 | instructionTypes: string[]; 11 | address: T; 12 | lookupTableAddress: string[]; 13 | } 14 | 15 | export type SignAllTransactions = 16 | | ((transaction: T[]) => Promise) 17 | | undefined; 18 | 19 | export interface MakeTransaction> { 20 | builder: TxBuilder; 21 | signers: Signer[]; 22 | transaction: Transaction; 23 | instructionTypes: string[]; 24 | execute: () => Promise<{ txId: string; signedTx: Transaction }>; 25 | extInfo: T; 26 | } 27 | 28 | export interface MakeV0Transaction> { 29 | builder: TxBuilder; 30 | signers: Signer[]; 31 | transaction: VersionedTransaction; 32 | instructionTypes: string[]; 33 | execute: () => Promise; 34 | extInfo: T; 35 | } 36 | 37 | export interface MakeMultiTransaction { 38 | builder: TxBuilder; 39 | signers: Signer[][]; 40 | transactions: Transaction[]; 41 | instructionTypes: string[]; 42 | execute: (params?: MultiTxExecuteParam) => Promise<{ 43 | txIds: string[]; 44 | signedTxs: Transaction[]; 45 | }>; 46 | extInfo: Record; 47 | } 48 | 49 | export interface InstructionReturn { 50 | instruction: TransactionInstruction; 51 | instructionType: string; 52 | } 53 | 54 | export interface ComputeBudgetConfig { 55 | units?: number; 56 | microLamports?: number; 57 | } 58 | 59 | export interface TxTipConfig { 60 | feePayer?: PublicKey; 61 | address: PublicKey; 62 | amount: BN; 63 | } 64 | 65 | export interface LoadParams { 66 | forceUpdate?: boolean; 67 | } 68 | 69 | export interface TransferAmountFee { 70 | amount: TokenAmount; 71 | fee: TokenAmount | undefined; 72 | expirationTime: number | undefined; 73 | } 74 | export interface GetTransferAmountFee { 75 | amount: BN; 76 | fee: BN | undefined; 77 | expirationTime: number | undefined; 78 | } 79 | 80 | // export type ReturnTypeFetchMultipleMintInfo = Mint & { feeConfig: TransferFeeConfig | undefined }; 81 | export type ReturnTypeFetchMultipleMintInfo = Mint & { feeConfig: ReturnType | undefined }; 82 | export interface ReturnTypeFetchMultipleMintInfos { 83 | [mint: string]: ReturnTypeFetchMultipleMintInfo & { programId: PublicKey }; 84 | } 85 | 86 | type Primitive = boolean | number | string | null | undefined | PublicKey; 87 | 88 | /** 89 | * 90 | * @example 91 | * ```typescript 92 | * interface A { 93 | * keyA: string; 94 | * keyB: string; 95 | * map: { 96 | * hello: string; 97 | * i: number; 98 | * }; 99 | * list: (string | number)[]; 100 | * keyC: number; 101 | * } 102 | * 103 | * type WrappedA = ReplaceType // { 104 | * keyA: boolean; 105 | * keyB: boolean; 106 | * map: { 107 | * hello: boolean; 108 | * i: number; 109 | * }; 110 | * list: (number | boolean)[]; 111 | * keyC: number; 112 | * } 113 | * ``` 114 | */ 115 | export type ReplaceType = { 116 | [T in keyof Old]: Old[T] extends From // to avoid case: Old[T] is an Object, 117 | ? Exclude | To // when match, directly replace 118 | : Old[T] extends Primitive // judge whether need recursively replace 119 | ? From extends Old[T] // it's an Object 120 | ? Exclude | To // directly replace 121 | : Old[T] // stay same 122 | : ReplaceType; // recursively replace 123 | }; 124 | 125 | export type MayArray = T | Array; 126 | 127 | export type MayDeepArray = T | Array>; 128 | 129 | export type MayFunction = T | ((...Params: PS) => T); 130 | 131 | export type ArrayItem> = T extends Array ? P : never; 132 | 133 | export type ExactPartial = { 134 | [P in Extract]?: T[P]; 135 | } & { 136 | [P in Exclude]: T[P]; 137 | }; 138 | 139 | export type ExactRequired = { 140 | [P in Extract]-?: T[P]; 141 | } & { 142 | [P in Exclude]: T[P]; 143 | }; 144 | 145 | /** 146 | * extract only string and number 147 | */ 148 | export type SKeyof = Extract; 149 | 150 | export type GetValue = K extends keyof T ? T[K] : undefined; 151 | /** 152 | * @example 153 | * type A = { a: number; b: string; c?: string } 154 | * type B = { a: string; c: string; d?: boolean } 155 | * 156 | * type D = SOR // { a: number | string; b: string | undefined; c: string | undefined; d: boolean | undefined } // ! if use SOR, you lost union type guard feature, try NOT to use this trick 157 | */ 158 | export type SOR = { [K in keyof T | keyof U]: GetValue | GetValue }; 159 | 160 | export type Fallback = T extends undefined ? FallbackT : T; 161 | 162 | /** 163 | * @example 164 | * type A = { a: number; b: string; c?: string } 165 | * type B = { a: string; c: string; d?: boolean } 166 | * 167 | * type D = Cover // { a: string; b: string; c: string; d?: boolean} 168 | */ 169 | export type Cover = { [K in SKeyof | SKeyof]: Fallback, GetValue> }; 170 | 171 | export type UnionCover = T extends T ? Cover : never; 172 | 173 | type MergeArr = (Arr extends (infer T)[] ? T : never)[]; 174 | 175 | /** 176 | * typescript type helper function 177 | * @example 178 | * type A = { hello: string; version: 3 }[] 179 | * type B = { hello: string; version: 5 }[] 180 | * type OK = MergeArr // ({ hello: string; version: 3 } | { hello: string; version: 5 })[] 181 | * type Wrong = A | B // { hello: string; version: 3 }[] | { hello: string; version: 5 }[] // <= this type can't have auto type intelligense of array.map 182 | */ 183 | export const unionArr = (arr: T): MergeArr => arr as unknown as MergeArr; 184 | -------------------------------------------------------------------------------- /src/raydium/utils1216/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utils1216"; 2 | export { default } from "./utils1216"; 3 | -------------------------------------------------------------------------------- /src/solana/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./type"; 2 | -------------------------------------------------------------------------------- /src/solana/type.ts: -------------------------------------------------------------------------------- 1 | export type Cluster = "mainnet" | "devnet"; 2 | -------------------------------------------------------------------------------- /test/init.ts: -------------------------------------------------------------------------------- 1 | import { Connection, clusterApiUrl } from "@solana/web3.js"; 2 | import { Raydium } from "../src/index"; 3 | 4 | async function init() { 5 | const raydium = await Raydium.load({ 6 | connection: new Connection(clusterApiUrl("mainnet-beta")), 7 | disableFeatureCheck: true, 8 | disableLoadToken: true, 9 | }); 10 | } 11 | 12 | init(); 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node14/tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "target": "es6", 6 | "lib": ["es2019", "DOM"], 7 | "outDir": "./lib", 8 | "allowJs": true, 9 | "checkJs": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "noImplicitAny": false, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "jsx": "react", 16 | "baseUrl": ".", 17 | "paths": { 18 | "@/*": ["src/*"] 19 | } 20 | }, 21 | "include": ["./src/**/*"], 22 | "exclude": ["node_modules"], 23 | "ts-node": { 24 | "files": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": [ 3 | "./src" 4 | ], 5 | "githubPages": false, 6 | "hideGenerator": true, 7 | "out": "dist", 8 | "preserveWatchOutput": true 9 | } --------------------------------------------------------------------------------