diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 5a90b444..a50394d7 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -17,13 +17,13 @@ jobs: lint: runs-on: ubuntu-latest + continue-on-error: true steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install - run: pnpm run lint release: - if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/docker/scripts/basedbot.sh b/docker/scripts/basedbot.sh new file mode 100755 index 00000000..0544938c --- /dev/null +++ b/docker/scripts/basedbot.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +node main/basedbot diff --git a/package.json b/package.json index 49977413..df9ccab4 100644 --- a/package.json +++ b/package.json @@ -30,18 +30,18 @@ "@sentry/integrations": "^7.114.0", "@sentry/node": "^8.11.0", "@sentry/tracing": "^7.114.0", - "@solana/spl-token": "^0.4.6", - "@solana/web3.js": "^1.91.8", + "@solana/spl-token": "^0.4.8", + "@solana/web3.js": "^1.95.0", "@staratlas/atlas-prime": "^0.13.1", - "@staratlas/cargo": "^1.0.2", - "@staratlas/points": "^1.0.4", - "@staratlas/claim-stake": "^0.11.5", - "@staratlas/crafting": "^1.0.2", - "@staratlas/data-source": "^0.7.4", + "@staratlas/cargo": "^1.0.5", + "@staratlas/claim-stake": "^0.11.6", + "@staratlas/crafting": "^1.0.5", + "@staratlas/data-source": "^0.7.6", "@staratlas/factory": "^0.7.0", "@staratlas/player-profile": "^0.9.1", + "@staratlas/points": "^1.0.5", "@staratlas/profile-faction": "^0.4.1", - "@staratlas/sage": "^1.0.2", + "@staratlas/sage": "^1.4.0", "big.js": "^6.2.1", "bip39": "^3.1.0", "bn.js": "^5.2.1", @@ -55,6 +55,7 @@ "superagent": "^9.0.2", "telegraf": "^4.16.3", "typeorm": "^0.3.20", + "undici": "^6.19.2", "winston": "^3.13.0" }, "devDependencies": { @@ -73,5 +74,5 @@ "ts-node-dev": "^2.0.0", "typescript": "^5.4.5" }, - "packageManager": "pnpm@9.1.0" + "packageManager": "pnpm@9.4.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1142a705..2cb7b0a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,41 +24,41 @@ importers: specifier: ^7.114.0 version: 7.114.0 '@solana/spl-token': - specifier: ^0.4.6 - version: 0.4.6(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^0.4.8 + version: 0.4.8(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10) '@solana/web3.js': - specifier: ^1.91.8 - version: 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + specifier: ^1.95.0 + version: 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/atlas-prime': specifier: ^0.13.1 version: 0.13.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/cargo': - specifier: ^1.0.2 - version: 1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^1.0.5 + version: 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/claim-stake': - specifier: ^0.11.5 - version: 0.11.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^0.11.6 + version: 0.11.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/crafting': - specifier: ^1.0.2 - version: 1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^1.0.5 + version: 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/data-source': - specifier: ^0.7.4 - version: 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^0.7.6 + version: 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/factory': specifier: ^0.7.0 - version: 0.7.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + version: 0.7.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10) '@staratlas/player-profile': specifier: ^0.9.1 version: 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/points': - specifier: ^1.0.4 - version: 1.0.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^1.0.5 + version: 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/profile-faction': specifier: ^0.4.1 version: 0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/sage': - specifier: ^1.0.2 - version: 1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + specifier: ^1.4.0 + version: 1.4.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) big.js: specifier: ^6.2.1 version: 6.2.1 @@ -98,6 +98,9 @@ importers: typeorm: specifier: ^0.3.20 version: 0.3.20(pg@8.11.5)(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.4.5)) + undici: + specifier: ^6.19.2 + version: 6.19.2 winston: specifier: ^3.13.0 version: 3.13.0 @@ -303,8 +306,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.24.5': - resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} '@babel/template@7.24.0': @@ -478,6 +481,9 @@ packages: '@noble/curves@1.4.0': resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} + '@noble/curves@1.4.2': + resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -761,32 +767,69 @@ packages: '@solana/codecs-core@2.0.0-preview.2': resolution: {integrity: sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==} + '@solana/codecs-core@2.0.0-preview.4': + resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-data-structures@2.0.0-preview.2': resolution: {integrity: sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==} + '@solana/codecs-data-structures@2.0.0-preview.4': + resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-numbers@2.0.0-preview.2': resolution: {integrity: sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==} + '@solana/codecs-numbers@2.0.0-preview.4': + resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==} + peerDependencies: + typescript: '>=5' + '@solana/codecs-strings@2.0.0-preview.2': resolution: {integrity: sha512-EgBwY+lIaHHgMJIqVOGHfIfpdmmUDNoNO/GAUGeFPf+q0dF+DtwhJPEMShhzh64X2MeCZcmSO6Kinx0Bvmmz2g==} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 + '@solana/codecs-strings@2.0.0-preview.4': + resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5' + '@solana/codecs@2.0.0-preview.2': resolution: {integrity: sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==} + '@solana/codecs@2.0.0-preview.4': + resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==} + peerDependencies: + typescript: '>=5' + '@solana/errors@2.0.0-preview.2': resolution: {integrity: sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==} hasBin: true + '@solana/errors@2.0.0-preview.4': + resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==} + hasBin: true + peerDependencies: + typescript: '>=5' + '@solana/options@2.0.0-preview.2': resolution: {integrity: sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==} - '@solana/spl-token-group@0.0.4': - resolution: {integrity: sha512-7+80nrEMdUKlK37V6kOe024+T7J4nNss0F8LQ9OOPYdWCCfJmsGUzVx2W3oeizZR4IHM6N4yC9v1Xqwc3BTPWw==} + '@solana/options@2.0.0-preview.4': + resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==} + peerDependencies: + typescript: '>=5' + + '@solana/spl-token-group@0.0.5': + resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==} engines: {node: '>=16'} peerDependencies: - '@solana/web3.js': ^1.91.6 + '@solana/web3.js': ^1.94.0 '@solana/spl-token-metadata@0.1.4': resolution: {integrity: sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==} @@ -800,18 +843,18 @@ packages: peerDependencies: '@solana/web3.js': ^1.88.0 - '@solana/spl-token@0.4.6': - resolution: {integrity: sha512-1nCnUqfHVtdguFciVWaY/RKcQz1IF4b31jnKgAmjU9QVN1q7dRUkTEWJZgTYIEtsULjVnC9jRqlhgGN39WbKKA==} + '@solana/spl-token@0.4.8': + resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==} engines: {node: '>=16'} peerDependencies: - '@solana/web3.js': ^1.91.6 + '@solana/web3.js': ^1.94.0 '@solana/spl-type-length-value@0.1.0': resolution: {integrity: sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==} engines: {node: '>=16'} - '@solana/web3.js@1.91.8': - resolution: {integrity: sha512-USa6OS1jbh8zOapRJ/CBZImZ8Xb7AJjROZl5adql9TpOoBN9BUzyyouS5oPuZHft7S7eB8uJPuXWYjMi6BHgOw==} + '@solana/web3.js@1.95.0': + resolution: {integrity: sha512-iHwJ/HcWrF9qbnI1ctwI1UXHJ0vZXRpnt+lI5UcQIk8WvJNuQ5gV06icxzM6B7ojUES85Q1/FM4jZ49UQ8yZZQ==} '@sqltools/formatter@1.2.5': resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} @@ -823,20 +866,17 @@ packages: '@staratlas/atlas-prime@0.13.1': resolution: {integrity: sha512-fFMjy0dFUgB+b9m+SZIIMSrrvB+8SVq/wJTFNVzDenr0YrXehbgtkfZWaxpffEAXdp433hbkP9yAArlf+weMPg==} - '@staratlas/cargo@1.0.2': - resolution: {integrity: sha512-pVh5Yl9drxyyV97xx64o5w6BZyuhQzi+cvxA8mX1YQZv0xCLCdskwshiqMclTlZkYXxHAmMui9sFRZZVPZDZqg==} + '@staratlas/cargo@1.0.5': + resolution: {integrity: sha512-si2UaONoFkopIy45xoGfMWMCYq8MYmpT6ksxp82zyIV0IPwJhci/50robNcKfgIzxtX67INg6JoDpqM+BdLs6w==} - '@staratlas/claim-stake@0.11.5': - resolution: {integrity: sha512-tp/SDJnG5N0emgidAf7gjdlBNR/ZasJo7uaDnk82Ja9ANr9FKsjRB4tGjUWQV3BV4fVni3VH2Xw3591pc2OYzw==} + '@staratlas/claim-stake@0.11.6': + resolution: {integrity: sha512-AEaMKAOQCntFZRxGTw0i+C3+S8MzTY+CJlgFPhb/T9QLAHO5CIby2g4P6cb8v5xaJIW5Z8Lh+nvn1+THX8fRXw==} - '@staratlas/crafting@1.0.2': - resolution: {integrity: sha512-3JDBjMz2z+MtXHQEOhDm7HooPTKAoC57t0mxqT56wd38lShZxGH28qp87wU9FvcHYyrygq831LnAxgA874wZZg==} + '@staratlas/crafting@1.0.5': + resolution: {integrity: sha512-Ng7AS4eO+ypm6/3AH6lViwv31N4XoR/motmWJcCYHUfS26JOYVtVJ5KBM/TzhqT+xHpJNgW60x42Ihg+zWB5ew==} - '@staratlas/data-source@0.7.4': - resolution: {integrity: sha512-DY4fGLJjKbn/c+h+PdbLHjg8r3skDvnj3ctJQpk4LsNKFYR7zRq/0x+H24WB53DgZbGelxcJIdhsY6178732TA==} - - '@staratlas/data-source@0.7.5': - resolution: {integrity: sha512-LtaCpV6G3X3r7pSUlkr6pDsm/B+90pKtryr4qnZRttJ7vguadSC7xzlQJ5ubEdhK3b1Xs8UFLBnEHpvzkJk9ow==} + '@staratlas/data-source@0.7.6': + resolution: {integrity: sha512-Clwgx60dQP+41IN7B3A6dzYXbJPz/yIXlqTJIdBHloB4+fglTFvlE7pwJ1qy6SLkEASBLH8HaXMY33RIasSEtg==} '@staratlas/factory@0.7.0': resolution: {integrity: sha512-lhEqin/QWLiBmmBRNt02raKkn5Q0ZM/ynz4h4M56jjucfGdokZaYJ/8yJhUUdHJ2cIFTY7bdBy+8vRmV7zw+NA==} @@ -844,8 +884,11 @@ packages: '@staratlas/player-profile@0.9.1': resolution: {integrity: sha512-bqPXn6fGl+gg5zZBEK4+/Cv9oriVzrNOTW1Z+6TMFSB/vMsE51DsdyN7BWMkvwfBg2XdHfL9VntlU8j8OYD8jQ==} - '@staratlas/points@1.0.4': - resolution: {integrity: sha512-/pPjOZc6DTwps3uvTDHN3UUEZPPktbbPGn6yl4KJbpuj08G39Gd+n7HZYI4pLUUxJrq3ktVBO2+FC1YYUSro/w==} + '@staratlas/points-store@1.0.5': + resolution: {integrity: sha512-I7rbLygc7RJEzSA/Bn2Zi1HeBMaxuOVwFFVTZdKNeODEdKBwqz+Z7NWX1Dsu+4Ce8Y/kDCzjBtT+kS7zY8Qn5w==} + + '@staratlas/points@1.0.5': + resolution: {integrity: sha512-uzwHoZ/U2h24/QSm3lg+3+SLyS2PmPwlVn3zDnc5QL//2lZ1Er7RuzZvAdKk5zzAMUgUNHa4isjwS2nf5NkrCw==} '@staratlas/profile-faction@0.4.1': resolution: {integrity: sha512-RaE7ZqX7VyPZBMpZm0sblFPykGhBr4IrtMsxVtgbU2hgtZk0rqXWA8vv8hR55fEtmweGuon06JPnGQfLBz4/yQ==} @@ -853,8 +896,11 @@ packages: '@staratlas/profile-vault@0.9.1': resolution: {integrity: sha512-nY6t1VTI/kRd/bQLSEeRTqwJ6QWRAC1du6vFN6zufJQyumuuZpmevKNZF1DkrSjZqhe3/2HgyR/ud7d62KGFxg==} - '@staratlas/sage@1.0.2': - resolution: {integrity: sha512-PWz8eUflXvk14+I+fCupnEAgsuQ2UAkgi32laZ/s3JqINnQOE/Dhpa9DPSgqas/5J5/fti5FS1JacmqpU2DcdQ==} + '@staratlas/sage@1.4.0': + resolution: {integrity: sha512-+junanrIl9BREAwZD9E/RzueVbyYJS9H7/aMpCDf76DUfHM3mVMwbVIjT0jo+oU/gNQuhOvfydltT9v6EGhZXQ==} + + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} '@telegraf/types@7.1.0': resolution: {integrity: sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==} @@ -1015,9 +1061,15 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@8.3.4': + resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + '@types/ws@7.4.7': resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + '@types/ws@8.5.11': + resolution: {integrity: sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1435,6 +1487,10 @@ packages: resolution: {integrity: sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==} engines: {node: '>=18'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -1768,6 +1824,9 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -2880,8 +2939,8 @@ packages: ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} - rpc-websockets@7.11.0: - resolution: {integrity: sha512-IkLYjayPv6Io8C/TdCL5gwgzd1hFz2vmBZrjMw/SPEXo51ETOhnzgS4Qy5GWi2JQN7HKHa66J3+2mv0fgNh/7w==} + rpc-websockets@9.0.2: + resolution: {integrity: sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw==} run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3046,12 +3105,13 @@ packages: resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==} engines: {node: '>=14.18.0'} - superstruct@0.14.2: - resolution: {integrity: sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==} - superstruct@0.15.5: resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3289,6 +3349,10 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici@6.19.2: + resolution: {integrity: sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==} + engines: {node: '>=18.17'} + update-browserslist-db@1.0.15: resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true @@ -3608,7 +3672,7 @@ snapshots: '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.24.5 - '@babel/runtime@7.24.5': + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 @@ -3645,9 +3709,9 @@ snapshots: '@coral-xyz/anchor@0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@noble/hashes': 1.4.0 - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 bs58: 4.0.1 buffer-layout: 1.2.2 @@ -3664,9 +3728,9 @@ snapshots: - encoding - utf-8-validate - '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 @@ -3924,6 +3988,10 @@ snapshots: dependencies: '@noble/hashes': 1.4.0 + '@noble/curves@1.4.2': + dependencies: + '@noble/hashes': 1.4.0 + '@noble/hashes@1.4.0': {} '@nodelib/fs.scandir@2.1.5': @@ -4194,9 +4262,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@project-serum/borsh@0.2.5(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@project-serum/borsh@0.2.5(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bn.js: 5.2.1 buffer-layout: 1.2.2 @@ -4297,7 +4365,7 @@ snapshots: '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) bigint-buffer: 1.1.5 bignumber.js: 9.1.2 transitivePeerDependencies: @@ -4313,17 +4381,35 @@ snapshots: dependencies: '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-core@2.0.0-preview.4(typescript@5.4.5)': + dependencies: + '@solana/errors': 2.0.0-preview.4(typescript@5.4.5) + typescript: 5.4.5 + '@solana/codecs-data-structures@2.0.0-preview.2': dependencies: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-data-structures@2.0.0-preview.4(typescript@5.4.5)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.4.5) + '@solana/errors': 2.0.0-preview.4(typescript@5.4.5) + typescript: 5.4.5 + '@solana/codecs-numbers@2.0.0-preview.2': dependencies: '@solana/codecs-core': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 + '@solana/codecs-numbers@2.0.0-preview.4(typescript@5.4.5)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.4.5) + '@solana/errors': 2.0.0-preview.4(typescript@5.4.5) + typescript: 5.4.5 + '@solana/codecs-strings@2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22)': dependencies: '@solana/codecs-core': 2.0.0-preview.2 @@ -4331,6 +4417,14 @@ snapshots: '@solana/errors': 2.0.0-preview.2 fastestsmallesttextencoderdecoder: 1.0.22 + '@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.4.5) + '@solana/errors': 2.0.0-preview.4(typescript@5.4.5) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.4.5 + '@solana/codecs@2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22)': dependencies: '@solana/codecs-core': 2.0.0-preview.2 @@ -4341,38 +4435,67 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder + '@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)': + dependencies: + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5) + '@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + '@solana/errors@2.0.0-preview.2': dependencies: chalk: 5.3.0 commander: 12.0.0 + '@solana/errors@2.0.0-preview.4(typescript@5.4.5)': + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + typescript: 5.4.5 + '@solana/options@2.0.0-preview.2': dependencies: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 - '@solana/spl-token-group@0.0.4(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)': + '@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)': dependencies: - '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/codecs-core': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.4.5) + '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5) + '@solana/errors': 2.0.0-preview.4(typescript@5.4.5) + typescript: 5.4.5 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)': + dependencies: + '@solana/codecs': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5) '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder + - typescript - '@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)': + '@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)': dependencies: '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22) '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/spl-token@0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -4380,28 +4503,29 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@solana/spl-token@0.4.6(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.4.8(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@solana/spl-token-group': 0.0.4(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22) - '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.5(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5) + '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate '@solana/spl-type-length-value@0.1.0': dependencies: buffer: 6.0.3 - '@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.24.5 - '@noble/curves': 1.4.0 + '@babel/runtime': 7.24.8 + '@noble/curves': 1.4.2 '@noble/hashes': 1.4.0 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 @@ -4413,8 +4537,8 @@ snapshots: fast-stable-stringify: 1.0.0 jayson: 4.1.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 7.11.0 - superstruct: 0.14.2 + rpc-websockets: 9.0.2 + superstruct: 2.0.2 transitivePeerDependencies: - bufferutil - encoding @@ -4424,8 +4548,8 @@ snapshots: '@staratlas/anchor@0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@project-serum/borsh': 0.2.5(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@project-serum/borsh': 0.2.5(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) base64-js: 1.5.1 bn.js: 5.2.1 bs58: 4.0.1 @@ -4446,10 +4570,10 @@ snapshots: '@staratlas/atlas-prime@0.13.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/profile-vault': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@types/node': 20.12.8 @@ -4463,12 +4587,12 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/cargo@1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/cargo@1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -4476,24 +4600,24 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/claim-stake@0.11.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/claim-stake@0.11.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/crafting@1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/crafting@1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@types/lodash': 4.17.0 lodash: 4.17.21 @@ -4503,11 +4627,11 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/data-source@0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/data-source@0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: '@noble/curves': 1.4.0 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) bs58: 5.0.0 camelcase: 7.0.1 @@ -4519,54 +4643,54 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/data-source@0.7.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/factory@0.7.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)': dependencies: - '@noble/curves': 1.4.0 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - bs58: 5.0.0 - camelcase: 7.0.1 + '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + big.js: 6.2.1 lodash: 4.17.21 - neverthrow: 6.2.1 + mobx: 6.12.3 + mobx-utils: 6.0.8(mobx@6.12.3) transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder + - typescript - utf-8-validate - '@staratlas/factory@0.7.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/player-profile@0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.4.6(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) - big.js: 6.2.1 - lodash: 4.17.21 - mobx: 6.12.3 - mobx-utils: 6.0.8(mobx@6.12.3) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/player-profile@0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/points-store@1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/points': 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/profile-faction': 0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/points@1.0.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/points@1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -4576,10 +4700,10 @@ snapshots: '@staratlas/profile-faction@0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -4589,10 +4713,10 @@ snapshots: '@staratlas/profile-vault@0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -4600,16 +4724,17 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@staratlas/sage@1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': + '@staratlas/sage@1.4.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.91.8(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@staratlas/anchor': 0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@staratlas/cargo': 1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@staratlas/crafting': 1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/cargo': 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/crafting': 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/data-source': 0.7.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) - '@staratlas/points': 1.0.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/points': 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) + '@staratlas/points-store': 1.0.5(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@staratlas/profile-faction': 0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10) '@types/lodash': 4.17.0 lodash: 4.17.21 @@ -4619,6 +4744,10 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate + '@swc/helpers@0.5.12': + dependencies: + tslib: 2.6.2 + '@telegraf/types@7.1.0': {} '@tsconfig/node10@1.0.11': {} @@ -4813,10 +4942,16 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/uuid@8.3.4': {} + '@types/ws@7.4.7': dependencies: '@types/node': 20.12.11 + '@types/ws@8.5.11': + dependencies: + '@types/node': 20.12.11 + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.32': @@ -5309,6 +5444,8 @@ snapshots: commander@12.0.0: {} + commander@12.1.0: {} + commander@2.20.3: {} component-emitter@1.3.1: {} @@ -5731,6 +5868,8 @@ snapshots: eventemitter3@4.0.7: {} + eventemitter3@5.0.1: {} + execa@5.1.1: dependencies: cross-spawn: 7.0.3 @@ -7008,9 +7147,13 @@ snapshots: hash-base: 3.1.0 inherits: 2.0.4 - rpc-websockets@7.11.0: + rpc-websockets@9.0.2: dependencies: - eventemitter3: 4.0.7 + '@swc/helpers': 0.5.12 + '@types/uuid': 8.3.4 + '@types/ws': 8.5.11 + buffer: 6.0.3 + eventemitter3: 5.0.1 uuid: 8.3.2 ws: 8.17.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: @@ -7195,10 +7338,10 @@ snapshots: transitivePeerDependencies: - supports-color - superstruct@0.14.2: {} - superstruct@0.15.5: {} + superstruct@2.0.2: {} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -7417,6 +7560,8 @@ snapshots: undici-types@5.26.5: {} + undici@6.19.2: {} + update-browserslist-db@1.0.15(browserslist@4.23.0): dependencies: browserslist: 4.23.0 diff --git a/src/config/config.ts b/src/config/config.ts index 96122668..50ab43aa 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -15,6 +15,7 @@ export interface Config { user: { keyMode: string mnemonic: string + pubKey: string secretKey: number[] walletId: number address1: string @@ -61,6 +62,7 @@ export const config: Config = { user: { keyMode: env.get('KEY_MODE'), secretKey: env.get('SECRET_KEY').split(',').map(s => Number(s)), + pubKey: env.get('PUBKEY'), mnemonic: env.get('MNEMONIC'), walletId: Number(env.get('WALLET_ID')), address1: env.get('BOT_ADDRESS_1'), diff --git a/src/dayjs.ts b/src/dayjs.ts index 3a4c2a38..e46bce92 100644 --- a/src/dayjs.ts +++ b/src/dayjs.ts @@ -9,6 +9,8 @@ import utc from 'dayjs/plugin/utc' export { Dayjs } from 'dayjs' export { Duration } from 'dayjs/plugin/duration' +export const now = (): dayjs.Dayjs => dayjs() + dayjs.extend(customParseFormat) dayjs.extend(advancedFormat) dayjs.extend(duration) diff --git a/src/main/basedbot/basedbot.ts b/src/main/basedbot/basedbot.ts new file mode 100644 index 00000000..e6d9ac6e --- /dev/null +++ b/src/main/basedbot/basedbot.ts @@ -0,0 +1,117 @@ +import { config } from '../../config' + +import { logger } from '../../logger' +import { Sentry } from '../../sentry' +import { sleep } from '../../service/sleep' +import { keyPair } from '../../service/wallet' + +import { mineBiomass } from './fsm/configs/mine-biomass' +import { mineCarbon } from './fsm/configs/mine-carbon' +import { mineConfig } from './fsm/configs/mine-config' +import { mineCopperOre } from './fsm/configs/mine-copper-ore' +import { mineHydrogen } from './fsm/configs/mine-hydrogen' +import { mineIronOre } from './fsm/configs/mine-iron-ore' +import { mineLumanite } from './fsm/configs/mine-lumanite' +import { mineNitrogen } from './fsm/configs/mine-nitrogen' +import { mineRochinol } from './fsm/configs/mine-rochinol' +import { mineSilicia } from './fsm/configs/mine-silicia' +import { mineTitaniumOre } from './fsm/configs/mine-titanium-ore' +import { createMiningStrategy } from './fsm/mine' +import { Strategy } from './fsm/strategy' +import { settleFleet } from './lib/sage/state/settle-fleet' +import { getPlayerContext, Player } from './lib/sage/state/user-account' +import { FleetInfo, getFleetInfo, getUserFleets } from './lib/sage/state/user-fleets' +import { getMapContext, mineableByCoordinates, WorldMap } from './lib/sage/state/world-map' +import { Coordinates } from './lib/util/coordinates' + +// eslint-disable-next-line require-await +export const create = async (): Promise => { + logger.info('Starting basedbot...') +} + +// eslint-disable-next-line require-await +export const stop = async (): Promise => { + logger.info('Stopping basedbot') +} + +type FleetStrategies = Map + +type BotConfig = { + player: Player + map: WorldMap + fleetStrategies: FleetStrategies +} + +const applyStrategy = (fleetInfo: FleetInfo, fleetStrategies: FleetStrategies): Promise => { + const strategy = fleetStrategies.get(fleetInfo.fleetName) + + if (!strategy) { + return Promise.resolve() + // logger.info(`No strategy for fleet: ${fleetInfo.fleetName}. Lazily loading Info Strategy...`) + // const infoStrategy = createInfoStrategy() + // + // fleetStrategies.set(fleetInfo.fleetName, infoStrategy) + // + // return infoStrategy.send(fleetInfo) + } + + return strategy.send(fleetInfo) +} + +const basedbot = async (botConfig: BotConfig) => { + const { player, map } = botConfig + const fleets = await getUserFleets(player) + const fleetInfos = await Promise.all(fleets.map(f => getFleetInfo(f, player, map))) + + await Promise.all(fleetInfos.map(fleetInfo => settleFleet(fleetInfo, player, map))) + await Promise.all(fleetInfos.map(fleetInfo => applyStrategy(fleetInfo, botConfig.fleetStrategies))) +} + +export const start = async (): Promise => { + const player = await getPlayerContext(keyPair.publicKey, keyPair) + const map = await getMapContext(player.game) + + const fleetStrategies: Map = config.sol.rpcEndpoint.includes('atlasnet') ? new Map([ + ['Atlantic Goliath Grouper Fleet', createMiningStrategy(mineBiomass(map), player)], + ['Baboon Fleet', createMiningStrategy(mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-19, 40), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-19, 40)).values().next().value + }), player)], + ['Silkworm Fleet', createMiningStrategy(mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-18, 23), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-18, 23)).values().next().value + }), player)], + ['Broadclub Cuttlefish Fleet', createMiningStrategy(mineCarbon(map), player)], + ['Elephant Fleet', createMiningStrategy(mineNitrogen(map), player)], + ['Gelada Fleet', createMiningStrategy(mineSilicia(map), player)], + ['Hectors Dolphin Fleet', createMiningStrategy(mineLumanite(map), player)], + ['Groundhog Fleet', createMiningStrategy(mineCopperOre(map), player)], + ['Lion Fleet', createMiningStrategy(mineIronOre(map), player)], + ['Rock Hyrax Fleet', createMiningStrategy(mineHydrogen(map), player)], + ['Snakes Fleet', createMiningStrategy(mineRochinol(map), player)], + ['Sugar Gliders Fleet', createMiningStrategy(mineHydrogen(map), player)], + ['Tortoise Fleet', createMiningStrategy(mineTitaniumOre(map), player)] + ]) : new Map([ + ['Bearded Dragon Fleet', createMiningStrategy(mineLumanite(map), player)], + ['Deer Mouse Fleet', createMiningStrategy(mineBiomass(map), player)], + ['Emu Fleet', createMiningStrategy(mineNitrogen(map), player)], + ['Toad Fleet', createMiningStrategy(mineSilicia(map), player)], + ['Tortoise Fleet', createMiningStrategy(mineHydrogen(map), player)] + ]) + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + await basedbot({ player, map, fleetStrategies }) + } + catch (e) { + Sentry.captureException(e) + logger.error(e) + } + finally { + await sleep(10000) + } + } +} diff --git a/src/main/basedbot/config.ts b/src/main/basedbot/config.ts new file mode 100644 index 00000000..b4df8633 --- /dev/null +++ b/src/main/basedbot/config.ts @@ -0,0 +1,664 @@ +export const config = { + '2XtbJPHPgYY9WnoMGamyLDRzjk9rHePhZDRV71aDTEMj': + { + name: + 'Koala Fleet', assignment: + '', mineResource: + '', dest: + '-38,25', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + 'EMAGoQSP89CJV5focVjrpEuE4CeqJ4k1DouQW7gUu7yX', transportResource1Perc: + 25000, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + '3MYNF7TtyDPdgPtMaBt2QLeze3T65M9Pi3mA3QgCHybF': + { + name: + 'Jaguarundi Fleet', assignment: + 'Mine', mineResource: + 'CARBWKWvxEuMcq3MqCxYfi7UoFVpL9c4rsQS99tw6i4X', dest: + '-30,30', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-30, 30], [-29, 30], [-29, 31], [-30, 31]], scanMin: + 0, scanMove: + 'true' + } + , + '4TGcBKFPVsy39ZxK6RFH2RfA3xNVxrrdSiHbx3ubreBb': + { + name: + 'Skinks Fleet', assignment: + 'Mine', mineResource: + 'MASS9GqtJz6ABisAxcUn3FeR4phMqH1XfG6LPKJePog', dest: + '-42,35', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-42, 35], [-41, 35], [-41, 36], [-42, 36]], scanMin: + 0, scanMove: + 'true' + } + , + '5DQrhp51sYPGD6N6RyErpyddtZrvuHG5J8UwaT5NNW5b': + { + name: + 'Leopard Fleet', assignment: + 'Mine', mineResource: + 'HYDR4EPHJcDPcaLYUcNCtrXUdt1PnaN4MvE655pevBYp', dest: + '-40,30', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-40, 30], [-39, 30], [-39, 31], [-40, 31]], scanMin: + 0, scanMove: + 'true' + } + , + '5SeZMPBT6bx1s9Ws1eCqnoBq7J7ReGKCPy6iAyPy3cA3': + { + name: + 'Ponies Fleet', assignment: + 'Transport', mineResource: + '', dest: + '-38,25', starbase: + '-40,30', moveType: + 'subwarp', subwarpPref: + 'true', moveTarget: + [], transportResource1: + 'EMAGoQSP89CJV5focVjrpEuE4CeqJ4k1DouQW7gUu7yX', transportResource1Perc: + 300, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + 'ironxrUhTEaBiR9Pgp6hy4qWx6V2FirDoXhsFP25GFP', transportSBResource1Perc: + 1200, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + '6onhXDvFEKN7tX53x1WFhz8Zdr1YDGVdsTN5qeAVYLUn': + { + name: + 'African Wild Ass Fleet', assignment: + 'Mine', mineResource: + 'CUore1tNkiubxSwDEtLc3Ybs1xfWLs8uGjyydUYZ25xc', dest: + '-47,30', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-47, 30], [-46, 30], [-46, 31], [-47, 31]], scanMin: + 0, scanMove: + 'true' + } + , + '6osKbuG4LT3CDcxtfhyjTFaQC87XMPtKa23NJw7f3A7a': + { + name: + 'Emu Fleet', assignment: + 'Mine', mineResource: + 'CARBWKWvxEuMcq3MqCxYfi7UoFVpL9c4rsQS99tw6i4X', dest: + '-40,30', starbase: + '-30,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-40, 30], [-39, 30], [-39, 31], [-40, 31]], scanMin: + 0, scanMove: + 'true' + } + , + '8RamWFKuGRe2xRaqWpkWMZAiPU7kBugVwqGEpMsxfg3': + { + name: + 'Buffalo Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + '95sAgb4FTvnttdept3Gov8Az75wxZ4z4mNDSzB3wN44Z': + { + name: + 'Raccoon Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + '98TnDNW11oqK3B7tFvYGj2u8v94oqY3FtvDBT8Ub1RUG': + { + name: + 'Llama Fleet', assignment: + 'Transport', mineResource: + '', dest: + '-38,25', starbase: + '-40,30', moveType: + 'subwarp', subwarpPref: + 'true', moveTarget: + [], transportResource1: + 'EMAGoQSP89CJV5focVjrpEuE4CeqJ4k1DouQW7gUu7yX', transportResource1Perc: + 300, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + 'ironxrUhTEaBiR9Pgp6hy4qWx6V2FirDoXhsFP25GFP', transportSBResource1Perc: + 1200, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + '9Kai8nmPcZm8YjjuKw7SBMsysRf5699DmfER7du5xBTT': + { + name: + 'King Vulture Fleet', assignment: + 'Mine', mineResource: + 'MASS9GqtJz6ABisAxcUn3FeR4phMqH1XfG6LPKJePog', dest: + '-42,35', starbase: + '-42,35', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-42, 35], [-41, 35], [-41, 36], [-42, 36]], scanMin: + 0, scanMove: + 'true' + } + , + '9MhnZ6HsfdKPtj5kauySf32hkcWzZgvf3qQ5kk87gDvv': + { + name: + 'Peafowl Fleet', assignment: + 'Transport', mineResource: + '', dest: + '-38,25', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + 'EMAGoQSP89CJV5focVjrpEuE4CeqJ4k1DouQW7gUu7yX', transportResource1Perc: + 4000, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + 'DZjYrVfAZZqRd6z1T2pqpzXU4upkts2gF3qKECiA8a3G': + { + name: + 'Angelfish King Fleet', assignment: + 'Mine', mineResource: + 'FeorejFjRRAfusN9Fg3WjEZ1dRCf74o6xwT5vDt3R34J', dest: + '-38,25', starbase: + '-38,25', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + 'E9xD74sDppDA2kuMVSqZyNQBcHSSw1pJNK4NShzRJrqb': + { + name: + 'Badger Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + 'GRxEwwsUr69EfHWzjhUFkh6TmakCxMcsWSEZopAvgczU': + { + name: + 'Irukandji Jellyfish Fleet', assignment: + 'Mine', mineResource: + 'HYDR4EPHJcDPcaLYUcNCtrXUdt1PnaN4MvE655pevBYp', dest: + '-40,30', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-40, 30], [-39, 30], [-39, 31], [-40, 31]], scanMin: + 0, scanMove: + 'true' + } + , + 'Gm26cs6kk1kTu5vH737Uu5YtfPGR2gKE3bGJQjBa9bAN': + { + name: + 'Black Footed Cat Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + 'GxV1BqAHkscVAF8tqabybaXKpuYxanQMGFRaVhoUb7qu': + { + name: + 'White Beaked Dolphin Fleet', assignment: + 'Mine', mineResource: + 'FeorejFjRRAfusN9Fg3WjEZ1dRCf74o6xwT5vDt3R34J', dest: + '-38,25', starbase: + '-40,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + [], transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-38, 25], [-37, 25], [-37, 26], [-38, 26]], scanMin: + 0, scanMove: + 'true' + } + , + 'HG9KteiUTNjGdam7i9b7WynTutZ5GEdEWoLKxW6dVaUi': + { + name: + 'Echidna Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + 'HM4iq6YSq7YAJShAz83Q9EDCiuCPNDEpPPqxY82QzLLN': + { + name: + 'Bison Fleet', assignment: + 'Mine', mineResource: + 'CUore1tNkiubxSwDEtLc3Ybs1xfWLs8uGjyydUYZ25xc', dest: + '-47,30', starbase: + '-47,30', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [[-47, 30], [-46, 30], [-46, 31], [-47, 31]], scanMin: + 0, scanMove: + 'true' + } + , + 'HvzQQ3GTKxH71RP31zG798ejN9ydE2gwZTARQzYhaGXa': + { + name: + 'Turtles Fleet', assignment: + '', mineResource: + '', dest: + '', starbase: + '', moveType: + 'warp', subwarpPref: + 'false', moveTarget: + '', transportResource1: + '', transportResource1Perc: + 0, transportResource2: + '', transportResource2Perc: + 0, transportResource3: + '', transportResource3Perc: + 0, transportResource4: + '', transportResource4Perc: + 0, transportSBResource1: + '', transportSBResource1Perc: + 0, transportSBResource2: + '', transportSBResource2Perc: + 0, transportSBResource3: + '', transportSBResource3Perc: + 0, transportSBResource4: + '', transportSBResource4Perc: + 0, scanBlock: + [], scanMin: + 0, scanMove: + 'true' + } + , + 'globalSettings': + { + priorityFee: + 20000, lowPriorityFeeMultiplier: + 40, saveProfile: + true, savedProfile: + ['AyN2Wy9D8ExKZthgj6QsjNC1fBsDyU8FfPj6MBm6KbRV', 0], confirmationCheckingDelay: + 200, debugLogLevel: + 1, transportUseAmmoBank: + true, transportStopOnError: + false, scanBlockPattern: + 'square', scanBlockLength: + 5, scanBlockResetAfterResupply: + false, scanResupplyOnLowFuel: + false, scanSectorRegenTime: + 90, scanPauseTime: + 600, scanStrikeCount: + 3, statusPanelOpacity: + 75, autoStartScript: + true, reloadPageOnFailedFleets: + 0 + } +} diff --git a/src/main/basedbot/fsm/configs/mine-biomass.ts b/src/main/basedbot/fsm/configs/mine-biomass.ts new file mode 100644 index 00000000..7d3a321e --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-biomass.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineBiomass = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-42, 35), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-42, 35)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-carbon.ts b/src/main/basedbot/fsm/configs/mine-carbon.ts new file mode 100644 index 00000000..d3b25813 --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-carbon.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineCarbon = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-30, 30), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-30, 30)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-config.ts b/src/main/basedbot/fsm/configs/mine-config.ts new file mode 100644 index 00000000..1e3588e0 --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-config.ts @@ -0,0 +1,17 @@ +import { WarpMode } from '../../lib/sage/act/move' +import { Mineable } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +export type MineConfig = { + homeBase: Coordinates + targetBase: Coordinates + resource: Mineable + warpMode: WarpMode +} + +export const mineConfig = (config: Partial & { resource: Mineable }) : MineConfig => ({ + homeBase: config.homeBase || Coordinates.fromNumber(-40, 30), + targetBase: config.targetBase || Coordinates.fromNumber(-22, 32), + resource: config.resource, + warpMode: config.warpMode || 'auto' +}) diff --git a/src/main/basedbot/fsm/configs/mine-copper-ore.ts b/src/main/basedbot/fsm/configs/mine-copper-ore.ts new file mode 100644 index 00000000..e20a143c --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-copper-ore.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineCopperOre = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-47, 30), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-47, 30)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-hydrogen.ts b/src/main/basedbot/fsm/configs/mine-hydrogen.ts new file mode 100644 index 00000000..6bd1511e --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-hydrogen.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineHydrogen = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-40, 30), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-40, 30)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-iron-ore.ts b/src/main/basedbot/fsm/configs/mine-iron-ore.ts new file mode 100644 index 00000000..3f09f808 --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-iron-ore.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineIronOre = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-38, 25), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-38, 25)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-lumanite.ts b/src/main/basedbot/fsm/configs/mine-lumanite.ts new file mode 100644 index 00000000..266facfe --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-lumanite.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineLumanite = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-22, 32), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-22, 32)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-nitrogen.ts b/src/main/basedbot/fsm/configs/mine-nitrogen.ts new file mode 100644 index 00000000..d29a2a44 --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-nitrogen.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineNitrogen = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-45, 15), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-45, 15)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-rochinol.ts b/src/main/basedbot/fsm/configs/mine-rochinol.ts new file mode 100644 index 00000000..accad83b --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-rochinol.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineRochinol = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(0, 16), + resource: mineableByCoordinates(map, Coordinates.fromNumber(0, 16)).values().next().value +}) diff --git a/src/main/basedbot/fsm/configs/mine-silicia.ts b/src/main/basedbot/fsm/configs/mine-silicia.ts new file mode 100644 index 00000000..eca0df8d --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-silicia.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineSilicia = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-22, 32), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-22, 32)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/configs/mine-titanium-ore.ts b/src/main/basedbot/fsm/configs/mine-titanium-ore.ts new file mode 100644 index 00000000..7f8397e7 --- /dev/null +++ b/src/main/basedbot/fsm/configs/mine-titanium-ore.ts @@ -0,0 +1,10 @@ +import { mineableByCoordinates, WorldMap } from '../../lib/sage/state/world-map' +import { Coordinates } from '../../lib/util/coordinates' + +import { MineConfig, mineConfig } from './mine-config' + +export const mineTitaniumOre = (map: WorldMap): MineConfig => mineConfig({ + homeBase: Coordinates.fromNumber(-40, 30), + targetBase: Coordinates.fromNumber(-8, 35), + resource: mineableByCoordinates(map, Coordinates.fromNumber(-8, 35)).values().next().value, +}) diff --git a/src/main/basedbot/fsm/info.ts b/src/main/basedbot/fsm/info.ts new file mode 100644 index 00000000..9e58a142 --- /dev/null +++ b/src/main/basedbot/fsm/info.ts @@ -0,0 +1,73 @@ +import dayjs from 'dayjs' + +import { now } from '../../../dayjs' +import { logger } from '../../../logger' +import { planetsByCoordinates } from '../lib/sage/state/planets-by-coordinates' +import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates' +import { FleetInfo } from '../lib/sage/state/user-fleets' +import { getName } from '../lib/sage/util' + +import { Strategy } from './strategy' + +const transition = async (fleetInfo: FleetInfo): Promise => { + switch (fleetInfo.fleetState.type) { + case 'Idle': { + const baseStation = await starbaseByCoordinates(fleetInfo.location) + const planets = await planetsByCoordinates(fleetInfo.location) + + logger.info(`${fleetInfo.fleetName} is idle at ${fleetInfo.fleetState.data.sector} [BaseStation: ${baseStation ? getName(baseStation) : 'N/A'} / Planets: ${planets.length}]`) + break + } + case 'StarbaseLoadingBay': + logger.info(`Fleet: ${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`) + break + case 'MoveWarp': { + const { fromSector, toSector, warpFinish } = fleetInfo.fleetState.data + + if (warpFinish.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MoveSubwarp': { + const { fromSector, toSector, arrivalTime } = fleetInfo.fleetState.data + + if (arrivalTime.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MineAsteroid': { + const { mineItem, end, amountMined, endReason } = fleetInfo.fleetState.data + + if (end.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`) + } + else { + const log = endReason === 'FULL' ? logger.info : logger.warn + + log(`Fleet: ${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`) + } + break + } + default: + logger.info(`Fleet: ${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`) + } + + return Promise.resolve() +} + +export type InfoStrategy = { + getConfig: () => false + send: (fleetInfo: FleetInfo) => Promise +} + +export const createInfoStrategy = (): Strategy => ({ + send: (fleetInfo: FleetInfo): Promise => transition(fleetInfo) +}) diff --git a/src/main/basedbot/fsm/mine.ts b/src/main/basedbot/fsm/mine.ts new file mode 100644 index 00000000..c6a1e231 --- /dev/null +++ b/src/main/basedbot/fsm/mine.ts @@ -0,0 +1,205 @@ +import dayjs from 'dayjs' + +import { now } from '../../../dayjs' +import { logger } from '../../../logger' +import { dock } from '../lib/sage/act/dock' +import { endMine } from '../lib/sage/act/end-mine' +import { loadCargo } from '../lib/sage/act/load-cargo' +import { mine } from '../lib/sage/act/mine' +import { move } from '../lib/sage/act/move' +import { undock } from '../lib/sage/act/undock' +import { unloadAllCargo } from '../lib/sage/act/unload-all-cargo' +import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates' +import { Player } from '../lib/sage/state/user-account' +import { FleetInfo } from '../lib/sage/state/user-fleets' +import { getName } from '../lib/sage/util' + +import { MineConfig } from './configs/mine-config' +import { Strategy } from './strategy' + +// eslint-disable-next-line complexity +const transition = async (fleetInfo: FleetInfo, player: Player, config: MineConfig): Promise => { + const cargoLoad = player.cargoTypes.filter(ct => !ct.data.mint.equals(player.game.data.mints.food)) + .reduce((acc, cargoType) => { + const load = fleetInfo.cargoLevels.cargo.get(cargoType.data.mint.toBase58()) ?? 0 + + return acc + load + }, 0) + + const { cargoCapacity } = fleetInfo.cargoStats + const cargoLevelFood = fleetInfo.cargoLevels.food + const cargoLevelAmmo = fleetInfo.cargoLevels.ammo + const cargoLevelFuel = fleetInfo.cargoLevels.fuel + const desiredFood = cargoCapacity / 10 + const toLoad = desiredFood - cargoLevelFood + const hasEnoughFood = toLoad <= 10 + const hasEnoughAmmo = cargoLevelAmmo >= fleetInfo.cargoStats.ammoCapacity - 100 + const hasEnoughFuel = cargoLevelFuel >= fleetInfo.cargoStats.fuelCapacity - 100 + const hasCargo = cargoLoad > 0 + const currentStarbase = await starbaseByCoordinates(fleetInfo.location) + const { fleetName, location } = fleetInfo + const { homeBase, targetBase, resource, warpMode } = config + const resourceName = getName(resource.mineItem) + const isAtHomeBase = homeBase.equals(location) + const isAtTargetBase = targetBase.equals(location) + const isSameBase = homeBase.equals(targetBase) + + switch (fleetInfo.fleetState.type) { + case 'Idle': { + logger.info(`${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`) + + if(!currentStarbase && cargoLevelFuel < 1) { + logger.warn(`Fleet: ${fleetName} is out of fuel and not at a starbase, need self destruction`) + + return Promise.resolve() + } + if (isAtHomeBase) { + logger.info(`Fleet: ${fleetName} is at home base`) + if (hasCargo) { + logger.info(`Fleet: ${fleetName} has ${cargoLoad} ${resourceName}, docking to unload`) + + return dock(fleetInfo, location, player) + } + if (!hasEnoughFood || !hasEnoughFuel || !hasEnoughAmmo) { + logger.info(`Fleet: ${fleetName} doesn't have enough resources, docking to resupply`) + + return dock(fleetInfo, location, player) + } + if (isSameBase) { + logger.info(`Fleet: ${fleetName} is at home/target base, mining`) + + return mine(fleetInfo, player, resource) + } + logger.info(`Fleet: ${fleetName} is at home base, moving to target base`) + + return move(fleetInfo, targetBase, player, warpMode) + } + + if (isAtTargetBase && !isSameBase) { + logger.info(`Fleet: ${fleetName} is at target base`) + if (hasCargo) { + logger.info(`Fleet: ${fleetName} has ${cargoLoad} ${resourceName}, returning home`) + + return move(fleetInfo, homeBase, player, warpMode) + } + if (hasEnoughFood) { + logger.info(`Fleet: ${fleetName} has enough food, mining`) + + return mine(fleetInfo, player, resource) + } + logger.info(`Fleet: ${fleetName} doesn't have enough food, returning home`) + + return move(fleetInfo, homeBase, player, warpMode) + } + + logger.info(`Fleet: ${fleetName} is at ${location}`) + + return move(fleetInfo, hasCargo || !hasEnoughFood ? homeBase : targetBase, player, warpMode) + } + case 'StarbaseLoadingBay': { + logger.info(`Fleet: ${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`) + + if (hasCargo) { + logger.info(`Fleet: ${fleetInfo.fleetName} has ${cargoLoad} cargo, unloading`) + + return unloadAllCargo(fleetInfo, fleetInfo.location, player, fleetInfo.fleet.data.cargoHold) + } + + if (!hasEnoughFuel) { + logger.info(`Fleet: ${fleetInfo.fleetName} is refueling`) + + return loadCargo( + fleetInfo, + player, + player.game.data.mints.fuel, + fleetInfo.fleet.data.fuelTank, + fleetInfo.cargoStats.fuelCapacity - cargoLevelFuel) + } + + if (!hasEnoughAmmo) { + logger.info(`Fleet: ${fleetInfo.fleetName} is rearming`) + + return loadCargo( + fleetInfo, + player, + player.game.data.mints.ammo, + fleetInfo.fleet.data.ammoBank, + fleetInfo.cargoStats.ammoCapacity - cargoLevelAmmo) + } + + if (!hasEnoughFood) { + logger.info(`Fleet: ${fleetInfo.fleetName} is loading ${desiredFood - cargoLevelFood} food`) + + return loadCargo( + fleetInfo, + player, + player.game.data.mints.food, + fleetInfo.fleet.data.cargoHold, + toLoad) + } + + return undock(fleetInfo.fleet, fleetInfo.location, player) + } + case 'MoveWarp': { + const { fromSector, toSector, warpFinish } = fleetInfo.fleetState.data + + if (warpFinish.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MoveSubwarp': { + const { fromSector, toSector, arrivalTime } = fleetInfo.fleetState.data + + if (arrivalTime.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MineAsteroid': { + const { mineItem, end, amountMined, endReason } = fleetInfo.fleetState.data + + if (end.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`) + + return endMine(fleetInfo, player, config.resource) + } + + const log = endReason === 'FULL' ? logger.info : logger.warn + + log(`Fleet: ${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`) + + break + } + case 'Respawn': { + const { destructionTime, ETA } = fleetInfo.fleetState.data + + if (ETA.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has respawned`) + } + else { + logger.info(`Fleet: ${fleetInfo.fleetName} respawning at ${fleetInfo.fleetState.data.sector}. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}. Destruction time: ${destructionTime}`) + } + break + } + default: + logger.info(`Fleet: ${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`) + + return Promise.resolve() + } +} + +export const createMiningStrategy = (miningConfig: MineConfig, p: Player): Strategy => { + const config = miningConfig + const player = p + + return { + send: (fleetInfo: FleetInfo): Promise => transition(fleetInfo, player, config) + } +} diff --git a/src/main/basedbot/fsm/strategy.ts b/src/main/basedbot/fsm/strategy.ts new file mode 100644 index 00000000..968958f7 --- /dev/null +++ b/src/main/basedbot/fsm/strategy.ts @@ -0,0 +1,5 @@ +import { FleetInfo } from '../lib/sage/state/user-fleets' + +export type Strategy = { + send: (fleetInfo: FleetInfo) => Promise +} diff --git a/src/main/basedbot/index.ts b/src/main/basedbot/index.ts new file mode 100644 index 00000000..f025b2a9 --- /dev/null +++ b/src/main/basedbot/index.ts @@ -0,0 +1,65 @@ +import { Sentry } from '../../sentry' // import this as early as possible to catch early startup errors + +import { logger } from '../../logger' + +import * as app from './basedbot' + +const stop = async (signal?: NodeJS.Signals) => { + logger.info(`Shutting down${signal ? ` (${signal})` : ''}`) + + try { + await app.stop() + } + catch (error) { + Sentry.captureException(error) + logger.error('Close failed') + logger.error((error as Error).stack) + + process.exitCode = 1 + } + process.exit() +} + +const start = async () => { + try { + await app.create() + await app.start() + } + catch (error) { + Sentry.captureException(error) + logger.error((error as Error).stack) + + process.exitCode = 1 + await stop() + } +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +process.on('unhandledRejection', async (reason: any | null | undefined, _promise: Promise) => { + logger.error('Unhandled rejection') + + if (reason) { + const { message }: { message: string } = reason + + if (message.includes('Event listener')) { + return + } + logger.error(message) + } + + Sentry.captureException(reason) + await stop() +}) + +process.on('uncaughtException', async (error) => { + Sentry.captureException(error) + logger.error('Uncaught exception') + logger.error(error.stack) + + await stop() +}) + +process.on('SIGINT', stop) +process.on('SIGTERM', stop) + +start() diff --git a/src/main/basedbot/lib/fleet-state/fleet-state.ts b/src/main/basedbot/lib/fleet-state/fleet-state.ts new file mode 100644 index 00000000..56206037 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/fleet-state.ts @@ -0,0 +1,244 @@ +import { CargoStats, Fleet, MiscStats } from '@staratlas/sage' +import Big from 'big.js' +import BN from 'bn.js' + +import { now } from '../../../../dayjs' +import { FleetCargo } from '../sage/state/fleet-cargo' +import { planetByKey } from '../sage/state/planet-by-key' +import { starbaseByKey } from '../sage/state/starbase-by-key' +import { WorldMap } from '../sage/state/world-map' +import { Coordinates } from '../util/coordinates' + +import { transformSector } from './transform/transform-sector' +import { transformTime } from './transform/transform-time' +import { isIdleData } from './type-guard/idle' +import { isMineAsteroidData } from './type-guard/mine-asteroid' +import { isMoveSubWarpData } from './type-guard/move-sub-warp' +import { isMoveWarpData } from './type-guard/move-warp' +import { isRespawnData } from './type-guard/respawn' +import { isStarbaseLoadingBayData } from './type-guard/starbase-loading-bay' +import { FleetState, FleetStateType, RawMineAsteroidData } from './types' + +const toBig = (bn: BN): Big => new Big(bn.toString()) +const toBN = (bigInt: Big): BN => new BN(bigInt.toString()) + +const calculateCurrentPosition = ( + startPos: Coordinates, + destPos: Coordinates, + startTime: BN, + endTime: BN, + currentTime: BN +): Coordinates => { + if (currentTime.gte(endTime)) { + return destPos + } + else if (currentTime.lte(startTime)) { + return startPos + } + + const totalTime = toBig(endTime.sub(startTime)) + const elapsedTime = toBig(currentTime.sub(startTime)) + const ratio = elapsedTime.div(totalTime) + + const xDifference = toBig(destPos.xBN.sub(startPos.xBN)) + const yDifference = toBig(destPos.yBN.sub(startPos.yBN)) + + const xt = toBig(startPos.xBN).add(xDifference.mul(ratio)) + const yt = toBig(startPos.yBN).add(yDifference.mul(ratio)) + + return Coordinates.fromBN(toBN(xt.round(0, 1)), toBN(yt.round(0, 1))) +} + +export type EndReason = 'FULL' | 'AMMO' | 'FOOD' + +type MiningStats = { + startTime: BN + endTime: BN + cargoLevel: number + miningRate: number + amountMined: number + ammoRequired: number + foodRequired: number + ammoConsumptionRate: number + foodConsumptionRate: number + ammoLevel: number + foodLevel: number + endReason: EndReason + maxMiningDuration: number + isMining: boolean +} + +const getMiningStats = ( + fleet: Fleet, + cargoLevels: FleetCargo, + mineAsteroidData: RawMineAsteroidData +): MiningStats => { + const cargoStats = fleet.data.stats.cargoStats as unknown as CargoStats + const { miningRate, cargoCapacity, foodConsumptionRate, ammoConsumptionRate } = cargoStats + + const startTime = mineAsteroidData.start + + let cargoLevel = 0 + + for (const [_, value] of cargoLevels.cargo) { + cargoLevel += value + } + + const cargoSpace = cargoCapacity - cargoLevel + + const miningRatePerSecond = miningRate / 10000 + const ammoConsumptionRatePerSecond = ammoConsumptionRate / 10000 + const foodConsumptionRatePerSecond = foodConsumptionRate / 10000 + + const durationToFull = cargoSpace / miningRatePerSecond + const durationToammoDepletion = cargoLevels.ammo / ammoConsumptionRatePerSecond + const durationToFoodDepletion = cargoLevels.food / foodConsumptionRatePerSecond + + const maxMiningDuration = Math.min(durationToFull, durationToammoDepletion, durationToFoodDepletion) + + const endReason = maxMiningDuration === durationToFull ? 'FULL' : maxMiningDuration === durationToammoDepletion ? 'AMMO' : 'FOOD' + + const n = new BN(now().unix()) + const miningDuration = n.sub(startTime).toNumber() + + const realMiningDuration = Math.min(miningDuration, maxMiningDuration) + + const amountMined = miningRatePerSecond * realMiningDuration + const ammoConsumed = ammoConsumptionRatePerSecond * realMiningDuration + const foodConsumed = foodConsumptionRatePerSecond * realMiningDuration + + return { + startTime, + endTime: startTime.add(new BN(maxMiningDuration)), + cargoLevel, + miningRate, + amountMined, + ammoRequired: ammoConsumed, + foodRequired: foodConsumed, + ammoConsumptionRate, + foodConsumptionRate, + ammoLevel: cargoLevels.ammo - ammoConsumed, + foodLevel: cargoLevels.food - foodConsumed, + endReason, + maxMiningDuration, + isMining: miningDuration < maxMiningDuration + } +} + +export const getFleetState = async (fleet: Fleet, map: WorldMap, cargoLevels: FleetCargo): Promise => { + const fleetStateKeys = Object.keys(fleet.state) as Array + const miscStats = fleet.data.stats.miscStats as unknown as MiscStats + + if (fleetStateKeys.length === 0) { + throw new Error('Fleet state is empty') + } + + const [type] = fleetStateKeys + const data = fleet.state[type as keyof typeof fleet.state] + + if (!data) { + throw new Error('Data is empty') + } + + switch (type) { + case 'Idle': + if (isIdleData(data)) { + return { + type, + data: { + sector: transformSector(data.sector) + } + } + } + break + case 'StarbaseLoadingBay': + if (isStarbaseLoadingBayData(data)) { + const starbase = await starbaseByKey(data.starbase) + + return { + type, + data: { + starbase, + lastUpdate: data.lastUpdate, + sector: transformSector(starbase.data.sector) + } + } + } + break + case 'MineAsteroid': + if (isMineAsteroidData(data)) { + const planet = await planetByKey(data.asteroid) + + const miningStats = getMiningStats(fleet, cargoLevels, data) + + return { + type, data: { + sector: transformSector(planet.data.sector), + lastUpdate: transformTime(data.lastUpdate), + amountMined: new BN(miningStats.amountMined), + asteroid: data.asteroid, + end: transformTime(miningStats.endTime), + mineItem: map.mineItems.get(data.resource.toBase58())!, + start: transformTime(data.start), + endReason: miningStats.endReason + } + } + } + break + case 'MoveWarp': + if (isMoveWarpData(data)) { + return { + type, data: { + fromSector: transformSector(data.fromSector), + toSector: transformSector(data.toSector), + warpStart: transformTime(data.warpStart), + warpFinish: transformTime(data.warpFinish), + sector: calculateCurrentPosition( + transformSector(data.fromSector), + transformSector(data.toSector), + data.warpStart, + data.warpFinish, + new BN(now().unix()) + ) + + } + } + } + break + case 'MoveSubwarp': + if (isMoveSubWarpData(data)) { + return { + type, data: { + fromSector: transformSector(data.fromSector), + toSector: transformSector(data.toSector), + departureTime: transformTime(data.departureTime), + arrivalTime: transformTime(data.arrivalTime), + fuelExpenditure: data.fuelExpenditure, + lastUpdate: transformTime(data.lastUpdate), + sector: calculateCurrentPosition( + transformSector(data.fromSector), + transformSector(data.toSector), + data.departureTime, + data.arrivalTime, + new BN(now().unix()) + ) + } + } + } + break + case 'Respawn': { + if (isRespawnData(data)) { + return { + type, data: { + sector: transformSector(data.sector), + destructionTime: transformTime(data.start), + ETA: transformTime(data.start.add(new BN(miscStats.respawnTime))) + } + } + } + } + break + } + + throw new Error('Data does not match expected type for the fleet state') +} diff --git a/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts b/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts new file mode 100644 index 00000000..84ed06d7 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts @@ -0,0 +1,6 @@ +import BN from 'bn.js' + +import { Coordinates } from '../../util/coordinates' + +export const transformSector = (sector: BN[]): Coordinates => + Coordinates.fromBN(sector[0] , sector[1]) diff --git a/src/main/basedbot/lib/fleet-state/transform/transform-time.ts b/src/main/basedbot/lib/fleet-state/transform/transform-time.ts new file mode 100644 index 00000000..6673f693 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/transform/transform-time.ts @@ -0,0 +1,6 @@ +import BN from 'bn.js' + +import dayjs from '../../../../../dayjs' + +export const transformTime = (time: BN): dayjs.Dayjs => + dayjs.unix(time.toNumber()) diff --git a/src/main/basedbot/lib/fleet-state/type-guard/idle.ts b/src/main/basedbot/lib/fleet-state/type-guard/idle.ts new file mode 100644 index 00000000..a4acc0d2 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/idle.ts @@ -0,0 +1,10 @@ +import BN from 'bn.js' + +import { RawIdleData } from '../types' + +export const isIdleData = (data: any): data is RawIdleData => + data && + Array.isArray(data.sector) && + data.sector.length === 2 && + data.sector[0] instanceof BN && + data.sector[1] instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts b/src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts new file mode 100644 index 00000000..980223a5 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts @@ -0,0 +1,13 @@ +import { PublicKey } from '@solana/web3.js' +import BN from 'bn.js' + +import { RawMineAsteroidData } from '../types' + +export const isMineAsteroidData = (data: any): data is RawMineAsteroidData => + data && + data.asteroid instanceof PublicKey && + data.resource instanceof PublicKey && + data.start instanceof BN && + data.end instanceof BN && + data.amountMined instanceof BN && + data.lastUpdate instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts b/src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts new file mode 100644 index 00000000..0ef91d41 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts @@ -0,0 +1,15 @@ +import BN from 'bn.js' + +import { RawMoveSubwarpData } from '../types' + +// TODO: Add all the fields that are required to be present in the data +export const isMoveSubWarpData = (data: any): data is RawMoveSubwarpData => + data && + Array.isArray(data.fromSector) && + data.fromSector.length === 2 && + data.fromSector[0] instanceof BN && + data.fromSector[1] instanceof BN && + Array.isArray(data.toSector) && + data.toSector.length === 2 && + data.toSector[0] instanceof BN && + data.toSector[1] instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts b/src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts new file mode 100644 index 00000000..4c60e848 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts @@ -0,0 +1,15 @@ +import BN from 'bn.js' + +import { RawMoveWarpData } from '../types' + +// TODO: Add all the fields that are required to be present in the data +export const isMoveWarpData = (data: any): data is RawMoveWarpData => + data && + Array.isArray(data.fromSector) && + data.fromSector.length === 2 && + data.fromSector[0] instanceof BN && + data.fromSector[1] instanceof BN && + Array.isArray(data.toSector) && + data.toSector.length === 2 && + data.toSector[0] instanceof BN && + data.toSector[1] instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/type-guard/respawn.ts b/src/main/basedbot/lib/fleet-state/type-guard/respawn.ts new file mode 100644 index 00000000..a40732a5 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/respawn.ts @@ -0,0 +1,10 @@ +import BN from 'bn.js' + +import { RawRespawnData } from '../types' + +export const isRespawnData = (data: any): data is RawRespawnData => + data && + Array.isArray(data.sector) && + data.sector.length === 2 && + data.sector[0] instanceof BN && + data.sector[1] instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts b/src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts new file mode 100644 index 00000000..9eb14a8a --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts @@ -0,0 +1,9 @@ +import { PublicKey } from '@solana/web3.js' +import BN from 'bn.js' + +import { RawStarbaseLoadingBayData } from '../types' + +export const isStarbaseLoadingBayData = (data: any): data is RawStarbaseLoadingBayData => + data && + data.starbase instanceof PublicKey && + data.lastUpdate instanceof BN diff --git a/src/main/basedbot/lib/fleet-state/types.ts b/src/main/basedbot/lib/fleet-state/types.ts new file mode 100644 index 00000000..bcd40a93 --- /dev/null +++ b/src/main/basedbot/lib/fleet-state/types.ts @@ -0,0 +1,62 @@ +import { PublicKey } from '@solana/web3.js' +import { MineItem, Starbase } from '@staratlas/sage' +import BN from 'bn.js' + +import dayjs from '../../../../dayjs' +import { Coordinates } from '../util/coordinates' + +import { EndReason } from './fleet-state' + +type BaseData = { + sector: Coordinates +} + +// Raw data types for incoming data +export type RawIdleData = { + sector: [BN, BN] +} + +export type IdleData = { + sector: Coordinates +} + +export type RawStarbaseLoadingBayData = { starbase: PublicKey; lastUpdate: BN } +export type StarbaseLoadingBayData = { starbase: Starbase; lastUpdate: BN } & BaseData + +export type RawMineAsteroidData = { asteroid: PublicKey; resource: PublicKey; start: BN; end: BN; amountMined: BN; lastUpdate: BN; sector: BN[] } +export type MineAsteroidData = { asteroid: PublicKey; mineItem: MineItem; start: dayjs.Dayjs; end: dayjs.Dayjs; amountMined: BN; lastUpdate: dayjs.Dayjs; endReason: EndReason } & BaseData + +export type RawMoveWarpData = { fromSector: BN[]; toSector: BN[]; warpStart: BN; warpFinish: BN } +export type MoveWarpData = { fromSector: Coordinates; toSector: Coordinates; warpStart: dayjs.Dayjs; warpFinish: dayjs.Dayjs } & BaseData + +export type RawMoveSubwarpData = { fromSector: BN[]; toSector: BN[]; currentSector: BN[]; departureTime: BN; arrivalTime: BN; fuelExpenditure: BN; lastUpdate: BN } +export type MoveSubwarpData = { fromSector: Coordinates; toSector: Coordinates; departureTime: dayjs.Dayjs; arrivalTime: dayjs.Dayjs; fuelExpenditure: BN; lastUpdate: dayjs.Dayjs } & BaseData + +export type RawRespawnData = { + sector: [BN, BN] + start: BN +} +export type RespawnData = { destructionTime: dayjs.Dayjs; ETA: dayjs.Dayjs } & BaseData +export type StarbaseUpgradeData = BaseData +export type StarbaseRepairData = BaseData + +export type FleetStateType = 'StarbaseLoadingBay' | 'Idle' | 'MineAsteroid' | 'MoveWarp' | 'MoveSubwarp' | 'Respawn' | 'StarbaseUpgrade' | 'StarbaseRepair' +/* eslint-disable @typescript-eslint/naming-convention */ +export type FleetStateDataMap = { + StarbaseLoadingBay: StarbaseLoadingBayData + Idle: IdleData + MineAsteroid: MineAsteroidData + MoveWarp: MoveWarpData + MoveSubwarp: MoveSubwarpData + Respawn: RespawnData + StarbaseUpgrade: StarbaseUpgradeData + StarbaseRepair: StarbaseRepairData +} +/* eslint-enable @typescript-eslint/naming-convention */ + +export type FleetState = { + [K in FleetStateType]: { + type: K + data: FleetStateDataMap[K] + } +}[FleetStateType]; diff --git a/src/main/basedbot/lib/programs.ts b/src/main/basedbot/lib/programs.ts new file mode 100644 index 00000000..2999ccd4 --- /dev/null +++ b/src/main/basedbot/lib/programs.ts @@ -0,0 +1,76 @@ +// eslint-disable-next-line import/named +import { Idl } from '@coral-xyz/anchor' +import { PublicKey } from '@solana/web3.js' +import { CargoProgram } from '@staratlas/cargo' +import { CraftingProgram } from '@staratlas/crafting' +import { ProgramMethods } from '@staratlas/data-source' +import { PlayerProfileProgram } from '@staratlas/player-profile' +import { PointsProgram } from '@staratlas/points' +import { ProfileFactionProgram } from '@staratlas/profile-faction' +import { ProfileFaction } from '@staratlas/profile-faction/dist/src/idl/profile_faction' +import { Points } from '@staratlas/points/dist/src/idl/points' +import { SageProgram } from '@staratlas/sage' +import { Sage } from '@staratlas/sage/dist/src/idl/sage' +import { Crafting } from '@staratlas/crafting/dist/src/idl/crafting' +import { PlayerProfile } from '@staratlas/player-profile/dist/src/idl/player_profile' +import { Cargo } from '@staratlas/cargo/dist/src/idl/cargo' + +import { anchorProvider } from '../../../service/sol/anchor' + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +export type StarAtlasProgram = ProgramMethods + +// export const xpCategoryIds = { +// dataRunningXpCategory: 'DataJpxFgHhzwu4zYJeHCnAv21YqWtanEBphNxXBHdEY', +// councilRankXpCategory: 'XPneyd1Wvoay3aAa24QiKyPjs8SUbZnGg5xvpKvTgN9', +// pilotingXpCategory: 'PiLotBQoUBUvKxMrrQbuR3qDhqgwLJctWsXj3uR7fGs', +// miningXpCategory: 'MineMBxARiRdMh7s1wdStSK4Ns3YfnLjBfvF5ZCnzuw', +// craftingXpCategory: 'CraftndAV62acibnaW7TiwEYwu8MmJZBdyrfyN54nre7' +// loyalities: '', +// } + +export const xpCategoryIds = { + dataRunningXpCategory: 'DXPsKQPMyaDtunxDWqiKTGWbQga3Wihck8zb8iSLATJQ', + councilRankXpCategory: 'CRXPW3csNpkEYU5U4DUp6Ln6aEEWq4PSUAwV8v6Ygcqg', + pilotingXpCategory: 'PXPfCZwu5Vuuj6aFdEUAXbxudDGeXVktTo6imwhZ5nC', + miningXpCategory: 'MXPkuZz7yXvqdEB8pGtyNknqhxbCzJNQzqixoEiW4Q7', + craftingXpCategory: 'CXPukKpixXCFPrfQmEUGR9VqnDvkUsKfPPLfdd4sKSH8', + loyalityCategory: 'LPpdwMuXRuGMz298EMbNcUioaARN8CUU6dA2qyq46g8' +} + +const programIds = { + sage: 'sagEaq3KgZBmUeE9gDYKHiDHbBZ37wcUtxBxMVLpwnU', + profile: 'PprofUW1pURCnMW2si88GWPXEEK3Bvh9Tksy8WtnoYJ', + cargo: 'CArGoi989iv3VL3xArrJXmYYDNhjwCX5ey5sY5KKwMG', + profileFaction: 'pFACzkX2eSpAjDyEohD6i3VRJvREtH9ynbtM1DwVFsj', + crafting: 'CRAFtUSjCW74gQtCS6LyJH33rhhVhdPhZxbPegE4Qwfq', + points: 'PointJfvuHi8DgGsPCy97EaZkQ6NvpghAAVkuquLf3w', +} + +// const programIds = { +// sage: 'SAGE2HAwep459SNq61LHvjxPk4pLPEJLoMETef7f7EE', +// profile: 'pprofELXjL5Kck7Jn5hCpwAL82DpTkSYBENzahVtbc9', +// cargo: 'Cargo2VNTPPTi9c1vq1Jw5d3BWUNr18MjRtSupAghKEk', +// profileFaction: 'pFACSRuobDmvfMKq1bAzwj27t6d2GJhSCHb1VcfnRmq', +// crafting: '', +// points: 'Point2iBvz7j5TMVef8nEgpmz4pDr7tU7v3RjAfkQbM', +// } + +export type StarAtlasPrograms = { + sage: StarAtlasProgram + points: StarAtlasProgram + playerProfile: StarAtlasProgram + cargo: StarAtlasProgram + profileFaction: StarAtlasProgram + crafting: StarAtlasProgram +} + +export const programs: StarAtlasPrograms = { + sage: SageProgram.buildProgram(new PublicKey(programIds.sage), anchorProvider), + points: PointsProgram.buildProgram(new PublicKey(programIds.points), anchorProvider), + playerProfile: PlayerProfileProgram.buildProgram(new PublicKey(programIds.profile), anchorProvider), + cargo: CargoProgram.buildProgram(new PublicKey(programIds.cargo), anchorProvider), + profileFaction: ProfileFactionProgram.buildProgram(new PublicKey(programIds.profileFaction), anchorProvider), + crafting: CraftingProgram.buildProgram(new PublicKey(programIds.crafting), anchorProvider) +} diff --git a/src/main/basedbot/lib/sage/act/dock.ts b/src/main/basedbot/lib/sage/act/dock.ts new file mode 100644 index 00000000..784b8c20 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/dock.ts @@ -0,0 +1,25 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' + +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { Coordinates } from '../../util/coordinates' +import { dockIx } from '../ix/dock' +import { programs } from '../../programs' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const dock = async (fleetInfo: FleetInfo, coordinates: Coordinates, player: Player): Promise => { + const starbase = await starbaseByCoordinates(coordinates) + + if(!starbase) { + throw new Error(`No starbase found at ${coordinates}`) + } + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + + const ix = dockIx(fleetInfo, player, starbase, starbasePlayer, programs) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/end-mine.ts b/src/main/basedbot/lib/sage/act/end-mine.ts new file mode 100644 index 00000000..74cb70ad --- /dev/null +++ b/src/main/basedbot/lib/sage/act/end-mine.ts @@ -0,0 +1,74 @@ +import { createAssociatedTokenAccountIdempotent, ixReturnsToIxs } from '@staratlas/data-source' + +import { logger } from '../../../../../logger' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { miningHandlerIx } from '../ix/fleet-state-handler' +import { stopMiningIx } from '../ix/stop-mining' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' +import { Mineable } from '../state/world-map' + +export const endMine = async (fleetInfo: FleetInfo, player: Player, mineable: Mineable): Promise => { + const { fleet } = fleetInfo + + if (!fleet.state.MineAsteroid) { + logger.warn('Fleet is not mining, cannot End Mine') + + return + } + + const ix = [] + + const foodTokenFromResult = createAssociatedTokenAccountIdempotent( + player.game.data.mints.food, + fleetInfo.fleet.data.cargoHold, + true + ) + + ix.push(foodTokenFromResult.instructions) + + const ammoTokenFromResult = createAssociatedTokenAccountIdempotent( + player.game.data.mints.ammo, + fleetInfo.fleet.data.ammoBank, + true + ) + + ix.push(ammoTokenFromResult.instructions) + + const resourceTokenFromResult = createAssociatedTokenAccountIdempotent( + mineable.mineItem.data.mint, + mineable.resource.data.mineItem, + true + ) + + ix.push(resourceTokenFromResult.instructions) + const resourceTokenToResult = createAssociatedTokenAccountIdempotent( + mineable.mineItem.data.mint, + fleetInfo.fleet.data.cargoHold, + true + ) + + ix.push(resourceTokenToResult.instructions) + + ix.push(miningHandlerIx( + fleetInfo, + player, + mineable, + foodTokenFromResult.address, + ammoTokenFromResult.address, + resourceTokenFromResult.address, + resourceTokenToResult.address, + programs)) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) + + await sendAndConfirmInstructions(await ixReturnsToIxs(stopMiningIx( + fleetInfo, + player, + mineable, + programs + ), player.signer)) +} diff --git a/src/main/basedbot/lib/sage/act/end-move.ts b/src/main/basedbot/lib/sage/act/end-move.ts new file mode 100644 index 00000000..ebead435 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/end-move.ts @@ -0,0 +1,25 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' + +import { logger } from '../../../../../logger' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { stopSubWarpIx } from '../ix/stop-subwarp' +import { stopWarpIx } from '../ix/stop-warp' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const endMove = async (fleetInfo: FleetInfo, player: Player): Promise => { + const { fleet } = fleetInfo + + if (!fleet.state.MoveWarp && !fleet.state.MoveSubwarp) { + logger.warn('Fleet is not moving, cannot End Move') + + return + } + + const ix = (fleet.state.MoveSubwarp ? stopSubWarpIx : stopWarpIx)(fleetInfo, player, programs) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/exit-respawn.ts b/src/main/basedbot/lib/sage/act/exit-respawn.ts new file mode 100644 index 00000000..15a6faea --- /dev/null +++ b/src/main/basedbot/lib/sage/act/exit-respawn.ts @@ -0,0 +1,34 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' + +import { logger } from '../../../../../logger' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { Coordinates } from '../../util/coordinates' +import { exitRespawnIx } from '../ix/exit-respawn' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const exitRespawn = async (fleetInfo: FleetInfo, location: Coordinates, player: Player): Promise => { + const { fleet } = fleetInfo + + if (!fleet.state.Respawn) { + logger.warn('Fleet is not respawning, cannot Exit Respawn') + + return + } + + const starbase = await starbaseByCoordinates(location) + + if(!starbase) { + throw new Error(`No starbase found at ${location}`) + } + + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + + const ix = exitRespawnIx(fleetInfo, player, starbase, starbasePlayer, programs) + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/load-cargo.ts b/src/main/basedbot/lib/sage/act/load-cargo.ts new file mode 100644 index 00000000..d4fd1067 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/load-cargo.ts @@ -0,0 +1,86 @@ +import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import { createAssociatedTokenAccountIdempotent, getParsedTokenAccountsByOwner, ixReturnsToIxs } from '@staratlas/data-source' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { loadCargoIx } from '../ix/load-cargo' +import { getCargoType } from '../state/cargo-types' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getCargoPodsForStarbasePlayer, getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const loadCargo = async ( + fleetInfo: FleetInfo, + player: Player, + mint: PublicKey, + hold: PublicKey, + amount: number + // eslint-disable-next-line max-params +): Promise => { + const starbase = await starbaseByCoordinates(fleetInfo.location) + + if(!starbase) { + throw new Error(`No starbase found at ${fleetInfo.location}`) + } + + const cargoType = getCargoType(player.cargoTypes, player.game, mint) + const fleetCargoTokenResult = createAssociatedTokenAccountIdempotent( + mint, + hold, + true, + ) + + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + const cargoPodFrom = await getCargoPodsForStarbasePlayer(starbasePlayer, programs) + + const allTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + cargoPodFrom.key, + TOKEN_PROGRAM_ID, + ) + + const cargoTokenAccountAddress = getAssociatedTokenAddressSync( + mint, + cargoPodFrom.key, + true, + ) + const cargoTokenAccount = allTokenAccounts.filter(it => + it.address.equals(cargoTokenAccountAddress), + )[0] + + if (!cargoTokenAccount) { + throw new Error('Cargo token account not found') + } + const fuelAmountAtOrigin = new BN( + cargoTokenAccount.delegatedAmount.toString(), + ) + + if (!cargoTokenAccount) { + throw new Error('Cargo not found at origin Starbase') + } + if (fuelAmountAtOrigin.lt(new BN(amount))) { + throw 'Not enough cargo available at origin Starbase' + } + + const ix = loadCargoIx( + fleetInfo, + player, + starbase, + starbasePlayer, + cargoPodFrom.key, + hold, + cargoTokenAccountAddress, + fleetCargoTokenResult.address, + mint, + cargoType.key, + programs, + new BN(amount)) + + const instructions = await ixReturnsToIxs([fleetCargoTokenResult.instructions, ix], player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/mine.ts b/src/main/basedbot/lib/sage/act/mine.ts new file mode 100644 index 00000000..b14d15d7 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/mine.ts @@ -0,0 +1,53 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' + +import { logger } from '../../../../../logger' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { startMiningIx } from '../ix/start-mining' +import { programs } from '../../programs' +import { getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' +import { Mineable } from '../state/world-map' + +import { undock } from './undock' + +export const mine = async ( + fleetInfo: FleetInfo, + player: Player, + mineable: Mineable +): Promise => { + const { fleet } = fleetInfo + + // TOOD: Check fuel cost for mining + + if (fleet.state.MineAsteroid) { + logger.warn('Fleet is already mining') + + return + } + + if (fleet.state.StarbaseLoadingBay) { + logger.info(`Fleet: ${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`) + + await undock(fleet, fleetInfo.location, player) + } + + if (fleet.state.MoveSubwarp || fleet.state.MoveWarp) { + logger.info(`Fleet: ${fleetInfo.fleetName} is moving, cannot mine`) + + return + } + const starbasePlayer = await getStarbasePlayer(player, mineable.starbase, programs) + + const ix = startMiningIx( + fleetInfo, + player, + mineable, + starbasePlayer, + programs + ) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/move.ts b/src/main/basedbot/lib/sage/act/move.ts new file mode 100644 index 00000000..f9147237 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/move.ts @@ -0,0 +1,53 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' + +import { logger } from '../../../../../logger' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { Coordinates } from '../../util/coordinates' +import { subWarpIx } from '../ix/subwarp' +import { warpIx } from '../ix/warp' +import { programs } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +import { undock } from './undock' + +export type WarpMode = 'warp' | 'subwarp' | 'auto' + +export const move = async ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player, + warpMode: WarpMode = 'auto', +): Promise => { + const { fleet } = fleetInfo + + if(fleet.state.MoveWarp || fleet.state.MoveSubwarp) { + logger.warn('Fleet is already moving') + + return + } + + if (fleet.state.StarbaseLoadingBay) { + logger.info(`Fleet: ${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`) + + await undock(fleet, fleetInfo.location, player) + } + + if(fleet.state.MineAsteroid) { + logger.info(`Fleet: ${fleetInfo.fleetName} is mining an asteroid, cannot move`) + + return + } + + const { maxWarpDistance } = fleetInfo.movementStats + const desiredDistance = fleetInfo.location.distanceFrom(coordinates) * 100 + + const canWarp = desiredDistance <= maxWarpDistance + const warp = warpMode === 'warp' || (warpMode === 'auto' && canWarp) + + const ix = (warp ? warpIx : subWarpIx)(fleetInfo, coordinates,player, programs) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/rearm.ts b/src/main/basedbot/lib/sage/act/rearm.ts new file mode 100644 index 00000000..389651ff --- /dev/null +++ b/src/main/basedbot/lib/sage/act/rearm.ts @@ -0,0 +1,64 @@ +import { createAssociatedTokenAccountIdempotent, getParsedTokenAccountsByOwner, ixReturnsToIxs } from '@staratlas/data-source' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { Coordinates } from '../../util/coordinates' +import { loadCargoIx } from '../ix/load-cargo' +import { getCargoType } from '../state/cargo-types' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getCargoPodsForStarbasePlayer, getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const rearm = async ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player +): Promise => { + const starbase = await starbaseByCoordinates(coordinates) + + if (!starbase) { + throw new Error(`No starbase found at ${coordinates}`) + } + + const cargoType = getCargoType(player.cargoTypes, player.game, player.game.data.mints.ammo) + const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( + player.game.data.mints.ammo, + fleetInfo.fleet.data.ammoBank, + true + ) + + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + const cargoPodFrom = await getCargoPodsForStarbasePlayer(starbasePlayer, programs) + + const starbaseTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + cargoPodFrom.key + ) + + const currentAmmo = fleetInfo.cargoLevels.ammo + const maxAmmo = fleetInfo.cargoStats.ammoCapacity + const ammoNeeded = maxAmmo - currentAmmo + + console.log(`Current Ammo: ${currentAmmo}, Max Ammo: ${maxAmmo}, Ammo Needed: ${ammoNeeded}`) + + const ix = loadCargoIx( + fleetInfo, + player, + starbase, + starbasePlayer, + cargoPodFrom.key, + fleetInfo.fleet.data.ammoBank, + starbaseTokenAccounts[0].address, + fleetFuelTokenResult.address, + player.game.data.mints.ammo, + cargoType.key, + programs, + new BN(ammoNeeded)) + + const instructions = await ixReturnsToIxs([fleetFuelTokenResult.instructions, ix], player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/refuel.ts b/src/main/basedbot/lib/sage/act/refuel.ts new file mode 100644 index 00000000..3f34ed8d --- /dev/null +++ b/src/main/basedbot/lib/sage/act/refuel.ts @@ -0,0 +1,64 @@ +import { createAssociatedTokenAccountIdempotent, getParsedTokenAccountsByOwner, ixReturnsToIxs } from '@staratlas/data-source' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { Coordinates } from '../../util/coordinates' +import { programs } from '../../programs' +import { loadCargoIx } from '../ix/load-cargo' +import { getCargoType } from '../state/cargo-types' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getCargoPodsForStarbasePlayer, getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const refuel = async ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player, +): Promise => { + const starbase = await starbaseByCoordinates(coordinates) + + if(!starbase) { + throw new Error(`No starbase found at ${coordinates}`) + } + + const cargoType = getCargoType(player.cargoTypes, player.game, player.game.data.mints.fuel) + const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( + player.game.data.mints.fuel, + fleetInfo.fleet.data.fuelTank, + true, + ) + + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + const cargoPodFrom = await getCargoPodsForStarbasePlayer(starbasePlayer, programs) + + const starbaseTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + cargoPodFrom.key, + ) + + const currentFuel = fleetInfo.cargoLevels.fuel + const maxFuel = fleetInfo.cargoStats.fuelCapacity + const fuelNeeded = maxFuel - currentFuel + + console.log(`Current Fuel: ${currentFuel}, Max Fuel: ${maxFuel}, Fuel Needed: ${fuelNeeded}`) + + const ix = loadCargoIx( + fleetInfo, + player, + starbase, + starbasePlayer, + cargoPodFrom.key, + fleetInfo.fleet.data.fuelTank, + starbaseTokenAccounts[0].address, + fleetFuelTokenResult.address, + player.game.data.mints.fuel, + cargoType.key, + programs, + new BN(fuelNeeded)) + + const instructions = await ixReturnsToIxs([fleetFuelTokenResult.instructions, ix], player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/undock.ts b/src/main/basedbot/lib/sage/act/undock.ts new file mode 100644 index 00000000..d07058c9 --- /dev/null +++ b/src/main/basedbot/lib/sage/act/undock.ts @@ -0,0 +1,38 @@ +import { ixReturnsToIxs } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { Coordinates } from '../../util/coordinates' +import { sageGame } from '../state/game' +import { programs } from '../../programs' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' + +export const undock = async (fleet: Fleet, coordinates: Coordinates, player: Player): Promise => { + const starbase = await starbaseByCoordinates(coordinates) + + if(!starbase) { + throw new Error(`No starbase found at ${coordinates}`) + } + const { sage } = programs + const game = await sageGame() + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + + const ix = Fleet.loadingBayToIdle( + sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleet.key, + starbase.key, + starbasePlayer.key, + game.key, + game.data.gameState, + player.keyIndex + ) + + const instructions = await ixReturnsToIxs(ix, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/act/unload-all-cargo.ts b/src/main/basedbot/lib/sage/act/unload-all-cargo.ts new file mode 100644 index 00000000..ff6be8ec --- /dev/null +++ b/src/main/basedbot/lib/sage/act/unload-all-cargo.ts @@ -0,0 +1,71 @@ +import { PublicKey } from '@solana/web3.js' +import { createAssociatedTokenAccountIdempotent, getParsedTokenAccountsByOwner, InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { Coordinates } from '../../util/coordinates' +import { unloadCargoIx } from '../ix/unload-cargo' +import { getCargoType } from '../state/cargo-types' +import { starbaseByCoordinates } from '../state/starbase-by-coordinates' +import { getCargoPodsForStarbasePlayer, getStarbasePlayer } from '../state/starbase-player' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const unloadAllCargo = async ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player, + hold: PublicKey + // eslint-disable-next-line max-params +): Promise => { + const starbase = await starbaseByCoordinates(coordinates) + + if (!starbase) { + throw new Error(`No starbase found at ${coordinates}`) + } + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + const cargoPodTo = await getCargoPodsForStarbasePlayer(starbasePlayer, programs) + + const fleetTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + hold + ) + + const tokenAddresses: string[] = [] + const withdrawInstructions: InstructionReturn[] = [] + + for (let i = 0; i < fleetTokenAccounts.length; i++) { + const fleetTokenAccount = fleetTokenAccounts[i] + const tokenToResult = createAssociatedTokenAccountIdempotent( + fleetTokenAccount.mint, + (await getCargoPodsForStarbasePlayer(starbasePlayer, programs)).key, + true + ) + + if (!tokenAddresses.includes(tokenToResult.address.toBase58())) { + tokenAddresses.push(tokenToResult.address.toBase58()) + withdrawInstructions.push(tokenToResult.instructions) + } + + const cargoType = getCargoType(player.cargoTypes, player.game, fleetTokenAccount.mint) + + withdrawInstructions.push(unloadCargoIx( + fleetInfo, + player, + starbase, + starbasePlayer, + fleetInfo.fleet.data.cargoHold, + cargoPodTo.key, + fleetTokenAccount.address, + tokenToResult.address, + fleetTokenAccount.mint, + cargoType.key, + programs, + new BN(fleetTokenAccount.delegatedAmount.toString()))) + } + const instructions = await ixReturnsToIxs(withdrawInstructions, player.signer) + + await sendAndConfirmInstructions(instructions) +} diff --git a/src/main/basedbot/lib/sage/ix/dock.ts b/src/main/basedbot/lib/sage/ix/dock.ts new file mode 100644 index 00000000..9d0b5fa4 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/dock.ts @@ -0,0 +1,25 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, Starbase, StarbasePlayer } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const dockIx = ( + fleetInfo: FleetInfo, + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.idleToLoadingBay( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + starbase.key, + starbasePlayer.key, + player.game.key, + player.game.data.gameState, + player.keyIndex +) diff --git a/src/main/basedbot/lib/sage/ix/exit-respawn.ts b/src/main/basedbot/lib/sage/ix/exit-respawn.ts new file mode 100644 index 00000000..290e4114 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/exit-respawn.ts @@ -0,0 +1,30 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, Starbase, StarbasePlayer } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const exitRespawnIx = ( + fleetInfo: FleetInfo, + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.respawnToLoadingBay( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + starbase.key, + starbasePlayer.key, + fleetInfo.fleet.data.cargoHold, + fleetInfo.fleet.data.fuelTank, + fleetInfo.fleet.data.ammoBank, + player.game.key, + player.game.data.gameState, + { + keyIndex: 0 + } +) diff --git a/src/main/basedbot/lib/sage/ix/fleet-state-handler.ts b/src/main/basedbot/lib/sage/ix/fleet-state-handler.ts new file mode 100644 index 00000000..18a4fba6 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/fleet-state-handler.ts @@ -0,0 +1,43 @@ +import { PublicKey } from '@solana/web3.js' +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { getCargoType } from '../state/cargo-types' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' +import { Mineable } from '../state/world-map' + +export const miningHandlerIx = ( + fleetInfo: FleetInfo, + player: Player, + mineable: Mineable, + foodTokenFrom: PublicKey, + ammoTokenFrom: PublicKey, + resourceTokenFrom: PublicKey, + resourceTokenTo: PublicKey, + programs: StarAtlasPrograms + // eslint-disable-next-line max-params +): InstructionReturn => Fleet.asteroidMiningHandler( + programs.sage, + programs.cargo, + fleetInfo.fleet.key, + mineable.starbase.key, + mineable.mineItem.key, + mineable.resource.key, + mineable.planet.key, + fleetInfo.fleet.data.cargoHold, + fleetInfo.fleet.data.ammoBank, + player.foodCargoType.key, + player.ammoCargoType.key, + getCargoType(player.cargoTypes, player.game, mineable.mineItem.data.mint).key, + player.game.data.cargo.statsDefinition, + player.game.data.gameState, + player.game.key, + foodTokenFrom, + ammoTokenFrom, + resourceTokenFrom, + resourceTokenTo, + player.game.data.mints.food, + player.game.data.mints.ammo +) diff --git a/src/main/basedbot/lib/sage/ix/load-cargo.ts b/src/main/basedbot/lib/sage/ix/load-cargo.ts new file mode 100644 index 00000000..3b9c5537 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/load-cargo.ts @@ -0,0 +1,47 @@ +import { PublicKey } from '@solana/web3.js' +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, Starbase, StarbasePlayer } from '@staratlas/sage' +import BN from 'bn.js' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const loadCargoIx = ( + fleetInfo: FleetInfo, + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + cargoPodFrom: PublicKey, + cargoPodTo: PublicKey, + tokenFrom: PublicKey, + tokenTo: PublicKey, + tokenMint: PublicKey, + cargoType: PublicKey, + programs: StarAtlasPrograms, + amount: BN + // eslint-disable-next-line max-params +): InstructionReturn => Fleet.depositCargoToFleet( + programs.sage, + programs.cargo, + player.signer, + player.profile.key, + player.profileFaction.key, + 'funder', + starbase.key, + starbasePlayer.key, + fleetInfo.fleet.key, + cargoPodFrom, + cargoPodTo, + cargoType, + player.game.data.cargo.statsDefinition, + tokenFrom, + tokenTo, + tokenMint, + player.game.key, + player.game.data.gameState, + { + amount, + keyIndex: 0 + } +) diff --git a/src/main/basedbot/lib/sage/ix/start-mining.ts b/src/main/basedbot/lib/sage/ix/start-mining.ts new file mode 100644 index 00000000..09ba7ef0 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/start-mining.ts @@ -0,0 +1,33 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, StarbasePlayer } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' +import { Mineable } from '../state/world-map' + +export const startMiningIx = ( + fleetInfo: FleetInfo, + player: Player, + mineable: Mineable, + starbasePlayer: StarbasePlayer, + programs: StarAtlasPrograms + // eslint-disable-next-line max-params +): InstructionReturn => Fleet.startMiningAsteroid( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + mineable.starbase.key, + starbasePlayer.key, + mineable.mineItem.key, + mineable.resource.key, + mineable.planet.key, + player.game.data.gameState, + player.game.key, + fleetInfo.fuelTokenAccount, + { + keyIndex: player.keyIndex + } +) diff --git a/src/main/basedbot/lib/sage/ix/stop-mining.ts b/src/main/basedbot/lib/sage/ix/stop-mining.ts new file mode 100644 index 00000000..669a1eb2 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/stop-mining.ts @@ -0,0 +1,45 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' +import { Mineable } from '../state/world-map' + +export const stopMiningIx = ( + fleetInfo: FleetInfo, + player: Player, + mineable: Mineable, + programs: StarAtlasPrograms + // eslint-disable-next-line max-params +): InstructionReturn => Fleet.stopMiningAsteroid( + programs.sage, + programs.cargo, + programs.points, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + mineable.mineItem.key, + mineable.resource.key, + mineable.planet.key, + fleetInfo.fleet.data.fuelTank, + player.fuelCargoType.key, + player.game.data.cargo.statsDefinition, + player.xpAccounts.mining.userPointsAccount, + player.xpAccounts.mining.pointsCategory, + player.xpAccounts.mining.pointsModifierAccount, + player.xpAccounts.piloting.userPointsAccount, + player.xpAccounts.piloting.pointsCategory, + player.xpAccounts.piloting.pointsModifierAccount, + player.xpAccounts.councilRank.userPointsAccount, + player.xpAccounts.councilRank.pointsCategory, + player.xpAccounts.councilRank.pointsModifierAccount, + player.game.data.gameState, + player.game.key, + fleetInfo.fuelTokenAccount, + player.game.data.mints.fuel, + { + keyIndex: player.keyIndex + } +) diff --git a/src/main/basedbot/lib/sage/ix/stop-subwarp.ts b/src/main/basedbot/lib/sage/ix/stop-subwarp.ts new file mode 100644 index 00000000..5e514002 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/stop-subwarp.ts @@ -0,0 +1,30 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const stopSubWarpIx = ( + fleetInfo: FleetInfo, + player: Player, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.movementSubwarpHandler( + programs.sage, + programs.cargo, + programs.points, + player.profile.key, + fleetInfo.fleet.key, + fleetInfo.fleet.data.fuelTank, + player.fuelCargoType.key, + player.game.data.cargo.statsDefinition, + fleetInfo.fuelTokenAccount, + player.game.data.mints.fuel, + player.xpAccounts.piloting.userPointsAccount, + player.xpAccounts.piloting.pointsCategory, + player.xpAccounts.piloting.pointsModifierAccount, + player.xpAccounts.councilRank.userPointsAccount, + player.xpAccounts.councilRank.pointsCategory, + player.xpAccounts.councilRank.pointsModifierAccount, + player.game.key +) diff --git a/src/main/basedbot/lib/sage/ix/stop-warp.ts b/src/main/basedbot/lib/sage/ix/stop-warp.ts new file mode 100644 index 00000000..d8e1d1e5 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/stop-warp.ts @@ -0,0 +1,24 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const stopWarpIx = ( + fleetInfo: FleetInfo, + player: Player, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.moveWarpHandler( + programs.sage, + programs.points, + player.profile.key, + fleetInfo.fleet.key, + player.xpAccounts.piloting.userPointsAccount, + player.xpAccounts.piloting.pointsCategory, + player.xpAccounts.piloting.pointsModifierAccount, + player.xpAccounts.councilRank.userPointsAccount, + player.xpAccounts.councilRank.pointsCategory, + player.xpAccounts.councilRank.pointsModifierAccount, + player.game.key +) diff --git a/src/main/basedbot/lib/sage/ix/subwarp.ts b/src/main/basedbot/lib/sage/ix/subwarp.ts new file mode 100644 index 00000000..d281d70b --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/subwarp.ts @@ -0,0 +1,26 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Coordinates } from '../../util/coordinates' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const subWarpIx = ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.startSubwarp( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + player.game.key, + player.game.data.gameState, + { + toSector: coordinates.toArray(), + keyIndex: player.keyIndex, + } +) diff --git a/src/main/basedbot/lib/sage/ix/undock.ts b/src/main/basedbot/lib/sage/ix/undock.ts new file mode 100644 index 00000000..29eafac6 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/undock.ts @@ -0,0 +1,25 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, Starbase, StarbasePlayer } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const undockIx = ( + fleetInfo: FleetInfo, + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.loadingBayToIdle( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + starbase.key, + starbasePlayer.key, + player.game.key, + player.game.data.gameState, + player.keyIndex +) diff --git a/src/main/basedbot/lib/sage/ix/unload-cargo.ts b/src/main/basedbot/lib/sage/ix/unload-cargo.ts new file mode 100644 index 00000000..468cb7e8 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/unload-cargo.ts @@ -0,0 +1,47 @@ +import { PublicKey } from '@solana/web3.js' +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet, Starbase, StarbasePlayer } from '@staratlas/sage' +import BN from 'bn.js' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const unloadCargoIx = ( + fleetInfo: FleetInfo, + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + cargoPodFrom: PublicKey, + cargoPodTo: PublicKey, + tokenFrom: PublicKey, + tokenTo: PublicKey, + tokenMint: PublicKey, + cargoType: PublicKey, + programs: StarAtlasPrograms, + amount: BN + // eslint-disable-next-line max-params +): InstructionReturn => Fleet.withdrawCargoFromFleet( + programs.sage, + programs.cargo, + player.signer, + 'funder', + player.profile.key, + player.profileFaction.key, + starbase.key, + starbasePlayer.key, + fleetInfo.fleet.key, + cargoPodFrom, + cargoPodTo, + cargoType, + player.game.data.cargo.statsDefinition, + tokenFrom, + tokenTo, + tokenMint, + player.game.key, + player.game.data.gameState, + { + amount, + keyIndex: 0 + } +) diff --git a/src/main/basedbot/lib/sage/ix/warp.ts b/src/main/basedbot/lib/sage/ix/warp.ts new file mode 100644 index 00000000..1fd9d32b --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/warp.ts @@ -0,0 +1,32 @@ +import { InstructionReturn } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' + +import { StarAtlasPrograms } from '../../programs' +import { Coordinates } from '../../util/coordinates' +import { Player } from '../state/user-account' +import { FleetInfo } from '../state/user-fleets' + +export const warpIx = ( + fleetInfo: FleetInfo, + coordinates: Coordinates, + player: Player, + programs: StarAtlasPrograms +): InstructionReturn => Fleet.warpToCoordinate( + programs.sage, + player.signer, + player.profile.key, + player.profileFaction.key, + fleetInfo.fleet.key, + fleetInfo.fleet.data.fuelTank, + player.fuelCargoType.key, + player.game.data.cargo.statsDefinition, + fleetInfo.fuelTokenAccount, + player.game.data.mints.fuel, + player.game.data.gameState, + player.game.key, + programs.cargo, + { + toSector: coordinates.toArray(), + keyIndex: player.keyIndex, + } +) diff --git a/src/main/basedbot/lib/sage/state/cargo-types.ts b/src/main/basedbot/lib/sage/state/cargo-types.ts new file mode 100644 index 00000000..acc1dcaf --- /dev/null +++ b/src/main/basedbot/lib/sage/state/cargo-types.ts @@ -0,0 +1,31 @@ +import { PublicKey } from '@solana/web3.js' +import { CargoType } from '@staratlas/cargo' +import { readAllFromRPC } from '@staratlas/data-source' +import { Game } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const getCargoTypes = async () : Promise> => { + const cargoTypesAccountData = + await readAllFromRPC( + connection, + programs.cargo, + CargoType + ) + + return cargoTypesAccountData + .filter(f => f.type === 'ok' && 'data' in f) + .map(f => (f as any).data) +} + +export const getCargoType = (cargoTypes: Array, game: Game, mint: PublicKey) : CargoType => { + const cargoType = cargoTypes.find(ct => + game.data.cargo.statsDefinition.equals(ct.data.statsDefinition) && + mint.equals(ct.data.mint) + ) + + if(!cargoType) {throw new Error(`Cargo type not found for mint ${mint}.`)} + + return cargoType +} diff --git a/src/main/basedbot/lib/sage/state/fleet-cargo.ts b/src/main/basedbot/lib/sage/state/fleet-cargo.ts new file mode 100644 index 00000000..b9ba5c5c --- /dev/null +++ b/src/main/basedbot/lib/sage/state/fleet-cargo.ts @@ -0,0 +1,124 @@ +import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import { createAssociatedTokenAccountIdempotent, ixReturnsToIxs } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' +import BN from 'bn.js' +import bs58 from 'bs58' + +import { logger } from '../../../../../logger' +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' + +import { Player } from './user-account' + +type PartialTokenAccount = { + amount: number | null + tokenAccount: string + mint: string + owner: string + delegate: string | null + delegatedAmount: number +}; + +const getTokenAccountForKey = async ( + key: PublicKey +): Promise => { + const tokenAccounts = await connection.getTokenAccountsByOwner(key, { + programId: TOKEN_PROGRAM_ID + }) + + const result: PartialTokenAccount[] = [] + /** Account data format: + * mint: Pubkey + * owner: Pubkey + * amount: u64 + * delegate: Coption + * state: AccountState + * is_native: Coption + * delegated_amount: u64 + * close_authority: Coption + * Options are 4 byte paddings: + * Found by Jerry: + * https://github.com/solana-labs/solana-program-library/blob/6ed7254d1a578ffbc2b091d28cb92b25e7cc511d/token/js/src/state/account.ts#L69 + */ + + for (const tokenAccount of tokenAccounts.value) { + const accountKey = tokenAccount.pubkey.toBase58() + const accountData = tokenAccount.account.data + + result.push({ + amount: new BN(accountData.slice(64, 72), 'le').toNumber(), + mint: bs58.encode(accountData.slice(0, 32)), + owner: bs58.encode(accountData.slice(32, 64)), + tokenAccount: accountKey, + delegate: bs58.encode(accountData.slice(76, 108)), + delegatedAmount: new BN(accountData.slice(121, 129), 'le').toNumber() + }) + } + + return result +} + +const getBalance = async (mint: PublicKey, bank: PublicKey, player: Player): Promise => { + const tokenAccount = getAssociatedTokenAddressSync( + mint, + bank, + true + ) + + try { + const balance = await connection.getTokenAccountBalance(tokenAccount) + + return balance.value.uiAmount ?? 0 + } + catch (e) { + if ((e as Error).message.includes('could not find account')) { + logger.warn(`No balance found for ${mint.toBase58()} at ${bank.toBase58()} creating new account`) + const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( + mint, + bank, + true, + ) + + await sendAndConfirmInstructions(await ixReturnsToIxs(fleetFuelTokenResult.instructions, player.signer)) + + return 0 + } + } + + return 0 +} + +const getFleetCargoBalances = async (fleet: Fleet): Promise> => { + const cargoHoldBalances = await getTokenAccountForKey( + new PublicKey(fleet.data.cargoHold) + ) + + return new Map( + cargoHoldBalances.map(tokenAccount => [tokenAccount.mint, tokenAccount.delegatedAmount]) + ) +} + +export type FleetCargo = { + ammo: number + cargo: Map + food: number + fuel: number + toolkit: number +} + +export const getFleetCargoBalance = async (fleet: Fleet, player: Player): Promise => { + const [ammo, fuel, cargo] = await Promise.all([ + getBalance(player.game.data.mints.ammo, fleet.data.ammoBank, player), + getBalance(player.game.data.mints.fuel, fleet.data.fuelTank, player), + getFleetCargoBalances(fleet) + ]) + + return { + ammo, + cargo, + food: cargo.get(player.game.data.mints.food.toBase58()) ?? 0, + fuel, + toolkit: cargo.get(player.game.data.mints.repairKit.toBase58()) ?? 0 + } +} diff --git a/src/main/basedbot/lib/sage/state/game.ts b/src/main/basedbot/lib/sage/state/game.ts new file mode 100644 index 00000000..a54eefce --- /dev/null +++ b/src/main/basedbot/lib/sage/state/game.ts @@ -0,0 +1,18 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Game } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const sageGame = async (): Promise => { + const [game] = await readAllFromRPC( + connection, + programs.sage as any, + Game, + 'processed', + []) + + if (game.type === 'error') {throw new Error('Error reading game account')} + + return game.data +} diff --git a/src/main/basedbot/lib/sage/state/mine-items.ts b/src/main/basedbot/lib/sage/state/mine-items.ts new file mode 100644 index 00000000..6a086f7d --- /dev/null +++ b/src/main/basedbot/lib/sage/state/mine-items.ts @@ -0,0 +1,23 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Game, MineItem } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const getMineItems = async (game: Game): Promise> => { + const mineItems = await readAllFromRPC( + connection, + programs.sage as any, + MineItem, + 'processed', + [{ + memcmp: { + offset: 8 + 1, + bytes: game.key.toBase58(), + }, + }]) + + return mineItems + .filter(p => p.type === 'ok' && 'data' in p) + .map(p => (p as any).data) +} diff --git a/src/main/basedbot/lib/sage/state/planet-by-key.ts b/src/main/basedbot/lib/sage/state/planet-by-key.ts new file mode 100644 index 00000000..5b6e289b --- /dev/null +++ b/src/main/basedbot/lib/sage/state/planet-by-key.ts @@ -0,0 +1,22 @@ +import { PublicKey } from '@solana/web3.js' +import { readFromRPC } from '@staratlas/data-source' +import { Planet } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const planetByKey = async (key: PublicKey): Promise => { + const planet = await readFromRPC( + connection, + programs.sage as any, + key, + Planet, + 'processed' + ) + + if (!planet) {throw new Error('no planet found')} + + if (planet.type === 'error') {throw new Error('Error reading planet account')} + + return planet.data +} diff --git a/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts b/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts new file mode 100644 index 00000000..be8193b8 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts @@ -0,0 +1,30 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Planet } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { Coordinates } from '../../util/coordinates' +import { programs } from '../../programs' + +export const planetsByCoordinates = async (coordinates: Coordinates): Promise> => { + const planets = await readAllFromRPC( + connection, + programs.sage as any, + Planet, + 'processed', + [{ + memcmp: { + offset: 105, + bytes: coordinates.xB58 + } + }, + { + memcmp: { + offset: 113, + bytes: coordinates.yB58 + } + }]) + + return planets + .filter(p => p.type === 'ok' && 'data' in p) + .map(p => (p as any).data) +} diff --git a/src/main/basedbot/lib/sage/state/planets.ts b/src/main/basedbot/lib/sage/state/planets.ts new file mode 100644 index 00000000..a8a7da6d --- /dev/null +++ b/src/main/basedbot/lib/sage/state/planets.ts @@ -0,0 +1,23 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Game, Planet } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const getPlanets = async (game: Game): Promise> => { + const planets = await readAllFromRPC( + connection, + programs.sage as any, + Planet, + 'processed', + [{ + memcmp: { + offset: 8 + 1 + 64, + bytes: game.key.toBase58(), + }, + }]) + + return planets + .filter(p => p.type === 'ok' && 'data' in p) + .map(p => (p as any).data) +} diff --git a/src/main/basedbot/lib/sage/state/resources.ts b/src/main/basedbot/lib/sage/state/resources.ts new file mode 100644 index 00000000..acecc07e --- /dev/null +++ b/src/main/basedbot/lib/sage/state/resources.ts @@ -0,0 +1,23 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Game, Resource } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const getResources = async (game: Game): Promise> => { + const resources = await readAllFromRPC( + connection, + programs.sage as any, + Resource, + 'processed', + [{ + memcmp: { + offset: 8 + 1, + bytes: game.key.toBase58(), + }, + }]) + + return resources + .filter(p => p.type === 'ok' && 'data' in p) + .map(p => (p as any).data) +} diff --git a/src/main/basedbot/lib/sage/state/settle-fleet.ts b/src/main/basedbot/lib/sage/state/settle-fleet.ts new file mode 100644 index 00000000..7dcf6ce6 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/settle-fleet.ts @@ -0,0 +1,52 @@ +import { now } from '../../../../../dayjs' +import { logger } from '../../../../../logger' +import { Coordinates } from '../../util/coordinates' +import { endMine } from '../act/end-mine' +import { endMove } from '../act/end-move' +import { exitRespawn } from '../act/exit-respawn' + +import { Player } from './user-account' +import { FleetInfo } from './user-fleets' +import { mineableByCoordinates, WorldMap } from './world-map' + +export const settleFleet = async (fleetInfo: FleetInfo, player: Player, map: WorldMap): Promise => { + switch (fleetInfo.fleetState.type) { + case 'MoveWarp': { + const { warpFinish } = fleetInfo.fleetState.data + + if (warpFinish.isBefore(now())) { + await endMove(fleetInfo, player) + } + break + } + case 'MoveSubwarp': { + const { arrivalTime } = fleetInfo.fleetState.data + + if (arrivalTime.isBefore(now())) { + await endMove(fleetInfo, player) + } + break + } + case 'MineAsteroid': { + const { end } = fleetInfo.fleetState.data + + if (end.isBefore(now())) { + const [mineable] = Array.from(mineableByCoordinates(map, fleetInfo.location)) + + await endMine(fleetInfo, player, mineable) + } + break + } + case 'Respawn': { + const { ETA } = fleetInfo.fleetState.data + + if (ETA.isBefore(now())) { + // TODO: Respawn at Home Base based on Faction + await exitRespawn(fleetInfo, Coordinates.fromNumber(-40, 30), player) + } + break + } + default: + logger.info(`Fleet: ${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`) + } +} diff --git a/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts b/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts new file mode 100644 index 00000000..73dffd00 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts @@ -0,0 +1,17 @@ +import { logger } from '../../../../../logger' +import { getName } from '../util' + +import { FleetInfo } from './user-fleets' + +export const showFleetCargoInfo = (fleetInfo: FleetInfo): void => { + const { ammo, cargo, food, fuel, toolkit } = fleetInfo.cargoLevels + + logger.info(`Fleet: ${fleetInfo.fleetName} cargo levels:`) + logger.info(`Ammo: ${ammo}`) + logger.info(`Food: ${food}`) + logger.info(`Fuel: ${fuel}`) + logger.info(`Toolkit: ${toolkit}`) + for (const [mint, amount] of cargo.entries()) { + logger.info(`${getName(mint)}: ${amount}`) + } +} diff --git a/src/main/basedbot/lib/sage/state/show-fleet-info.ts b/src/main/basedbot/lib/sage/state/show-fleet-info.ts new file mode 100644 index 00000000..bce5a0eb --- /dev/null +++ b/src/main/basedbot/lib/sage/state/show-fleet-info.ts @@ -0,0 +1,59 @@ +import dayjs, { now } from '../../../../../dayjs' +import { logger } from '../../../../../logger' +import { getName } from '../util' + +import { planetsByCoordinates } from './planets-by-coordinates' +import { starbaseByCoordinates } from './starbase-by-coordinates' +import { FleetInfo } from './user-fleets' + +export const showFleetInfo = async (fleetInfo: FleetInfo): Promise => { + switch (fleetInfo.fleetState.type) { + case 'Idle': { + const baseStation = await starbaseByCoordinates(fleetInfo.location) + const planets = await planetsByCoordinates(fleetInfo.location) + + logger.info(`${fleetInfo.fleetName} is idle at ${fleetInfo.fleetState.data.sector} [BaseStation: ${baseStation ? getName(baseStation) : 'N/A'} / Planets: ${planets.length}]`) + break + } + case 'StarbaseLoadingBay': + logger.info(`Fleet: ${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`) + break + case 'MoveWarp': { + const { fromSector, toSector, warpFinish } = fleetInfo.fleetState.data + + if (warpFinish.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MoveSubwarp': { + const { fromSector, toSector, arrivalTime } = fleetInfo.fleetState.data + + if (arrivalTime.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`) + } + else { + logger.info(`${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`) + } + break + } + case 'MineAsteroid': { + const { mineItem, end, amountMined, endReason } = fleetInfo.fleetState.data + + if (end.isBefore(now())) { + logger.info(`Fleet: ${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`) + } + else { + const log = endReason ==='FULL' ? logger.info : logger.warn + + log(`Fleet: ${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`) + } + break + } + default: + logger.info(`Fleet: ${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`) + } +} diff --git a/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts b/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts new file mode 100644 index 00000000..bb1e8046 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts @@ -0,0 +1,37 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Starbase } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { Coordinates } from '../../util/coordinates' +import { programs } from '../../programs' + +export const starbaseByCoordinates = async (coordinates: Coordinates) : Promise => { + const [starbase] = await readAllFromRPC( + connection, + programs.sage as any, + Starbase, + 'processed', + [ + { + memcmp: { + offset: 41, + bytes: coordinates.xB58 + } + }, + { + memcmp: { + offset: 49, + bytes: coordinates.yB58 + } + } + ] + ) + + if (!starbase) { + return null + } + + if (starbase.type === 'error') {throw new Error('Error reading starbase account')} + + return starbase.data +} diff --git a/src/main/basedbot/lib/sage/state/starbase-by-key.ts b/src/main/basedbot/lib/sage/state/starbase-by-key.ts new file mode 100644 index 00000000..ce1c0099 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/starbase-by-key.ts @@ -0,0 +1,22 @@ +import { PublicKey } from '@solana/web3.js' +import { readFromRPC } from '@staratlas/data-source' +import { Starbase } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const starbaseByKey = async (key: PublicKey): Promise => { + const starbase = await readFromRPC( + connection, + programs.sage as any, + key, + Starbase, + 'processed' + ) + + if (!starbase) {throw new Error('no starbase found')} + + if (starbase.type === 'error') {throw new Error('Error reading starbase account')} + + return starbase.data +} diff --git a/src/main/basedbot/lib/sage/state/starbase-player.ts b/src/main/basedbot/lib/sage/state/starbase-player.ts new file mode 100644 index 00000000..0a9e9a07 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/starbase-player.ts @@ -0,0 +1,112 @@ +import { Keypair } from '@solana/web3.js' +import { CargoPod } from '@staratlas/cargo' +import { ixReturnsToIxs, readAllFromRPC } from '@staratlas/data-source' +import { SagePlayerProfile, Starbase, StarbasePlayer } from '@staratlas/sage' + +import { logger } from '../../../../../logger' +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { StarAtlasPrograms } from '../../programs' + +import { Player } from './user-account' + +export const getCargoPodsForStarbasePlayer = + async (starbasePlayer: StarbasePlayer, programs: StarAtlasPrograms): Promise => { + const [cargoPod] = await readAllFromRPC( + connection, + programs.cargo, + CargoPod, + 'processed', + [ + { + memcmp: { + offset: 8 + 1 + 32, + bytes: starbasePlayer.key.toBase58() + } + } + ] + ) + + if (!cargoPod) { + throw new Error('Error reading cargo pods') + } + + if (cargoPod.type === 'error') {throw new Error('Error reading cargoPods account')} + + return cargoPod.data + } + +export const getStarbasePlayer = + async (player: Player, starbase: Starbase, programs: StarAtlasPrograms): Promise => { + const [starbasePlayer] = await readAllFromRPC( + connection, + programs.sage, + StarbasePlayer, + 'processed', + [ + { + memcmp: { + offset: 9, + bytes: player.profile.key.toBase58() + } + }, + { + memcmp: { + offset: 73, + bytes: starbase.key.toBase58() + } + } + ] + ) + + if (!starbasePlayer) { + const [sageProfileAddress] = SagePlayerProfile.findAddress( + programs.sage, + player.profile.key, + player.game.key + ) + const [starbasePlayerAddress] = StarbasePlayer.findAddress( + programs.sage, + starbase.key, + sageProfileAddress, + starbase.data.seqId + ) + + const instructionReturns = [ + StarbasePlayer.registerStarbasePlayer( + programs.sage, + player.profileFaction.key, + sageProfileAddress, + starbase.key, + player.game.key, + player.game.data.gameState, + starbase.data.seqId + ), + StarbasePlayer.createCargoPod( + programs.sage, + programs.cargo, + starbasePlayerAddress, + player.signer, + player.profile.key, + player.profileFaction.key, + starbase.key, + player.game.data.cargo.statsDefinition, + player.game.key, + player.game.data.gameState, + { + keyIndex: 0, + podSeeds: Array.from(Keypair.generate().publicKey.toBuffer()) + } + ) + ] + + logger.warn('Starbase player not found, creating', { player: player.profile.key.toBase58(), starbase: starbase.key.toBase58() }) + await sendAndConfirmInstructions(await ixReturnsToIxs(instructionReturns, player.signer)) + + return getStarbasePlayer(player, starbase, programs) + } + + if (starbasePlayer.type === 'error') {throw new Error('Error reading starbasePlayer account')} + + return starbasePlayer.data + } diff --git a/src/main/basedbot/lib/sage/state/starbases.ts b/src/main/basedbot/lib/sage/state/starbases.ts new file mode 100644 index 00000000..7ee4de31 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/starbases.ts @@ -0,0 +1,23 @@ +import { readAllFromRPC } from '@staratlas/data-source' +import { Game, Starbase } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs } from '../../programs' + +export const getStarbases = async (game: Game): Promise> => { + const starbases = await readAllFromRPC( + connection, + programs.sage as any, + Starbase, + 'processed', + [{ + memcmp: { + offset: 8 + 1, + bytes: game.key.toBase58(), + }, + }]) + + return starbases + .filter(p => p.type === 'ok' && 'data' in p) + .map(p => (p as any).data) +} diff --git a/src/main/basedbot/lib/sage/state/user-account.ts b/src/main/basedbot/lib/sage/state/user-account.ts new file mode 100644 index 00000000..b6fd21bd --- /dev/null +++ b/src/main/basedbot/lib/sage/state/user-account.ts @@ -0,0 +1,146 @@ +import { Keypair, PublicKey } from '@solana/web3.js' +import { CargoType } from '@staratlas/cargo' +import { AsyncSigner, keypairToAsyncSigner, readAllFromRPC } from '@staratlas/data-source' +import { PlayerProfile } from '@staratlas/player-profile' +import { PointsModifier } from '@staratlas/points' +import { ProfileFactionAccount } from '@staratlas/profile-faction' +import { Game } from '@staratlas/sage' + +import { connection } from '../../../../../service/sol' +import { programs, xpCategoryIds } from '../../programs' + +import { getCargoType, getCargoTypes } from './cargo-types' +import { sageGame } from './game' + +export type XpAccounts = { + councilRank: XpAccount + dataRunning: XpAccount + piloting: XpAccount + mining: XpAccount + crafting: XpAccount +} + +export type XpAccount = { + userPointsAccount: PublicKey + pointsCategory: PublicKey + pointsModifierAccount: PublicKey +} + +export type Player = { + // TODO: what is this? + keyIndex: number + profile: PlayerProfile + profileFaction: ProfileFactionAccount + xpAccounts: XpAccounts + signer: AsyncSigner + game: Game + cargoTypes: Array + fuelCargoType: CargoType + foodCargoType: CargoType + ammoCargoType: CargoType +} + +const getXpAccount = async (playerProfile: PublicKey, xpCategory: PublicKey): Promise => { + const [userXpAccount] = PublicKey.findProgramAddressSync( + [ + Buffer.from('UserPointsAccount'), + xpCategory.toBuffer(), + playerProfile.toBuffer() + ], + programs.points.programId + ) + + const [pointsModifierAccount] = await readAllFromRPC( + connection, + programs.points as any, + PointsModifier, + 'processed', + [ + { + memcmp: { + offset: 9, + bytes: xpCategory.toBase58() + } + }, + ]) + + if (pointsModifierAccount.type === 'error') {throw new Error('Error reading points modifier account')} + + return { + userPointsAccount: userXpAccount, + pointsModifierAccount: pointsModifierAccount.key, + pointsCategory: xpCategory + } +} + +const getKeyIndex = (_: PlayerProfile): number => 0 + +export const getPlayerContext = async (user: PublicKey, signer: Keypair): Promise => { + const myProfiles = await readAllFromRPC( + connection, + programs.playerProfile as any, + PlayerProfile, + 'processed', + [ + { + memcmp: { + offset: PlayerProfile.MIN_DATA_SIZE + 2, + bytes: user.toBase58() + } + } + ] + ) + + // TODO: only support one profile for now + + const [profile] = myProfiles + + if (!profile) { + throw new Error('no player profile found') + } + + if (profile.type === 'error') {throw new Error('Error reading account')} + + const keyIndex = getKeyIndex(profile.data) + + const [profileFaction] = await readAllFromRPC(connection, programs.profileFaction as any, ProfileFactionAccount, 'processed', [ + { + memcmp: { + offset: 9, + bytes: profile.key.toBase58() + } + } + ]) + + if (profileFaction.type === 'error') {throw new Error('Error reading faction account')} + + const xpAccounts = { + councilRank: await getXpAccount(profile.key, new PublicKey(xpCategoryIds.councilRankXpCategory)), + dataRunning: await getXpAccount(profile.key, new PublicKey(xpCategoryIds.dataRunningXpCategory)), + piloting: await getXpAccount(profile.key, new PublicKey(xpCategoryIds.pilotingXpCategory)), + mining: await getXpAccount(profile.key, new PublicKey(xpCategoryIds.miningXpCategory)), + crafting: await getXpAccount(profile.key, new PublicKey(xpCategoryIds.craftingXpCategory)) + } + + const game = await sageGame() + const cargoTypes = await getCargoTypes() + + return { + profile: profile.data, + profileFaction: profileFaction.data, + keyIndex, + xpAccounts, + signer: keypairToAsyncSigner(signer), + game, + cargoTypes, + fuelCargoType: getCargoType(cargoTypes, game, game.data.mints.fuel), + foodCargoType: getCargoType(cargoTypes, game, game.data.mints.food), + ammoCargoType: getCargoType(cargoTypes, game, game.data.mints.ammo) + } +} + +// export const getAccountName = (profileProgram: StarAtlasProgram, account: PlayerProfile): string => { +// const playerNameAddress = PlayerName.findAddress(profileProgram.program as any, account.key) +// const decodedPlayerName = PlayerName.decodeData({ +// accountInfo: account.accountInfo, accountId: playerNameAddress }, profileProgram.program as any) +// } diff --git a/src/main/basedbot/lib/sage/state/user-fleets.ts b/src/main/basedbot/lib/sage/state/user-fleets.ts new file mode 100644 index 00000000..74ae98b3 --- /dev/null +++ b/src/main/basedbot/lib/sage/state/user-fleets.ts @@ -0,0 +1,125 @@ +import { getOrCreateAssociatedTokenAccount } from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import { readAllFromRPC } from '@staratlas/data-source' +import { Fleet } from '@staratlas/sage' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { keyPair } from '../../../../../service/wallet' +import { FleetState } from '../../fleet-state/types' +import { Coordinates } from '../../util/coordinates' +import { getFleetState } from '../../fleet-state/fleet-state' +import { programs } from '../../programs' +import { getName } from '../util' + +import { FleetCargo, getFleetCargoBalance } from './fleet-cargo' +import { Player } from './user-account' +import { WorldMap } from './world-map' + +type ShipCounts = { + total: number + updated: number + xxs: number + xs: number + s: number + m: number + l: number + capital: number + commander: number + titan: number +} + +type MovementStats = { + subwarpSpeed: number + warpSpeed: number + maxWarpDistance: number + warpCooldown: number + subwarpFuelConsumptionRate: number + warpFuelConsumptionRate: number + planetExitFuelAmount: number +} + +type CargoStats = { + cargoCapacity: number + fuelCapacity: number + ammoCapacity: number + ammoConsumptionRate: number + foodConsumptionRate: number + miningRate: number + upgradeRate: number +} + +type MiscStats = { + crew: number + respawnTime: number + scanCooldown: number + scanRepairKitAmount: number +} + +export type FleetInfo = { + fleet: Fleet + location: Coordinates + fleetName: string + shipCounts: ShipCounts + fuelTokenAccount: PublicKey + warpCooldownExpiresAt: BN + scanCooldownExpiresAt: BN + movementStats: MovementStats + cargoStats: CargoStats + miscStats: MiscStats + fleetState: FleetState + cargoLevels: FleetCargo +} + +export const getUserFleets = async (player: Player): Promise> => { + const fleets = await readAllFromRPC( + connection, + programs.sage as any, + Fleet, + 'processed', + [ + { + memcmp: { + offset: 8 + 1 + 32, // 8 (discriminator) + 1 (version) + 32 (gameId) + bytes: player.profile.key.toBase58() + } + } + ] + ) + + return fleets.filter(f => f.type === 'ok' && 'data' in f).map(f => (f as any).data) +} + +export const getFleetInfo = async (fleet: Fleet, player: Player, map: WorldMap): Promise => { + const cargoLevels = await getFleetCargoBalance(fleet, player) + const fleetState = await getFleetState(fleet, map, cargoLevels) + const shipCounts = fleet.data.shipCounts as unknown as ShipCounts + const movementStats = fleet.data.stats.movementStats as unknown as MovementStats + const cargoStats = fleet.data.stats.cargoStats as unknown as CargoStats + const miscStats = fleet.data.stats.miscStats as unknown as MiscStats + const location = fleetState.data.sector + const fleetName = getName(fleet) + + const fuelTokenAccount = await getOrCreateAssociatedTokenAccount( + connection, + keyPair, + player.game.data.mints.fuel, + fleet.data.fuelTank, + true + ) + + return { + fleet, + location, + miscStats, + movementStats, + cargoStats, + fleetName, + fuelTokenAccount: fuelTokenAccount.address, + shipCounts, + fleetState, + warpCooldownExpiresAt: fleet.data.warpCooldownExpiresAt, + scanCooldownExpiresAt: fleet.data.scanCooldownExpiresAt, + cargoLevels, + } +} diff --git a/src/main/basedbot/lib/sage/state/world-map.ts b/src/main/basedbot/lib/sage/state/world-map.ts new file mode 100644 index 00000000..2242ad3b --- /dev/null +++ b/src/main/basedbot/lib/sage/state/world-map.ts @@ -0,0 +1,111 @@ +import { Game, MineItem, Planet, Resource, Starbase } from '@staratlas/sage' + +import { logger } from '../../../../../logger' +import { transformSector } from '../../fleet-state/transform/transform-sector' +import { Coordinates } from '../../util/coordinates' + +import { getMineItems } from './mine-items' +import { getPlanets } from './planets' +import { getResources } from './resources' +import { getStarbases } from './starbases' + +export type PlanetId = string +export type ResourceId = string +export type StarbaseId = string + +export type WorldMap = { + starbases: Array + planets: Map> + mineItems: Map + resources: Map> +} + +export type Mineable = { + starbase: Starbase + planet: Planet + resource: Resource + mineItem: MineItem +} + +export const planetsByStarbase = + (planets: Map>, starbase: Starbase): Set => + planets.get(starbase.key.toBase58()) ?? new Set() + +export const resourcesByPlanet = + (resources: Map>, planet: Planet): Set => + resources.get(planet.key.toBase58()) ?? new Set() + +export const mineItemByResource = + (mineItems: Map, resource: Resource): MineItem | undefined => + mineItems.get(resource.key.toBase58()) + +export const mineableByCoordinates = (map: WorldMap, coordinates: Coordinates): Set => { + const starbase = map.starbases.find(s => transformSector(s.data.sector).equals(coordinates)) + + if(!starbase) { + logger.warn(`No starbase found at ${coordinates}`) + + return new Set() + } + const planets = planetsByStarbase(map.planets, starbase) + const minables = new Set() + + planets.forEach((planet) => { + const resources = resourcesByPlanet(map.resources, planet) + + resources.forEach((resource) => { + const mineItem = mineItemByResource(map.mineItems, resource) + + if(mineItem) { + minables.add({ + starbase, + planet, + resource, + mineItem + }) + } + }) + }) + + return minables +} + +export const getMapContext = async (game: Game): Promise => { + const [starbases, pl, mI, res] = await Promise.all([ + getStarbases(game), + getPlanets(game), + getMineItems(game), + getResources(game) + ]) + + const planets = new Map>() + const resources = new Map>() + const mineItems = new Map() + + starbases.forEach((s) => { + const location = transformSector(s.data.sector) + const planetSet = planets.get(s.key.toBase58()) ?? new Set() + + pl.filter(p => transformSector(p.data.sector).equals(location)).forEach((p) => { + planetSet.add(p) + }) + + planets.set(s.key.toBase58(), planetSet) + }) + + res.forEach((r) => { + const resourceSet = resources.get(r.data.location.toBase58()) ?? new Set() + + mineItems.set(r.key.toBase58(), mI.find(m => m.key.equals(r.data.mineItem))!) + + resourceSet.add(r) + resources.set(r.data.location.toBase58(), resourceSet) + }) + + return { + starbases, + planets, + mineItems, + resources + } +} diff --git a/src/main/basedbot/lib/sage/util.ts b/src/main/basedbot/lib/sage/util.ts new file mode 100644 index 00000000..756bc4ea --- /dev/null +++ b/src/main/basedbot/lib/sage/util.ts @@ -0,0 +1,11 @@ +import { byteArrayToString } from '@staratlas/data-source' + +export const getName = (item: any): string => { + const data = item?.data?.name ?? item?.data?.fleetLabel + + return data ? byteArrayToString(data) : 'N/A' +} + +// export const getFleetName = (fleet: Fleet): string => byteArrayToString(fleet.data.fleetLabel) +// export const getStarbaseName = (starbase: Starbase): string => byteArrayToString(starbase.data.name) +// export const getPlanetName = (planet: Planet): string => byteArrayToString(planet.data.name) diff --git a/src/main/basedbot/lib/util/coordinates.ts b/src/main/basedbot/lib/util/coordinates.ts new file mode 100644 index 00000000..ea2a81a8 --- /dev/null +++ b/src/main/basedbot/lib/util/coordinates.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +/* eslint-disable no-underscore-dangle */ + +import BN from 'bn.js' +import bs58 from 'bs58' + +export class Coordinates { + private readonly _x: BN + private readonly _y: BN + + public static fromString = (str: string): Coordinates => { + const [x, y] = str.split(',') + + return new Coordinates(new BN(x, 10), new BN(y, 10)) + } + + public static fromBN = (x: BN, y: BN): Coordinates => new Coordinates(x, y) + + public static fromNumber = (x: number, y: number): Coordinates => new Coordinates(x, y) + + private constructor (x: BN | number, y: BN | number) { + this._x = typeof x === 'number' ? new BN(x, 10) : x + this._y = typeof y === 'number' ? new BN(y, 10) : y + // logger.debug('Coordinates', { x: this._x.toNumber(), y: this._y.toNumber() }) + } + + private static toB58 = (bn: BN): string => bs58.encode(bn.toTwos(64).toArrayLike(Buffer, 'le', 8)) + + get xBN (): BN { + return this._x + } + + get yBN (): BN { + return this._y + } + + get xB58 (): string { + return Coordinates.toB58(this._x) + } + + get yB58 (): string { + return Coordinates.toB58(this._y) + } + + get x (): number { + return this._x.toNumber() + } + + get y (): number { + return this._y.toNumber() + } + + public distanceFrom = (other: Coordinates): number => { + const x = this._x.sub(other._x) + const y = this._y.sub(other._y) + + return Math.sqrt(x.mul(x).add(y.mul(y)).toNumber()) + } + + public equals = (other: Coordinates): boolean => this._x.eq(other._x) && this._y.eq(other._y) + + public toString = (): string => `${this._x.toNumber()},${this._y.toNumber()}` + + public toArray = (): [BN, BN] => [this._x, this._y] +} + +/* eslint-enable @typescript-eslint/naming-convention */ +/* eslint-enable no-underscore-dangle */ diff --git a/src/service/sleep.ts b/src/service/sleep.ts new file mode 100644 index 00000000..b78a9934 --- /dev/null +++ b/src/service/sleep.ts @@ -0,0 +1,6 @@ +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const sleep = (ms: number) => + // eslint-disable-next-line promise/avoid-new + new Promise((resolve) => { + setTimeout(resolve, ms) + }) diff --git a/src/service/sol/anchor.ts b/src/service/sol/anchor.ts new file mode 100644 index 00000000..2835756a --- /dev/null +++ b/src/service/sol/anchor.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line import/named +import { AnchorProvider, Wallet } from '@coral-xyz/anchor' + +import { keyPair } from '../wallet' + +import { connection } from './const' + +export const anchorProvider = new AnchorProvider(connection, new Wallet(keyPair), {}) diff --git a/src/service/sol/const/connection.ts b/src/service/sol/const/connection.ts index 0fff534d..fa2ffc77 100644 --- a/src/service/sol/const/connection.ts +++ b/src/service/sol/const/connection.ts @@ -1,8 +1,15 @@ import { Connection } from '@solana/web3.js' import { config } from '../../../config' +import { fetchWithRetries } from '../undici-retry' export const connection = new Connection(config.sol.rpcEndpoint, { wsEndpoint: config.sol.wsEndpoint, commitment: 'confirmed', + fetch: ( + input: RequestInfo | URL, + init?: RequestInit + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + ): Promise => fetchWithRetries(input, init, 5) }) diff --git a/src/service/sol/priority-fee/compute-unit-instruction.ts b/src/service/sol/priority-fee/compute-unit-instruction.ts index 81fb60a1..7fc7865c 100644 --- a/src/service/sol/priority-fee/compute-unit-instruction.ts +++ b/src/service/sol/priority-fee/compute-unit-instruction.ts @@ -38,7 +38,7 @@ export const createComputeUnitInstruction = async (instructions: TransactionInstruction[]): Promise => { const units = await getSimulationUnits(instructions, keyPair.publicKey, []) - logger.info(`Esitmated Compute Units: ${units}`) + logger.debug(`Esitmated Compute Units: ${units}`) return ComputeBudgetProgram.setComputeUnitLimit({ units: units ? units + 500 : 200_000 }) } diff --git a/src/service/sol/priority-fee/priority-fee-instruction.ts b/src/service/sol/priority-fee/priority-fee-instruction.ts index af098e3f..29773bf4 100644 --- a/src/service/sol/priority-fee/priority-fee-instruction.ts +++ b/src/service/sol/priority-fee/priority-fee-instruction.ts @@ -8,7 +8,7 @@ export const createPriorityFeeInstruction = async (): Promise fee.prioritizationFee.valueOf())) - logger.info(`Estimated priority fee: ${maxPriorityFee}`) + logger.debug(`Estimated priority fee: ${maxPriorityFee}`) return ComputeBudgetProgram.setComputeUnitPrice({ microLamports: maxPriorityFee }) } diff --git a/src/service/sol/send-and-confirm-tx.ts b/src/service/sol/send-and-confirm-tx.ts index b34d6cf9..2667c515 100644 --- a/src/service/sol/send-and-confirm-tx.ts +++ b/src/service/sol/send-and-confirm-tx.ts @@ -22,13 +22,16 @@ type BlockhashWithExpiryBlockHeight = Readonly<{ const confirmTx = async (txId: string): Promise => { const res = await connection.getSignatureStatus(txId) - logger.debug(`Signature: ${txId} with status: ${JSON.stringify(res)}`) + // logger.debug(`Signature: ${txId} with status: ${JSON.stringify(res)}`) if (res?.value && 'confirmationStatus' in res.value) { if (res.value.confirmationStatus === 'finalized' || res.value.confirmationStatus === 'confirmed' || res.value.confirmationStatus === 'processed') { - logger.info(`Transaction ${res.value.confirmationStatus}: ${txId} with status: ${res.value.confirmationStatus}`) + const log = res.value.err ? logger.warn: logger.info - logger.info(`https://solscan.io/tx/${txId}`) + // log(`Transaction ${res.value.confirmationStatus}: ${txId} with status: ${res.value.confirmationStatus}`) + log(`Signature: ${txId} with status: ${JSON.stringify(res)}`) + + // logger.info(`https://solscan.io/tx/${txId}`) return txId } @@ -48,11 +51,17 @@ export const sendAndConfirmTx = async ( /* eslint-disable no-await-in-loop */ while (blockheight <= blockHash.lastValidBlockHeight) { try { - txId = await connection.sendRawTransaction(transaction.serialize()) + txId = await connection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }) } catch (e) { const message = (e as any).message as string + const logs = (e as any).logs as string[] + + logs.filter(log => log.includes('AnchorError')).forEach((log) => { + logger.error(log) + }) + if (message.includes('has already been processed') && txId) { await confirmTx(txId) diff --git a/src/service/sol/undici-retry.ts b/src/service/sol/undici-retry.ts new file mode 100644 index 00000000..846ab235 --- /dev/null +++ b/src/service/sol/undici-retry.ts @@ -0,0 +1,43 @@ +import { + Agent, + RequestInfo, + RequestInit, + Response, + fetch, + setGlobalDispatcher, +} from 'undici' + +setGlobalDispatcher(new Agent({ connections: 100 })) + +export const fetchWithRetries = async( + input: URL | RequestInfo, + init: RequestInit = {}, + retryAttempts = 0, +): Promise => { + let attempt = 0 + + init.headers ||= { 'Content-Type': 'application/json' } + + while (attempt < retryAttempts) { + try { + // eslint-disable-next-line no-await-in-loop + const response = await fetch(input, init) + + if (response.status === 502) { + console.log('Retrying due to 502') + attempt++ + // eslint-disable-next-line no-await-in-loop,promise/avoid-new,no-loop-func + await new Promise((resolve) => {setTimeout(resolve, 100 * attempt)}) + } + else { + return response + } + } + catch (e) { + console.log(`Retrying due to error ${e}`, e) + attempt++ + } + } + + throw new Error('Max retries reached') +} diff --git a/src/service/wallet/init-keypair.ts b/src/service/wallet/init-keypair.ts index 18393ba9..47d67146 100644 --- a/src/service/wallet/init-keypair.ts +++ b/src/service/wallet/init-keypair.ts @@ -1,26 +1,41 @@ -import { Keypair } from '@solana/web3.js' +import { Keypair, PublicKey } from '@solana/web3.js' import { mnemonicToSeedSync } from 'bip39' import { derivePath } from 'ed25519-hd-key' import { config } from '../../config' import { logger } from '../../logger' -const initKeypairBySecretKey = (key: number[]): Keypair => { +const initKeypairBySecretKey = (key: number[], pubKey: PublicKey): Keypair => { const keypair = Keypair.fromSecretKey(new Uint8Array(key)) - logger.info(`key => ${keypair.publicKey.toBase58()}`) + if (keypair.publicKey.equals(pubKey)) { + logger.info(`Found keypair for ${pubKey.toBase58()}`) - return keypair + return keypair + } + + throw new Error('PubKey does not match Private key') } -const initKeypairByMnemonic = (mnemonic: string, accountNumber: number): Keypair => { +const initKeypairByMnemonic = (mnemonic: string, pubKey: PublicKey): Keypair => { const seed = mnemonicToSeedSync(mnemonic, '') - const path = `m/44'/501'/${accountNumber}'/0'` - const keypair = Keypair.fromSeed(derivePath(path, seed.toString('hex')).key) - logger.debug(`${path} => ${keypair.publicKey.toBase58()}`) + for (let i = 0; i < 1000; ++i) { + const path = `m/44'/501'/${i}'/0'` + const keypair = Keypair.fromSeed(derivePath(path, seed.toString('hex')).key) + + logger.debug(`${path} => ${keypair.publicKey.toBase58()}`) + + if (keypair.publicKey.equals(pubKey)) { + logger.info(`Found keypair for ${pubKey.toBase58()} at ${path}`) + + return keypair + } + } - return keypair + throw new Error('PubKey not found in derivation Path') } -export const keyPair = config.user.keyMode === 'mnemonic' ? initKeypairByMnemonic(config.user.mnemonic, config.user.walletId) : initKeypairBySecretKey(config.user.secretKey) +export const keyPair = config.user.keyMode === 'mnemonic' + ? initKeypairByMnemonic(config.user.mnemonic, new PublicKey(config.user.pubKey)) + : initKeypairBySecretKey(config.user.secretKey, new PublicKey(config.user.pubKey))