diff --git a/.eslintrc.js b/.eslintrc.js
index b2df7577..b2a64a01 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,52 +1,52 @@
const namingConventions = [
{
selector: 'default',
- format: ['camelCase']
+ format: ['camelCase'],
},
{
selector: 'variable',
modifiers: ['const'],
- format: ['camelCase', 'UPPER_CASE']
+ format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'variable',
modifiers: ['unused'],
leadingUnderscore: 'require',
- format: ['camelCase']
+ format: ['camelCase'],
},
{
selector: 'variable',
modifiers: ['unused', 'destructured'],
leadingUnderscore: 'allow',
- format: ['camelCase']
+ format: ['camelCase'],
},
{
selector: 'property',
- format: ['camelCase', 'UPPER_CASE']
+ format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'parameter',
modifiers: ['unused'],
leadingUnderscore: 'require',
- format: ['camelCase']
+ format: ['camelCase'],
},
{
selector: 'typeLike',
- format: ['PascalCase']
+ format: ['PascalCase'],
},
{
selector: 'enumMember',
- format: ['UPPER_CASE']
+ format: ['UPPER_CASE'],
},
{
selector: 'objectLiteralProperty',
modifiers: ['requiresQuotes'],
- format: null
+ format: null,
},
{
selector: 'import',
format: ['camelCase', 'PascalCase', 'snake_case', 'UPPER_CASE'],
- }
+ },
]
module.exports = {
@@ -54,24 +54,25 @@ module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2021,
- sourceType: 'module'
+ sourceType: 'module',
},
env: {
es6: true,
- node: true
+ node: true,
},
ignorePatterns: [
'.idea',
'build',
'data',
'node_modules',
- 'package-lock.json'
+ 'package-lock.json',
],
plugins: [
'@typescript-eslint',
'filenames',
'import',
- 'promise'
+ 'promise',
+ 'prettier',
],
extends: [
'eslint:all',
@@ -80,14 +81,13 @@ module.exports = {
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
- 'plugin:promise/recommended'
+ 'plugin:promise/recommended',
+ 'plugin:prettier/recommended',
],
rules: {
// http://eslint.org/docs/rules/
'array-bracket-newline': ['error', 'consistent'],
'array-element-newline': 'off',
- 'arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
- 'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
'callback-return': ['error', ['callback', 'cb', 'done']],
'class-methods-use-this': 'off',
'consistent-return': 'off',
@@ -99,30 +99,28 @@ module.exports = {
'function-paren-newline': 'off',
'id-length': 'off',
'implicit-arrow-linebreak': 'off',
- 'indent': ['error', 4, {
- SwitchCase: 1,
- // workaround until https://github.com/typescript-eslint/typescript-eslint/issues/1824 is fixed
- ignoredNodes: [
- 'ClassBody.body > PropertyDefinition[decorators.length > 0] > .key'
- ]
- }],
'init-declarations': 'off',
'line-comment-position': 'off',
- 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
- 'max-len': ['error', { code: 120, ignoreStrings: true, ignoreTemplateLiterals: true }],
+ 'lines-between-class-members': [
+ 'error',
+ 'always',
+ { exceptAfterSingleLine: true },
+ ],
+ 'max-len': [
+ 'error',
+ { code: 120, ignoreStrings: true, ignoreTemplateLiterals: true },
+ ],
'max-lines': 'off',
'max-lines-per-function': 'off',
- 'max-params': ['error', { max: 5 }],
+ 'max-params': ['error', { max: 15 }],
'max-statements': 'off',
'multiline-comment-style': 'off',
'multiline-ternary': ['error', 'always-multiline'],
'new-cap': ['error', { capIsNew: false }],
'newline-after-var': ['error', 'always'],
- 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 6 }],
'no-await-in-loop': 'error',
'no-confusing-arrow': 'off',
'no-console': 'off',
- 'no-extra-parens': 'off',
'no-inline-comments': 'off',
'no-invalid-this': 'off',
'no-magic-numbers': 'off',
@@ -134,7 +132,6 @@ module.exports = {
'no-prototype-builtins': 'error',
'no-shadow': 'off',
'no-sync': 'off',
- 'no-template-curly-in-string': 'error',
'no-ternary': 'off',
'no-trailing-spaces': 'error',
'no-undefined': 'off',
@@ -142,49 +139,62 @@ module.exports = {
'no-unused-vars': 'off',
'no-use-before-define': 'off',
'no-warning-comments': 'off',
- 'object-curly-newline': ['error', {
- ObjectExpression: { multiline: true, consistent: true },
- ObjectPattern: { multiline: true, consistent: true },
- ImportDeclaration: { multiline: true },
- ExportDeclaration: { multiline: true, consistent: true, minProperties: 3 }
- }],
- 'object-curly-spacing': ['error', 'always'],
'object-property-newline': 'off',
'one-var': ['error', { uninitialized: 'always', initialized: 'never' }],
'padded-blocks': ['error', 'never'],
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' },
- { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] },
- { blankLine: 'always', prev: '*', next: 'return' }
+ {
+ blankLine: 'any',
+ prev: ['const', 'let', 'var'],
+ next: ['const', 'let', 'var'],
+ },
+ { blankLine: 'always', prev: '*', next: 'return' },
],
'prefer-named-capture-group': 'off',
'require-atomic-updates': 'off',
- 'quote-props': ['error', 'consistent-as-needed'],
- 'quotes': ['error', 'single', { avoidEscape: true }],
+ quotes: ['error', 'single', { avoidEscape: true }],
'require-jsdoc': 'off',
'require-unicode-regexp': 'off',
- 'semi': ['error', 'never'],
+ semi: ['error', 'never'],
'sort-imports': 'off',
'sort-keys': 'off',
- 'strict': 'off',
+ strict: 'off',
// https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
'@typescript-eslint/explicit-function-return-type': 'off',
- '@typescript-eslint/explicit-module-boundary-types': 'error',
- '@typescript-eslint/member-delimiter-style': ['error', { multiline: { delimiter: 'none' } }],
+ '@typescript-eslint/explicit-module-boundary-types': 'warn',
+ '@typescript-eslint/member-delimiter-style': [
+ 'error',
+ { multiline: { delimiter: 'none' } },
+ ],
'@typescript-eslint/naming-convention': ['error', ...namingConventions],
- '@typescript-eslint/no-extra-parens': ['error', 'all', { returnAssign: false, nestedBinaryExpressions: false }],
'@typescript-eslint/no-shadow': 'error',
- '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true, varsIgnorePattern: '^_+$' }],
+ '@typescript-eslint/no-unused-vars': [
+ 'warn',
+ {
+ args: 'all',
+ argsIgnorePattern: '^_',
+ caughtErrors: 'all',
+ caughtErrorsIgnorePattern: '^_',
+ destructuredArrayIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ ignoreRestSiblings: true,
+ },
+ ],
'@typescript-eslint/no-use-before-define': 'error',
- '@typescript-eslint/no-explicit-any': 'warn',
+ '@typescript-eslint/no-explicit-any': 'off',
// https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules
'import/default': 'error',
'import/export': 'error',
'import/exports-last': 'off',
- 'import/extensions': ['error', 'never', { json: 'always', scss: 'always' }],
+ 'import/extensions': [
+ 'error',
+ 'never',
+ { json: 'always', scss: 'always' },
+ ],
'import/first': 'error',
'import/group-exports': 'off',
'import/max-dependencies': ['error', { max: 25 }],
@@ -195,17 +205,17 @@ module.exports = {
'import/no-amd': 'error',
'import/no-anonymous-default-export': 'off',
'import/no-commonjs': 'error',
- 'import/no-cycle': 'error',
+ 'import/no-cycle': 'warn',
'import/no-default-export': 'off',
'import/no-deprecated': 'error',
'import/no-duplicates': 'error',
'import/no-dynamic-require': 'error',
- 'import/no-extraneous-dependencies': ['error', {
- devDependencies: [
- '**/*.test.ts',
- 'test/**/*.ts'
- ]
- }],
+ 'import/no-extraneous-dependencies': [
+ 'error',
+ {
+ devDependencies: ['**/*.test.ts', 'test/**/*.ts'],
+ },
+ ],
'import/no-internal-modules': 'off',
'import/no-mutable-exports': 'off',
'import/no-named-as-default': 'off',
@@ -216,27 +226,41 @@ module.exports = {
'import/no-self-import': 'error',
'import/no-unassigned-import': 'off',
'import/no-unresolved': 'error',
- 'import/no-restricted-paths': ['error', {
- zones: [
- { target: './src', from: './e2e' }
- ]
- }],
- 'import/order': ['error', {
- 'pathGroups': ['.', '..', '../..', '../../..', '../../../..'].map(p => ({
- pattern: `${p}/sentry`,
- group: 'internal',
- position: 'before'
- })),
- 'groups': [['builtin', 'external'], ['internal', 'parent', 'type'], ['sibling', 'index']],
- 'newlines-between': 'always'
- }],
+ 'import/no-restricted-paths': [
+ 'error',
+ {
+ zones: [{ target: './src', from: './e2e' }],
+ },
+ ],
+ 'import/order': [
+ 'error',
+ {
+ pathGroups: ['.', '..', '../..', '../../..', '../../../..'].map(
+ (p) => ({
+ pattern: `${p}/sentry`,
+ group: 'internal',
+ position: 'before',
+ }),
+ ),
+ groups: [
+ ['builtin', 'external'],
+ ['internal', 'parent', 'type'],
+ ['sibling', 'index'],
+ ],
+ 'newlines-between': 'always',
+ },
+ ],
'import/no-unused-modules': 'error',
'import/no-useless-path-segments': ['error', { noUselessIndex: true }],
'import/prefer-default-export': 'off',
'import/unambiguous': 'error',
// https://github.com/selaux/eslint-plugin-filenames#rules
- 'filenames/match-regex': ['error', '^[a-z0-9-]+(\\.(d|test|e2e|fixture|schema|request|response))?$', true],
+ 'filenames/match-regex': [
+ 'error',
+ '^[a-z0-9-]+(\\.(d|test|e2e|fixture|schema|request|response))?$',
+ true,
+ ],
'filenames/match-exported': ['error', 'kebab'],
'filenames/no-index': 'off',
@@ -253,7 +277,7 @@ module.exports = {
'promise/no-return-wrap': 'error',
'promise/param-names': 'error',
'promise/prefer-await-to-callbacks': 'error',
- 'promise/prefer-await-to-then': 'error'
+ 'promise/prefer-await-to-then': 'error',
},
overrides: [
{
@@ -262,50 +286,54 @@ module.exports = {
'filenames/match-regex': 'off',
'import/no-commonjs': 'off',
'import/unambiguous': 'off',
- '@typescript-eslint/naming-convention': 'off'
- }
+ '@typescript-eslint/naming-convention': 'off',
+ },
},
{
files: ['*.config.ts'],
rules: {
- 'filenames/match-exported': 'off'
- }
+ 'filenames/match-exported': 'off',
+ },
},
{
files: ['*.d.ts'],
rules: {
- 'import/unambiguous': 'off'
- }
+ 'import/unambiguous': 'off',
+ },
},
{
files: ['src/db/entities/**/*.ts'],
rules: {
- 'import/no-cycle': 'off'
- }
+ 'import/no-cycle': 'off',
+ },
},
{
files: ['src/db/columns/**/*.ts'],
rules: {
- '@typescript-eslint/naming-convention': ['error', ...namingConventions, {
- selector: 'variable',
- modifiers: ['exported'],
- format: ['PascalCase']
- }]
- }
+ '@typescript-eslint/naming-convention': [
+ 'error',
+ ...namingConventions,
+ {
+ selector: 'variable',
+ modifiers: ['exported'],
+ format: ['PascalCase'],
+ },
+ ],
+ },
},
{
files: ['src/index.ts', 'src/main/*/index.ts'],
rules: {
- 'promise/prefer-await-to-callbacks': 'off'
- }
+ 'promise/prefer-await-to-callbacks': 'off',
+ },
},
{
files: ['src/main/**/*.ts'],
rules: {
'max-depth': ['error', { max: 6 }],
'no-continue': 'off',
- 'no-await-in-loop': 'off'
- }
- }
- ]
+ 'no-await-in-loop': 'off',
+ },
+ },
+ ],
}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 897f695a..051e08e3 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,17 +1,17 @@
version: 2
updates:
- - package-ecosystem: "github-actions"
- directory: "/"
+ - package-ecosystem: 'github-actions'
+ directory: '/'
schedule:
- interval: "daily"
- - package-ecosystem: "npm"
- directory: "/"
+ interval: 'daily'
+ - package-ecosystem: 'npm'
+ directory: '/'
schedule:
- interval: "daily"
+ interval: 'daily'
groups:
sentry:
patterns:
- - "*sentry*"
+ - '*sentry*'
eslint:
patterns:
- - "*eslint*"
+ - '*eslint*'
diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index 5a90b444..8f20bdd9 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -1,55 +1,53 @@
name: cicd
on:
- push:
- branches: [ "*" ]
- pull_request:
- branches: [ "main" ]
+ push:
+ branches: ['*']
+ pull_request:
+ branches: ['main']
jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ./.github/actions/install
+ - run: pnpm run build
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: ./.github/actions/install
- - run: pnpm run build
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ./.github/actions/install
+ - run: pnpm run lint
- lint:
- runs-on: ubuntu-latest
- 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
- - uses: ./.github/actions/install
- - run: pnpm run build
- - run: pnpm install --frozen-lockfile --ignore-scripts --prod
- - uses: docker/metadata-action@v5
- id: metadata
- with:
- images: runmymind/fleetbot
- tags: |
- type=schedule
- type=ref,event=branch
- type=ref,event=tag
- type=ref,event=pr
- type=sha
- type=raw,value=latest,enable={{is_default_branch}}
- - uses: docker/login-action@v3
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - uses: int128/kaniko-action@v1
- with:
- push: true
- file: ./docker/Dockerfile
- tags: ${{ steps.metadata.outputs.tags }}
- labels: ${{ steps.metadata.outputs.labels }}
- cache: true
- cache-repository: runmymind/fleetbot/cache
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: ./.github/actions/install
+ - run: pnpm run build
+ - run: pnpm install --frozen-lockfile --ignore-scripts --prod
+ - uses: docker/metadata-action@v5
+ id: metadata
+ with:
+ images: runmymind/fleetbot
+ tags: |
+ type=schedule
+ type=ref,event=branch
+ type=ref,event=tag
+ type=ref,event=pr
+ type=sha
+ type=raw,value=latest,enable={{is_default_branch}}
+ - uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - uses: int128/kaniko-action@v1
+ with:
+ push: true
+ file: ./docker/Dockerfile
+ tags: ${{ steps.metadata.outputs.tags }}
+ labels: ${{ steps.metadata.outputs.labels }}
+ cache: true
+ cache-repository: runmymind/fleetbot/cache
diff --git a/.gitignore b/.gitignore
index 8acf6f0c..59102c7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,5 @@
/node_modules
.env*
!.env.example
-.prettierrc
*.iml
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..ad4ed129
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,6 @@
+{
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "endOfLine": "auto"
+}
diff --git a/.yamllint.yml b/.yamllint.yml
index f5e13cf4..f8b33ff9 100644
--- a/.yamllint.yml
+++ b/.yamllint.yml
@@ -1,24 +1,24 @@
extends: default
ignore: |
- node_modules
+ node_modules
rules:
- braces:
- min-spaces-inside: 1
- max-spaces-inside: 1
- brackets:
- min-spaces-inside: 1
- max-spaces-inside: 1
- document-start: disable
- document-end:
- present: false
- empty-lines:
- max: 1
- max-start: 0
- max-end: 0
- empty-values: enable
- line-length: disable
- octal-values: enable
- truthy:
- level: error
+ braces:
+ min-spaces-inside: 1
+ max-spaces-inside: 1
+ brackets:
+ min-spaces-inside: 1
+ max-spaces-inside: 1
+ document-start: disable
+ document-end:
+ present: false
+ empty-lines:
+ max: 1
+ max-start: 0
+ max-end: 0
+ empty-values: enable
+ line-length: disable
+ octal-values: enable
+ truthy:
+ level: error
diff --git a/README.md b/README.md
index 81d8c062..cf13b75f 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@
+
## About The Project
fleetbot is an automatic fleet refill service bot for Star Atlas SCORE,
@@ -54,21 +55,25 @@ an early stage mini-game in the Star Atlas universe.
`fleetbot` contains of four core components:
### resource manager
+
The resource manager makes sure that `fleetbot` always has enough `R4`. (FOOD, TOOL, FUEL and AMMO). Basically it
calculates how much is needed for a given time period based on users and their burn. As soon as `fleetbot` runs low on a
particular resource, it uses the official Star Atlas marketplace to restock them.
### bookkeeper
+
The bookkeeper manages user's accounts, handles deposits and withdrawals and keeps track of individual balances. Since
all user's funds are kept in one wallet, this needs to be baked by a persistence layer to store additional information
which can not efficiently be read from the blockchain itself.
### refill service
+
The most crucial part for `fleetbot` users. Checks your fleets and refills them as soon as they run out of resources.
Ships aren't getting full refills by default, but only what is really needed. This leaves the option for the user to
manually add resources as well.
### telegram bot
+
To interact with `fleetbot`, telegram can be used. See telegram section further down for more information.
(back to top)
@@ -80,6 +85,7 @@ To interact with `fleetbot`, telegram can be used. See telegram section further
(back to top)
+
## Usage
`fleetbot` can be used in several ways, depending on user's preference. To be fully aware of what the bot is actually doing
@@ -90,24 +96,27 @@ everyone, there are more convenient solutions available as well.
To compile and run the source code, the following prerequisites are needed:
-* node environment
-* empty postgres database
-* telegram bot
-* unused solana wallet
+- node environment
+- empty postgres database
+- telegram bot
+- unused solana wallet
See `.env.example` for required information. Create a copy (`.env.`) and configure the environment.
1. clone repo
- ```sh
- git clone https://github.com/mindrunner/fleetbot.git
- ```
+
+ ```sh
+ git clone https://github.com/mindrunner/fleetbot.git
+ ```
2. install dependencies
+
```sh
npm install
```
3. run database migrations
+
```sh
npm run typeorm migration:run
```
@@ -123,6 +132,7 @@ An official docker image is available and automatically build from `main` branch
to use `docker-compose`.
Example compose file:
+
```
version: '3.5'
@@ -155,7 +165,9 @@ For convenience, there is an officially hosted `fleetbot` available at `https://
See the following section on how to interact with the bot on telegram.
+
## Telegram
+
Interaction with `fleetbot` is possible with telegram. The bot has a `/help` command which explains all important
commands. Usage should be pretty straight forward as follows:
@@ -209,18 +221,18 @@ Commands for verified users:
(back to top)
-
+
## Roadmap
-- [] Migrate to SPL-Token 0.3.x interface
+- [] Migrate to SPL-Token 0.3.x interface
See the [open issues](https://github.com/mindrunner/fleetbot/issues) for a full list of proposed features (and known issues).
(back to top)
-
+
## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
@@ -237,34 +249,40 @@ Don't forget to give the project a star! Thanks again!
(back to top)
+
## Disclaimer
Before using this service or code, do your own research about crypto-world, metaverses, wallets and all the potential
danger in this universe.
-- `fleetbot` is not responsible for any loss of funds
-- `fleetbot` will never DM you
-- `fleetbot` will never ask you for seed phrases or private keys
+- `fleetbot` is not responsible for any loss of funds
+- `fleetbot` will never DM you
+- `fleetbot` will never ask you for seed phrases or private keys
(back to top)
+
## License
+
Distributed under the MIT License. See `LICENSE` for more information.
(back to top)
+
## Donations
+
`fleetbot` accepts donations in `SOL`, `POLIS` or `USDC` at the same addresses it operates:
-* fleetbot.sol
-* ANDqa82T21G1RXwxpbf9v7jZXXfFSeezaUf7MJGTH6BZ
-(back to top)
+- fleetbot.sol
+- ANDqa82T21G1RXwxpbf9v7jZXXfFSeezaUf7MJGTH6BZ
+(back to top)
+
[contributors-shield]: https://img.shields.io/github/contributors/mindrunner/fleetbot.svg?style=for-the-badge
[contributors-url]: https://github.com/mindrunner/fleetbot/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/mindrunner/fleetbot.svg?style=for-the-badge
@@ -275,7 +293,5 @@ Distributed under the MIT License. See `LICENSE` for more information.
[issues-url]: https://github.com/mindrunner/fleetbot/issues
[license-shield]: https://img.shields.io/github/license/mindrunner/fleetbot.svg?style=for-the-badge
[license-url]: https://github.com/mindrunner/fleetbot/blob/master/LICENSE
-
[node.js]: https://img.shields.io/badge/node.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
[Node-url]: https://nodejs.org
-
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 e70ebff6..79de906b 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"build": "tsc",
"build:docker": "docker build -t fleetbot -f docker/app/Dockerfile .",
"db:migrate": "npm run typeorm -- migration:run",
- "lint": "eslint --ext .ts --ext .js .",
+ "lint": "eslint --ext .ts .",
"lint:docker": "docker run --rm -i hadolint/hadolint < cicd/docker/app/Dockerfile",
"lint:yaml": "yamllint .",
"clean": "rm -rf build",
@@ -30,22 +30,23 @@
"@sentry/integrations": "^7.114.0",
"@sentry/node": "^8.11.0",
"@sentry/tracing": "^7.114.0",
- "@solana/spl-token": "^0.4.6",
+ "@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.3",
"@staratlas/atlas-prime": "^0.13.1",
"@staratlas/cargo": "^1.1.0",
- "@staratlas/points": "^1.0.4",
- "@staratlas/claim-stake": "^0.11.5",
+ "@staratlas/claim-stake": "^0.11.6",
"@staratlas/crafting": "^1.1.0",
- "@staratlas/data-source": "^0.7.4",
+ "@staratlas/data-source": "^0.8.0",
"@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",
"bs58": "^5.0.0",
+ "chance": "^1.1.12",
"cron": "^3.1.7",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
@@ -55,11 +56,13 @@
"superagent": "^9.0.2",
"telegraf": "^4.16.3",
"typeorm": "^0.3.20",
+ "undici": "^6.19.2",
"winston": "^3.13.0"
},
"devDependencies": {
"@types/big.js": "^6.2.2",
"@types/bn.js": "^5.1.5",
+ "@types/chance": "^1.1.6",
"@types/bs58": "^4.0.4",
"@types/pg": "^8.11.6",
"@types/superagent": "^8.1.7",
@@ -69,9 +72,12 @@
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-promise": "^6.2.0",
+ "eslint-plugin-prettier": "^5.1.3",
+ "eslint-config-prettier": "^9.1.0",
+ "prettier": "^3.2.5",
"ts-jest": "^29.1.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 197c38a1..2afaa465 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -24,8 +24,8 @@ importers:
specifier: ^7.114.0
version: 7.114.0
'@solana/spl-token':
- specifier: ^0.4.6
- version: 0.4.6(@solana/web3.js@1.95.3(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.3(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.95.3
version: 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -36,29 +36,29 @@ importers:
specifier: ^1.1.0
version: 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(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.1.0
version: 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(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.8.0
+ version: 0.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(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.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(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)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ specifier: ^1.4.0
+ version: 1.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
big.js:
specifier: ^6.2.1
version: 6.2.1
@@ -71,6 +71,9 @@ importers:
bs58:
specifier: ^5.0.0
version: 5.0.0
+ chance:
+ specifier: ^1.1.12
+ version: 1.1.12
cron:
specifier: ^3.1.7
version: 3.1.7
@@ -98,6 +101,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.8
winston:
specifier: ^3.13.0
version: 3.13.0
@@ -111,6 +117,9 @@ importers:
'@types/bs58':
specifier: ^4.0.4
version: 4.0.4
+ '@types/chance':
+ specifier: ^1.1.6
+ version: 1.1.6
'@types/pg':
specifier: ^8.11.6
version: 8.11.6
@@ -126,15 +135,24 @@ importers:
eslint:
specifier: ^8.57.0
version: 8.57.0
+ eslint-config-prettier:
+ specifier: ^9.1.0
+ version: 9.1.0(eslint@8.57.0)
eslint-plugin-filenames:
specifier: ^1.3.2
version: 1.3.2(eslint@8.57.0)
eslint-plugin-import:
specifier: ^2.29.1
version: 2.29.1(@typescript-eslint/parser@7.13.1(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+ eslint-plugin-prettier:
+ specifier: ^5.1.3
+ version: 5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3)
eslint-plugin-promise:
specifier: ^6.2.0
version: 6.2.0(eslint@8.57.0)
+ prettier:
+ specifier: ^3.2.5
+ version: 3.3.3
ts-jest:
specifier: ^29.1.5
version: 29.1.5(@babel/core@7.24.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.11)(ts-node@10.9.2(@types/node@20.12.11)(typescript@5.4.5)))(typescript@5.4.5)
@@ -361,6 +379,15 @@ packages:
resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ '@ethereumjs/rlp@4.0.1':
+ resolution: {integrity: sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ '@ethereumjs/util@8.1.0':
+ resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==}
+ engines: {node: '>=14'}
+
'@faker-js/faker@8.4.1':
resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'}
@@ -477,8 +504,110 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
- '@noble/curves@1.4.0':
- resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==}
+ '@metaplex-foundation/beet-solana@0.4.1':
+ resolution: {integrity: sha512-/6o32FNUtwK8tjhotrvU/vorP7umBuRFvBZrC6XCk51aKidBHe5LPVPA5AjGPbV3oftMfRuXPNd9yAGeEqeCDQ==}
+
+ '@metaplex-foundation/beet@0.7.2':
+ resolution: {integrity: sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg==}
+
+ '@metaplex-foundation/digital-asset-standard-api@1.0.4':
+ resolution: {integrity: sha512-YSYyMnIoKNykDZTXsSCeiIOJ7NT5Ke2pzghXDsinRwHvwIZWv+zY5kJQBvTglAzYlt/GaI+noAhUZXXmSbp07A==}
+ peerDependencies:
+ '@metaplex-foundation/umi': '>= 0.8.2 < 1'
+
+ '@metaplex-foundation/mpl-bubblegum@4.2.1':
+ resolution: {integrity: sha512-r9kHrVmkzJApbXwd7cmJyO0mAV3qsJaTjv5ks6PUT1Bzjj9QCvlJYg2UYQJLUTcrY5TjE9wXLpwUqNgllXH/Cw==}
+ peerDependencies:
+ '@metaplex-foundation/umi': '>= 0.8.9 < 1'
+
+ '@metaplex-foundation/mpl-token-metadata@3.2.1':
+ resolution: {integrity: sha512-26W1NhQwDWmLOg/pBRYut7x/vEs/5kFS2sWVEY5/X0f2jJOLhnd4NaZQcq+5u+XZsXvm1jq2AtrRGPNK43oqWQ==}
+ peerDependencies:
+ '@metaplex-foundation/umi': '>= 0.8.2 < 1'
+
+ '@metaplex-foundation/mpl-toolbox@0.9.4':
+ resolution: {integrity: sha512-fd6JxfoLbj/MM8FG2x91KYVy1U6AjBQw4qjt7+Da3trzQaWnSaYHDcYRG/53xqfvZ9qofY1T2t53GXPlD87lnQ==}
+ peerDependencies:
+ '@metaplex-foundation/umi': '>= 0.8.2 < 1'
+
+ '@metaplex-foundation/umi-bundle-defaults@0.9.2':
+ resolution: {integrity: sha512-kV3tfvgvRjVP1p9OFOtH+ibOtN9omVJSwKr0We4/9r45e5LTj+32su0V/rixZUkG1EZzzOYBsxhtIE0kIw/Hrw==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+ '@solana/web3.js': ^1.72.0
+
+ '@metaplex-foundation/umi-downloader-http@0.9.2':
+ resolution: {integrity: sha512-tzPT9hBwenzTzAQg07rmsrqZfgguAXELbcJrsYMoASp5VqWFXYIP00g94KET6XLjWUXH4P1J2zoa6hGennPXHA==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+
+ '@metaplex-foundation/umi-eddsa-web3js@0.9.2':
+ resolution: {integrity: sha512-hhPCxXbYIp4BC4z9gK78sXpWLkNSrfv4ndhF5ruAkdIp7GcRVYKj0QnOUO6lGYGiIkNlw20yoTwOe1CT//OfTQ==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+ '@solana/web3.js': ^1.72.0
+
+ '@metaplex-foundation/umi-http-fetch@0.9.2':
+ resolution: {integrity: sha512-YCZuBu24T9ZzEDe4+w12LEZm/fO9pkyViZufGgASC5NX93814Lvf6Ssjn/hZzjfA7CvZbvLFbmujc6CV3Q/m9Q==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+
+ '@metaplex-foundation/umi-options@0.8.9':
+ resolution: {integrity: sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==}
+
+ '@metaplex-foundation/umi-program-repository@0.9.2':
+ resolution: {integrity: sha512-g3+FPqXEmYsBa8eETtUE2gb2Oe3mqac0z3/Ur1TvAg5TtIy3mzRzOy/nza+sgzejnfcxcVg835rmpBaxpBnjDA==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+
+ '@metaplex-foundation/umi-public-keys@0.8.9':
+ resolution: {integrity: sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==}
+
+ '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2':
+ resolution: {integrity: sha512-YRwVf6xH0jPBAUgMhEPi+UbjioAeqTXmjsN2TnmQCPAmHbrHrMRj0rlWYwFLWAgkmoxazYrXP9lqOFRrfOGAEA==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+
+ '@metaplex-foundation/umi-rpc-web3js@0.9.2':
+ resolution: {integrity: sha512-MqcsBz8B4wGl6jxsf2Jo/rAEpYReU9VCSR15QSjhvADHMmdFxCIZCCAgE+gDE2Vuanfl437VhOcP3g5Uw8C16Q==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+ '@solana/web3.js': ^1.72.0
+
+ '@metaplex-foundation/umi-serializer-data-view@0.9.2':
+ resolution: {integrity: sha512-5vGptadJxUxvUcyrwFZxXlEc6Q7AYySBesizCtrBFUY8w8PnF2vzmS45CP1MLySEATNH6T9mD4Rs0tLb87iQyA==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+
+ '@metaplex-foundation/umi-serializers-core@0.8.9':
+ resolution: {integrity: sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==}
+
+ '@metaplex-foundation/umi-serializers-encodings@0.8.9':
+ resolution: {integrity: sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==}
+
+ '@metaplex-foundation/umi-serializers-numbers@0.8.9':
+ resolution: {integrity: sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==}
+
+ '@metaplex-foundation/umi-serializers@0.9.0':
+ resolution: {integrity: sha512-hAOW9Djl4w4ioKeR4erDZl5IG4iJdP0xA19ZomdaCbMhYAAmG/FEs5khh0uT2mq53/MnzWcXSUPoO8WBN4Q+Vg==}
+
+ '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2':
+ resolution: {integrity: sha512-fR1Kf21uylMFd1Smkltmj4jTNxhqSWf416owsJ+T+cvJi2VCOcOwq/3UFzOrpz78fA0RhsajKYKj0HYsRnQI1g==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+ '@solana/web3.js': ^1.72.0
+
+ '@metaplex-foundation/umi-web3js-adapters@0.9.2':
+ resolution: {integrity: sha512-RQqUTtHYY9fmEMnq7s3Hiv/81flGaoI0ZVVoafnFVaQLnxU6QBKxtboRZHk43XtD9CiFh5f9izrMJX7iK7KlOA==}
+ peerDependencies:
+ '@metaplex-foundation/umi': ^0.9.2
+ '@solana/web3.js': ^1.72.0
+
+ '@metaplex-foundation/umi@0.9.2':
+ resolution: {integrity: sha512-9i4Acm4pruQfJcpRrc2EauPBwkfDN0I9QTvJyZocIlKgoZwD6A6wH0PViH1AjOVG5CQCd1YI3tJd5XjYE1ElBw==}
+
+ '@noble/curves@1.4.2':
+ resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==}
'@noble/curves@1.6.0':
resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==}
@@ -692,6 +821,10 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@pkgr/core@0.1.1':
+ resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
'@prisma/instrumentation@5.15.0':
resolution: {integrity: sha512-fCWOOOajTKOUEp43gRmBqwt6oN9bPJcLiloi2OG/2ED0N5z62Cuza6FDrlm3SJHQAXYlXqLE0HLdEE5WcUkOzg==}
@@ -701,6 +834,15 @@ packages:
peerDependencies:
'@solana/web3.js': ^1.2.0
+ '@scure/base@1.1.8':
+ resolution: {integrity: sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==}
+
+ '@scure/bip32@1.4.0':
+ resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==}
+
+ '@scure/bip39@1.3.0':
+ resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==}
+
'@sentry-internal/tracing@7.114.0':
resolution: {integrity: sha512-dOuvfJN7G+3YqLlUY4HIjyWHaRP8vbOgF+OsE5w2l7ZEn1rMAaUbPntAR8AF9GBA6j2zWNoSo8e7GjbJxVofSg==}
engines: {node: '>=8'}
@@ -829,11 +971,11 @@ packages:
peerDependencies:
typescript: '>=5'
- '@solana/spl-token-group@0.0.4':
- resolution: {integrity: sha512-7+80nrEMdUKlK37V6kOe024+T7J4nNss0F8LQ9OOPYdWCCfJmsGUzVx2W3oeizZR4IHM6N4yC9v1Xqwc3BTPWw==}
+ '@solana/spl-account-compression@0.2.0':
+ resolution: {integrity: sha512-nHpa+hTUpjLdV9x4LXlp7k0WIkr8kUGjY/SPh+vuTUy4SEIIDjrGJ6/B0hUdd8+mFfrq2x4j/tgJvPsm4K5AJw==}
engines: {node: '>=16'}
peerDependencies:
- '@solana/web3.js': ^1.91.6
+ '@solana/web3.js': ^1.50.1
'@solana/spl-token-group@0.0.5':
resolution: {integrity: sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ==}
@@ -853,12 +995,6 @@ packages:
peerDependencies:
'@solana/web3.js': ^1.88.0
- '@solana/spl-token@0.4.6':
- resolution: {integrity: sha512-1nCnUqfHVtdguFciVWaY/RKcQz1IF4b31jnKgAmjU9QVN1q7dRUkTEWJZgTYIEtsULjVnC9jRqlhgGN39WbKKA==}
- engines: {node: '>=16'}
- peerDependencies:
- '@solana/web3.js': ^1.91.6
-
'@solana/spl-token@0.4.8':
resolution: {integrity: sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA==}
engines: {node: '>=16'}
@@ -885,17 +1021,17 @@ packages:
'@staratlas/cargo@1.1.0':
resolution: {integrity: sha512-7td0gxru4dkygO+xv+RQs9bkizKj6+blAMRSPP5ozOZ9QQ35HwCBIDJXB6Kk8sfVcq2JOHiWyQtyjcAD6GHArQ==}
- '@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.1.0':
resolution: {integrity: sha512-lxvdGBZatVBL63o1EHcUAxk1k1jHwEv6dZsulpgXpsOfnBP7Btzt8Ky1knBD7HvM/gBQplxMKopolRGFCQekXQ==}
- '@staratlas/data-source@0.7.4':
- resolution: {integrity: sha512-DY4fGLJjKbn/c+h+PdbLHjg8r3skDvnj3ctJQpk4LsNKFYR7zRq/0x+H24WB53DgZbGelxcJIdhsY6178732TA==}
+ '@staratlas/crew@0.7.0':
+ resolution: {integrity: sha512-tr8RDDs1waOSVwVwR9WlD6Z6iDvRHprqQu4XQmSSy6oy5Sv2FNofFWENsC0dmysUl/n0OLzKfT8/Q4D3hKRlTA==}
- '@staratlas/data-source@0.7.5':
- resolution: {integrity: sha512-LtaCpV6G3X3r7pSUlkr6pDsm/B+90pKtryr4qnZRttJ7vguadSC7xzlQJ5ubEdhK3b1Xs8UFLBnEHpvzkJk9ow==}
+ '@staratlas/data-source@0.7.7':
+ resolution: {integrity: sha512-DQfR/a9MXr1Gds/YTR9RylQZlx5gbCe4NjynQ/45je3rnnblNfhma0WoHrSHz/r/lWwJwN7yRwSJkfaDW8QhuQ==}
'@staratlas/data-source@0.8.0':
resolution: {integrity: sha512-fUwoQDwGcBPXN+s4foGT2XItK4XJh2C4UCBN7HlQM1pO3NQfOxGW7tLrvP67ZMyE4ltrwN3bA+GoUub9vj33vA==}
@@ -909,17 +1045,23 @@ 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.1.0':
+ resolution: {integrity: sha512-Rrbsnku5flephKSNkb6OYkUxGWmQAsHH76ZQKISBViXSPcQkpZ3lIYaqYaztEDx6rXSY1J5ftkJEAnxMVtkJ4A==}
+
+ '@staratlas/points@1.1.0':
+ resolution: {integrity: sha512-9XyG5i3xz4Om6Blq/HxYqnGGAnklyCLlokIXpLAxYWEU53JT7ldhLiXCgKuA8n/aWi0Y3YCOnmPuuQ5JwD+cZw==}
'@staratlas/profile-faction@0.4.1':
resolution: {integrity: sha512-RaE7ZqX7VyPZBMpZm0sblFPykGhBr4IrtMsxVtgbU2hgtZk0rqXWA8vv8hR55fEtmweGuon06JPnGQfLBz4/yQ==}
+ '@staratlas/profile-faction@0.6.0':
+ resolution: {integrity: sha512-OLENOQ12cG12g8xkmZsf6oFvtCkTlsI/ljGGwpOzic0iCD282bydVStJ//N0nRxBfDBGYCyRI9TBkR3uF69D2g==}
+
'@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.6.0':
+ resolution: {integrity: sha512-fwaPyxs3Rr1dLR1GjMV0nnqG/L68Paa1E26h9AxXEf/Lx+im5x5u+jsc2m7/uwEVMo53ThPt+2ToIaK4kdgAAQ==}
'@swc/helpers@0.5.13':
resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==}
@@ -966,6 +1108,9 @@ packages:
'@types/bs58@4.0.4':
resolution: {integrity: sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==}
+ '@types/chance@1.1.6':
+ resolution: {integrity: sha512-V+pm3stv1Mvz8fSKJJod6CglNGVqEQ6OyuqitoDkWywEODM/eJd1eSuIp9xt6DrX8BWZ2eDSIzbw1tPCUTvGbQ==}
+
'@types/connect@3.4.36':
resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
@@ -1011,6 +1156,9 @@ packages:
'@types/keygrip@1.0.6':
resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
+ '@types/keyv@3.1.4':
+ resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+
'@types/koa-compose@3.2.8':
resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==}
@@ -1059,6 +1207,9 @@ packages:
'@types/range-parser@1.2.7':
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
+ '@types/responselike@1.0.3':
+ resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
+
'@types/send@0.17.4':
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
@@ -1167,6 +1318,9 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
+ abs@1.3.14:
+ resolution: {integrity: sha512-PrS26IzwKLWwuURpiKl8wRmJ2KdR/azaVrLEBWG/TALwT20Y7qjtYp1qcMLHA4206hBHY5phv3w4pjf9NPv4Vw==}
+
acorn-import-assertions@1.9.0:
resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
peerDependencies:
@@ -1226,6 +1380,9 @@ packages:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
+ ansicolors@0.3.2:
+ resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==}
+
any-promise@1.3.0:
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
@@ -1277,6 +1434,9 @@ packages:
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+ assert@2.1.0:
+ resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
+
async@3.2.5:
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
@@ -1350,6 +1510,9 @@ packages:
bip39@3.1.0:
resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==}
+ bn.js@4.11.6:
+ resolution: {integrity: sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==}
+
bn.js@5.2.1:
resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
@@ -1406,6 +1569,9 @@ packages:
resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==}
engines: {node: '>=4.5'}
+ buffer-reverse@1.0.1:
+ resolution: {integrity: sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==}
+
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@@ -1440,6 +1606,10 @@ packages:
caniuse-lite@1.0.30001617:
resolution: {integrity: sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==}
+ capture-stack-trace@1.0.2:
+ resolution: {integrity: sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==}
+ engines: {node: '>=0.10.0'}
+
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -1452,6 +1622,9 @@ packages:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+ chance@1.1.12:
+ resolution: {integrity: sha512-vVBIGQVnwtUG+SYe0ge+3MvF78cvSpuCOEUJr7sVEk2vSBuMW6OXNJjSzdtzrlxNUEaoqH2GBd5Y/+18BEB01Q==}
+
char-regex@1.0.2:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
@@ -1534,6 +1707,13 @@ packages:
cookiejar@2.1.4:
resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+ create-error-class@3.0.2:
+ resolution: {integrity: sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==}
+ engines: {node: '>=0.10.0'}
+
create-hash@1.2.0:
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
@@ -1562,6 +1742,9 @@ packages:
resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==}
engines: {node: '>=8'}
+ crypto-js@4.2.0:
+ resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
+
css-select@4.3.0:
resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
@@ -1609,6 +1792,10 @@ packages:
babel-plugin-macros:
optional: true
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -1616,6 +1803,9 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
+ deffy@2.2.4:
+ resolution: {integrity: sha512-pLc9lsbsWjr6RxmJ2OLyvm+9l4j1yK69h+TML/gUit/t3vTijpkNGh8LioaJYTGO7F25m6HZndADcUOo2PsiUg==}
+
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@@ -1682,6 +1872,9 @@ packages:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
+ duplexer2@0.1.4:
+ resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
+
dynamic-dedupe@0.3.0:
resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==}
@@ -1710,6 +1903,9 @@ packages:
entities@2.2.0:
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
+ err@1.1.1:
+ resolution: {integrity: sha512-N97Ybd2jJHVQ+Ft3Q5+C2gM3kgygkdeQmEqbN2z15UTVyyEsIwLA1VK39O1DHEJhXbwIFcJLqm6iARNhFANcQA==}
+
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
@@ -1762,6 +1958,12 @@ packages:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
+ eslint-config-prettier@9.1.0:
+ resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
@@ -1801,6 +2003,20 @@ packages:
'@typescript-eslint/parser':
optional: true
+ eslint-plugin-prettier@5.1.3:
+ resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ '@types/eslint': '>=8.0.0'
+ eslint: '>=8.0.0'
+ eslint-config-prettier: '*'
+ prettier: '>=3.0.0'
+ peerDependenciesMeta:
+ '@types/eslint':
+ optional: true
+ eslint-config-prettier:
+ optional: true
+
eslint-plugin-promise@6.2.0:
resolution: {integrity: sha512-QmAqwizauvnKOlifxyDj2ObfULpHQawlg/zQdgEixur9vl0CvZGv/LCJV2rtj3210QCoeGBzVMfMXqGAOr/4fA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1845,6 +2061,16 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
+ ethereum-bloom-filters@1.2.0:
+ resolution: {integrity: sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==}
+
+ ethereum-cryptography@2.2.1:
+ resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==}
+
+ ethjs-unit@0.1.6:
+ resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
@@ -1855,6 +2081,9 @@ packages:
eventemitter3@5.0.1:
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
+ exec-limiter@3.2.13:
+ resolution: {integrity: sha512-86Ri699bwiHZVBzTzNj8gspqAhCPchg70zPVWIh3qzUOA1pUMcb272Em3LPk8AE0mS95B9yMJhtqF8vFJAn0dA==}
+
execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
@@ -1874,6 +2103,9 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ fast-diff@1.3.0:
+ resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+
fast-glob@3.3.2:
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
engines: {node: '>=8.6.0'}
@@ -1965,6 +2197,9 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ function.name@1.0.13:
+ resolution: {integrity: sha512-mVrqdoy5npWZyoXl4DxCeuVF6delDcQjVS9aPdvLYlBxtMTZDR2B5GVEQEoM1jJyspCqg3C0v4ABkLE7tp9xFA==}
+
function.prototype.name@1.1.6:
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
engines: {node: '>= 0.4'}
@@ -1996,6 +2231,18 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
+ git-package-json@1.4.10:
+ resolution: {integrity: sha512-DRAcvbzd2SxGK7w8OgYfvKqhFliT5keX0lmSmVdgScgf1kkl5tbbo7Pam6uYoCa1liOiipKxQZG8quCtGWl/fA==}
+
+ git-source@1.1.10:
+ resolution: {integrity: sha512-XZZ7ZgnLL35oLgM/xjnLYgtlKlxJG0FohC1kWDvGkU7s1VKGXK0pFF/g1itQEwQ3D+uTQzBnzPi8XbqOv7Wc1Q==}
+
+ git-up@1.2.1:
+ resolution: {integrity: sha512-SRVN3rOLACva8imc7BFrB6ts5iISWKH1/h/1Z+JZYoUI7UVQM7gQqk4M2yxUENbq2jUUT09NEND5xwP1i7Ktlw==}
+
+ git-url-parse@5.0.1:
+ resolution: {integrity: sha512-4uSiOgrryNEMBX+gTWogenYRUh2j1D+95STTSEF2RCTgLkfJikl8c7BGr0Bn274hwuxTsbS2/FQ5pVS9FoXegQ==}
+
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -2032,12 +2279,19 @@ packages:
gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+ got@5.7.1:
+ resolution: {integrity: sha512-1qd54GLxvVgzuidFmw9ze9umxS3rzhdBH6Wt6BTYrTQUXTN01vGGYXwzLzYLowNx8HBH3/c7kRyvx90fh13i7Q==}
+ engines: {node: '>=0.10.0 <7'}
+
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+ gry@5.0.8:
+ resolution: {integrity: sha512-meq9ZjYVpLzZh3ojhTg7IMad9grGsx6rUUKHLqPnhLXzJkRQvEL2U3tQpS5/WentYTtHtxkT3Ew/mb10D6F6/g==}
+
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -2079,6 +2333,9 @@ packages:
highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
+ hosted-git-info@2.8.9:
+ resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
@@ -2134,10 +2391,17 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
internal-slot@1.0.7:
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
engines: {node: '>= 0.4'}
+ is-arguments@1.1.1:
+ resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+ engines: {node: '>= 0.4'}
+
is-array-buffer@3.0.4:
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
@@ -2186,10 +2450,22 @@ packages:
resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
engines: {node: '>=6'}
+ is-generator-function@1.0.10:
+ resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
+ engines: {node: '>= 0.4'}
+
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
+ is-hex-prefixed@1.0.0:
+ resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ is-nan@1.3.2:
+ resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
+ engines: {node: '>= 0.4'}
+
is-negative-zero@2.0.3:
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
engines: {node: '>= 0.4'}
@@ -2206,14 +2482,29 @@ packages:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
+ is-redirect@1.0.0:
+ resolution: {integrity: sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==}
+ engines: {node: '>=0.10.0'}
+
is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
+ is-retry-allowed@1.2.0:
+ resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==}
+ engines: {node: '>=0.10.0'}
+
is-shared-array-buffer@1.0.3:
resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
engines: {node: '>= 0.4'}
+ is-ssh@1.4.0:
+ resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==}
+
+ is-stream@1.1.0:
+ resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+ engines: {node: '>=0.10.0'}
+
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@@ -2233,6 +2524,9 @@ packages:
is-weakref@1.0.2:
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -2268,6 +2562,9 @@ packages:
resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
engines: {node: '>=8'}
+ iterate-object@1.3.4:
+ resolution: {integrity: sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==}
+
jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
@@ -2409,6 +2706,9 @@ packages:
js-sha256@0.9.0:
resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==}
+ js-sha3@0.8.0:
+ resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -2474,6 +2774,9 @@ packages:
lie@3.1.1:
resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==}
+ limit-it@3.2.10:
+ resolution: {integrity: sha512-T0NK99pHnkimldr1WUqvbGV1oWDku/xC9J/OqzJFsV1jeOS6Bwl8W7vkeQIBqwiON9dTALws+rX/XPMQqWerDQ==}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -2516,6 +2819,10 @@ packages:
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+ lowercase-keys@1.0.1:
+ resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==}
+ engines: {node: '>=0.10.0'}
+
lru-cache@10.2.2:
resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==}
engines: {node: 14 || >=16.14}
@@ -2547,10 +2854,17 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
+ merkletreejs@0.3.11:
+ resolution: {integrity: sha512-LJKTl4iVNTndhL+3Uz/tfkjD0klIWsHlUzgtuNnNrsf7bAlXR30m+xYB7lHr5Z/l6e/yAIsr26Dabx6Buo4VGQ==}
+ engines: {node: '>= 7.6.0'}
+
methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
+ micro-ftch@0.3.1:
+ resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==}
+
micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'}
@@ -2652,6 +2966,16 @@ packages:
node-releases@2.0.14:
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+ node-status-codes@1.0.0:
+ resolution: {integrity: sha512-1cBMgRxdMWE8KeWCqk2RIOrvUb0XCwYfEsY5/y2NlXyq4Y/RumnOZvTj4Nbr77+Vb2C+kyBoRTdkNOS8L3d/aQ==}
+ engines: {node: '>=0.10.0'}
+
+ noop6@1.0.9:
+ resolution: {integrity: sha512-DB3Hwyd89dPr5HqEPg3YHjzvwh/mCqizC1zZ8vyofqc+TQRyPDnT4wgXXbLGF4z9YAzwwTLi8pNLhGqcbSjgkA==}
+
+ normalize-package-data@2.5.0:
+ resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -2663,6 +2987,16 @@ packages:
nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+ number-to-bn@1.7.0:
+ resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
+ oargv@3.4.10:
+ resolution: {integrity: sha512-SXaMANv9sr7S/dP0vj0+Ybipa47UE1ntTWQ2rpPRhC6Bsvfl+Jg03Xif7jfL0sWKOYWK8oPjcZ5eJ82t8AP/8g==}
+
+ obj-def@1.0.9:
+ resolution: {integrity: sha512-bQ4ya3VYD6FAA1+s6mEhaURRHSmw4+sKaXE6UyXZ1XDYc5D+c7look25dFdydmLd18epUegh398gdDkMUZI9xg==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -2670,6 +3004,10 @@ packages:
object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
+ object-is@1.1.6:
+ resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
+ engines: {node: '>= 0.4'}
+
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -2696,6 +3034,9 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ one-by-one@3.2.8:
+ resolution: {integrity: sha512-HR/pSzZdm46Xqj58K+Bu64kMbSTw8/u77AwWvV+rprO/OsuR++pPlkUJn+SmwqBGRgHKwSKQ974V3uls7crIeQ==}
+
one-time@1.0.0:
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
@@ -2711,6 +3052,10 @@ packages:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
+ os-tmpdir@1.0.2:
+ resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+ engines: {node: '>=0.10.0'}
+
p-limit@2.3.0:
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
engines: {node: '>=6'}
@@ -2735,6 +3080,17 @@ packages:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'}
+ package-json-path@1.0.9:
+ resolution: {integrity: sha512-uNu7f6Ef7tQHZRnkyVnCtzdSYVN9uBtge/sG7wzcUaawFWkPYUq67iXxRGrQSg/q0tzxIB8jSyIYUKjG2Jn//A==}
+
+ package-json@2.4.0:
+ resolution: {integrity: sha512-PRg65iXMTt/uK8Rfh5zvzkUbfAPitF17YaCY+IbHsYgksiLvtzWWTUildHth3mVaZ7871OJ7gtP4LBRBlmAdXg==}
+ engines: {node: '>=0.10.0'}
+
+ package.json@2.0.1:
+ resolution: {integrity: sha512-pSxZ6XR5yEawRN2ekxx9IKgPN5uNAYco7MCPxtBEWMKO3UKWa1X2CtQMzMgloeGj2g2o6cue3Sb5iPkByIJqlw==}
+ deprecated: Use pkg.json instead.
+
pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
@@ -2742,10 +3098,17 @@ packages:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
+ parse-json@2.2.0:
+ resolution: {integrity: sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==}
+ engines: {node: '>=0.10.0'}
+
parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
+ parse-url@1.3.11:
+ resolution: {integrity: sha512-1wj9nkgH/5EboDxLwaTMGJh3oH3f+Gue+aGdh631oCqoSBpokzmMmOldvOeBPtB8GJBYJbaF93KPzlkU+Y1ksg==}
+
parse5-htmlparser2-tree-adapter@6.0.1:
resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==}
@@ -2827,6 +3190,14 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
+ pinkie-promise@2.0.1:
+ resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==}
+ engines: {node: '>=0.10.0'}
+
+ pinkie@2.0.4:
+ resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
+ engines: {node: '>=0.10.0'}
+
pirates@4.0.6:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
@@ -2878,6 +3249,19 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ prepend-http@1.0.4:
+ resolution: {integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==}
+ engines: {node: '>=0.10.0'}
+
+ prettier-linter-helpers@1.0.0:
+ resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+ engines: {node: '>=6.0.0'}
+
+ prettier@3.3.3:
+ resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
+ engines: {node: '>=14'}
+ hasBin: true
+
pretty-error@4.0.0:
resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==}
@@ -2885,10 +3269,19 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
+ protocols@1.4.8:
+ resolution: {integrity: sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==}
+
+ protocols@2.0.1:
+ resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==}
+
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@@ -2906,9 +3299,29 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ r-json@1.3.0:
+ resolution: {integrity: sha512-xesd+RHCpymPCYd9DvDvUr1w1IieSChkqYF1EpuAYrvCfLXji9NP36DvyYZJZZB5soVDvZ0WUtBoZaU1g5Yt9A==}
+
+ r-package-json@1.0.9:
+ resolution: {integrity: sha512-G4Vpf1KImWmmPFGdtWQTU0L9zk0SjqEC4qs/jE7AQ+Ylmr5kizMzGeC4wnHp5+ijPqNN+2ZPpvyjVNdN1CDVcg==}
+
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+ read-all-stream@3.1.0:
+ resolution: {integrity: sha512-DI1drPHbmBcUDWrJ7ull/F2Qb8HkwBncVx8/RpKYFSIACYaVRQReISYPdZz/mt1y1+qMCOrfReTopERmaxtP6w==}
+ engines: {node: '>=0.10.0'}
+
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@@ -2927,6 +3340,13 @@ packages:
resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
engines: {node: '>= 0.4'}
+ registry-auth-token@3.4.0:
+ resolution: {integrity: sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==}
+
+ registry-url@3.1.0:
+ resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==}
+ engines: {node: '>=0.10.0'}
+
renderkid@3.0.0:
resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==}
@@ -2985,6 +3405,9 @@ packages:
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
engines: {node: '>=0.4'}
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -3003,6 +3426,10 @@ packages:
resolution: {integrity: sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==}
engines: {node: '>= 0.10'}
+ semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -3056,6 +3483,9 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
+ sliced@1.0.1:
+ resolution: {integrity: sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==}
+
snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
@@ -3069,6 +3499,18 @@ packages:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
+ spdx-correct@3.2.0:
+ resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
+
+ spdx-exceptions@2.5.0:
+ resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==}
+
+ spdx-expression-parse@3.0.1:
+ resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+
+ spdx-license-ids@3.0.20:
+ resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==}
+
split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
@@ -3106,6 +3548,9 @@ packages:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
engines: {node: '>= 0.4'}
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -3129,6 +3574,10 @@ packages:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
+ strip-hex-prefix@1.0.0:
+ resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==}
+ engines: {node: '>=6.5.0', npm: '>=3'}
+
strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
@@ -3164,6 +3613,10 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ synckit@0.8.8:
+ resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
telegraf@4.16.3:
resolution: {integrity: sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==}
engines: {node: ^12.20.0 || >=14.13.1}
@@ -3192,6 +3645,14 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+ timed-out@3.1.3:
+ resolution: {integrity: sha512-3RB4qgvPkxF/FGPnrzaWLhW1rxNK2sdH0mFjbhxkfTR6QXvcM3EtYm9L44UrhODZrZ+yhDXeMncLqi8QXn2MJg==}
+ engines: {node: '>=0.10.0'}
+
+ tmp@0.0.28:
+ resolution: {integrity: sha512-c2mmfiBmND6SOVxzogm1oda0OJ1HZVIk/5n26N59dDTh80MUeavpiCls4PGAdkX1PFkKokLpcf7prSjCeXLsJg==}
+ engines: {node: '>=0.4.0'}
+
tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
@@ -3213,6 +3674,10 @@ packages:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
+ treeify@1.1.0:
+ resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==}
+ engines: {node: '>=0.6'}
+
triple-beam@1.4.1:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
@@ -3374,17 +3839,34 @@ packages:
typeorm-aurora-data-api-driver:
optional: true
+ typescript-collections@1.3.3:
+ resolution: {integrity: sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ==}
+
typescript@5.4.5:
resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
engines: {node: '>=14.17'}
hasBin: true
+ typpy@2.3.13:
+ resolution: {integrity: sha512-vOxIcQz9sxHi+rT09SJ5aDgVgrPppQjwnnayTrMye1ODaU8gIZTDM19t9TxmEElbMihx2Nq/0/b/MtyKfayRqA==}
+
+ ul@5.2.15:
+ resolution: {integrity: sha512-svLEUy8xSCip5IWnsRa0UOg+2zP0Wsj4qlbjTmX6GJSmvKMHADBuHOm1dpNkWqWPIGuVSqzUkV3Cris5JrlTRQ==}
+
unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+ undici@6.19.8:
+ resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==}
+ engines: {node: '>=18.17'}
+
+ unzip-response@1.0.2:
+ resolution: {integrity: sha512-pwCcjjhEcpW45JZIySExBHYv5Y9EeL2OIGEfrSKp2dMUFGFv4CpvZkwJbVge8OvGH2BNNtJBx67DuKuJhf+N5Q==}
+ engines: {node: '>=0.10'}
+
update-browserslist-db@1.0.15:
resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==}
hasBin: true
@@ -3394,13 +3876,23 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ url-parse-lax@1.0.0:
+ resolution: {integrity: sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==}
+ engines: {node: '>=0.10.0'}
+
utf-8-validate@5.0.10:
resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==}
engines: {node: '>=6.14.2'}
+ utf8@3.0.0:
+ resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==}
+
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ util@0.12.5:
+ resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
+
utila@0.4.0:
resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==}
@@ -3419,9 +3911,19 @@ packages:
resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==}
engines: {node: '>=10.12.0'}
+ validate-npm-package-license@3.0.4:
+ resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+
+ w-json@1.3.10:
+ resolution: {integrity: sha512-XadVyw0xE+oZ5FGApXsdswv96rOhStzKqL53uSe5UaTadABGkWIg1+DTx8kiZ/VqTZTBneoL0l65RcPe4W3ecw==}
+
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+ web3-utils@1.10.4:
+ resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==}
+ engines: {node: '>=8.0.0'}
+
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -3799,6 +4301,14 @@ snapshots:
'@eslint/js@8.57.0': {}
+ '@ethereumjs/rlp@4.0.1': {}
+
+ '@ethereumjs/util@8.1.0':
+ dependencies:
+ '@ethereumjs/rlp': 4.0.1
+ ethereum-cryptography: 2.2.1
+ micro-ftch: 0.3.1
+
'@faker-js/faker@8.4.1': {}
'@humanwhocodes/config-array@0.11.14':
@@ -4016,67 +4526,204 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
- '@noble/curves@1.4.0':
+ '@metaplex-foundation/beet-solana@0.4.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
dependencies:
- '@noble/hashes': 1.4.0
+ '@metaplex-foundation/beet': 0.7.2
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ bs58: 5.0.0
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
- '@noble/curves@1.6.0':
+ '@metaplex-foundation/beet@0.7.2':
dependencies:
- '@noble/hashes': 1.5.0
+ ansicolors: 0.3.2
+ assert: 2.1.0
+ bn.js: 5.2.1
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
- '@noble/hashes@1.4.0': {}
+ '@metaplex-foundation/digital-asset-standard-api@1.0.4(@metaplex-foundation/umi@0.9.2)':
+ dependencies:
+ '@metaplex-foundation/umi': 0.9.2
+ package.json: 2.0.1
- '@noble/hashes@1.5.0': {}
+ '@metaplex-foundation/mpl-bubblegum@4.2.1(@metaplex-foundation/umi@0.9.2)':
+ dependencies:
+ '@metaplex-foundation/digital-asset-standard-api': 1.0.4(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/mpl-token-metadata': 3.2.1(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/mpl-toolbox': 0.9.4(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi': 0.9.2
+ '@noble/hashes': 1.5.0
+ merkletreejs: 0.3.11
- '@nodelib/fs.scandir@2.1.5':
+ '@metaplex-foundation/mpl-token-metadata@3.2.1(@metaplex-foundation/umi@0.9.2)':
dependencies:
- '@nodelib/fs.stat': 2.0.5
- run-parallel: 1.2.0
+ '@metaplex-foundation/mpl-toolbox': 0.9.4(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi': 0.9.2
- '@nodelib/fs.stat@2.0.5': {}
+ '@metaplex-foundation/mpl-toolbox@0.9.4(@metaplex-foundation/umi@0.9.2)':
+ dependencies:
+ '@metaplex-foundation/umi': 0.9.2
- '@nodelib/fs.walk@1.2.8':
+ '@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
dependencies:
- '@nodelib/fs.scandir': 2.1.5
- fastq: 1.17.1
+ '@metaplex-foundation/umi': 0.9.2
+ '@metaplex-foundation/umi-downloader-http': 0.9.2(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi-eddsa-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@metaplex-foundation/umi-http-fetch': 0.9.2(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi-rpc-chunk-get-accounts': 0.9.2(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@metaplex-foundation/umi-serializer-data-view': 0.9.2(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ transitivePeerDependencies:
+ - encoding
- '@opentelemetry/api-logs@0.51.1':
+ '@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2)':
dependencies:
- '@opentelemetry/api': 1.9.0
+ '@metaplex-foundation/umi': 0.9.2
- '@opentelemetry/api-logs@0.52.0':
+ '@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
dependencies:
- '@opentelemetry/api': 1.9.0
+ '@metaplex-foundation/umi': 0.9.2
+ '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@noble/curves': 1.6.0
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
- '@opentelemetry/api-logs@0.52.1':
+ '@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2)':
dependencies:
- '@opentelemetry/api': 1.9.0
+ '@metaplex-foundation/umi': 0.9.2
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
- '@opentelemetry/api@1.9.0': {}
+ '@metaplex-foundation/umi-options@0.8.9': {}
- '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)':
+ '@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2)':
dependencies:
- '@opentelemetry/api': 1.9.0
+ '@metaplex-foundation/umi': 0.9.2
- '@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0)':
+ '@metaplex-foundation/umi-public-keys@0.8.9':
dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/semantic-conventions': 1.25.0
+ '@metaplex-foundation/umi-serializers-encodings': 0.8.9
- '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)':
+ '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2)':
dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/semantic-conventions': 1.25.1
+ '@metaplex-foundation/umi': 0.9.2
- '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)':
+ '@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.25.1
- '@types/connect': 3.4.36
- transitivePeerDependencies:
- - supports-color
+ '@metaplex-foundation/umi': 0.9.2
+ '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+
+ '@metaplex-foundation/umi-serializer-data-view@0.9.2(@metaplex-foundation/umi@0.9.2)':
+ dependencies:
+ '@metaplex-foundation/umi': 0.9.2
+
+ '@metaplex-foundation/umi-serializers-core@0.8.9': {}
+
+ '@metaplex-foundation/umi-serializers-encodings@0.8.9':
+ dependencies:
+ '@metaplex-foundation/umi-serializers-core': 0.8.9
+
+ '@metaplex-foundation/umi-serializers-numbers@0.8.9':
+ dependencies:
+ '@metaplex-foundation/umi-serializers-core': 0.8.9
+
+ '@metaplex-foundation/umi-serializers@0.9.0':
+ dependencies:
+ '@metaplex-foundation/umi-options': 0.8.9
+ '@metaplex-foundation/umi-public-keys': 0.8.9
+ '@metaplex-foundation/umi-serializers-core': 0.8.9
+ '@metaplex-foundation/umi-serializers-encodings': 0.8.9
+ '@metaplex-foundation/umi-serializers-numbers': 0.8.9
+
+ '@metaplex-foundation/umi-transaction-factory-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@metaplex-foundation/umi': 0.9.2
+ '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+
+ '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@metaplex-foundation/umi': 0.9.2
+ '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ buffer: 6.0.3
+
+ '@metaplex-foundation/umi@0.9.2':
+ dependencies:
+ '@metaplex-foundation/umi-options': 0.8.9
+ '@metaplex-foundation/umi-public-keys': 0.8.9
+ '@metaplex-foundation/umi-serializers': 0.9.0
+
+ '@noble/curves@1.4.2':
+ dependencies:
+ '@noble/hashes': 1.4.0
+
+ '@noble/curves@1.6.0':
+ dependencies:
+ '@noble/hashes': 1.5.0
+
+ '@noble/hashes@1.4.0': {}
+
+ '@noble/hashes@1.5.0': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@opentelemetry/api-logs@0.51.1':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/api-logs@0.52.0':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/api-logs@0.52.1':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/api@1.9.0': {}
+
+ '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.25.0
+
+ '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/semantic-conventions': 1.25.1
+
+ '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)':
+ dependencies:
+ '@opentelemetry/api': 1.9.0
+ '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)
+ '@opentelemetry/semantic-conventions': 1.25.1
+ '@types/connect': 3.4.36
+ transitivePeerDependencies:
+ - supports-color
'@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)':
dependencies:
@@ -4288,6 +4935,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@pkgr/core@0.1.1': {}
+
'@prisma/instrumentation@5.15.0':
dependencies:
'@opentelemetry/api': 1.9.0
@@ -4302,6 +4951,19 @@ snapshots:
bn.js: 5.2.1
buffer-layout: 1.2.2
+ '@scure/base@1.1.8': {}
+
+ '@scure/bip32@1.4.0':
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.8
+
+ '@scure/bip39@1.3.0':
+ dependencies:
+ '@noble/hashes': 1.4.0
+ '@scure/base': 1.1.8
+
'@sentry-internal/tracing@7.114.0':
dependencies:
'@sentry/core': 7.114.0
@@ -4507,13 +5169,20 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- '@solana/spl-token-group@0.0.4(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)':
+ '@solana/spl-account-compression@0.2.0(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
dependencies:
- '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22)
- '@solana/spl-type-length-value': 0.1.0
+ '@metaplex-foundation/beet': 0.7.2
+ '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ bn.js: 5.2.1
+ borsh: 0.7.0
+ js-sha3: 0.8.0
+ typescript-collections: 1.3.3
transitivePeerDependencies:
- - fastestsmallesttextencoderdecoder
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
'@solana/spl-token-group@0.0.5(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)':
dependencies:
@@ -4545,20 +5214,6 @@ snapshots:
- fastestsmallesttextencoderdecoder
- utf-8-validate
- '@solana/spl-token@0.4.6(@solana/web3.js@1.95.3(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-group': 0.0.4(@solana/web3.js@1.95.3(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.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)
- '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
- buffer: 6.0.3
- transitivePeerDependencies:
- - bufferutil
- - encoding
- - fastestsmallesttextencoderdecoder
- - utf-8-validate
-
'@solana/spl-token@0.4.8(@solana/web3.js@1.95.3(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
@@ -4629,7 +5284,7 @@ snapshots:
'@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3(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.3(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.7(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
@@ -4657,12 +5312,12 @@ snapshots:
- typescript
- 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.95.3(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.3(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.7(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- bufferutil
- encoding
@@ -4685,23 +5340,30 @@ snapshots:
- typescript
- utf-8-validate
- '@staratlas/data-source@0.7.4(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)':
+ '@staratlas/crew@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.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)
+ '@metaplex-foundation/mpl-bubblegum': 4.2.1(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/mpl-token-metadata': 3.2.1(@metaplex-foundation/umi@0.9.2)
+ '@metaplex-foundation/umi': 0.9.2
+ '@metaplex-foundation/umi-bundle-defaults': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))
+ '@project-serum/anchor': '@staratlas/anchor@0.25.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)'
+ '@solana/spl-account-compression': 0.2.0(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.3(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.3(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
- lodash: 4.17.21
- neverthrow: 6.2.1
+ '@staratlas/data-source': 0.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/player-profile': 0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
+ - supports-color
+ - typescript
- utf-8-validate
- '@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.7(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)':
dependencies:
'@noble/curves': 1.6.0
'@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)
@@ -4734,10 +5396,10 @@ snapshots:
- typescript
- utf-8-validate
- '@staratlas/factory@0.7.0(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:
'@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.95.3(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.3(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.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)
big.js: 6.2.1
lodash: 4.17.21
@@ -4747,6 +5409,7 @@ snapshots:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
+ - typescript
- utf-8-validate
'@staratlas/player-profile@0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
@@ -4765,24 +5428,41 @@ snapshots:
dependencies:
'@solana/web3.js': 1.95.3(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.7(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-store@1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
dependencies:
- '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3(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.3(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.3(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/player-profile': 0.9.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)
+ '@staratlas/data-source': 0.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/player-profile': 0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/points': 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
+ - typescript
+ - utf-8-validate
+
+ '@staratlas/points@1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.3(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.3(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.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/player-profile': 0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - fastestsmallesttextencoderdecoder
+ - typescript
- utf-8-validate
'@staratlas/profile-faction@0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)':
@@ -4790,7 +5470,7 @@ snapshots:
'@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3(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.3(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.7(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
@@ -4798,12 +5478,26 @@ snapshots:
- fastestsmallesttextencoderdecoder
- utf-8-validate
+ '@staratlas/profile-faction@0.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
+ dependencies:
+ '@solana/spl-token': 0.4.8(@solana/web3.js@1.95.3(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.3(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.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/player-profile': 0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - fastestsmallesttextencoderdecoder
+ - typescript
+ - utf-8-validate
+
'@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.95.3(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.3(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.7(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
@@ -4811,23 +5505,26 @@ snapshots:
- fastestsmallesttextencoderdecoder
- utf-8-validate
- '@staratlas/sage@1.0.2(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
+ '@staratlas/sage@1.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)':
dependencies:
- '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.3(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.3(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.3(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.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
'@staratlas/crafting': 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(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/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/profile-faction': 0.4.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@5.0.10)
+ '@staratlas/crew': 0.7.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/data-source': 0.8.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/player-profile': 0.11.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/points': 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/points-store': 1.1.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
+ '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.4.5)(utf-8-validate@5.0.10)
'@types/lodash': 4.17.0
lodash: 4.17.21
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
+ - supports-color
- typescript
- utf-8-validate
@@ -4886,6 +5583,8 @@ snapshots:
'@types/node': 20.12.8
base-x: 3.0.9
+ '@types/chance@1.1.6': {}
+
'@types/connect@3.4.36':
dependencies:
'@types/node': 20.12.11
@@ -4941,6 +5640,10 @@ snapshots:
'@types/keygrip@1.0.6': {}
+ '@types/keyv@3.1.4':
+ dependencies:
+ '@types/node': 20.12.11
+
'@types/koa-compose@3.2.8':
dependencies:
'@types/koa': 2.14.0
@@ -5002,6 +5705,10 @@ snapshots:
'@types/range-parser@1.2.7': {}
+ '@types/responselike@1.0.3':
+ dependencies:
+ '@types/node': 20.12.11
+
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
@@ -5137,6 +5844,10 @@ snapshots:
dependencies:
event-target-shim: 5.0.1
+ abs@1.3.14:
+ dependencies:
+ ul: 5.2.15
+
acorn-import-assertions@1.9.0(acorn@8.11.3):
dependencies:
acorn: 8.11.3
@@ -5185,6 +5896,8 @@ snapshots:
ansi-styles@6.2.1: {}
+ ansicolors@0.3.2: {}
+
any-promise@1.3.0: {}
anymatch@3.1.3:
@@ -5254,6 +5967,14 @@ snapshots:
asap@2.0.6: {}
+ assert@2.1.0:
+ dependencies:
+ call-bind: 1.0.7
+ is-nan: 1.3.2
+ object-is: 1.1.6
+ object.assign: 4.1.5
+ util: 0.12.5
+
async@3.2.5: {}
asynckit@0.4.0: {}
@@ -5352,6 +6073,8 @@ snapshots:
dependencies:
'@noble/hashes': 1.4.0
+ bn.js@4.11.6: {}
+
bn.js@5.2.1: {}
boolbase@1.0.0: {}
@@ -5415,6 +6138,8 @@ snapshots:
buffer-layout@1.2.2: {}
+ buffer-reverse@1.0.1: {}
+
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
@@ -5445,6 +6170,8 @@ snapshots:
caniuse-lite@1.0.30001617: {}
+ capture-stack-trace@1.0.2: {}
+
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
@@ -5458,6 +6185,8 @@ snapshots:
chalk@5.3.0: {}
+ chance@1.1.12: {}
+
char-regex@1.0.2: {}
chokidar@3.6.0:
@@ -5549,6 +6278,12 @@ snapshots:
cookiejar@2.1.4: {}
+ core-util-is@1.0.3: {}
+
+ create-error-class@3.0.2:
+ dependencies:
+ capture-stack-trace: 1.0.2
+
create-hash@1.2.0:
dependencies:
cipher-base: 1.0.4
@@ -5602,6 +6337,8 @@ snapshots:
crypto-hash@1.3.0: {}
+ crypto-js@4.2.0: {}
+
css-select@4.3.0:
dependencies:
boolbase: 1.0.0
@@ -5642,10 +6379,16 @@ snapshots:
dedent@1.5.3: {}
+ deep-extend@0.6.0: {}
+
deep-is@0.1.4: {}
deepmerge@4.3.1: {}
+ deffy@2.2.4:
+ dependencies:
+ typpy: 2.3.13
+
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.0
@@ -5714,6 +6457,10 @@ snapshots:
dotenv@16.4.5: {}
+ duplexer2@0.1.4:
+ dependencies:
+ readable-stream: 2.3.8
+
dynamic-dedupe@0.3.0:
dependencies:
xtend: 4.0.2
@@ -5737,6 +6484,10 @@ snapshots:
entities@2.2.0: {}
+ err@1.1.1:
+ dependencies:
+ typpy: 2.3.13
+
error-ex@1.3.2:
dependencies:
is-arrayish: 0.2.1
@@ -5830,6 +6581,10 @@ snapshots:
escape-string-regexp@4.0.0: {}
+ eslint-config-prettier@9.1.0(eslint@8.57.0):
+ dependencies:
+ eslint: 8.57.0
+
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
@@ -5883,6 +6638,15 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
+ eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3):
+ dependencies:
+ eslint: 8.57.0
+ prettier: 3.3.3
+ prettier-linter-helpers: 1.0.0
+ synckit: 0.8.8
+ optionalDependencies:
+ eslint-config-prettier: 9.1.0(eslint@8.57.0)
+
eslint-plugin-promise@6.2.0(eslint@8.57.0):
dependencies:
eslint: 8.57.0
@@ -5957,12 +6721,33 @@ snapshots:
esutils@2.0.3: {}
+ ethereum-bloom-filters@1.2.0:
+ dependencies:
+ '@noble/hashes': 1.5.0
+
+ ethereum-cryptography@2.2.1:
+ dependencies:
+ '@noble/curves': 1.4.2
+ '@noble/hashes': 1.4.0
+ '@scure/bip32': 1.4.0
+ '@scure/bip39': 1.3.0
+
+ ethjs-unit@0.1.6:
+ dependencies:
+ bn.js: 4.11.6
+ number-to-bn: 1.7.0
+
event-target-shim@5.0.1: {}
eventemitter3@4.0.7: {}
eventemitter3@5.0.1: {}
+ exec-limiter@3.2.13:
+ dependencies:
+ limit-it: 3.2.10
+ typpy: 2.3.13
+
execa@5.1.1:
dependencies:
cross-spawn: 7.0.3
@@ -5989,6 +6774,8 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fast-diff@1.3.0: {}
+
fast-glob@3.3.2:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -6077,6 +6864,10 @@ snapshots:
function-bind@1.1.2: {}
+ function.name@1.0.13:
+ dependencies:
+ noop6: 1.0.9
+
function.prototype.name@1.1.6:
dependencies:
call-bind: 1.0.7
@@ -6108,6 +6899,31 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
+ git-package-json@1.4.10:
+ dependencies:
+ deffy: 2.2.4
+ err: 1.1.1
+ gry: 5.0.8
+ normalize-package-data: 2.5.0
+ oargv: 3.4.10
+ one-by-one: 3.2.8
+ r-json: 1.3.0
+ r-package-json: 1.0.9
+ tmp: 0.0.28
+
+ git-source@1.1.10:
+ dependencies:
+ git-url-parse: 5.0.1
+
+ git-up@1.2.1:
+ dependencies:
+ is-ssh: 1.4.0
+ parse-url: 1.3.11
+
+ git-url-parse@5.0.1:
+ dependencies:
+ git-up: 1.2.1
+
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -6157,10 +6973,37 @@ snapshots:
dependencies:
get-intrinsic: 1.2.4
+ got@5.7.1:
+ dependencies:
+ '@types/keyv': 3.1.4
+ '@types/responselike': 1.0.3
+ create-error-class: 3.0.2
+ duplexer2: 0.1.4
+ is-redirect: 1.0.0
+ is-retry-allowed: 1.2.0
+ is-stream: 1.1.0
+ lowercase-keys: 1.0.1
+ node-status-codes: 1.0.0
+ object-assign: 4.1.1
+ parse-json: 2.2.0
+ pinkie-promise: 2.0.1
+ read-all-stream: 3.1.0
+ readable-stream: 2.3.8
+ timed-out: 3.1.3
+ unzip-response: 1.0.2
+ url-parse-lax: 1.0.0
+
graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
+ gry@5.0.8:
+ dependencies:
+ abs: 1.3.14
+ exec-limiter: 3.2.13
+ one-by-one: 3.2.8
+ ul: 5.2.15
+
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -6193,6 +7036,8 @@ snapshots:
highlight.js@10.7.3: {}
+ hosted-git-info@2.8.9: {}
+
html-escaper@2.0.2: {}
htmlparser2@6.1.0:
@@ -6262,12 +7107,19 @@ snapshots:
inherits@2.0.4: {}
+ ini@1.3.8: {}
+
internal-slot@1.0.7:
dependencies:
es-errors: 1.3.0
hasown: 2.0.2
side-channel: 1.0.6
+ is-arguments@1.1.1:
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+
is-array-buffer@3.0.4:
dependencies:
call-bind: 1.0.7
@@ -6310,10 +7162,21 @@ snapshots:
is-generator-fn@2.1.0: {}
+ is-generator-function@1.0.10:
+ dependencies:
+ has-tostringtag: 1.0.2
+
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
+ is-hex-prefixed@1.0.0: {}
+
+ is-nan@1.3.2:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+
is-negative-zero@2.0.3: {}
is-number-object@1.0.7:
@@ -6324,15 +7187,25 @@ snapshots:
is-path-inside@3.0.3: {}
+ is-redirect@1.0.0: {}
+
is-regex@1.1.4:
dependencies:
call-bind: 1.0.7
has-tostringtag: 1.0.2
+ is-retry-allowed@1.2.0: {}
+
is-shared-array-buffer@1.0.3:
dependencies:
call-bind: 1.0.7
+ is-ssh@1.4.0:
+ dependencies:
+ protocols: 2.0.1
+
+ is-stream@1.1.0: {}
+
is-stream@2.0.1: {}
is-string@1.0.7:
@@ -6351,6 +7224,8 @@ snapshots:
dependencies:
call-bind: 1.0.7
+ isarray@1.0.0: {}
+
isarray@2.0.5: {}
isexe@2.0.0: {}
@@ -6400,6 +7275,8 @@ snapshots:
html-escaper: 2.0.2
istanbul-lib-report: 3.0.1
+ iterate-object@1.3.4: {}
+
jackspeak@2.3.6:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -6735,6 +7612,8 @@ snapshots:
js-sha256@0.9.0: {}
+ js-sha3@0.8.0: {}
+
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -6785,6 +7664,10 @@ snapshots:
dependencies:
immediate: 3.0.6
+ limit-it@3.2.10:
+ dependencies:
+ typpy: 2.3.13
+
lines-and-columns@1.2.4: {}
localforage@1.10.0:
@@ -6826,6 +7709,8 @@ snapshots:
dependencies:
tslib: 2.6.2
+ lowercase-keys@1.0.1: {}
+
lru-cache@10.2.2: {}
lru-cache@5.1.1:
@@ -6854,8 +7739,18 @@ snapshots:
merge2@1.4.1: {}
+ merkletreejs@0.3.11:
+ dependencies:
+ bignumber.js: 9.1.2
+ buffer-reverse: 1.0.1
+ crypto-js: 4.2.0
+ treeify: 1.1.0
+ web3-utils: 1.10.4
+
methods@1.1.2: {}
+ micro-ftch@0.3.1: {}
+
micromatch@4.0.5:
dependencies:
braces: 3.0.2
@@ -6929,6 +7824,17 @@ snapshots:
node-releases@2.0.14: {}
+ node-status-codes@1.0.0: {}
+
+ noop6@1.0.9: {}
+
+ normalize-package-data@2.5.0:
+ dependencies:
+ hosted-git-info: 2.8.9
+ resolve: 1.22.8
+ semver: 5.7.2
+ validate-npm-package-license: 3.0.4
+
normalize-path@3.0.0: {}
npm-run-path@4.0.1:
@@ -6939,10 +7845,29 @@ snapshots:
dependencies:
boolbase: 1.0.0
+ number-to-bn@1.7.0:
+ dependencies:
+ bn.js: 4.11.6
+ strip-hex-prefix: 1.0.0
+
+ oargv@3.4.10:
+ dependencies:
+ iterate-object: 1.3.4
+ ul: 5.2.15
+
+ obj-def@1.0.9:
+ dependencies:
+ deffy: 2.2.4
+
object-assign@4.1.1: {}
object-inspect@1.13.1: {}
+ object-is@1.1.6:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+
object-keys@1.1.1: {}
object.assign@4.1.5:
@@ -6977,6 +7902,11 @@ snapshots:
dependencies:
wrappy: 1.0.2
+ one-by-one@3.2.8:
+ dependencies:
+ obj-def: 1.0.9
+ sliced: 1.0.1
+
one-time@1.0.0:
dependencies:
fn.name: 1.1.0
@@ -7003,6 +7933,8 @@ snapshots:
type-check: 0.4.0
word-wrap: 1.2.5
+ os-tmpdir@1.0.2: {}
+
p-limit@2.3.0:
dependencies:
p-try: 2.2.0
@@ -7023,12 +7955,33 @@ snapshots:
p-try@2.2.0: {}
+ package-json-path@1.0.9:
+ dependencies:
+ abs: 1.3.14
+
+ package-json@2.4.0:
+ dependencies:
+ got: 5.7.1
+ registry-auth-token: 3.4.0
+ registry-url: 3.1.0
+ semver: 5.7.2
+
+ package.json@2.0.1:
+ dependencies:
+ git-package-json: 1.4.10
+ git-source: 1.1.10
+ package-json: 2.4.0
+
pako@2.1.0: {}
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
+ parse-json@2.2.0:
+ dependencies:
+ error-ex: 1.3.2
+
parse-json@5.2.0:
dependencies:
'@babel/code-frame': 7.24.2
@@ -7036,6 +7989,11 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse-url@1.3.11:
+ dependencies:
+ is-ssh: 1.4.0
+ protocols: 1.4.8
+
parse5-htmlparser2-tree-adapter@6.0.1:
dependencies:
parse5: 6.0.1
@@ -7110,6 +8068,12 @@ snapshots:
picomatch@2.3.1: {}
+ pinkie-promise@2.0.1:
+ dependencies:
+ pinkie: 2.0.4
+
+ pinkie@2.0.4: {}
+
pirates@4.0.6: {}
pkg-dir@4.2.0:
@@ -7142,6 +8106,14 @@ snapshots:
prelude-ls@1.2.1: {}
+ prepend-http@1.0.4: {}
+
+ prettier-linter-helpers@1.0.0:
+ dependencies:
+ fast-diff: 1.3.0
+
+ prettier@3.3.3: {}
+
pretty-error@4.0.0:
dependencies:
lodash: 4.17.21
@@ -7153,11 +8125,17 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
+ process-nextick-args@2.0.1: {}
+
prompts@2.4.2:
dependencies:
kleur: 3.0.3
sisteransi: 1.0.5
+ protocols@1.4.8: {}
+
+ protocols@2.0.1: {}
+
proxy-from-env@1.1.0: {}
punycode@2.3.1: {}
@@ -7170,8 +8148,43 @@ snapshots:
queue-microtask@1.2.3: {}
+ r-json@1.3.0:
+ dependencies:
+ w-json: 1.3.10
+
+ r-package-json@1.0.9:
+ dependencies:
+ package-json-path: 1.0.9
+ r-json: 1.3.0
+
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
react-is@18.3.1: {}
+ read-all-stream@3.1.0:
+ dependencies:
+ pinkie-promise: 2.0.1
+ readable-stream: 2.3.8
+
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@@ -7193,6 +8206,15 @@ snapshots:
es-errors: 1.3.0
set-function-name: 2.0.2
+ registry-auth-token@3.4.0:
+ dependencies:
+ rc: 1.2.8
+ safe-buffer: 5.2.1
+
+ registry-url@3.1.0:
+ dependencies:
+ rc: 1.2.8
+
renderkid@3.0.0:
dependencies:
css-select: 4.3.0
@@ -7266,6 +8288,8 @@ snapshots:
has-symbols: 1.0.3
isarray: 2.0.5
+ safe-buffer@5.1.2: {}
+
safe-buffer@5.2.1: {}
safe-compare@1.1.4:
@@ -7282,6 +8306,8 @@ snapshots:
sandwich-stream@2.0.2: {}
+ semver@5.7.2: {}
+
semver@6.3.1: {}
semver@7.6.2: {}
@@ -7334,6 +8360,8 @@ snapshots:
slash@3.0.0: {}
+ sliced@1.0.1: {}
+
snake-case@3.0.4:
dependencies:
dot-case: 3.0.4
@@ -7351,6 +8379,20 @@ snapshots:
source-map@0.6.1: {}
+ spdx-correct@3.2.0:
+ dependencies:
+ spdx-expression-parse: 3.0.1
+ spdx-license-ids: 3.0.20
+
+ spdx-exceptions@2.5.0: {}
+
+ spdx-expression-parse@3.0.1:
+ dependencies:
+ spdx-exceptions: 2.5.0
+ spdx-license-ids: 3.0.20
+
+ spdx-license-ids@3.0.20: {}
+
split2@4.2.0: {}
sprintf-js@1.0.3: {}
@@ -7397,6 +8439,10 @@ snapshots:
define-properties: 1.2.1
es-object-atoms: 1.0.0
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
@@ -7415,6 +8461,10 @@ snapshots:
strip-final-newline@2.0.0: {}
+ strip-hex-prefix@1.0.0:
+ dependencies:
+ is-hex-prefixed: 1.0.0
+
strip-json-comments@2.0.1: {}
strip-json-comments@3.1.1: {}
@@ -7451,6 +8501,11 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ synckit@0.8.8:
+ dependencies:
+ '@pkgr/core': 0.1.1
+ tslib: 2.6.2
+
telegraf@4.16.3:
dependencies:
'@telegraf/types': 7.1.0
@@ -7487,6 +8542,12 @@ snapshots:
through@2.3.8: {}
+ timed-out@3.1.3: {}
+
+ tmp@0.0.28:
+ dependencies:
+ os-tmpdir: 1.0.2
+
tmpl@1.0.5: {}
to-fast-properties@2.0.0: {}
@@ -7501,6 +8562,8 @@ snapshots:
tree-kill@1.2.2: {}
+ treeify@1.1.0: {}
+
triple-beam@1.4.1: {}
ts-api-utils@1.3.0(typescript@5.4.5):
@@ -7644,8 +8707,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ typescript-collections@1.3.3: {}
+
typescript@5.4.5: {}
+ typpy@2.3.13:
+ dependencies:
+ function.name: 1.0.13
+
+ ul@5.2.15:
+ dependencies:
+ deffy: 2.2.4
+ typpy: 2.3.13
+
unbox-primitive@1.0.2:
dependencies:
call-bind: 1.0.7
@@ -7655,6 +8729,10 @@ snapshots:
undici-types@5.26.5: {}
+ undici@6.19.8: {}
+
+ unzip-response@1.0.2: {}
+
update-browserslist-db@1.0.15(browserslist@4.23.0):
dependencies:
browserslist: 4.23.0
@@ -7665,13 +8743,27 @@ snapshots:
dependencies:
punycode: 2.3.1
+ url-parse-lax@1.0.0:
+ dependencies:
+ prepend-http: 1.0.4
+
utf-8-validate@5.0.10:
dependencies:
node-gyp-build: 4.8.1
optional: true
+ utf8@3.0.0: {}
+
util-deprecate@1.0.2: {}
+ util@0.12.5:
+ dependencies:
+ inherits: 2.0.4
+ is-arguments: 1.1.1
+ is-generator-function: 1.0.10
+ is-typed-array: 1.1.13
+ which-typed-array: 1.1.15
+
utila@0.4.0: {}
uuid@8.3.2: {}
@@ -7686,10 +8778,28 @@ snapshots:
'@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 2.0.0
+ validate-npm-package-license@3.0.4:
+ dependencies:
+ spdx-correct: 3.2.0
+ spdx-expression-parse: 3.0.1
+
+ w-json@1.3.10: {}
+
walker@1.0.8:
dependencies:
makeerror: 1.0.12
+ web3-utils@1.10.4:
+ dependencies:
+ '@ethereumjs/util': 8.1.0
+ bn.js: 5.2.1
+ ethereum-bloom-filters: 1.2.0
+ ethereum-cryptography: 2.2.1
+ ethjs-unit: 0.1.6
+ number-to-bn: 1.7.0
+ randombytes: 2.1.0
+ utf8: 3.0.0
+
webidl-conversions@3.0.1: {}
whatwg-url@5.0.0:
diff --git a/src/config/config.ts b/src/config/config.ts
index 96122668..c72a5a5c 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
@@ -52,19 +53,23 @@ export const config: Config = {
app: {
version: env.get('APP_VERSION'),
logLevel: env.get('LOG_LEVEL'),
- quickstart: env.get('QUICKSTART') === 'true'
+ quickstart: env.get('QUICKSTART') === 'true',
},
bot: {
telegramToken: env.get('TELEGRAM_TOKEN'),
- owner: env.get('BOT_OWNER')
+ owner: env.get('BOT_OWNER'),
},
user: {
keyMode: env.get('KEY_MODE'),
- secretKey: env.get('SECRET_KEY').split(',').map(s => Number(s)),
+ 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'),
- address2: env.get('BOT_ADDRESS_2')
+ address2: env.get('BOT_ADDRESS_2'),
},
db: {
host: env.get('DATABASE_HOST'),
@@ -72,7 +77,7 @@ export const config: Config = {
username: env.get('DATABASE_USER'),
password: env.get('DATABASE_PASSWORD'),
database: env.get('DATABASE_NAME'),
- logging: env.get('DATABASE_LOGGING') as LoggerOptions
+ logging: env.get('DATABASE_LOGGING') as LoggerOptions,
},
sol: {
rpcEndpoint: env.get('RPC_ENDPOINT'),
@@ -83,11 +88,11 @@ export const config: Config = {
toolMint: env.get('TOOL_MINT'),
foodMint: env.get('FOOD_MINT'),
fuelMint: env.get('FUEL_MINT'),
- ammoMint: env.get('AMMO_MINT')
+ ammoMint: env.get('AMMO_MINT'),
},
cron: {
refillInterval: env.get('REFILL_INTERVAL'),
bookkeeperInterval: env.get('BOOKKEEPER_INTERVAL'),
- resourceInterval: env.get('RESOURCE_INTERVAL')
- }
+ resourceInterval: env.get('RESOURCE_INTERVAL'),
+ },
}
diff --git a/src/dayjs.ts b/src/dayjs.ts
index 3a4c2a38..90c48db8 100644
--- a/src/dayjs.ts
+++ b/src/dayjs.ts
@@ -1,7 +1,7 @@
import dayjs from 'dayjs'
+import advancedFormat from 'dayjs/plugin/advancedFormat'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import duration from 'dayjs/plugin/duration'
-import advancedFormat from 'dayjs/plugin/advancedFormat'
import minMax from 'dayjs/plugin/minMax'
import relativeTime from 'dayjs/plugin/relativeTime'
import utc from 'dayjs/plugin/utc'
@@ -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/db/columns/relation-id.ts b/src/db/columns/relation-id.ts
index 3ec56419..f9c5a347 100644
--- a/src/db/columns/relation-id.ts
+++ b/src/db/columns/relation-id.ts
@@ -1,3 +1,5 @@
import { Column, ColumnOptions } from 'typeorm'
-export const RelationIdColumn = (options: ColumnOptions = {}): ReturnType => Column(options)
+export const RelationIdColumn = (
+ options: ColumnOptions = {},
+): ReturnType => Column(options)
diff --git a/src/db/db-config.ts b/src/db/db-config.ts
index ebf2c1ab..303174d2 100644
--- a/src/db/db-config.ts
+++ b/src/db/db-config.ts
@@ -9,7 +9,9 @@ const dbConfig: DataSourceOptions = {
synchronize: false,
entities: [`${__dirname}/entities/**/!(*test).{ts,js}`],
migrations: [`${__dirname}/migrations/**/*.{ts,js}`],
- logging: ((config.db.logging || 'all') as string).split(',').map(l => l.trim()) as LoggerOptions
+ logging: ((config.db.logging || 'all') as string)
+ .split(',')
+ .map((l) => l.trim()) as LoggerOptions,
}
export default dbConfig
diff --git a/src/db/db.ts b/src/db/db.ts
index 121262b4..b5ab2aaa 100644
--- a/src/db/db.ts
+++ b/src/db/db.ts
@@ -11,7 +11,10 @@ export let dataSource: DataSource
export const connect = async (logging?: LoggerOptions): Promise => {
logger.info(`Attempting db connection for ${config.db.database}`)
- dataSource = new DataSource({ ...dbConfig, logging: logging || dbConfig.logging })
+ dataSource = new DataSource({
+ ...dbConfig,
+ logging: logging || dbConfig.logging,
+ })
await dataSource.initialize()
diff --git a/src/db/entities/bonus.ts b/src/db/entities/bonus.ts
index 63c75e6c..8438b51e 100644
--- a/src/db/entities/bonus.ts
+++ b/src/db/entities/bonus.ts
@@ -1,4 +1,10 @@
-import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
+import {
+ BaseEntity,
+ Column,
+ Entity,
+ ManyToOne,
+ PrimaryGeneratedColumn,
+} from 'typeorm'
import { RelationIdColumn } from '../columns'
@@ -21,6 +27,8 @@ export class Bonus extends BaseEntity {
@RelationIdColumn()
walletPublicKey: Wallet['publicKey']
- @ManyToOne(() => Wallet, wallet => wallet.transactions, { onDelete: 'CASCADE' })
+ @ManyToOne(() => Wallet, (wallet) => wallet.transactions, {
+ onDelete: 'CASCADE',
+ })
wallet: Wallet
}
diff --git a/src/db/entities/refill.ts b/src/db/entities/refill.ts
index ae24a449..2b4e1eda 100644
--- a/src/db/entities/refill.ts
+++ b/src/db/entities/refill.ts
@@ -42,6 +42,8 @@ export class Refill extends BaseEntity {
@RelationIdColumn()
walletPublicKey: Wallet['publicKey']
- @ManyToOne(() => Wallet, wallet => wallet.refills, { onDelete: 'CASCADE' })
+ @ManyToOne(() => Wallet, (wallet) => wallet.refills, {
+ onDelete: 'CASCADE',
+ })
wallet: Wallet
}
diff --git a/src/db/entities/ship-info.ts b/src/db/entities/ship-info.ts
index 2a052ccd..ebf43c25 100644
--- a/src/db/entities/ship-info.ts
+++ b/src/db/entities/ship-info.ts
@@ -1,4 +1,11 @@
-import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'
+import {
+ BaseEntity,
+ Column,
+ CreateDateColumn,
+ Entity,
+ PrimaryColumn,
+ UpdateDateColumn,
+} from 'typeorm'
@Entity()
export class ShipInfo extends BaseEntity {
diff --git a/src/db/entities/transaction.ts b/src/db/entities/transaction.ts
index 4bdc2c37..4e65de9c 100644
--- a/src/db/entities/transaction.ts
+++ b/src/db/entities/transaction.ts
@@ -1,4 +1,12 @@
-import { BaseEntity, Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, Unique } from 'typeorm'
+import {
+ BaseEntity,
+ Column,
+ Entity,
+ Index,
+ ManyToOne,
+ PrimaryGeneratedColumn,
+ Unique,
+} from 'typeorm'
import { RelationIdColumn } from '../columns'
@@ -29,6 +37,8 @@ export class Transaction extends BaseEntity {
@RelationIdColumn()
walletPublicKey: Wallet['publicKey']
- @ManyToOne(() => Wallet, wallet => wallet.transactions, { onDelete: 'CASCADE' })
+ @ManyToOne(() => Wallet, (wallet) => wallet.transactions, {
+ onDelete: 'CASCADE',
+ })
wallet: Wallet
}
diff --git a/src/db/entities/wallet.ts b/src/db/entities/wallet.ts
index 84811a94..fe2d8064 100644
--- a/src/db/entities/wallet.ts
+++ b/src/db/entities/wallet.ts
@@ -1,5 +1,14 @@
import Big from 'big.js'
-import { BaseEntity, Column, CreateDateColumn, Entity, Index, OneToMany, PrimaryColumn, UpdateDateColumn } from 'typeorm'
+import {
+ BaseEntity,
+ Column,
+ CreateDateColumn,
+ Entity,
+ Index,
+ OneToMany,
+ PrimaryColumn,
+ UpdateDateColumn,
+} from 'typeorm'
import { Bonus } from './bonus'
import { Refill } from './refill'
@@ -46,24 +55,39 @@ export class Wallet extends BaseEntity {
@Column({ default: true })
notify: boolean
- @OneToMany(() => Transaction, transaction => transaction.wallet)
+ @OneToMany(() => Transaction, (transaction) => transaction.wallet)
transactions: Promise
- @OneToMany(() => Bonus, bonus => bonus.wallet)
+ @OneToMany(() => Bonus, (bonus) => bonus.wallet)
bonuses: Promise
- @OneToMany(() => Refill, refill => refill.wallet)
+ @OneToMany(() => Refill, (refill) => refill.wallet)
refills: Promise
- async getBalance (): Promise {
- const deposit = (await this.transactions).reduce((acc, cur) => acc.add(cur.amount), Big(0))
- const bonus = (await this.bonuses).reduce((acc, cur) => acc.add(cur.amount), Big(0))
- const spent = (await this.refills).reduce((acc, cur) => acc.add(cur.price), Big(0))
-
- return deposit.add(bonus).minus(spent).minus(await this.totalTipped())
+ async getBalance(): Promise {
+ const deposit = (await this.transactions).reduce(
+ (acc, cur) => acc.add(cur.amount),
+ Big(0),
+ )
+ const bonus = (await this.bonuses).reduce(
+ (acc, cur) => acc.add(cur.amount),
+ Big(0),
+ )
+ const spent = (await this.refills).reduce(
+ (acc, cur) => acc.add(cur.price),
+ Big(0),
+ )
+
+ return deposit
+ .add(bonus)
+ .minus(spent)
+ .minus(await this.totalTipped())
}
- async totalTipped (): Promise {
- return (await this.refills).reduce((acc, cur) => acc.add(Big(cur.price).mul(cur.tip)), Big(0))
+ async totalTipped(): Promise {
+ return (await this.refills).reduce(
+ (acc, cur) => acc.add(Big(cur.price).mul(cur.tip)),
+ Big(0),
+ )
}
}
diff --git a/src/db/migrations/1665261222505-init.ts b/src/db/migrations/1665261222505-init.ts
index b2fad742..ad95d31a 100644
--- a/src/db/migrations/1665261222505-init.ts
+++ b/src/db/migrations/1665261222505-init.ts
@@ -1,19 +1,37 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Init1665261222505 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('CREATE TABLE "refill" ("signature" character varying NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "fleet" character varying NOT NULL, "price" double precision NOT NULL, "food" integer NOT NULL, "tool" integer NOT NULL, "fuel" integer NOT NULL, "ammo" integer NOT NULL, "walletPublicKey" character varying, CONSTRAINT "PK_3e9de3152e2613e44bee3cc366a" PRIMARY KEY ("signature"))')
- await queryRunner.query('CREATE TABLE "transaction" ("signature" character varying NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" double precision NOT NULL, "walletPublicKey" character varying, CONSTRAINT "PK_504a5a02c8957257be1e0c49510" PRIMARY KEY ("signature"))')
- await queryRunner.query('CREATE TABLE "wallet" ("publicKey" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "nextRefill" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_ee618336017c495b4b88f6694e3" PRIMARY KEY ("publicKey"))')
- await queryRunner.query('CREATE INDEX "IDX_8aacaefa0dd21764082066fb86" ON "wallet" ("nextRefill") ')
- await queryRunner.query('ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'CREATE TABLE "refill" ("signature" character varying NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "fleet" character varying NOT NULL, "price" double precision NOT NULL, "food" integer NOT NULL, "tool" integer NOT NULL, "fuel" integer NOT NULL, "ammo" integer NOT NULL, "walletPublicKey" character varying, CONSTRAINT "PK_3e9de3152e2613e44bee3cc366a" PRIMARY KEY ("signature"))',
+ )
+ await queryRunner.query(
+ 'CREATE TABLE "transaction" ("signature" character varying NOT NULL, "time" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" double precision NOT NULL, "walletPublicKey" character varying, CONSTRAINT "PK_504a5a02c8957257be1e0c49510" PRIMARY KEY ("signature"))',
+ )
+ await queryRunner.query(
+ 'CREATE TABLE "wallet" ("publicKey" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "nextRefill" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_ee618336017c495b4b88f6694e3" PRIMARY KEY ("publicKey"))',
+ )
+ await queryRunner.query(
+ 'CREATE INDEX "IDX_8aacaefa0dd21764082066fb86" ON "wallet" ("nextRefill") ',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"')
- await queryRunner.query('ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"')
- await queryRunner.query('DROP INDEX "public"."IDX_8aacaefa0dd21764082066fb86"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"',
+ )
+ await queryRunner.query(
+ 'DROP INDEX "public"."IDX_8aacaefa0dd21764082066fb86"',
+ )
await queryRunner.query('DROP TABLE "wallet"')
await queryRunner.query('DROP TABLE "transaction"')
await queryRunner.query('DROP TABLE "refill"')
diff --git a/src/db/migrations/1665312398768-refill.ts b/src/db/migrations/1665312398768-refill.ts
index 6523b9ba..533dde36 100644
--- a/src/db/migrations/1665312398768-refill.ts
+++ b/src/db/migrations/1665312398768-refill.ts
@@ -1,15 +1,27 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Refill1665312398768 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"')
- await queryRunner.query('ALTER TABLE "refill" ALTER COLUMN "walletPublicKey" SET NOT NULL')
- await queryRunner.query('ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ALTER COLUMN "walletPublicKey" SET NOT NULL',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"')
- await queryRunner.query('ALTER TABLE "refill" ALTER COLUMN "walletPublicKey" DROP NOT NULL')
- await queryRunner.query('ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "refill" DROP CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ALTER COLUMN "walletPublicKey" DROP NOT NULL',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD CONSTRAINT "FK_13d26ed77e9d9fd183eceb62599" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
}
diff --git a/src/db/migrations/1665312398769-shipname.ts b/src/db/migrations/1665312398769-shipname.ts
index 93026cba..28107835 100644
--- a/src/db/migrations/1665312398769-shipname.ts
+++ b/src/db/migrations/1665312398769-shipname.ts
@@ -1,11 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Shipname1665312398769 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('CREATE TABLE "ship_info" ("mint" character varying NOT NULL, "name" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_9a25e31a5bdbb88e239433c7898" PRIMARY KEY ("mint"))')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'CREATE TABLE "ship_info" ("mint" character varying NOT NULL, "name" character varying NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_9a25e31a5bdbb88e239433c7898" PRIMARY KEY ("mint"))',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('DROP TABLE "ship_info"')
}
}
diff --git a/src/db/migrations/1665395111187-tips.ts b/src/db/migrations/1665395111187-tips.ts
index 503e32b9..130d4974 100644
--- a/src/db/migrations/1665395111187-tips.ts
+++ b/src/db/migrations/1665395111187-tips.ts
@@ -1,12 +1,16 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Tips1665395111187 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "refill" ADD "tip" double precision NOT NULL DEFAULT \'0.15\'')
- await queryRunner.query('ALTER TABLE "wallet" ADD "tip" double precision NOT NULL DEFAULT \'0.15\'')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD "tip" double precision NOT NULL DEFAULT \'0.15\'',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "tip" double precision NOT NULL DEFAULT \'0.15\'',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "tip"')
await queryRunner.query('ALTER TABLE "refill" DROP COLUMN "tip"')
}
diff --git a/src/db/migrations/1665595883444-registration.ts b/src/db/migrations/1665595883444-registration.ts
index 326fbc7e..30163621 100644
--- a/src/db/migrations/1665595883444-registration.ts
+++ b/src/db/migrations/1665595883444-registration.ts
@@ -1,18 +1,28 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Registration1665595883444 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "wallet" ADD "nick" character varying')
- await queryRunner.query('ALTER TABLE "wallet" ADD "authed" boolean NOT NULL DEFAULT false')
- await queryRunner.query('ALTER TABLE "wallet" ADD "authTxAmount" double precision')
- await queryRunner.query('ALTER TABLE "wallet" ADD "authExpire" TIMESTAMP WITH TIME ZONE')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "nick" character varying',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "authed" boolean NOT NULL DEFAULT false',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "authTxAmount" double precision',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "authExpire" TIMESTAMP WITH TIME ZONE',
+ )
await queryRunner.query('ALTER TABLE "wallet" ADD "telegramId" integer')
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "telegramId"')
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "authExpire"')
- await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "authTxAmount"')
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" DROP COLUMN "authTxAmount"',
+ )
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "authed"')
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "nick"')
}
diff --git a/src/db/migrations/1665601528905-imagename.ts b/src/db/migrations/1665601528905-imagename.ts
index 4e0c4f7d..78d27442 100644
--- a/src/db/migrations/1665601528905-imagename.ts
+++ b/src/db/migrations/1665601528905-imagename.ts
@@ -1,11 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Imagename1665601528905 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "ship_info" ADD "imageName" character varying NOT NULL')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "ship_info" ADD "imageName" character varying NOT NULL',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "ship_info" DROP COLUMN "imageName"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "ship_info" DROP COLUMN "imageName"',
+ )
}
}
diff --git a/src/db/migrations/1665620874572-disable-wallet.ts b/src/db/migrations/1665620874572-disable-wallet.ts
index 5523beb0..e26861fd 100644
--- a/src/db/migrations/1665620874572-disable-wallet.ts
+++ b/src/db/migrations/1665620874572-disable-wallet.ts
@@ -1,11 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class DisableWallet1665620874572 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "wallet" ADD "enabled" boolean NOT NULL DEFAULT true')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "enabled" boolean NOT NULL DEFAULT true',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "enabled"')
}
}
diff --git a/src/db/migrations/1665623724921-index.ts b/src/db/migrations/1665623724921-index.ts
index d86896f4..818b2e10 100644
--- a/src/db/migrations/1665623724921-index.ts
+++ b/src/db/migrations/1665623724921-index.ts
@@ -1,13 +1,21 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Index1665623724921 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('CREATE INDEX "IDX_1332d7bfbb2ff3d47e92d7ab49" ON "wallet" ("telegramId") ')
- await queryRunner.query('CREATE INDEX "IDX_33a938fb6968e2895273fda726" ON "wallet" ("enabled") ')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'CREATE INDEX "IDX_1332d7bfbb2ff3d47e92d7ab49" ON "wallet" ("telegramId") ',
+ )
+ await queryRunner.query(
+ 'CREATE INDEX "IDX_33a938fb6968e2895273fda726" ON "wallet" ("enabled") ',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('DROP INDEX "public"."IDX_33a938fb6968e2895273fda726"')
- await queryRunner.query('DROP INDEX "public"."IDX_1332d7bfbb2ff3d47e92d7ab49"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'DROP INDEX "public"."IDX_33a938fb6968e2895273fda726"',
+ )
+ await queryRunner.query(
+ 'DROP INDEX "public"."IDX_1332d7bfbb2ff3d47e92d7ab49"',
+ )
}
}
diff --git a/src/db/migrations/1665681627046-notify.ts b/src/db/migrations/1665681627046-notify.ts
index 058cace5..c6036d53 100644
--- a/src/db/migrations/1665681627046-notify.ts
+++ b/src/db/migrations/1665681627046-notify.ts
@@ -1,11 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Notify1665681627046 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "wallet" ADD "notify" boolean NOT NULL DEFAULT true')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ADD "notify" boolean NOT NULL DEFAULT true',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('ALTER TABLE "wallet" DROP COLUMN "notify"')
}
}
diff --git a/src/db/migrations/1665694120757-tx-relation-id.ts b/src/db/migrations/1665694120757-tx-relation-id.ts
index ae56e378..e309610e 100644
--- a/src/db/migrations/1665694120757-tx-relation-id.ts
+++ b/src/db/migrations/1665694120757-tx-relation-id.ts
@@ -1,15 +1,27 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class TxRelationId1665694120757 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"')
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "walletPublicKey" SET NOT NULL')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "walletPublicKey" SET NOT NULL',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"')
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "walletPublicKey" DROP NOT NULL')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "FK_0c2fe605e9898819004cd7f2113"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "walletPublicKey" DROP NOT NULL',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "FK_0c2fe605e9898819004cd7f2113" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
}
diff --git a/src/db/migrations/1668120405025-balance.ts b/src/db/migrations/1668120405025-balance.ts
index 9b5158af..b5e0bf8a 100644
--- a/src/db/migrations/1668120405025-balance.ts
+++ b/src/db/migrations/1668120405025-balance.ts
@@ -1,13 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Balance1668120405025 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "refill" ADD "preBalance" double precision NOT NULL DEFAULT \'0\'')
- await queryRunner.query('ALTER TABLE "refill" ADD "postBalance" double precision NOT NULL DEFAULT \'0\'')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD "preBalance" double precision NOT NULL DEFAULT \'0\'',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "refill" ADD "postBalance" double precision NOT NULL DEFAULT \'0\'',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "refill" DROP COLUMN "postBalance"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "refill" DROP COLUMN "postBalance"',
+ )
await queryRunner.query('ALTER TABLE "refill" DROP COLUMN "preBalance"')
}
}
diff --git a/src/db/migrations/1675021579415-telegram-id-bigint.ts b/src/db/migrations/1675021579415-telegram-id-bigint.ts
index 35271ec5..270961e7 100644
--- a/src/db/migrations/1675021579415-telegram-id-bigint.ts
+++ b/src/db/migrations/1675021579415-telegram-id-bigint.ts
@@ -1,11 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class TelegramIdBigint1675021579415 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "wallet" ALTER COLUMN "telegramId" SET DATA TYPE bigint')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ALTER COLUMN "telegramId" SET DATA TYPE bigint',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "wallet" ALTER COLUMN "telegramId" SET DATA TYPE integer')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "wallet" ALTER COLUMN "telegramId" SET DATA TYPE integer',
+ )
}
}
diff --git a/src/db/migrations/1681496450836-r4-support.ts b/src/db/migrations/1681496450836-r4-support.ts
index f6737e8a..2d96659d 100644
--- a/src/db/migrations/1681496450836-r4-support.ts
+++ b/src/db/migrations/1681496450836-r4-support.ts
@@ -3,17 +3,33 @@ import { MigrationInterface, QueryRunner } from 'typeorm'
export class R4Support1681496450836 implements MigrationInterface {
name = 'R4Support1681496450836'
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" ADD "originalAmount" double precision')
- await queryRunner.query('ALTER TABLE "transaction" ADD "resource" character varying')
- await queryRunner.query('UPDATE "transaction" SET "originalAmount" = "amount"')
- await queryRunner.query('UPDATE "transaction" SET "resource" = \'ATLAS\'')
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "originalAmount" SET NOT NULL')
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "resource" SET NOT NULL')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD "originalAmount" double precision',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD "resource" character varying',
+ )
+ await queryRunner.query(
+ 'UPDATE "transaction" SET "originalAmount" = "amount"',
+ )
+ await queryRunner.query(
+ 'UPDATE "transaction" SET "resource" = \'ATLAS\'',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "originalAmount" SET NOT NULL',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "resource" SET NOT NULL',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" DROP COLUMN "resource"')
- await queryRunner.query('ALTER TABLE "transaction" DROP COLUMN "originalAmount"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP COLUMN "resource"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP COLUMN "originalAmount"',
+ )
}
}
diff --git a/src/db/migrations/1685807496144-multi-resource-tx-support.ts b/src/db/migrations/1685807496144-multi-resource-tx-support.ts
index b54e7711..e4c4a422 100644
--- a/src/db/migrations/1685807496144-multi-resource-tx-support.ts
+++ b/src/db/migrations/1685807496144-multi-resource-tx-support.ts
@@ -3,23 +3,49 @@ import { MigrationInterface, QueryRunner } from 'typeorm'
export class MultiResourceTxSupport1685807496144 implements MigrationInterface {
name = 'MultiResourceTxSupport1685807496144'
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" ADD "id" uuid NOT NULL default gen_random_uuid()')
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "PK_504a5a02c8957257be1e0c49510"')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "PK_1607c7f366fba66c851dce79058" PRIMARY KEY ("signature", "id")')
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "PK_1607c7f366fba66c851dce79058"')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "PK_89eadb93a89810556e1cbcd6ab9" PRIMARY KEY ("id")')
- await queryRunner.query('CREATE INDEX "IDX_504a5a02c8957257be1e0c4951" ON "transaction" ("signature") ')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "UQ_6720d355d907ad611603f0a3dc6" UNIQUE ("signature", "resource")')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD "id" uuid NOT NULL default gen_random_uuid()',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "PK_504a5a02c8957257be1e0c49510"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "PK_1607c7f366fba66c851dce79058" PRIMARY KEY ("signature", "id")',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "PK_1607c7f366fba66c851dce79058"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "PK_89eadb93a89810556e1cbcd6ab9" PRIMARY KEY ("id")',
+ )
+ await queryRunner.query(
+ 'CREATE INDEX "IDX_504a5a02c8957257be1e0c4951" ON "transaction" ("signature") ',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "UQ_6720d355d907ad611603f0a3dc6" UNIQUE ("signature", "resource")',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "UQ_6720d355d907ad611603f0a3dc6"')
- await queryRunner.query('DROP INDEX "public"."IDX_504a5a02c8957257be1e0c4951"')
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "PK_89eadb93a89810556e1cbcd6ab9"')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "PK_1607c7f366fba66c851dce79058" PRIMARY KEY ("signature", "id")')
- await queryRunner.query('ALTER TABLE "transaction" DROP CONSTRAINT "PK_1607c7f366fba66c851dce79058"')
- await queryRunner.query('ALTER TABLE "transaction" ADD CONSTRAINT "PK_504a5a02c8957257be1e0c49510" PRIMARY KEY ("signature")')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "UQ_6720d355d907ad611603f0a3dc6"',
+ )
+ await queryRunner.query(
+ 'DROP INDEX "public"."IDX_504a5a02c8957257be1e0c4951"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "PK_89eadb93a89810556e1cbcd6ab9"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "PK_1607c7f366fba66c851dce79058" PRIMARY KEY ("signature", "id")',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" DROP CONSTRAINT "PK_1607c7f366fba66c851dce79058"',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ADD CONSTRAINT "PK_504a5a02c8957257be1e0c49510" PRIMARY KEY ("signature")',
+ )
await queryRunner.query('ALTER TABLE "transaction" DROP COLUMN "id"')
}
}
diff --git a/src/db/migrations/1689091973427-tx-cache.ts b/src/db/migrations/1689091973427-tx-cache.ts
index 7d526f8c..2ac6ae77 100644
--- a/src/db/migrations/1689091973427-tx-cache.ts
+++ b/src/db/migrations/1689091973427-tx-cache.ts
@@ -1,13 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class TxCache1689091973427 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('CREATE TABLE "tx_cache" ("id" character varying NOT NULL, "tx" jsonb NOT NULL, CONSTRAINT "PK_a48880d12cdce61b512e5c20c02" PRIMARY KEY ("id"))')
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "id" DROP DEFAULT')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'CREATE TABLE "tx_cache" ("id" character varying NOT NULL, "tx" jsonb NOT NULL, CONSTRAINT "PK_a48880d12cdce61b512e5c20c02" PRIMARY KEY ("id"))',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "id" DROP DEFAULT',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()',
+ )
await queryRunner.query('DROP TABLE "tx_cache"')
}
}
diff --git a/src/db/migrations/1689092742010-auto-generated-id-column.ts b/src/db/migrations/1689092742010-auto-generated-id-column.ts
index a37d5a4b..6aeab3cf 100644
--- a/src/db/migrations/1689092742010-auto-generated-id-column.ts
+++ b/src/db/migrations/1689092742010-auto-generated-id-column.ts
@@ -1,11 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class AutoGeneratedIdColumn1689092742010 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "transaction" ALTER COLUMN "id" DROP DEFAULT')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "transaction" ALTER COLUMN "id" DROP DEFAULT',
+ )
}
}
diff --git a/src/db/migrations/1689511318618-bonuses.ts b/src/db/migrations/1689511318618-bonuses.ts
index 936b039e..2049c1bf 100644
--- a/src/db/migrations/1689511318618-bonuses.ts
+++ b/src/db/migrations/1689511318618-bonuses.ts
@@ -1,13 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class Bonuses1689511318618 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('CREATE TABLE "bonus" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "time" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" double precision NOT NULL, "walletPublicKey" character varying NOT NULL, CONSTRAINT "PK_885c9ca672f42874b1a5cb4d9e7" PRIMARY KEY ("id"))')
- await queryRunner.query('ALTER TABLE "bonus" ADD CONSTRAINT "FK_81fa518bdf061dcbe81b60ccb94" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'CREATE TABLE "bonus" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "time" TIMESTAMP WITH TIME ZONE NOT NULL, "amount" double precision NOT NULL, "walletPublicKey" character varying NOT NULL, CONSTRAINT "PK_885c9ca672f42874b1a5cb4d9e7" PRIMARY KEY ("id"))',
+ )
+ await queryRunner.query(
+ 'ALTER TABLE "bonus" ADD CONSTRAINT "FK_81fa518bdf061dcbe81b60ccb94" FOREIGN KEY ("walletPublicKey") REFERENCES "wallet"("publicKey") ON DELETE CASCADE ON UPDATE NO ACTION',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "bonus" DROP CONSTRAINT "FK_81fa518bdf061dcbe81b60ccb94"')
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "bonus" DROP CONSTRAINT "FK_81fa518bdf061dcbe81b60ccb94"',
+ )
await queryRunner.query('DROP TABLE "bonus"')
}
}
diff --git a/src/db/migrations/1689512138737-bonuses-reason.ts b/src/db/migrations/1689512138737-bonuses-reason.ts
index 978fc71f..2f33f11c 100644
--- a/src/db/migrations/1689512138737-bonuses-reason.ts
+++ b/src/db/migrations/1689512138737-bonuses-reason.ts
@@ -1,11 +1,13 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class BonusesReason1689512138737 implements MigrationInterface {
- public async up (queryRunner: QueryRunner): Promise {
- await queryRunner.query('ALTER TABLE "bonus" ADD "reason" character varying NOT NULL')
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ 'ALTER TABLE "bonus" ADD "reason" character varying NOT NULL',
+ )
}
- public async down (queryRunner: QueryRunner): Promise {
+ public async down(queryRunner: QueryRunner): Promise {
await queryRunner.query('ALTER TABLE "bonus" DROP COLUMN "reason"')
}
}
diff --git a/src/lib/check-atlas-transactions.ts b/src/lib/check-atlas-transactions.ts
index 513bd6fa..f6cf5926 100644
--- a/src/lib/check-atlas-transactions.ts
+++ b/src/lib/check-atlas-transactions.ts
@@ -1,7 +1,14 @@
// eslint-disable-next-line import/named
import { getAssociatedTokenAddressSync } from '@solana/spl-token'
// eslint-disable-next-line import/named
-import { ParsedInstruction, ParsedTransactionWithMeta, SignaturesForAddressOptions } from '@solana/web3.js'
+import {
+ // eslint-disable-next-line import/named
+ ParsedInstruction,
+ // eslint-disable-next-line import/named
+ ParsedTransactionWithMeta,
+ // eslint-disable-next-line import/named
+ SignaturesForAddressOptions,
+} from '@solana/web3.js'
import dayjs from '../dayjs'
import { Transaction } from '../db/entities'
@@ -11,82 +18,135 @@ import { keyPair, resource } from '../service/wallet'
import { ensureWallet } from './ensure-wallet'
-export const checkAtlasTransactions = async (options?: SignaturesForAddressOptions): Promise => {
+export const checkAtlasTransactions = async (
+ options?: SignaturesForAddressOptions,
+): Promise => {
const atlasTokenAccount = getAssociatedTokenAddressSync(
resource.atlas,
keyPair.publicKey,
- true
+ true,
+ )
+ const signatureList = await connection.getSignaturesForAddress(
+ atlasTokenAccount,
+ options,
)
- const signatureList = await connection.getSignaturesForAddress(atlasTokenAccount, options)
- const transactionList : ParsedTransactionWithMeta[] = []
+ const transactionList: ParsedTransactionWithMeta[] = []
for (const signature of signatureList) {
// https://docs.solana.com/developing/versioned-transactions#max-supported-transaction-version
const parsedSignature =
// eslint-disable-next-line no-await-in-loop
- await connection.getParsedTransaction(signature.signature, { maxSupportedTransactionVersion: 0 })
+ await connection.getParsedTransaction(signature.signature, {
+ maxSupportedTransactionVersion: 0,
+ })
if (parsedSignature) {
transactionList.push(parsedSignature)
}
}
- const txList: ParsedTransactionWithMeta[] =
- transactionList.filter((tx): tx is ParsedTransactionWithMeta => tx !== null)
+ const txList: ParsedTransactionWithMeta[] = transactionList.filter(
+ (tx): tx is ParsedTransactionWithMeta => tx !== null,
+ )
- const transferList = txList.filter(tx => tx.meta?.postTokenBalances?.length === 2)
+ const transferList = txList.filter(
+ (tx) => tx.meta?.postTokenBalances?.length === 2,
+ )
await Promise.all(
- transferList.map(tx => tx.transaction.message.instructions.map(async (instr) => {
- const instruction: ParsedInstruction = instr as ParsedInstruction
-
- if (instruction.program === 'spl-token' && instruction.parsed.info.mint === resource.atlas.toString()) {
- const { info } = instruction.parsed
-
- const sender = info.authority ?? info.multisigAuthority
- const amount = info.tokenAmount.uiAmount
- const blockTime = tx.blockTime || 0
- const time = dayjs.unix(blockTime).toDate()
- const [signature] = tx.transaction.signatures
-
- const transaction = await Transaction.findOneBy({ signature })
- const log = transaction ? logger.debug : logger.info
-
- if (sender === keyPair.publicKey.toString()) {
- const receiver = tx.meta?.postTokenBalances?.filter(
- tb => tb.owner?.toString() !== keyPair.publicKey.toString())[0].owner as string
-
- log(`${receiver} -${amount} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`)
- const wallet = await ensureWallet(receiver)
-
- const tr = await Transaction.findOneBy({ signature, resource: 'ATLAS' })
-
- if (!tr) {
- await Transaction.create({ wallet, amount: -amount, signature, time, originalAmount: amount, resource: 'ATLAS' }).save()
- }
- }
- else {
- log(`${sender} +${amount} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`)
-
- const wallet = await ensureWallet(sender)
-
- if (wallet.telegramId && !wallet.authed && dayjs().isBefore(wallet.authExpire) && !transaction) {
- if (wallet.authTxAmount === amount) {
- wallet.authed = true
-
- logger.info(`Successfully assigned ${wallet.telegramId} to ${wallet.publicKey}`)
+ transferList.map((tx) =>
+ tx.transaction.message.instructions.map(async (instr) => {
+ const instruction: ParsedInstruction =
+ instr as ParsedInstruction
+
+ if (
+ instruction.program === 'spl-token' &&
+ instruction.parsed.info.mint === resource.atlas.toString()
+ ) {
+ const { info } = instruction.parsed
+
+ const sender = info.authority ?? info.multisigAuthority
+ const amount = info.tokenAmount.uiAmount
+ const blockTime = tx.blockTime || 0
+ const time = dayjs.unix(blockTime).toDate()
+ const [signature] = tx.transaction.signatures
+
+ const transaction = await Transaction.findOneBy({
+ signature,
+ })
+ const log = transaction ? logger.debug : logger.info
+
+ if (sender === keyPair.publicKey.toString()) {
+ const receiver = tx.meta?.postTokenBalances?.filter(
+ (tb) =>
+ tb.owner?.toString() !==
+ keyPair.publicKey.toString(),
+ )[0].owner as string
+
+ log(
+ `${receiver} -${amount} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`,
+ )
+ const wallet = await ensureWallet(receiver)
+
+ const tr = await Transaction.findOneBy({
+ signature,
+ resource: 'ATLAS',
+ })
+
+ if (!tr) {
+ await Transaction.create({
+ wallet,
+ amount: -amount,
+ signature,
+ time,
+ originalAmount: amount,
+ resource: 'ATLAS',
+ }).save()
}
- else {
- logger.warn(`Amount mismatch, got ${amount}, expected ${wallet.authTxAmount}`)
+ } else {
+ log(
+ `${sender} +${amount} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`,
+ )
+
+ const wallet = await ensureWallet(sender)
+
+ if (
+ wallet.telegramId &&
+ !wallet.authed &&
+ dayjs().isBefore(wallet.authExpire) &&
+ !transaction
+ ) {
+ if (wallet.authTxAmount === amount) {
+ wallet.authed = true
+
+ logger.info(
+ `Successfully assigned ${wallet.telegramId} to ${wallet.publicKey}`,
+ )
+ } else {
+ logger.warn(
+ `Amount mismatch, got ${amount}, expected ${wallet.authTxAmount}`,
+ )
+ }
+ }
+ const tr = await Transaction.findOneBy({
+ signature,
+ resource: 'ATLAS',
+ })
+
+ if (!tr) {
+ await Transaction.create({
+ wallet,
+ amount,
+ signature,
+ time,
+ originalAmount: amount,
+ resource: 'ATLAS',
+ }).save()
}
- }
- const tr = await Transaction.findOneBy({ signature, resource: 'ATLAS' })
-
- if (!tr) {
- await Transaction.create({ wallet, amount, signature, time, originalAmount: amount, resource: 'ATLAS' }).save()
}
}
- }
- })))
+ }),
+ ),
+ )
}
diff --git a/src/lib/check-r4-transactions.ts b/src/lib/check-r4-transactions.ts
index b7cd45cd..61bd0ba0 100644
--- a/src/lib/check-r4-transactions.ts
+++ b/src/lib/check-r4-transactions.ts
@@ -1,6 +1,12 @@
import { getAssociatedTokenAddressSync } from '@solana/spl-token'
// eslint-disable-next-line import/named
-import { ParsedInstruction, PublicKey, SignaturesForAddressOptions } from '@solana/web3.js'
+import {
+ // eslint-disable-next-line import/named
+ ParsedInstruction,
+ PublicKey,
+ // eslint-disable-next-line import/named
+ SignaturesForAddressOptions,
+} from '@solana/web3.js'
import Big from 'big.js'
import dayjs from '../dayjs'
@@ -14,7 +20,7 @@ import { keyPair } from '../service/wallet'
import { ensureWallet } from './ensure-wallet'
import { getPrice } from './get-price'
-type ResourceName = 'food' | 'tool' | 'fuel' | 'ammo';
+type ResourceName = 'food' | 'tool' | 'fuel' | 'ammo'
export const checkR4Transactions = async (
wallet: Wallet,
@@ -106,7 +112,7 @@ export const checkR4Transactions = async (
// eslint-disable-next-line max-depth
if (sender === keyPair.publicKey.toString()) {
const receiver = tx.meta?.postTokenBalances?.filter(
- tb =>
+ (tb) =>
tb.owner?.toString() !==
keyPair.publicKey.toString(),
)[0].owner as string
@@ -128,8 +134,7 @@ export const checkR4Transactions = async (
time,
}).save()
}
- }
- else {
+ } else {
log(
`${sender} +${originalAmount} ${resourceName.toUpperCase()} worth ${price.toFixed(AD)} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`,
)
diff --git a/src/lib/check-transactions.ts b/src/lib/check-transactions.ts
index 8c44ba43..595cd083 100644
--- a/src/lib/check-transactions.ts
+++ b/src/lib/check-transactions.ts
@@ -3,7 +3,11 @@ import { LessThan, MoreThan } from 'typeorm'
import dayjs from '../dayjs'
import { Transaction, Wallet } from '../db/entities'
import { logger } from '../logger'
-import { getBalanceAtlas, getResourceBalances, getResourcePrices } from '../service/gm'
+import {
+ getBalanceAtlas,
+ getResourceBalances,
+ getResourcePrices,
+} from '../service/gm'
import { AD } from '../service/sol'
import { keyPair, resource } from '../service/wallet'
@@ -16,26 +20,44 @@ export const checkTransactions = async (): Promise => {
const resources = await getResourceBalances(keyPair.publicKey)
logger.info(`ATLAS balance: ${atlasBalance.toFixed(AD)}`)
- logger.info(`FOOD balance: ${resources.food} (${resources.food.mul(prices.food).toFixed(AD)} ATLAS)`)
- logger.info(`TOOL balance: ${resources.tool} (${resources.tool.mul(prices.tool).toFixed(AD)} ATLAS)`)
- logger.info(`FUEL balance: ${resources.fuel} (${resources.fuel.mul(prices.fuel).toFixed(AD)} ATLAS)`)
- logger.info(`AMMO balance: ${resources.ammo} (${resources.ammo.mul(prices.ammo).toFixed(AD)} ATLAS)`)
+ logger.info(
+ `FOOD balance: ${resources.food} (${resources.food.mul(prices.food).toFixed(AD)} ATLAS)`,
+ )
+ logger.info(
+ `TOOL balance: ${resources.tool} (${resources.tool.mul(prices.tool).toFixed(AD)} ATLAS)`,
+ )
+ logger.info(
+ `FUEL balance: ${resources.fuel} (${resources.fuel.mul(prices.fuel).toFixed(AD)} ATLAS)`,
+ )
+ logger.info(
+ `AMMO balance: ${resources.ammo} (${resources.ammo.mul(prices.ammo).toFixed(AD)} ATLAS)`,
+ )
- const total = atlasBalance.add(
- resources.food.mul(prices.food)).add(
- resources.ammo.mul(prices.ammo)).add(
- resources.fuel.mul(prices.fuel)).add(
- resources.tool.mul(prices.tool))
+ const total = atlasBalance
+ .add(resources.food.mul(prices.food))
+ .add(resources.ammo.mul(prices.ammo))
+ .add(resources.fuel.mul(prices.fuel))
+ .add(resources.tool.mul(prices.tool))
const [[lastInTx], [lastOutTx]] = await Promise.all([
- Transaction.find({ where: { amount: MoreThan(0) }, order: { time: 'DESC' }, take: 1 }),
- Transaction.find({ where: { amount: LessThan(0) }, order: { time: 'DESC' }, take: 1 })
+ Transaction.find({
+ where: { amount: MoreThan(0) },
+ order: { time: 'DESC' },
+ take: 1,
+ }),
+ Transaction.find({
+ where: { amount: LessThan(0) },
+ order: { time: 'DESC' },
+ take: 1,
+ }),
])
let getSigOptions
if (lastInTx && lastOutTx) {
- const until = dayjs(lastInTx.time).isBefore(lastOutTx.time) ? lastInTx.signature : lastOutTx.signature
+ const until = dayjs(lastInTx.time).isBefore(lastOutTx.time)
+ ? lastInTx.signature
+ : lastOutTx.signature
getSigOptions = { until }
}
@@ -48,12 +70,36 @@ export const checkTransactions = async (): Promise => {
for (const wallet of wallets) {
// eslint-disable-next-line no-await-in-loop
- await checkR4Transactions(wallet, 'tool', resource.tool, prices, getSigOptions)
+ await checkR4Transactions(
+ wallet,
+ 'tool',
+ resource.tool,
+ prices,
+ getSigOptions,
+ )
// eslint-disable-next-line no-await-in-loop
- await checkR4Transactions(wallet, 'ammo', resource.ammo, prices, getSigOptions)
+ await checkR4Transactions(
+ wallet,
+ 'ammo',
+ resource.ammo,
+ prices,
+ getSigOptions,
+ )
// eslint-disable-next-line no-await-in-loop
- await checkR4Transactions(wallet, 'food', resource.food, prices, getSigOptions)
+ await checkR4Transactions(
+ wallet,
+ 'food',
+ resource.food,
+ prices,
+ getSigOptions,
+ )
// eslint-disable-next-line no-await-in-loop
- await checkR4Transactions(wallet, 'fuel', resource.fuel, prices, getSigOptions)
+ await checkR4Transactions(
+ wallet,
+ 'fuel',
+ resource.fuel,
+ prices,
+ getSigOptions,
+ )
}
}
diff --git a/src/lib/const/max.ts b/src/lib/const/max.ts
index 2d2a33f6..3fd992bd 100644
--- a/src/lib/const/max.ts
+++ b/src/lib/const/max.ts
@@ -1,3 +1,3 @@
import Big from 'big.js'
-export const max = (a: Big, b: Big): Big => a.gte(b) ? a : b
+export const max = (a: Big, b: Big): Big => (a.gte(b) ? a : b)
diff --git a/src/lib/ensure-wallet.ts b/src/lib/ensure-wallet.ts
index 2ca5ea7d..fe5b907c 100644
--- a/src/lib/ensure-wallet.ts
+++ b/src/lib/ensure-wallet.ts
@@ -1,7 +1,10 @@
import { Wallet } from '../db/entities'
export const ensureWallet = async (publicKey: string): Promise => {
- await Wallet.upsert({ publicKey: publicKey.toString() }, { conflictPaths: ['publicKey'], skipUpdateIfNoValuesChanged: true })
+ await Wallet.upsert(
+ { publicKey: publicKey.toString() },
+ { conflictPaths: ['publicKey'], skipUpdateIfNoValuesChanged: true },
+ )
return Wallet.findOneOrFail({ where: { publicKey: publicKey.toString() } })
}
diff --git a/src/lib/fleet-depletion-info.ts b/src/lib/fleet-depletion-info.ts
index fbc8ee39..0fb2e132 100644
--- a/src/lib/fleet-depletion-info.ts
+++ b/src/lib/fleet-depletion-info.ts
@@ -9,18 +9,25 @@ type FleetDepletionInfo = {
human: string
}
-export const fleetDepletionInfo = async (shipStakingInfo: ShipStakingInfo): Promise => {
- const info = await getScoreVarsShipInfo(connection, fleetProgram, shipStakingInfo.shipMint)
+export const fleetDepletionInfo = async (
+ shipStakingInfo: ShipStakingInfo,
+): Promise => {
+ const info = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ shipStakingInfo.shipMint,
+ )
const remainingResources = getFleetRemainingResources(info, shipStakingInfo)
const secondsLeft = Math.min(
remainingResources.food.secondsLeft,
remainingResources.ammo.secondsLeft,
remainingResources.fuel.secondsLeft,
- remainingResources.tool.secondsLeft)
+ remainingResources.tool.secondsLeft,
+ )
return {
seconds: secondsLeft,
- human: dayjs.duration(secondsLeft, 'seconds').humanize(false)
+ human: dayjs.duration(secondsLeft, 'seconds').humanize(false),
}
}
diff --git a/src/lib/get-price.ts b/src/lib/get-price.ts
index 0616bf88..76b79bc5 100644
--- a/src/lib/get-price.ts
+++ b/src/lib/get-price.ts
@@ -10,5 +10,8 @@ export const getPrice = (amount: Amounts, price?: Amounts): Big => {
const totalAmmoPrice = amount.ammo.mul(p.ammo)
const totalToolPrice = amount.tool.mul(p.tool)
- return totalFuelPrice.add(totalFoodPrice).add(totalAmmoPrice).add(totalToolPrice)
+ return totalFuelPrice
+ .add(totalFoodPrice)
+ .add(totalAmmoPrice)
+ .add(totalToolPrice)
}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 654edf69..99c73a72 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1,9 +1,9 @@
-export * from './const'
-export * from './telegram'
export * from './check-transactions'
+export * from './const'
export * from './fleet-depletion-info'
export * from './get-price'
-export * from './refill-strategy'
export * from './refill'
export * from './refill-player'
+export * from './refill-strategy'
export * from './stock-resources'
+export * from './telegram'
diff --git a/src/lib/refill-player.ts b/src/lib/refill-player.ts
index 5a8061be..35f93d04 100644
--- a/src/lib/refill-player.ts
+++ b/src/lib/refill-player.ts
@@ -15,9 +15,18 @@ import { fleetDepletionInfo } from './fleet-depletion-info'
import { RefillStrategy } from './refill-strategy'
import { getShipName } from './stock-resources'
-export const refillPlayer = async (player: PublicKey, strategy: RefillStrategy): Promise => {
- const wallet = await Wallet.findOneByOrFail({ publicKey: player.toString() })
- const shipStakingInfos = await getAllFleetsForUserPublicKey(connection, player, fleetProgram)
+export const refillPlayer = async (
+ player: PublicKey,
+ strategy: RefillStrategy,
+): Promise => {
+ const wallet = await Wallet.findOneByOrFail({
+ publicKey: player.toString(),
+ })
+ const shipStakingInfos = await getAllFleetsForUserPublicKey(
+ connection,
+ player,
+ fleetProgram,
+ )
if (shipStakingInfos.length === 0) {
logger.warn(`${player.toString()} has no staked ships`)
@@ -25,7 +34,13 @@ export const refillPlayer = async (player: PublicKey, strategy: RefillStrategy):
return []
}
- await Promise.all(shipStakingInfos.map(async shipStakingInfo => logger.info(`${player.toString()} ${await getShipName(shipStakingInfo)} depleting in ${(await fleetDepletionInfo(shipStakingInfo)).human}`)))
+ await Promise.all(
+ shipStakingInfos.map(async (shipStakingInfo) =>
+ logger.info(
+ `${player.toString()} ${await getShipName(shipStakingInfo)} depleting in ${(await fleetDepletionInfo(shipStakingInfo)).human}`,
+ ),
+ ),
+ )
const refills: FleetRefill[] = await strategy(shipStakingInfos)
@@ -33,61 +48,95 @@ export const refillPlayer = async (player: PublicKey, strategy: RefillStrategy):
const playerBalance = await wallet.getBalance()
if (playerBalance.lt(totalCost)) {
- logger.warn(`${player.toString()} credits insufficient! Has ${playerBalance.toFixed(AD)}, need ${totalCost.toFixed(AD)}`)
- await Wallet.update({ publicKey: wallet.publicKey }, { nextRefill: dayjs().add(1, 'day').toDate() })
+ logger.warn(
+ `${player.toString()} credits insufficient! Has ${playerBalance.toFixed(AD)}, need ${totalCost.toFixed(AD)}`,
+ )
+ await Wallet.update(
+ { publicKey: wallet.publicKey },
+ { nextRefill: dayjs().add(1, 'day').toDate() },
+ )
return []
}
- const fleetRefills = await Promise.all(refills
- .filter(refill =>
- refill.amount.tool.gt(0) ||
- refill.amount.fuel.gt(0) ||
- refill.amount.ammo.gt(0) ||
- refill.amount.food.gt(0))
- .map(async (refill) => {
- const shipName = await getShipName(refill.shipStakingInfo)
-
- logger.warn(`${player.toString()}: Refilling ${JSON.stringify(refill.amount)} for ${shipName} costs ${refill.price.toFixed(AD)} ATLAS`)
-
- try {
- const tx = await refillFleet(player, refill.shipStakingInfo, refill.amount)
-
- return Refill.create({
- signature: tx,
- walletPublicKey: wallet.publicKey,
- fleet: shipName,
- preBalance: playerBalance.toNumber(),
- postBalance: playerBalance.sub(refill.price).toNumber(),
- tip: wallet.tip,
- price: refill.price.toNumber(),
- food: refill.amount.food.toNumber(),
- tool: refill.amount.tool.toNumber(),
- fuel: refill.amount.fuel.toNumber(),
- ammo: refill.amount.ammo.toNumber()
- }).save()
- }
- catch (e) {
- Sentry.captureException(e)
- logger.error(`Error refilling fleet: ${(e as Error).message}`)
-
- return null
- }
- }))
-
- const depletionInfos = await Promise.all(shipStakingInfos.map(async (shipStakingInfo) => {
- const depletionInfo = await fleetDepletionInfo(shipStakingInfo)
-
- logger.info(`${player.toString()} ${await getShipName(shipStakingInfo)} depleting in ${depletionInfo.human}`)
-
- return depletionInfo
- }))
-
- const secondsLeft = depletionInfos.reduce((acc, cur) => Math.min(cur.seconds, acc), Number.MAX_SAFE_INTEGER)
-
- const nextRefill = dayjs().add(Big(secondsLeft).minus(Big(secondsLeft).div(4)).toNumber(), 'second').toDate()
+ const fleetRefills = await Promise.all(
+ refills
+ .filter(
+ (refill) =>
+ refill.amount.tool.gt(0) ||
+ refill.amount.fuel.gt(0) ||
+ refill.amount.ammo.gt(0) ||
+ refill.amount.food.gt(0),
+ )
+ .map(async (refill) => {
+ const shipName = await getShipName(refill.shipStakingInfo)
+
+ logger.warn(
+ `${player.toString()}: Refilling ${JSON.stringify(refill.amount)} for ${shipName} costs ${refill.price.toFixed(AD)} ATLAS`,
+ )
+
+ try {
+ const tx = await refillFleet(
+ player,
+ refill.shipStakingInfo,
+ refill.amount,
+ )
+
+ return Promise.all(
+ tx.map((signature) => {
+ return Refill.create({
+ signature,
+ walletPublicKey: wallet.publicKey,
+ fleet: shipName,
+ preBalance: playerBalance.toNumber(),
+ postBalance: playerBalance
+ .sub(refill.price)
+ .toNumber(),
+ tip: wallet.tip,
+ price: refill.price.toNumber(),
+ food: refill.amount.food.toNumber(),
+ tool: refill.amount.tool.toNumber(),
+ fuel: refill.amount.fuel.toNumber(),
+ ammo: refill.amount.ammo.toNumber(),
+ }).save()
+ }),
+ )
+ } catch (e) {
+ Sentry.captureException(e)
+ logger.error(
+ `Error refilling fleet: ${(e as Error).message}`,
+ )
+
+ return null
+ }
+ }),
+ )
+
+ const depletionInfos = await Promise.all(
+ shipStakingInfos.map(async (shipStakingInfo) => {
+ const depletionInfo = await fleetDepletionInfo(shipStakingInfo)
+
+ logger.info(
+ `${player.toString()} ${await getShipName(shipStakingInfo)} depleting in ${depletionInfo.human}`,
+ )
+
+ return depletionInfo
+ }),
+ )
+
+ const secondsLeft = depletionInfos.reduce(
+ (acc, cur) => Math.min(cur.seconds, acc),
+ Number.MAX_SAFE_INTEGER,
+ )
+
+ const nextRefill = dayjs()
+ .add(
+ Big(secondsLeft).minus(Big(secondsLeft).div(4)).toNumber(),
+ 'second',
+ )
+ .toDate()
await Wallet.update({ publicKey: wallet.publicKey }, { nextRefill })
- return fleetRefills.filter((f): f is Refill => f !== null)
+ return fleetRefills.filter((f): f is Refill[] => f !== null).flat()
}
diff --git a/src/lib/refill-strategy/full-refill-strategy.ts b/src/lib/refill-strategy/full-refill-strategy.ts
index b1c5d9a1..876547a4 100644
--- a/src/lib/refill-strategy/full-refill-strategy.ts
+++ b/src/lib/refill-strategy/full-refill-strategy.ts
@@ -9,30 +9,39 @@ import { getPrice } from '../get-price'
import { RefillStrategy } from './refill-strategy'
-export const fullRefillStrategy: RefillStrategy = shipStakingInfos =>
- Promise.all(shipStakingInfos.map(async (shipStakingInfo) => {
- const info = await getScoreVarsShipInfo(connection, fleetProgram, shipStakingInfo.shipMint)
+export const fullRefillStrategy: RefillStrategy = (shipStakingInfos) =>
+ Promise.all(
+ shipStakingInfos.map(async (shipStakingInfo) => {
+ const info = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ shipStakingInfo.shipMint,
+ )
- const remainingResources = getFleetRemainingResources(info, shipStakingInfo)
+ const remainingResources = getFleetRemainingResources(
+ info,
+ shipStakingInfo,
+ )
- const amount: Amounts = {
- food: Big(remainingResources.food.maxUnits)
- .minus(remainingResources.food.unitsLeft)
- .round(0, Big.roundDown),
- ammo: Big(remainingResources.ammo.maxUnits)
- .minus(remainingResources.ammo.unitsLeft)
- .round(0, Big.roundDown),
- fuel: Big(remainingResources.fuel.maxUnits)
- .minus(remainingResources.fuel.unitsLeft)
- .round(0, Big.roundDown),
- tool: Big(remainingResources.tool.maxUnits)
- .minus(remainingResources.tool.unitsLeft)
- .round(0, Big.roundDown)
- }
+ const amount: Amounts = {
+ food: Big(remainingResources.food.maxUnits)
+ .minus(remainingResources.food.unitsLeft)
+ .round(0, Big.roundDown),
+ ammo: Big(remainingResources.ammo.maxUnits)
+ .minus(remainingResources.ammo.unitsLeft)
+ .round(0, Big.roundDown),
+ fuel: Big(remainingResources.fuel.maxUnits)
+ .minus(remainingResources.fuel.unitsLeft)
+ .round(0, Big.roundDown),
+ tool: Big(remainingResources.tool.maxUnits)
+ .minus(remainingResources.tool.unitsLeft)
+ .round(0, Big.roundDown),
+ }
- return {
- shipStakingInfo,
- amount,
- price: getPrice(amount)
- } as FleetRefill
- }))
+ return {
+ shipStakingInfo,
+ amount,
+ price: getPrice(amount),
+ } as FleetRefill
+ }),
+ )
diff --git a/src/lib/refill-strategy/index.ts b/src/lib/refill-strategy/index.ts
index 12b87712..3bb6f852 100644
--- a/src/lib/refill-strategy/index.ts
+++ b/src/lib/refill-strategy/index.ts
@@ -1,3 +1,3 @@
-export * from './refill-strategy'
-export * from './optimal-refill-strategy'
export * from './full-refill-strategy'
+export * from './optimal-refill-strategy'
+export * from './refill-strategy'
diff --git a/src/lib/refill-strategy/optimal-refill-strategy.ts b/src/lib/refill-strategy/optimal-refill-strategy.ts
index 2a1f7718..deaabc34 100644
--- a/src/lib/refill-strategy/optimal-refill-strategy.ts
+++ b/src/lib/refill-strategy/optimal-refill-strategy.ts
@@ -9,60 +9,82 @@ import { getPrice } from '../get-price'
import { RefillStrategy } from './refill-strategy'
-export const optimalRefillStrategy: RefillStrategy = async (shipStakingInfos) => {
- const targets = await Promise.all(shipStakingInfos.map(async (shipStakingInfo) => {
- const info = await getScoreVarsShipInfo(connection, fleetProgram, shipStakingInfo.shipMint)
- const remainingResources = getFleetRemainingResources(info, shipStakingInfo)
-
- return Math.min(
- remainingResources.food.maxSeconds,
- remainingResources.ammo.maxSeconds,
- remainingResources.fuel.maxSeconds,
- remainingResources.tool.maxSeconds
- )
- }))
- const target = targets.reduce((acc, cur) => Math.min(acc, cur), Number.MAX_SAFE_INTEGER)
+export const optimalRefillStrategy: RefillStrategy = async (
+ shipStakingInfos,
+) => {
+ const targets = await Promise.all(
+ shipStakingInfos.map(async (shipStakingInfo) => {
+ const info = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ shipStakingInfo.shipMint,
+ )
+ const remainingResources = getFleetRemainingResources(
+ info,
+ shipStakingInfo,
+ )
- return Promise.all(shipStakingInfos.map(async (shipStakingInfo) => {
- const info = await getScoreVarsShipInfo(connection, fleetProgram, shipStakingInfo.shipMint)
- const remainingResources = getFleetRemainingResources(info, shipStakingInfo)
+ return Math.min(
+ remainingResources.food.maxSeconds,
+ remainingResources.ammo.maxSeconds,
+ remainingResources.fuel.maxSeconds,
+ remainingResources.tool.maxSeconds,
+ )
+ }),
+ )
+ const target = targets.reduce(
+ (acc, cur) => Math.min(acc, cur),
+ Number.MAX_SAFE_INTEGER,
+ )
- const amount: Amounts = {
- food: max(
- Big(target)
- .minus(remainingResources.food.secondsLeft)
- .mul(remainingResources.food.burnRatePerFleet)
- .round(),
- Big(0)
- ),
- ammo: max(
- Big(target)
- .minus(remainingResources.ammo.secondsLeft)
- .mul(remainingResources.ammo.burnRatePerFleet)
- .round(),
- Big(0)
- ),
- fuel: max(
- Big(target)
- .minus(remainingResources.fuel.secondsLeft)
- .mul(remainingResources.fuel.burnRatePerFleet)
- .round(),
- Big(0)
- ),
- tool: max(
- Big(target)
- .minus(remainingResources.tool.secondsLeft)
- .mul(remainingResources.tool.burnRatePerFleet)
- .round(),
- Big(0)
+ return Promise.all(
+ shipStakingInfos.map(async (shipStakingInfo) => {
+ const info = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ shipStakingInfo.shipMint,
+ )
+ const remainingResources = getFleetRemainingResources(
+ info,
+ shipStakingInfo,
)
- }
+ const amount: Amounts = {
+ food: max(
+ Big(target)
+ .minus(remainingResources.food.secondsLeft)
+ .mul(remainingResources.food.burnRatePerFleet)
+ .round(),
+ Big(0),
+ ),
+ ammo: max(
+ Big(target)
+ .minus(remainingResources.ammo.secondsLeft)
+ .mul(remainingResources.ammo.burnRatePerFleet)
+ .round(),
+ Big(0),
+ ),
+ fuel: max(
+ Big(target)
+ .minus(remainingResources.fuel.secondsLeft)
+ .mul(remainingResources.fuel.burnRatePerFleet)
+ .round(),
+ Big(0),
+ ),
+ tool: max(
+ Big(target)
+ .minus(remainingResources.tool.secondsLeft)
+ .mul(remainingResources.tool.burnRatePerFleet)
+ .round(),
+ Big(0),
+ ),
+ }
- return {
- shipStakingInfo,
- amount,
- price: getPrice(amount)
- } as FleetRefill
- }))
+ return {
+ shipStakingInfo,
+ amount,
+ price: getPrice(amount),
+ } as FleetRefill
+ }),
+ )
}
diff --git a/src/lib/refill-strategy/refill-strategy.ts b/src/lib/refill-strategy/refill-strategy.ts
index 73f9c031..2299df9e 100644
--- a/src/lib/refill-strategy/refill-strategy.ts
+++ b/src/lib/refill-strategy/refill-strategy.ts
@@ -2,4 +2,6 @@ import { ShipStakingInfo } from '@staratlas/factory'
import { FleetRefill } from '../const'
-export type RefillStrategy = (shipStakingInfos: ShipStakingInfo[]) => Promise
+export type RefillStrategy = (
+ shipStakingInfos: ShipStakingInfo[],
+) => Promise
diff --git a/src/lib/refill.ts b/src/lib/refill.ts
index c37dae42..c2e6f33a 100644
--- a/src/lib/refill.ts
+++ b/src/lib/refill.ts
@@ -19,27 +19,41 @@ export const refill = async (): Promise => {
/* eslint-disable no-await-in-loop */
for (const player of players) {
if (dayjs().isAfter(player.nextRefill)) {
- await refillPlayer(new PublicKey(player.publicKey), optimalRefillStrategy)
+ await refillPlayer(
+ new PublicKey(player.publicKey),
+ optimalRefillStrategy,
+ )
}
- const fleets = await getAllFleetsForUserPublicKey(connection, new PublicKey(player.publicKey), fleetProgram)
+ const fleets = await getAllFleetsForUserPublicKey(
+ connection,
+ new PublicKey(player.publicKey),
+ fleetProgram,
+ )
const burnRate = await getDailyBurnRate(fleets)
const price = getResourcePrices()
const balance = await player.getBalance()
- const burnPerDay =
- max(burnRate.food.mul(price.food).add(
- burnRate.fuel.mul(price.fuel)).add(
- burnRate.tool.mul(price.tool)).add(
- burnRate.fuel.mul(price.ammo)), Big(0.00000001))
+ const burnPerDay = max(
+ burnRate.food
+ .mul(price.food)
+ .add(burnRate.fuel.mul(price.fuel))
+ .add(burnRate.tool.mul(price.tool))
+ .add(burnRate.fuel.mul(price.ammo)),
+ Big(0.00000001),
+ )
const balanceTime = balance.div(burnPerDay)
await player.reload()
logger.info('-----------------------------------------------------')
- logger.info(`${player.publicKey} next refill in ${dayjs.duration(dayjs().diff(player.nextRefill, 'second'), 'second').humanize()}`)
- logger.info(`Balance: ${balance.toFixed(AD)} ATLAS / Burn ${burnPerDay.toFixed(AD)} ATLAS per day / Credit for ${dayjs.duration(balanceTime.toNumber(), 'day').humanize(false)}`)
+ logger.info(
+ `${player.publicKey} next refill in ${dayjs.duration(dayjs().diff(player.nextRefill, 'second'), 'second').humanize()}`,
+ )
+ logger.info(
+ `Balance: ${balance.toFixed(AD)} ATLAS / Burn ${burnPerDay.toFixed(AD)} ATLAS per day / Credit for ${dayjs.duration(balanceTime.toNumber(), 'day').humanize(false)}`,
+ )
logger.info(`Total Tipped: ${(await player.totalTipped()).toFixed(AD)}`)
logger.info('-----------------------------------------------------')
}
diff --git a/src/lib/stock-resources.ts b/src/lib/stock-resources.ts
index 94d2bdbc..bec91e1a 100644
--- a/src/lib/stock-resources.ts
+++ b/src/lib/stock-resources.ts
@@ -1,7 +1,12 @@
// eslint-disable-next-line import/named
import { AnchorProvider, Idl, Program } from '@coral-xyz/anchor'
import { Connection, PublicKey } from '@solana/web3.js'
-import { getScoreIDL, getScoreVarsShipInfo, getShipStakingAccount, ShipStakingInfo } from '@staratlas/factory'
+import {
+ ShipStakingInfo,
+ getScoreIDL,
+ getScoreVarsShipInfo,
+ getShipStakingAccount,
+} from '@staratlas/factory'
import Big from 'big.js'
import superagent from 'superagent'
@@ -10,7 +15,11 @@ import { ShipInfo, Wallet } from '../db/entities'
import { logger } from '../logger'
import { getFleetRemainingResources, getTimePass } from '../service/fleet'
import { Amounts } from '../service/fleet/const'
-import { buyResources, getResourceBalances, getResourcePrices } from '../service/gm'
+import {
+ buyResources,
+ getResourceBalances,
+ getResourcePrices,
+} from '../service/gm'
import { AD, connection, fleetProgram } from '../service/sol'
import { keyPair } from '../service/wallet'
@@ -25,7 +34,7 @@ import { keyPair } from '../service/wallet'
const getAllFleetsForUserPublicKey = async (
conn: Connection,
playerPublicKey: PublicKey,
- programId: PublicKey
+ programId: PublicKey,
): Promise => {
const idl = getScoreIDL(programId)
// @ts-expect-error 123
@@ -41,7 +50,7 @@ const getAllFleetsForUserPublicKey = async (
const [playerShipStakingAccount] = await getShipStakingAccount(
programId,
ship.account.shipMint as PublicKey,
- playerPublicKey
+ playerPublicKey,
)
playerShipStakingAccounts.push(playerShipStakingAccount)
@@ -57,33 +66,37 @@ const getAllFleetsForUserPublicKey = async (
if (fleet) {
playerFleets.push(fleet)
}
- }
- catch (e) {
+ } catch (e) {
logger.error(e)
}
}
- logger.info(`Found ${playerFleets.length} fleets for ${playerPublicKey.toString()}`)
+ logger.info(
+ `Found ${playerFleets.length} fleets for ${playerPublicKey.toString()}`,
+ )
return playerFleets
}
-export const getShipName = async (shipStakingInfo: ShipStakingInfo): Promise => {
+export const getShipName = async (
+ shipStakingInfo: ShipStakingInfo,
+): Promise => {
const mint = shipStakingInfo.shipMint.toString()
let shipInfo = await ShipInfo.findOneBy({ mint })
if (!shipInfo) {
try {
- const shipNftInfo = await superagent.get(`https://galaxy.staratlas.com/nfts/${mint}`)
+ const shipNftInfo = await superagent.get(
+ `https://galaxy.staratlas.com/nfts/${mint}`,
+ )
const urlSplit = shipNftInfo.body.image.slice(0, -4).split('/')
const imageName = urlSplit[urlSplit.length - 1]
shipInfo = await ShipInfo.create({
mint,
name: shipNftInfo.body.name,
- imageName
+ imageName,
}).save()
- }
- catch {
+ } catch {
return 'n/a'
}
}
@@ -91,99 +104,162 @@ export const getShipName = async (shipStakingInfo: ShipStakingInfo): Promise => {
- const fleetInfos = await Promise.all(fleets.map(async (fleet) => {
- const shipInfo = await getScoreVarsShipInfo(
- connection,
- fleetProgram,
- fleet.shipMint
- )
-
- return {
- info: shipInfo,
- fleet
- }
- }))
-
- return fleetInfos.reduce((sum, fleetInfo) => {
- const { fleet } = fleetInfo
- const { info } = fleetInfo
- const timePass = getTimePass(fleet)
- const pendingReward = Number(fleet.shipQuantityInEscrow) *
- (Number(fleet.totalTimeStaked) - Number(fleet.stakedTimePaid) + timePass) *
- Number(info.rewardRatePerSecond)
-
- return sum.add(pendingReward)
- }, Big(0)).div(100000000)
+export const getPendingRewards = async (
+ fleets: ShipStakingInfo[],
+): Promise => {
+ const fleetInfos = await Promise.all(
+ fleets.map(async (fleet) => {
+ const shipInfo = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ fleet.shipMint,
+ )
+
+ return {
+ info: shipInfo,
+ fleet,
+ }
+ }),
+ )
+
+ return fleetInfos
+ .reduce((sum, fleetInfo) => {
+ const { fleet } = fleetInfo
+ const { info } = fleetInfo
+ const timePass = getTimePass(fleet)
+ const pendingReward =
+ Number(fleet.shipQuantityInEscrow) *
+ (Number(fleet.totalTimeStaked) -
+ Number(fleet.stakedTimePaid) +
+ timePass) *
+ Number(info.rewardRatePerSecond)
+
+ return sum.add(pendingReward)
+ }, Big(0))
+ .div(100000000)
}
-export const getDailyBurnRate = async (fleets: ShipStakingInfo[]): Promise => {
+export const getDailyBurnRate = async (
+ fleets: ShipStakingInfo[],
+): Promise => {
const dayInSeconds = 86400
const resourcePerDay: Amounts = {
food: Big(0),
tool: Big(0),
ammo: Big(0),
- fuel: Big(0)
+ fuel: Big(0),
}
- await Promise.all(fleets.map(async (shipStakingInfo) => {
- const info = await getScoreVarsShipInfo(connection, fleetProgram, shipStakingInfo.shipMint)
- const remaining = getFleetRemainingResources(info, shipStakingInfo)
-
- resourcePerDay.food = resourcePerDay.food.add(Big(remaining.food.burnRatePerFleet).mul(dayInSeconds))
- resourcePerDay.tool = resourcePerDay.tool.add(Big(remaining.tool.burnRatePerFleet).mul(dayInSeconds))
- resourcePerDay.ammo = resourcePerDay.ammo.add(Big(remaining.ammo.burnRatePerFleet).mul(dayInSeconds))
- resourcePerDay.fuel = resourcePerDay.fuel.add(Big(remaining.fuel.burnRatePerFleet).mul(dayInSeconds))
- }))
+ await Promise.all(
+ fleets.map(async (shipStakingInfo) => {
+ const info = await getScoreVarsShipInfo(
+ connection,
+ fleetProgram,
+ shipStakingInfo.shipMint,
+ )
+ const remaining = getFleetRemainingResources(info, shipStakingInfo)
+
+ resourcePerDay.food = resourcePerDay.food.add(
+ Big(remaining.food.burnRatePerFleet).mul(dayInSeconds),
+ )
+ resourcePerDay.tool = resourcePerDay.tool.add(
+ Big(remaining.tool.burnRatePerFleet).mul(dayInSeconds),
+ )
+ resourcePerDay.ammo = resourcePerDay.ammo.add(
+ Big(remaining.ammo.burnRatePerFleet).mul(dayInSeconds),
+ )
+ resourcePerDay.fuel = resourcePerDay.fuel.add(
+ Big(remaining.fuel.burnRatePerFleet).mul(dayInSeconds),
+ )
+ }),
+ )
return resourcePerDay
}
const logStats = (balance: Amounts, dailyBurn: Amounts) => {
- logger.info(`TOOL balance: ${balance.tool}, burning ${dailyBurn.tool.toFixed(0)} per day, last for ${dailyBurn.tool.eq(0) ? 0 : dayjs.duration(balance.tool.div(dailyBurn.tool).toNumber(), 'day').humanize()}`)
- logger.info(`AMMO balance: ${balance.ammo}, burning ${dailyBurn.ammo.toFixed(0)} per day, last for ${dailyBurn.ammo.eq(0) ? 0 : dayjs.duration(balance.ammo.div(dailyBurn.ammo).toNumber(), 'day').humanize()}`)
- logger.info(`FOOD balance: ${balance.food}, burning ${dailyBurn.food.toFixed(0)} per day, last for ${dailyBurn.food.eq(0) ? 0 : dayjs.duration(balance.food.div(dailyBurn.food).toNumber(), 'day').humanize()}`)
- logger.info(`FUEL balance: ${balance.fuel}, burning ${dailyBurn.fuel.toFixed(0)} per day, last for ${dailyBurn.fuel.eq(0) ? 0 : dayjs.duration(balance.fuel.div(dailyBurn.fuel).toNumber(), 'day').humanize()}`)
+ logger.info(
+ `TOOL balance: ${balance.tool}, burning ${dailyBurn.tool.toFixed(0)} per day, last for ${dailyBurn.tool.eq(0) ? 0 : dayjs.duration(balance.tool.div(dailyBurn.tool).toNumber(), 'day').humanize()}`,
+ )
+ logger.info(
+ `AMMO balance: ${balance.ammo}, burning ${dailyBurn.ammo.toFixed(0)} per day, last for ${dailyBurn.ammo.eq(0) ? 0 : dayjs.duration(balance.ammo.div(dailyBurn.ammo).toNumber(), 'day').humanize()}`,
+ )
+ logger.info(
+ `FOOD balance: ${balance.food}, burning ${dailyBurn.food.toFixed(0)} per day, last for ${dailyBurn.food.eq(0) ? 0 : dayjs.duration(balance.food.div(dailyBurn.food).toNumber(), 'day').humanize()}`,
+ )
+ logger.info(
+ `FUEL balance: ${balance.fuel}, burning ${dailyBurn.fuel.toFixed(0)} per day, last for ${dailyBurn.fuel.eq(0) ? 0 : dayjs.duration(balance.fuel.div(dailyBurn.fuel).toNumber(), 'day').humanize()}`,
+ )
}
export const stockResources = async (): Promise => {
const wallets = await Wallet.findBy({ enabled: true })
- const dailyBurn = (await Promise.all(wallets.map(async wallet =>
- getDailyBurnRate(
- await getAllFleetsForUserPublicKey(connection, new PublicKey(wallet.publicKey), fleetProgram)))
- ) ) .reduce((acc, cur) => ({
- tool: acc.tool.add(cur.tool),
- ammo: acc.ammo.add(cur.ammo),
- food: acc.food.add(cur.food),
- fuel: acc.fuel.add(cur.fuel)
- }), { ammo: Big(0), food: Big(0), fuel: Big(0), tool: Big(0) } as Amounts)
+ const dailyBurn = (
+ await Promise.all(
+ wallets.map(async (wallet) =>
+ getDailyBurnRate(
+ await getAllFleetsForUserPublicKey(
+ connection,
+ new PublicKey(wallet.publicKey),
+ fleetProgram,
+ ),
+ ),
+ ),
+ )
+ ).reduce(
+ (acc, cur) => ({
+ tool: acc.tool.add(cur.tool),
+ ammo: acc.ammo.add(cur.ammo),
+ food: acc.food.add(cur.food),
+ fuel: acc.fuel.add(cur.fuel),
+ }),
+ { ammo: Big(0), food: Big(0), fuel: Big(0), tool: Big(0) } as Amounts,
+ )
const balance = await getResourceBalances(keyPair.publicKey)
logStats(balance, dailyBurn)
const amount: Amounts = {
- food: balance.food.lt(dailyBurn.food.mul(14)) ? dailyBurn.food.mul(21) : Big(0),
- ammo: balance.ammo.lt(dailyBurn.ammo.mul(14)) ? dailyBurn.ammo.mul(21) : Big(0),
- fuel: balance.fuel.lt(dailyBurn.fuel.mul(14)) ? dailyBurn.fuel.mul(21) : Big(0),
- tool: balance.tool.lt(dailyBurn.tool.mul(14)) ? dailyBurn.tool.mul(21) : Big(0)
+ food: balance.food.lt(dailyBurn.food.mul(14))
+ ? dailyBurn.food.mul(21)
+ : Big(0),
+ ammo: balance.ammo.lt(dailyBurn.ammo.mul(14))
+ ? dailyBurn.ammo.mul(21)
+ : Big(0),
+ fuel: balance.fuel.lt(dailyBurn.fuel.mul(14))
+ ? dailyBurn.fuel.mul(21)
+ : Big(0),
+ tool: balance.tool.lt(dailyBurn.tool.mul(14))
+ ? dailyBurn.tool.mul(21)
+ : Big(0),
}
- if (amount.food.gt(0) || amount.ammo.gt(0) || amount.fuel.gt(0) || amount.tool.gt(0)) {
+ if (
+ amount.food.gt(0) ||
+ amount.ammo.gt(0) ||
+ amount.fuel.gt(0) ||
+ amount.tool.gt(0)
+ ) {
const price = getResourcePrices()
const totalFuelPrice = amount.fuel.mul(price.fuel)
const totalFoodPrice = amount.food.mul(price.food)
const totalAmmoPrice = amount.ammo.mul(price.ammo)
const totalToolPrice = amount.tool.mul(price.tool)
- const totalPrice = totalFuelPrice.add(totalFoodPrice).add(totalAmmoPrice).add(totalToolPrice)
+ const totalPrice = totalFuelPrice
+ .add(totalFoodPrice)
+ .add(totalAmmoPrice)
+ .add(totalToolPrice)
- logger.info(`Buying Resources...${JSON.stringify(amount)} for ${totalPrice.toFixed(AD)} ATLAS`)
+ logger.info(
+ `Buying Resources...${JSON.stringify(amount)} for ${totalPrice.toFixed(AD)} ATLAS`,
+ )
const txs = await buyResources(amount)
- txs.forEach(tx => logger.info(tx))
+ txs.forEach((tx) => logger.info(tx))
logStats(await getResourceBalances(keyPair.publicKey), dailyBurn)
}
}
diff --git a/src/lib/telegram/commands/disable-notify.ts b/src/lib/telegram/commands/disable-notify.ts
index 7ef59abb..9a68f58f 100644
--- a/src/lib/telegram/commands/disable-notify.ts
+++ b/src/lib/telegram/commands/disable-notify.ts
@@ -14,7 +14,9 @@ export const disableNotify = (bot: Telegraf): void => {
ctx.user.notify = false
await ctx.user.save()
- await ctx.reply('I will stay silent unless there is something urgent happening.')
+ await ctx.reply(
+ 'I will stay silent unless there is something urgent happening.',
+ )
})
})
}
diff --git a/src/lib/telegram/commands/logout.ts b/src/lib/telegram/commands/logout.ts
index 3aa1d11b..4d5d4532 100644
--- a/src/lib/telegram/commands/logout.ts
+++ b/src/lib/telegram/commands/logout.ts
@@ -14,7 +14,9 @@ export const logout = (bot: Telegraf): void => {
}
if (ctx.user.enabled) {
- await ctx.reply('Your wallet is currently enabled, toggle with /disable command')
+ await ctx.reply(
+ 'Your wallet is currently enabled, toggle with /disable command',
+ )
return
}
diff --git a/src/lib/telegram/commands/meme/kitten.ts b/src/lib/telegram/commands/meme/kitten.ts
index 9413c78c..beaebe86 100644
--- a/src/lib/telegram/commands/meme/kitten.ts
+++ b/src/lib/telegram/commands/meme/kitten.ts
@@ -11,8 +11,7 @@ export const kitten = (bot: Telegraf): void => {
try {
await ctx.replyWithPhoto(`https://placekitten.com/${x}/${x}`)
- }
- catch (e: any) {
+ } catch (e: any) {
logger.error(`Cannot send Photo: ${e.message}`)
await ctx.reply('Meow!')
}
diff --git a/src/lib/telegram/commands/meme/porn.ts b/src/lib/telegram/commands/meme/porn.ts
index 2e6333eb..0c2ba0ea 100644
--- a/src/lib/telegram/commands/meme/porn.ts
+++ b/src/lib/telegram/commands/meme/porn.ts
@@ -18,8 +18,7 @@ export const porn = (bot: Telegraf): void => {
const imageUrl = `https://storage.googleapis.com/nft-assets/items/${imageName}.jpg`
await ctx.replyWithPhoto(imageUrl)
- }
- catch (e: any) {
+ } catch (e: any) {
logger.error(`Cannot send Photo: ${e.message}`)
await ctx.reply(':-*')
}
diff --git a/src/lib/telegram/commands/meme/wen.ts b/src/lib/telegram/commands/meme/wen.ts
index 507b06ac..43c0e305 100644
--- a/src/lib/telegram/commands/meme/wen.ts
+++ b/src/lib/telegram/commands/meme/wen.ts
@@ -7,9 +7,10 @@ export const wen = (bot: Telegraf): void => {
bot.command(['wen'], async (ctx) => {
await ctx.persistentChatAction('upload_photo', async () => {
try {
- await ctx.replyWithAnimation('http://2damnfunny.com/wp-content/uploads/2013/06/Very-Thoon-Husky-Dog-Meme-Gif.gif')
- }
- catch (e: any) {
+ await ctx.replyWithAnimation(
+ 'http://2damnfunny.com/wp-content/uploads/2013/06/Very-Thoon-Husky-Dog-Meme-Gif.gif',
+ )
+ } catch (e: any) {
logger.error(`Cannot send Photo: ${e.message}`)
await ctx.reply('Thoon!')
}
diff --git a/src/lib/telegram/commands/refill.ts b/src/lib/telegram/commands/refill.ts
index 350c20ea..fd0aa004 100644
--- a/src/lib/telegram/commands/refill.ts
+++ b/src/lib/telegram/commands/refill.ts
@@ -3,7 +3,10 @@ import { Telegraf } from 'telegraf'
import { AD } from '../../../service/sol'
import { refillPlayer } from '../../refill-player'
-import { fullRefillStrategy, optimalRefillStrategy } from '../../refill-strategy'
+import {
+ fullRefillStrategy,
+ optimalRefillStrategy,
+} from '../../refill-strategy'
import { ContextMessageUpdate } from '../context-message-update'
import { unauthorized } from '../response'
@@ -17,20 +20,35 @@ export const refill = (bot: Telegraf): void => {
}
if (!ctx.user.enabled) {
- await ctx.reply('Your wallet is currently disabled, toggle with /enable command')
+ await ctx.reply(
+ 'Your wallet is currently disabled, toggle with /enable command',
+ )
return
}
- const strategy = ctx.params.length === 1 && ctx.params[0] === 'full' ? fullRefillStrategy : optimalRefillStrategy
- const strategyName = ctx.params.length === 1 && ctx.params[0] === 'full' ? 'full' : 'optimal'
+ const strategy =
+ ctx.params.length === 1 && ctx.params[0] === 'full'
+ ? fullRefillStrategy
+ : optimalRefillStrategy
+ const strategyName =
+ ctx.params.length === 1 && ctx.params[0] === 'full'
+ ? 'full'
+ : 'optimal'
- await ctx.reply('Just saying... There should not be any reason to do this now, but as you wish, I am going to refill your fleets, ser!')
- await ctx.reply(`Using ${strategyName} refill strategy. Give me a moment please...`)
- const userRefills = await refillPlayer(new PublicKey(ctx.user.publicKey), strategy)
+ await ctx.reply(
+ 'Just saying... There should not be any reason to do this now, but as you wish, I am going to refill your fleets, ser!',
+ )
+ await ctx.reply(
+ `Using ${strategyName} refill strategy. Give me a moment please...`,
+ )
+ const userRefills = await refillPlayer(
+ new PublicKey(ctx.user.publicKey),
+ strategy,
+ )
for (const userRefill of userRefills) {
- // eslint-disable-next-line no-await-in-loop
+ // eslint-disable-next-line no-await-in-loop
await ctx.replyWithHTML(`
Signature: click
Time: ${userRefill.time.toLocaleDateString()} ${userRefill.time.toLocaleTimeString()}
@@ -39,12 +57,13 @@ export const refill = (bot: Telegraf): void => {
Tool: ${userRefill.tool}
Fuel: ${userRefill.fuel}
Ammo ${userRefill.ammo}
-Price: ${userRefill.price.toFixed(AD)} ATLAS`
- )
+Price: ${userRefill.price.toFixed(AD)} ATLAS`)
}
if (strategyName === 'optimal') {
- await ctx.reply('Pro Tip: Trigger a full refill with \'/refill full\'')
+ await ctx.reply(
+ "Pro Tip: Trigger a full refill with '/refill full'",
+ )
}
})
})
diff --git a/src/lib/telegram/commands/refills.ts b/src/lib/telegram/commands/refills.ts
index 09e3c492..66c13167 100644
--- a/src/lib/telegram/commands/refills.ts
+++ b/src/lib/telegram/commands/refills.ts
@@ -15,7 +15,9 @@ export const refills = (bot: Telegraf): void => {
}
if (!ctx.user.enabled) {
- await ctx.reply('Your wallet is currently disabled, toggle with /enable command')
+ await ctx.reply(
+ 'Your wallet is currently disabled, toggle with /enable command',
+ )
return
}
@@ -23,10 +25,14 @@ export const refills = (bot: Telegraf): void => {
const take = 10
await ctx.reply(`Showing the last ${take} refills`)
- const userRefills = await Refill.find({ where: { walletPublicKey: ctx.user.publicKey }, take, order: { time: 'DESC' } })
+ const userRefills = await Refill.find({
+ where: { walletPublicKey: ctx.user.publicKey },
+ take,
+ order: { time: 'DESC' },
+ })
for (const refill of userRefills) {
- // eslint-disable-next-line no-await-in-loop
+ // eslint-disable-next-line no-await-in-loop
await ctx.replyWithHTML(`
Signature: click
Time: ${refill.time.toLocaleDateString()} ${refill.time.toLocaleTimeString()}
@@ -35,8 +41,7 @@ export const refills = (bot: Telegraf): void => {
Tool: ${refill.tool}
Fuel: ${refill.fuel}
Ammo ${refill.ammo}
-Price: ${refill.price.toFixed(AD)} ATLAS`
- )
+Price: ${refill.price.toFixed(AD)} ATLAS`)
}
})
})
diff --git a/src/lib/telegram/commands/stats.ts b/src/lib/telegram/commands/stats.ts
index 08cf0c6e..d843383e 100644
--- a/src/lib/telegram/commands/stats.ts
+++ b/src/lib/telegram/commands/stats.ts
@@ -21,7 +21,9 @@ export const stats = (bot: Telegraf): void => {
}
if (!ctx.user.enabled) {
- await ctx.reply('Your wallet is currently disabled, toggle with /enable command')
+ await ctx.reply(
+ 'Your wallet is currently disabled, toggle with /enable command',
+ )
return
}
@@ -34,40 +36,74 @@ export const stats = (bot: Telegraf): void => {
const fleets = await getAllFleetsForUserPublicKey(
connection,
new PublicKey(player.publicKey),
- fleetProgram)
- const [burnRate, price, playerBalance, pendingRewards] = await Promise.all([
- getDailyBurnRate(fleets),
- getResourcePrices(),
- player.getBalance(),
- getPendingRewards(fleets)
- ])
- const burnPerDay =
- burnRate.food.mul(price.food).add(
- burnRate.fuel.mul(price.fuel)).add(
- burnRate.tool.mul(price.tool)).add(
- burnRate.fuel.mul(price.ammo))
- const balanceTime = burnPerDay.gt(0) ? playerBalance.div(burnPerDay) : Big(0)
-
- const diffToNextRefill = dayjs().diff(player.nextRefill, 'second')
-
- const todayRefills = await Refill.findBy({ walletPublicKey: player.publicKey, time: MoreThanOrEqual(dayjs().startOf('day').toDate()) })
- const burnToday = todayRefills.reduce((curr, acc) => curr.add(acc.price), Big(0))
+ fleetProgram,
+ )
+ const [burnRate, price, playerBalance, pendingRewards] =
+ await Promise.all([
+ getDailyBurnRate(fleets),
+ getResourcePrices(),
+ player.getBalance(),
+ getPendingRewards(fleets),
+ ])
+ const burnPerDay = burnRate.food
+ .mul(price.food)
+ .add(burnRate.fuel.mul(price.fuel))
+ .add(burnRate.tool.mul(price.tool))
+ .add(burnRate.fuel.mul(price.ammo))
+ const balanceTime = burnPerDay.gt(0)
+ ? playerBalance.div(burnPerDay)
+ : Big(0)
+
+ const diffToNextRefill = dayjs().diff(
+ player.nextRefill,
+ 'second',
+ )
+
+ const todayRefills = await Refill.findBy({
+ walletPublicKey: player.publicKey,
+ time: MoreThanOrEqual(dayjs().startOf('day').toDate()),
+ })
+ const burnToday = todayRefills.reduce(
+ (curr, acc) => curr.add(acc.price),
+ Big(0),
+ )
const yesterdayRefills = await Refill.findBy({
- walletPublicKey: player.publicKey, time: Between(
+ walletPublicKey: player.publicKey,
+ time: Between(
dayjs().subtract(1, 'day').startOf('day').toDate(),
- dayjs().subtract(1, 'day').endOf('day').toDate()
- )
+ dayjs().subtract(1, 'day').endOf('day').toDate(),
+ ),
})
- const burnYesterday = yesterdayRefills.reduce((curr, acc) => curr.add(acc.price), Big(0))
+ const burnYesterday = yesterdayRefills.reduce(
+ (curr, acc) => curr.add(acc.price),
+ Big(0),
+ )
- const refills7d = await Refill.findBy({ walletPublicKey: player.publicKey, time: MoreThanOrEqual(dayjs().subtract(7, 'day').startOf('day').toDate()) })
- const burn7d = refills7d.reduce((curr, acc) => curr.add(acc.price), Big(0))
+ const refills7d = await Refill.findBy({
+ walletPublicKey: player.publicKey,
+ time: MoreThanOrEqual(
+ dayjs().subtract(7, 'day').startOf('day').toDate(),
+ ),
+ })
+ const burn7d = refills7d.reduce(
+ (curr, acc) => curr.add(acc.price),
+ Big(0),
+ )
- const refills30d = await Refill.findBy({ walletPublicKey: player.publicKey, time: MoreThanOrEqual(dayjs().subtract(30, 'day').startOf('day').toDate()) })
- const burn30d = refills30d.reduce((curr, acc) => curr.add(acc.price), Big(0))
+ const refills30d = await Refill.findBy({
+ walletPublicKey: player.publicKey,
+ time: MoreThanOrEqual(
+ dayjs().subtract(30, 'day').startOf('day').toDate(),
+ ),
+ })
+ const burn30d = refills30d.reduce(
+ (curr, acc) => curr.add(acc.price),
+ Big(0),
+ )
- const avg = (b: Big[]) => b.reduce((c, a) => c.add(a), Big(0)).div(b.length)
+ const avg = (b: Big[]) =>
+ b.reduce((c, a) => c.add(a), Big(0)).div(b.length)
const burnAvg7 = burn7d.div(7)
const burnAvg30 = burn30d.div(30)
@@ -76,7 +112,8 @@ export const stats = (bot: Telegraf): void => {
const drift = burnAvg.sub(burnPerDay)
- await ctx.replyWithMarkdownV2(`
+ await ctx.replyWithMarkdownV2(
+ `
*Next refill in:* ${dayjs.duration(diffToNextRefill, 'second').humanize()}
*Balance:* ${playerBalance.toFixed(AD)} ATLAS
*Burn \\(estimate\\):* ${burnPerDay.toFixed(AD)} ATLAS per day
@@ -87,7 +124,7 @@ export const stats = (bot: Telegraf): void => {
*Drift:* ${drift.toFixed(AD).toString().replace('-', '\\-')} ATLAS
*Pending Rewards:* ${pendingRewards.toFixed(AD)} ATLAS
*Credit for:* ${dayjs.duration(balanceTime.toNumber(), 'day').humanize(false)}
-*Current Tips:* ${player.tip * 100} %`.replace(/\./g, '\\.')
+*Current Tips:* ${player.tip * 100} %`.replace(/\./g, '\\.'),
)
}
})
diff --git a/src/lib/telegram/commands/support.ts b/src/lib/telegram/commands/support.ts
index 425008e6..2dcf95ee 100644
--- a/src/lib/telegram/commands/support.ts
+++ b/src/lib/telegram/commands/support.ts
@@ -6,7 +6,9 @@ import { ContextMessageUpdate } from '../context-message-update'
export const support = (bot: Telegraf): void => {
bot.command(['support'], async (ctx) => {
await ctx.persistentChatAction('typing', async () => {
- await ctx.reply(`I really don't know why you would need that, but just in case you want to talk to a human, please contact ${config.bot.owner}`)
+ await ctx.reply(
+ `I really don't know why you would need that, but just in case you want to talk to a human, please contact ${config.bot.owner}`,
+ )
})
})
}
diff --git a/src/lib/telegram/commands/tip.ts b/src/lib/telegram/commands/tip.ts
index 7efbeb12..8240be1f 100644
--- a/src/lib/telegram/commands/tip.ts
+++ b/src/lib/telegram/commands/tip.ts
@@ -14,7 +14,9 @@ export const tip = (bot: Telegraf): void => {
}
if (ctx.params.length !== 1) {
- await ctx.reply(`Tip is currently set to ${Big(ctx.user.tip).mul(100).toFixed()} %`)
+ await ctx.reply(
+ `Tip is currently set to ${Big(ctx.user.tip).mul(100).toFixed()} %`,
+ )
await ctx.reply('To update setting, use /tip {percentage}')
return
@@ -26,14 +28,15 @@ export const tip = (bot: Telegraf): void => {
try {
ctx.user.tip = Big(tipPercent).div(100).abs().toNumber()
await ctx.user.save()
- }
- catch {
+ } catch {
await ctx.reply('Tip amount must be be a positive number!')
return
}
- await ctx.reply(`Tip set to ${Big(ctx.user.tip).mul(100).toFixed()} %`)
+ await ctx.reply(
+ `Tip set to ${Big(ctx.user.tip).mul(100).toFixed()} %`,
+ )
})
})
}
diff --git a/src/lib/telegram/commands/transactions.ts b/src/lib/telegram/commands/transactions.ts
index 45c6dda0..215020fd 100644
--- a/src/lib/telegram/commands/transactions.ts
+++ b/src/lib/telegram/commands/transactions.ts
@@ -15,7 +15,9 @@ export const transactions = (bot: Telegraf): void => {
}
if (!ctx.user.enabled) {
- await ctx.reply('Your wallet is currently disabled, toggle with /enable command')
+ await ctx.reply(
+ 'Your wallet is currently disabled, toggle with /enable command',
+ )
return
}
@@ -23,15 +25,18 @@ export const transactions = (bot: Telegraf): void => {
const take = 10
await ctx.reply(`Showing the last ${take} transactions`)
- const userTransactions = await Transaction.find({ where: { walletPublicKey: ctx.user.publicKey }, take, order: { time: 'DESC' } })
+ const userTransactions = await Transaction.find({
+ where: { walletPublicKey: ctx.user.publicKey },
+ take,
+ order: { time: 'DESC' },
+ })
for (const transaction of userTransactions) {
// eslint-disable-next-line no-await-in-loop
await ctx.replyWithHTML(`
Signature: click
Time: ${transaction.time.toLocaleDateString()} ${transaction.time.toLocaleTimeString()}
-Amount: ${transaction.amount.toFixed(AD)} ATLAS`
- )
+Amount: ${transaction.amount.toFixed(AD)} ATLAS`)
}
})
})
diff --git a/src/lib/telegram/commands/verify.ts b/src/lib/telegram/commands/verify.ts
index 8f3c5372..5b27b721 100644
--- a/src/lib/telegram/commands/verify.ts
+++ b/src/lib/telegram/commands/verify.ts
@@ -4,7 +4,12 @@ import { Telegraf } from 'telegraf'
import { Wallet } from '../../../db/entities'
import { ContextMessageUpdate } from '../context-message-update'
-import { alreadyRegistered, authPending, unknownWallet, wrongParamCount } from '../response'
+import {
+ alreadyRegistered,
+ authPending,
+ unknownWallet,
+ wrongParamCount,
+} from '../response'
export const verify = (bot: Telegraf): void => {
bot.command(['verify'], async (ctx) => {
@@ -40,13 +45,21 @@ export const verify = (bot: Telegraf): void => {
return
}
- ctx.reply('Alright! To verify this wallet belongs to you, I need you to send a small amount of ATLAS to me. Don\'t worry. I will add this to your balance of course.')
+ ctx.reply(
+ "Alright! To verify this wallet belongs to you, I need you to send a small amount of ATLAS to me. Don't worry. I will add this to your balance of course.",
+ )
wallet.telegramId = ctx.from.id
wallet.authExpire = dayjs().add(1, 'hour').toDate()
- wallet.authTxAmount = faker.datatype.number({ min: 0.1, max: 0.999, precision: 0.001 })
+ wallet.authTxAmount = faker.datatype.number({
+ min: 0.1,
+ max: 0.999,
+ precision: 0.001,
+ })
await wallet.save()
await authPending(ctx, wallet)
- ctx.reply('This may take a moment. You can check the status with /verify command')
+ ctx.reply(
+ 'This may take a moment. You can check the status with /verify command',
+ )
})
})
}
diff --git a/src/lib/telegram/commands/withdraw.ts b/src/lib/telegram/commands/withdraw.ts
index c3c3ed0d..8f7c5dd6 100644
--- a/src/lib/telegram/commands/withdraw.ts
+++ b/src/lib/telegram/commands/withdraw.ts
@@ -26,21 +26,41 @@ export const withdraw = (bot: Telegraf): void => {
const wallet = ctx.user
const userBalance = await wallet.getBalance()
- const withdrawAmount = ctx.params[0] === 'all' ? userBalance : Big(ctx.params[0]).abs()
+ const withdrawAmount =
+ ctx.params[0] === 'all' ? userBalance : Big(ctx.params[0]).abs()
if (withdrawAmount.gt(userBalance)) {
- await ctx.reply(`Amount ${withdrawAmount.toFixed(AD)} exceeds user balance ${userBalance.toFixed(AD)}`)
+ await ctx.reply(
+ `Amount ${withdrawAmount.toFixed(AD)} exceeds user balance ${userBalance.toFixed(AD)}`,
+ )
return
}
- await ctx.reply(`Sending ${withdrawAmount} ATLAS to ${ctx.user.publicKey}`)
- const signature = await sendAtlas(new PublicKey(ctx.user.publicKey), withdrawAmount.toNumber())
+ await ctx.reply(
+ `Sending ${withdrawAmount} ATLAS to ${ctx.user.publicKey}`,
+ )
+ const signatures = await sendAtlas(
+ new PublicKey(ctx.user.publicKey),
+ withdrawAmount.toNumber(),
+ )
- await ctx.reply(`https://solscan.io/tx/${signature}`)
const amount = -withdrawAmount
- await Transaction.create({ wallet, amount, signature, time: dayjs().toDate(), originalAmount: amount, resource: 'ATLAS' }).save()
+ await Promise.all(
+ signatures.map(async (signature) => {
+ await ctx.reply(`https://solscan.io/tx/${signature}`)
+
+ return Transaction.create({
+ wallet,
+ amount,
+ signature,
+ time: dayjs().toDate(),
+ originalAmount: amount,
+ resource: 'ATLAS',
+ }).save()
+ }),
+ )
})
})
}
diff --git a/src/lib/telegram/middleware/auth.ts b/src/lib/telegram/middleware/auth.ts
index 85846bdd..27d26a41 100644
--- a/src/lib/telegram/middleware/auth.ts
+++ b/src/lib/telegram/middleware/auth.ts
@@ -3,7 +3,10 @@ import { Middleware } from 'telegraf'
import { Wallet } from '../../../db/entities'
import { ContextMessageUpdate } from '../context-message-update'
-export const auth: Middleware = async (ctx: ContextMessageUpdate, next: any) => {
+export const auth: Middleware = async (
+ ctx: ContextMessageUpdate,
+ next: any,
+) => {
if (ctx.from?.id) {
const wallet = await Wallet.findOneBy({ telegramId: ctx.from.id })
diff --git a/src/lib/telegram/middleware/params.ts b/src/lib/telegram/middleware/params.ts
index d66b42dd..9fb3e81c 100644
--- a/src/lib/telegram/middleware/params.ts
+++ b/src/lib/telegram/middleware/params.ts
@@ -2,7 +2,10 @@ import { Middleware } from 'telegraf'
import { ContextMessageUpdate } from '../context-message-update'
-export const params: Middleware = (ctx: ContextMessageUpdate, next: any) => {
+export const params: Middleware = (
+ ctx: ContextMessageUpdate,
+ next: any,
+) => {
if (ctx.message && 'text' in ctx.message) {
ctx.params = ctx.message.text.split(' ').splice(1)
}
diff --git a/src/lib/telegram/response/already-registered.ts b/src/lib/telegram/response/already-registered.ts
index 99b9b289..8706542b 100644
--- a/src/lib/telegram/response/already-registered.ts
+++ b/src/lib/telegram/response/already-registered.ts
@@ -2,6 +2,9 @@ import { Message } from 'telegraf/types'
import { ContextMessageUpdate } from '../context-message-update'
-export const alreadyRegistered = (ctx: ContextMessageUpdate): Promise => ctx.reply(`
+export const alreadyRegistered = (
+ ctx: ContextMessageUpdate,
+): Promise =>
+ ctx.reply(`
Looks like you are already registered and ready to go!
`)
diff --git a/src/lib/telegram/response/auth-pending.ts b/src/lib/telegram/response/auth-pending.ts
index e0a7395c..10e41965 100644
--- a/src/lib/telegram/response/auth-pending.ts
+++ b/src/lib/telegram/response/auth-pending.ts
@@ -4,7 +4,10 @@ import dayjs from '../../../dayjs'
import { Wallet } from '../../../db/entities'
import { ContextMessageUpdate } from '../context-message-update'
-export const authPending = (ctx: ContextMessageUpdate, wallet: Wallet): Promise => {
+export const authPending = (
+ ctx: ContextMessageUpdate,
+ wallet: Wallet,
+): Promise => {
const authExpire = dayjs().diff(wallet.authExpire, 'second')
return ctx.reply(`
diff --git a/src/lib/telegram/response/index.ts b/src/lib/telegram/response/index.ts
index 3ee03bb0..ace3b11f 100644
--- a/src/lib/telegram/response/index.ts
+++ b/src/lib/telegram/response/index.ts
@@ -1,5 +1,5 @@
-export * from './unauthorized'
export * from './already-registered'
+export * from './auth-pending'
+export * from './unauthorized'
export * from './unknown-wallet'
export * from './wrong-param-count'
-export * from './auth-pending'
diff --git a/src/lib/telegram/response/unauthorized.ts b/src/lib/telegram/response/unauthorized.ts
index ede5ef41..6ef48867 100644
--- a/src/lib/telegram/response/unauthorized.ts
+++ b/src/lib/telegram/response/unauthorized.ts
@@ -2,6 +2,9 @@ import { Message } from 'telegraf/types'
import { ContextMessageUpdate } from '../context-message-update'
-export const unauthorized = (ctx: ContextMessageUpdate): Promise => ctx.reply(`
+export const unauthorized = (
+ ctx: ContextMessageUpdate,
+): Promise =>
+ ctx.reply(`
Huh, who are you? Please authorize first!
`)
diff --git a/src/lib/telegram/response/unknown-wallet.ts b/src/lib/telegram/response/unknown-wallet.ts
index 69ad3fe6..bdc4f310 100644
--- a/src/lib/telegram/response/unknown-wallet.ts
+++ b/src/lib/telegram/response/unknown-wallet.ts
@@ -2,6 +2,9 @@ import { Message } from 'telegraf/types'
import { ContextMessageUpdate } from '../context-message-update'
-export const unknownWallet = (ctx: ContextMessageUpdate): Promise => ctx.reply(`
+export const unknownWallet = (
+ ctx: ContextMessageUpdate,
+): Promise =>
+ ctx.reply(`
Could not find wallet. Please send some ATLAS first!
`)
diff --git a/src/lib/telegram/response/wrong-param-count.ts b/src/lib/telegram/response/wrong-param-count.ts
index 7e44269c..72ae4e2e 100644
--- a/src/lib/telegram/response/wrong-param-count.ts
+++ b/src/lib/telegram/response/wrong-param-count.ts
@@ -2,5 +2,7 @@ import { Message } from 'telegraf/types'
import { ContextMessageUpdate } from '../context-message-update'
-export const wrongParamCount =
- (ctx: ContextMessageUpdate, message: string): Promise => ctx.reply(message)
+export const wrongParamCount = (
+ ctx: ContextMessageUpdate,
+ message: string,
+): Promise => ctx.reply(message)
diff --git a/src/lib/telegram/telegram-bot.ts b/src/lib/telegram/telegram-bot.ts
index d3563b55..7a7eae3d 100644
--- a/src/lib/telegram/telegram-bot.ts
+++ b/src/lib/telegram/telegram-bot.ts
@@ -7,7 +7,10 @@ import { ContextMessageUpdate } from './context-message-update'
import { auth } from './middleware'
import { params } from './middleware/params'
-const telegramBot: Telegraf = new Telegraf(config.bot.telegramToken, { handlerTimeout: 360_000 })
+const telegramBot: Telegraf = new Telegraf(
+ config.bot.telegramToken,
+ { handlerTimeout: 360_000 },
+)
telegramBot.use(auth)
telegramBot.use(params)
diff --git a/src/logger.ts b/src/logger.ts
index e2d7425d..32c8b875 100644
--- a/src/logger.ts
+++ b/src/logger.ts
@@ -5,18 +5,21 @@ import { config } from './config'
const prettyError = new PrettyError()
.skipNodeFiles()
- .skip((traceLine: Record): boolean => traceLine.packageName !== '[current]')
+ .skip(
+ (traceLine: Record): boolean =>
+ traceLine.packageName !== '[current]',
+ )
.appendStyle({
'pretty-error': {
- marginLeft: 0
+ marginLeft: 0,
},
'pretty-error > trace': {
- marginTop: 0
+ marginTop: 0,
},
'pretty-error > trace > item': {
bullet: '',
- marginBottom: 0
- }
+ marginBottom: 0,
+ },
})
const prettyErrorFormat = winston.format((info) => {
@@ -24,25 +27,22 @@ const prettyErrorFormat = winston.format((info) => {
return {
...info,
message: prettyError.render(info),
- stack: undefined
+ stack: undefined,
}
}
return info
})
-const format =
- winston.format.combine(
- winston.format.colorize(),
- winston.format.errors({ stack: true }),
- prettyErrorFormat(),
- winston.format.simple()
- )
+const format = winston.format.combine(
+ winston.format.colorize(),
+ winston.format.errors({ stack: true }),
+ prettyErrorFormat(),
+ winston.format.simple(),
+)
export const logger = winston.createLogger({
level: config.app?.logLevel || 'info',
format,
- transports: [
- new winston.transports.Console()
- ]
+ transports: [new winston.transports.Console()],
})
diff --git a/src/main/basedbot/basedbot.ts b/src/main/basedbot/basedbot.ts
new file mode 100644
index 00000000..525362a3
--- /dev/null
+++ b/src/main/basedbot/basedbot.ts
@@ -0,0 +1,286 @@
+import {
+ getAssociatedTokenAddressSync,
+ TOKEN_PROGRAM_ID,
+} from '@solana/spl-token'
+import { PublicKey } from '@solana/web3.js'
+import {
+ getParsedTokenAccountsByOwner,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import {
+ Fleet,
+ Game,
+ getCleanPodsByStarbasePlayerAccounts,
+ getPodCleanupInstructions,
+ Starbase,
+} from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { Sentry } from '../../sentry'
+
+import { logger } from '../../logger'
+import { sleep } from '../../service/sleep'
+import { connection } from '../../service/sol'
+import { sendAndConfirmInstructions } from '../../service/sol/send-and-confirm-tx'
+import { keyPair } from '../../service/wallet'
+
+import { getFleetStrategy } from './fleet-strategies/get-fleet-strategy'
+import { StrategyConfig } from './fleet-strategies/strategy-config'
+import { createInfoStrategy } from './fsm/info'
+import { programs } from './lib/programs'
+import { createFleet, FleetShip } from './lib/sage/act/create-fleet'
+import { depositCargo } from './lib/sage/act/deposit-cargo'
+import { ensureShips } from './lib/sage/act/deposit-ship'
+import { getCargoStatsDefinition } from './lib/sage/state/cargo-stats-definition'
+import { sageGame } from './lib/sage/state/game'
+import { settleFleet } from './lib/sage/state/settle-fleet'
+import { getStarbasePlayer } from './lib/sage/state/starbase-player'
+import { getPlayerContext, Player } from './lib/sage/state/user-account'
+import {
+ FleetInfo,
+ getFleetInfo,
+ getUserFleets,
+} from './lib/sage/state/user-fleets'
+import { getMapContext, WorldMap } from './lib/sage/state/world-map'
+// eslint-disable-next-line import/max-dependencies
+import { getName } from './lib/sage/util'
+
+// 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 BotConfig = {
+ player: Player
+ map: WorldMap
+ fleetStrategies: StrategyConfig
+}
+
+const applyStrategy = (
+ fleetInfo: FleetInfo,
+ config: StrategyConfig,
+): Promise => {
+ const { strategy } = config.match(fleetInfo.fleetName, config.map)
+
+ if (!strategy) {
+ logger.warn(
+ `No strategy for fleet: ${fleetInfo.fleetName}. Using Info Strategy...`,
+ )
+
+ return createInfoStrategy().apply(fleetInfo)
+ }
+
+ return strategy.apply(fleetInfo)
+}
+
+export const getTokenBalance = async (
+ account: PublicKey,
+ mint: PublicKey,
+): Promise => {
+ const allTokenAccounts = await getParsedTokenAccountsByOwner(
+ connection,
+ account,
+ TOKEN_PROGRAM_ID,
+ )
+
+ const sourceTokenAccount = getAssociatedTokenAddressSync(
+ mint,
+ account,
+ true,
+ )
+ const [mintTokenAccount] = allTokenAccounts.filter((it) =>
+ it.address.equals(sourceTokenAccount),
+ )
+
+ if (!mintTokenAccount) {
+ logger.warn('Token account not found, assuming empty balance.')
+ }
+
+ return new BN(mintTokenAccount ? mintTokenAccount.amount.toString() : 0)
+}
+
+const importR4 = async (player: Player, game: Game): Promise => {
+ await Promise.all(
+ [
+ game.data.mints.food,
+ game.data.mints.ammo,
+ game.data.mints.fuel,
+ game.data.mints.repairKit,
+ ].map(async (mint) => {
+ const amountAtOrigin = await getTokenBalance(
+ player.signer.publicKey(),
+ mint,
+ )
+
+ if (amountAtOrigin.gtn(0)) {
+ logger.info(
+ `Importing R4 for ${mint.toBase58()}: ${amountAtOrigin}`,
+ )
+
+ await depositCargo(
+ player,
+ game,
+ player.homeStarbase,
+ mint,
+ amountAtOrigin,
+ )
+ }
+ }),
+ )
+}
+
+const ensureFleets = async (
+ player: Player,
+ game: Game,
+ fleets: Array,
+ fleetStrategies: StrategyConfig,
+): Promise => {
+ const existingFleets = fleets.map(getName)
+ const wantedFleets = Array.from(fleetStrategies.map.keys())
+
+ const neededFleets = wantedFleets.filter((f) => !existingFleets.includes(f))
+
+ if (neededFleets.length > 0) {
+ logger.info('Creating fleets:', neededFleets)
+ }
+
+ const neededShips = new Map()
+
+ neededFleets.forEach((fleetName) => {
+ const fleetStrategy = fleetStrategies.map.get(fleetName)!
+
+ fleetStrategy.fleet?.forEach((fleetShip) => {
+ const curr = neededShips.get(fleetShip.shipMint.toBase58()) ?? 0
+
+ neededShips.set(
+ fleetShip.shipMint.toBase58(),
+ curr + fleetShip.count,
+ )
+ })
+ })
+
+ const shipMints = Array.from(neededShips.keys())
+ .map((mint) => [
+ {
+ count: neededShips.get(mint) ?? 0,
+ shipMint: new PublicKey(mint),
+ } as FleetShip,
+ ])
+ .flat()
+
+ await ensureShips(player, game, player.homeStarbase, shipMints)
+
+ await Promise.all(
+ neededFleets.map((fleetName) => {
+ const fleetStrategy = fleetStrategies.map.get(fleetName)!
+
+ if (!fleetStrategy.fleet) {
+ logger.info('Cannot ensure fleet without config.')
+
+ return Promise.resolve()
+ }
+
+ return createFleet(
+ player,
+ game,
+ player.homeStarbase,
+ fleetStrategy.fleet!,
+ fleetName,
+ )
+ }),
+ )
+}
+
+const cleanupPods = async (player: Player, game: Game, starbase: Starbase) => {
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+ const podCleanup = await getCleanPodsByStarbasePlayerAccounts(
+ connection,
+ programs.cargo,
+ starbasePlayer.key,
+ )
+ const [cargoStatsDefinition] = await getCargoStatsDefinition()
+
+ if (!podCleanup) {
+ logger.info('Nothing to Clean up')
+
+ return
+ }
+
+ const ixs = getPodCleanupInstructions(
+ podCleanup,
+ programs.sage,
+ programs.cargo,
+ starbasePlayer.key,
+ starbase.key,
+ player.profile.key,
+ player.profileFaction.key,
+ cargoStatsDefinition.key,
+ game.key,
+ game.data.gameState,
+ player.signer,
+ 0,
+ )
+
+ await sendAndConfirmInstructions(await ixReturnsToIxs(ixs, player.signer))
+}
+
+const basedbot = async (botConfig: BotConfig) => {
+ logger.info(
+ '-------------------------------------------------------------------------------------',
+ )
+ const { player, map } = botConfig
+ const [fleets, game] = await Promise.all([
+ getUserFleets(player),
+ sageGame(),
+ ])
+ const fleetInfos = await Promise.all(
+ fleets.map((f) => getFleetInfo(f, player, map)),
+ )
+
+ await cleanupPods(player, game, player.homeStarbase)
+
+ await Promise.all([
+ importR4(player, game),
+ ensureFleets(player, game, fleets, botConfig.fleetStrategies),
+ ])
+
+ await Promise.all(
+ fleetInfos.map((fleetInfo) => settleFleet(fleetInfo, player, game)),
+ )
+ await Promise.all(
+ fleetInfos.map((fleetInfo) =>
+ applyStrategy(fleetInfo, botConfig.fleetStrategies),
+ ),
+ )
+ logger.info(
+ '-------------------------------------------------------------------------------------',
+ )
+}
+
+export const start = async (): Promise => {
+ const player = await getPlayerContext(keyPair.publicKey, keyPair)
+ const game = await sageGame()
+ const map = await getMapContext(game)
+ const fleetStrategies = getFleetStrategy(map, player, game)
+
+ // 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/fleet-strategies/atlasnet-fc-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts
new file mode 100644
index 00000000..f4527be7
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts
@@ -0,0 +1,133 @@
+import { Game } from '@staratlas/sage'
+import { Chance } from 'chance'
+
+import { mine } from '../fsm/configs/mine/mine'
+import { createInfoStrategy } from '../fsm/info'
+import { createMiningStrategy } from '../fsm/mine'
+import { createTransportStrategy, transport } from '../fsm/transport'
+import { FleetShips } from '../lib/sage/act/create-fleet'
+import { Calico, Ogrika, Pearce, ships } from '../lib/sage/ships'
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+import {
+ Faction,
+ galaxySectorsData,
+ SectorInfo,
+} from '../lib/util/galaxy-sectors-data'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { makeStrategyMap, StrategyConfig, StrategyMap } from './strategy-config'
+
+export const randomIntFromInterval = (min: number, max: number): number => {
+ return Math.floor(Math.random() * (max - min + 1) + min)
+}
+
+const getRandomFleetForFaction = (faction: Faction): FleetShips => {
+ switch (faction) {
+ case Faction.MUD:
+ return [
+ {
+ shipMint: ships[Pearce.D9].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Pearce.F4].mint,
+ count: randomIntFromInterval(2, 3),
+ },
+ ]
+ case Faction.ONI:
+ return [
+ {
+ shipMint: ships[Ogrika.Niruch].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Ogrika.Sunpaa].mint,
+ count: randomIntFromInterval(2, 4),
+ },
+ ]
+ case Faction.UST:
+ return [
+ {
+ shipMint: ships[Calico.Guardian].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Calico.Evac].mint,
+ count: randomIntFromInterval(2, 4),
+ },
+ ]
+ default:
+ throw new Error('Unknown Faction')
+ }
+}
+
+const getRandomFleetName = (chance: Chance.Chance, maxLen: number): string => {
+ const getName = () => `${chance.animal()} Fleet`
+
+ let name = getName()
+
+ while (name.length > maxLen) {
+ name = getName()
+ }
+
+ return name
+}
+
+const randomSector = (chance: Chance.Chance, sectors: Array) =>
+ sectors[chance.integer({ min: 0, max: sectors.length - 1 })].coordinates
+
+export const atlasnetFcStrategy =
+ (count: number) =>
+ (
+ map: WorldMap,
+ player: Player,
+ game: Game,
+ seed: string = 'basedbot',
+ ): StrategyConfig => {
+ const strategyMap: StrategyMap = makeStrategyMap()
+ const chance = new Chance(seed)
+ const sectors = galaxySectorsData()
+ .filter((sector) => sector.closestFaction === player.faction)
+ .sort((a, b) => a.name.localeCompare(b.name))
+
+ for (let i = 0; i < count; i++) {
+ const home = randomSector(chance, sectors)
+ const target = randomSector(chance, sectors)
+
+ strategyMap.set(getRandomFleetName(chance, 32), {
+ fleet: getRandomFleetForFaction(player.faction),
+ strategy: createMiningStrategy(
+ mine(map, home, target),
+ player,
+ game,
+ ),
+ })
+ // No transport fleet needed if mining fleet uses CSS as home base.
+ if (!home.equals(player.homeCoordinates)) {
+ strategyMap.set(getRandomFleetName(chance, 32), {
+ fleet: getRandomFleetForFaction(player.faction),
+ strategy: createTransportStrategy(
+ transport(
+ map,
+ player.homeCoordinates,
+ home,
+ new Set([
+ game.data.mints.fuel,
+ game.data.mints.ammo,
+ game.data.mints.food,
+ game.data.mints.repairKit,
+ ]),
+ ),
+ player,
+ game,
+ ),
+ })
+ }
+ }
+
+ return {
+ match: nameMapMatcher(createInfoStrategy()),
+ map: strategyMap,
+ }
+ }
diff --git a/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts
new file mode 100644
index 00000000..8ba68694
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts
@@ -0,0 +1,400 @@
+import { Game } from '@staratlas/sage'
+
+import { mineBiomass } from '../fsm/configs/mine/mine-biomass'
+import { mineCarbon } from '../fsm/configs/mine/mine-carbon'
+import { mineConfig } from '../fsm/configs/mine/mine-config'
+import { mineCopperOre } from '../fsm/configs/mine/mine-copper-ore'
+import { mineHydrogen } from '../fsm/configs/mine/mine-hydrogen'
+import { mineIronOre } from '../fsm/configs/mine/mine-iron-ore'
+import { mineLumanite } from '../fsm/configs/mine/mine-lumanite'
+import { mineNitrogen } from '../fsm/configs/mine/mine-nitrogen'
+import { mineRochinol } from '../fsm/configs/mine/mine-rochinol'
+import { mineSilicia } from '../fsm/configs/mine/mine-silicia'
+import { mineTitaniumOre } from '../fsm/configs/mine/mine-titanium-ore'
+import { createInfoStrategy } from '../fsm/info'
+import { createMiningStrategy } from '../fsm/mine'
+import { Player } from '../lib/sage/state/user-account'
+import { mineableByCoordinates, WorldMap } from '../lib/sage/state/world-map'
+import { Coordinates } from '../lib/util/coordinates'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { StrategyConfig } from './strategy-config'
+
+export const atlasnetLuStrategy = (
+ map: WorldMap,
+ player: Player,
+ game: Game,
+): StrategyConfig => {
+ return {
+ match: nameMapMatcher(createInfoStrategy()),
+ map: new Map([
+ [
+ 'Armadillo Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineBiomass(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Barnacle Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineConfig({
+ homeBase: Coordinates.fromNumber(-40, 30),
+ targetBase: Coordinates.fromNumber(-19, 40),
+ resource: mineableByCoordinates(
+ map,
+ Coordinates.fromNumber(-19, 40),
+ )
+ .values()
+ .next().value,
+ }),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Cobra Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineConfig({
+ homeBase: Coordinates.fromNumber(-40, 30),
+ targetBase: Coordinates.fromNumber(-18, 23),
+ resource: mineableByCoordinates(
+ map,
+ Coordinates.fromNumber(-18, 23),
+ )
+ .values()
+ .next().value,
+ }),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Falcon Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineCarbon(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Geoffroys Cat Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineNitrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Gerbils Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineSilicia(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Grasshopper Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineLumanite(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Guanaco Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineCopperOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'King Cobra Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineIronOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Pacific Sardine Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Porpoise Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineRochinol(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Rabbit Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Smalltooth Sawfish Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineTitaniumOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Sugar Gliders Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineIronOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Turkey Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+
+ [
+ 'Aardwolf Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineBiomass(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Antelope Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineConfig({
+ homeBase: Coordinates.fromNumber(-40, 30),
+ targetBase: Coordinates.fromNumber(-19, 40),
+ resource: mineableByCoordinates(
+ map,
+ Coordinates.fromNumber(-19, 40),
+ )
+ .values()
+ .next().value,
+ }),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Boa Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineConfig({
+ homeBase: Coordinates.fromNumber(-40, 30),
+ targetBase: Coordinates.fromNumber(-18, 23),
+ resource: mineableByCoordinates(
+ map,
+ Coordinates.fromNumber(-18, 23),
+ )
+ .values()
+ .next().value,
+ }),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Chinchillas Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineCarbon(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Fathead Sculpin Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineNitrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Giant Tortoise Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineSilicia(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Kultarr Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineLumanite(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Leopard Seal Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineCopperOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Pangolin Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineIronOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Rhinoceros Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Snow Leopard Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineRochinol(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Southern White Faced Owl Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Turkeys Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineTitaniumOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Zebra Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineIronOre(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ [
+ 'Guinea Fowl Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(map),
+ player,
+ game,
+ ),
+ },
+ ],
+ ]),
+ }
+}
diff --git a/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts
new file mode 100644
index 00000000..e7f98968
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts
@@ -0,0 +1,149 @@
+import { Game } from '@staratlas/sage'
+import { Chance } from 'chance'
+
+import { mine } from '../fsm/configs/mine/mine'
+import { createInfoStrategy } from '../fsm/info'
+import { createMiningStrategy } from '../fsm/mine'
+import { createTransportStrategy, transport } from '../fsm/transport'
+import { FleetShips } from '../lib/sage/act/create-fleet'
+import { Calico, Ogrika, Pearce, ships } from '../lib/sage/ships'
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+import {
+ Faction,
+ galaxySectorsData,
+ SectorInfo,
+} from '../lib/util/galaxy-sectors-data'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { makeStrategyMap, StrategyConfig, StrategyMap } from './strategy-config'
+
+export const randomIntFromInterval = (min: number, max: number): number => {
+ return Math.floor(Math.random() * (max - min + 1) + min)
+}
+
+const getRandomFleetForFaction = (faction: Faction): FleetShips => {
+ switch (faction) {
+ case Faction.MUD:
+ return [
+ {
+ shipMint: ships[Pearce.D9].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Pearce.F4].mint,
+ count: randomIntFromInterval(2, 3),
+ },
+ ]
+ case Faction.ONI:
+ return [
+ {
+ shipMint: ships[Ogrika.Niruch].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Ogrika.Sunpaa].mint,
+ count: randomIntFromInterval(2, 4),
+ },
+ ]
+ case Faction.UST:
+ return [
+ {
+ shipMint: ships[Calico.Guardian].mint,
+ count: randomIntFromInterval(1, 2),
+ },
+ {
+ shipMint: ships[Calico.Evac].mint,
+ count: randomIntFromInterval(2, 4),
+ },
+ ]
+ default:
+ throw new Error('Unknown Faction')
+ }
+}
+
+const getRandomFleetName = (chance: Chance.Chance, maxLen: number): string => {
+ const getName = () => `${chance.animal()} Fleet`
+
+ let name = getName()
+
+ while (name.length > maxLen) {
+ name = getName()
+ }
+
+ return name
+}
+
+const randomSector = (chance: Chance.Chance, sectors: Array) =>
+ sectors[chance.integer({ min: 0, max: sectors.length - 1 })].coordinates
+
+export const atlasnetQtStrategy =
+ (count: number) =>
+ (
+ map: WorldMap,
+ player: Player,
+ game: Game,
+ seed: string = 'basedbot',
+ ): StrategyConfig => {
+ const strategyMap: StrategyMap = makeStrategyMap()
+ const chance = new Chance(seed)
+ const sectors = galaxySectorsData()
+ .filter((sector) => sector.closestFaction === player.faction)
+ .filter((sector) =>
+ [
+ 'MUD-2',
+ 'MUD-3',
+ 'MUD-4',
+ 'MUD-5',
+ 'ONI-2',
+ 'ONI-3',
+ 'ONI-4',
+ 'ONI-5',
+ 'UST-2',
+ 'UST-3',
+ 'UST-4',
+ 'UST-5',
+ ].includes(sector.name),
+ )
+ .sort((a, b) => a.name.localeCompare(b.name))
+
+ for (let i = 0; i < count; i++) {
+ const home = randomSector(chance, sectors)
+ const target = randomSector(chance, sectors)
+
+ strategyMap.set(getRandomFleetName(chance, 32), {
+ fleet: getRandomFleetForFaction(player.faction),
+ strategy: createMiningStrategy(
+ mine(map, home, target),
+ player,
+ game,
+ ),
+ })
+ // No transport fleet needed if mining fleet uses CSS as home base.
+ if (!home.equals(player.homeCoordinates)) {
+ strategyMap.set(getRandomFleetName(chance, 32), {
+ fleet: getRandomFleetForFaction(player.faction),
+ strategy: createTransportStrategy(
+ transport(
+ map,
+ player.homeCoordinates,
+ home,
+ new Set([
+ game.data.mints.fuel,
+ game.data.mints.ammo,
+ game.data.mints.food,
+ game.data.mints.repairKit,
+ ]),
+ ),
+ player,
+ game,
+ ),
+ })
+ }
+ }
+
+ return {
+ match: nameMapMatcher(createInfoStrategy()),
+ map: strategyMap,
+ }
+ }
diff --git a/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts b/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts
new file mode 100644
index 00000000..29ed0b2c
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts
@@ -0,0 +1,21 @@
+import { Game } from '@staratlas/sage'
+
+import { createDestructStrategy, destructConfig } from '../fsm/destruct'
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { makeStrategyMap, StrategyConfig } from './strategy-config'
+
+export const destructAllStrategy = (
+ worldMap: WorldMap,
+ player: Player,
+ game: Game,
+): StrategyConfig => {
+ return {
+ match: nameMapMatcher(
+ createDestructStrategy(destructConfig({ worldMap }), player, game),
+ ),
+ map: makeStrategyMap(),
+ }
+}
diff --git a/src/main/basedbot/fleet-strategies/disband-all-strategy.ts b/src/main/basedbot/fleet-strategies/disband-all-strategy.ts
new file mode 100644
index 00000000..496d2b9d
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/disband-all-strategy.ts
@@ -0,0 +1,26 @@
+import { Game } from '@staratlas/sage'
+
+import { disbandConfig } from '../fsm/configs/disband-config'
+import { createDisbandStrategy } from '../fsm/disband'
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { makeStrategyMap, StrategyConfig } from './strategy-config'
+
+export const disbandAllStrategy = (
+ worldMap: WorldMap,
+ player: Player,
+ game: Game,
+): StrategyConfig => {
+ return {
+ match: nameMapMatcher(
+ createDisbandStrategy(
+ disbandConfig({ worldMap, homeBase: player.homeCoordinates }),
+ player,
+ game,
+ ),
+ ),
+ map: makeStrategyMap(),
+ }
+}
diff --git a/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts b/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts
new file mode 100644
index 00000000..34ae08d1
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts
@@ -0,0 +1,37 @@
+import { Game } from '@staratlas/sage'
+
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+
+import { atlasnetFcStrategy } from './atlasnet-fc-strategy'
+import { atlasnetLuStrategy } from './atlasnet-lu-strategy'
+import { atlasnetQtStrategy } from './atlasnet-qt-strategy'
+import { mainnetLuStrategy } from './mainnet-lu-strategy'
+import { StrategyConfig } from './strategy-config'
+
+export const getFleetStrategy = (
+ map: WorldMap,
+ player: Player,
+ game: Game,
+): StrategyConfig => {
+ switch (player.publicKey.toString()) {
+ case 'k49Y5xwN7Nyi19TqDR4zbCFuAt8kgy6qMaJ6Kj1wHrn':
+ return atlasnetLuStrategy(map, player, game)
+ case 'AePY3wEoUFcFuXeUU9X26YK6tNKQMZovBgvY54LK2B8N':
+ return mainnetLuStrategy(map, player, game)
+ case 'CgHvzwGbwWv3CwLTvEgeqSKeD8EwMdTfiiCG3dFrKVVC':
+ return atlasnetFcStrategy(150)(map, player, game, 'mud')
+ // return destructAllStrategy(map, player, game)
+ case '9KBrgWVjsmdZ3YEjcsa3wrbbJREgZgS7vDbgoz2aHaNm':
+ return atlasnetFcStrategy(150)(map, player, game, 'ustur')
+ // return destructAllStrategy(map, player, game)
+ case 'FUwHSqujzcPD44SDZYJXuk73NbkEyYQwcLMioHhpjbx2':
+ return atlasnetFcStrategy(150)(map, player, game, 'oni')
+ // return destructAllStrategy(map, player, game)
+ case '34ghznSJCYEMrS1aC55UYZZUuxfuurA9441aKnigmYyz':
+ return atlasnetQtStrategy(1)(map, player, game, 'le.local')
+ // return destructAllStrategy(map, player, game)
+ default:
+ throw new Error('Unknown strategy')
+ }
+}
diff --git a/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts b/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts
new file mode 100644
index 00000000..4ba3d211
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts
@@ -0,0 +1,33 @@
+import { Game } from '@staratlas/sage'
+
+import { mineHydrogen } from '../fsm/configs/mine/mine-hydrogen'
+import { createInfoStrategy } from '../fsm/info'
+import { createMiningStrategy } from '../fsm/mine'
+import { Player } from '../lib/sage/state/user-account'
+import { WorldMap } from '../lib/sage/state/world-map'
+
+import { nameMapMatcher } from './name-map-matcher'
+import { StrategyConfig } from './strategy-config'
+
+export const mainnetLuStrategy = (
+ worldMap: WorldMap,
+ player: Player,
+ game: Game,
+): StrategyConfig => {
+ return {
+ match: nameMapMatcher(createInfoStrategy()),
+ map: new Map([
+ [
+ 'Red Ruffed Lemur Fleet',
+ {
+ fleet: null,
+ strategy: createMiningStrategy(
+ mineHydrogen(worldMap),
+ player,
+ game,
+ ),
+ },
+ ],
+ ]),
+ }
+}
diff --git a/src/main/basedbot/fleet-strategies/name-map-matcher.ts b/src/main/basedbot/fleet-strategies/name-map-matcher.ts
new file mode 100644
index 00000000..dfd07fea
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/name-map-matcher.ts
@@ -0,0 +1,14 @@
+import { Strategy } from '../fsm/strategy'
+
+import { FleetStrategy, StrategyMap } from './strategy-config'
+
+export const nameMapMatcher =
+ (fallback: Strategy) =>
+ (key: string, strategyMap: StrategyMap): FleetStrategy => {
+ return (
+ strategyMap.get(key) || {
+ strategy: fallback,
+ fleet: null,
+ }
+ )
+ }
diff --git a/src/main/basedbot/fleet-strategies/number-map-matcher.ts b/src/main/basedbot/fleet-strategies/number-map-matcher.ts
new file mode 100644
index 00000000..6d0223b0
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/number-map-matcher.ts
@@ -0,0 +1,20 @@
+import { Strategy } from '../fsm/strategy'
+
+import { FleetStrategy, StrategyMap } from './strategy-config'
+
+export const numberMapMatcher =
+ (fallback: Strategy) =>
+ (key: string, strategyMap: StrategyMap): FleetStrategy => {
+ const fallbackFleetStrategy = {
+ strategy: fallback,
+ fleet: null,
+ }
+ const extractNumbers = (str: string): Array | undefined =>
+ str.match(/\d+/g)?.map(Number)
+ const [index] = extractNumbers(key) ?? []
+ const mapKey = Array.from(strategyMap.keys()).find(extractNumbers)
+
+ if (!index || !mapKey) return fallbackFleetStrategy
+
+ return strategyMap.get(mapKey) || fallbackFleetStrategy
+ }
diff --git a/src/main/basedbot/fleet-strategies/strategy-config.ts b/src/main/basedbot/fleet-strategies/strategy-config.ts
new file mode 100644
index 00000000..8b168526
--- /dev/null
+++ b/src/main/basedbot/fleet-strategies/strategy-config.ts
@@ -0,0 +1,16 @@
+import { Strategy } from '../fsm/strategy'
+import { FleetShips } from '../lib/sage/act/create-fleet'
+
+export type FleetStrategy = {
+ fleet: FleetShips | null
+ strategy: Strategy
+}
+export type StrategyMap = Map
+export const makeStrategyMap = (): StrategyMap =>
+ new Map()
+export type MapMatcher = (key: string, map: StrategyMap) => FleetStrategy
+
+export type StrategyConfig = {
+ map: StrategyMap
+ match: MapMatcher
+}
diff --git a/src/main/basedbot/fsm/configs/disband-config.ts b/src/main/basedbot/fsm/configs/disband-config.ts
new file mode 100644
index 00000000..0b0f2bd7
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/disband-config.ts
@@ -0,0 +1,20 @@
+import { WarpMode } from '../../lib/sage/act/move'
+import { WorldMap } from '../../lib/sage/state/world-map'
+import { Coordinates } from '../../lib/util/coordinates'
+
+export type DisbandConfig = {
+ worldMap: WorldMap
+ homeBase: Coordinates
+ warpMode: WarpMode
+}
+
+export const disbandConfig = (
+ config: Partial & {
+ worldMap: WorldMap
+ homeBase: Coordinates
+ },
+): DisbandConfig => ({
+ worldMap: config.worldMap,
+ homeBase: config.homeBase,
+ warpMode: config.warpMode || 'auto',
+})
diff --git a/src/main/basedbot/fsm/configs/mine/mine-biomass.ts b/src/main/basedbot/fsm/configs/mine/mine-biomass.ts
new file mode 100644
index 00000000..8e8abe99
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-biomass.ts
@@ -0,0 +1,16 @@
+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/mine-carbon.ts b/src/main/basedbot/fsm/configs/mine/mine-carbon.ts
new file mode 100644
index 00000000..9870950b
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-carbon.ts
@@ -0,0 +1,16 @@
+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/mine-config.ts b/src/main/basedbot/fsm/configs/mine/mine-config.ts
new file mode 100644
index 00000000..ab77cbe0
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-config.ts
@@ -0,0 +1,23 @@
+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 & {
+ homeBase: Coordinates
+ targetBase: Coordinates
+ resource: Mineable
+ },
+): MineConfig => ({
+ homeBase: config.homeBase,
+ targetBase: config.targetBase,
+ resource: config.resource,
+ warpMode: config.warpMode || 'auto',
+})
diff --git a/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts
new file mode 100644
index 00000000..91d01cd8
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts
@@ -0,0 +1,16 @@
+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/mine-diamond.ts b/src/main/basedbot/fsm/configs/mine/mine-diamond.ts
new file mode 100644
index 00000000..efda752d
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-diamond.ts
@@ -0,0 +1,16 @@
+import {
+ mineableByCoordinates,
+ WorldMap,
+} from '../../../lib/sage/state/world-map'
+import { Coordinates } from '../../../lib/util/coordinates'
+
+import { MineConfig, mineConfig } from './mine-config'
+
+export const mineDiamond = (map: WorldMap): MineConfig =>
+ mineConfig({
+ homeBase: Coordinates.fromNumber(-40, 30),
+ targetBase: Coordinates.fromNumber(-16, 0),
+ resource: mineableByCoordinates(map, Coordinates.fromNumber(-16, 0))
+ .values()
+ .next().value,
+ })
diff --git a/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts b/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts
new file mode 100644
index 00000000..f05555ae
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts
@@ -0,0 +1,16 @@
+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/mine-iron-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts
new file mode 100644
index 00000000..02c1dcac
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts
@@ -0,0 +1,16 @@
+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/mine-lumanite.ts b/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts
new file mode 100644
index 00000000..dbefb537
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts
@@ -0,0 +1,16 @@
+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(-23, 4),
+ resource: mineableByCoordinates(map, Coordinates.fromNumber(-23, 4))
+ .values()
+ .next().value,
+ })
diff --git a/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts b/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts
new file mode 100644
index 00000000..151aaa94
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts
@@ -0,0 +1,16 @@
+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/mine-rochinol.ts b/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts
new file mode 100644
index 00000000..3afd1581
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts
@@ -0,0 +1,16 @@
+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/mine-silicia.ts b/src/main/basedbot/fsm/configs/mine/mine-silicia.ts
new file mode 100644
index 00000000..d14f626b
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-silicia.ts
@@ -0,0 +1,16 @@
+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/mine-titanium-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts
new file mode 100644
index 00000000..cea72d41
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts
@@ -0,0 +1,16 @@
+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/configs/mine/mine.ts b/src/main/basedbot/fsm/configs/mine/mine.ts
new file mode 100644
index 00000000..863698bb
--- /dev/null
+++ b/src/main/basedbot/fsm/configs/mine/mine.ts
@@ -0,0 +1,18 @@
+import {
+ mineableByCoordinates,
+ WorldMap,
+} from '../../../lib/sage/state/world-map'
+import { Coordinates } from '../../../lib/util/coordinates'
+
+import { mineConfig, MineConfig } from './mine-config'
+
+export const mine = (
+ map: WorldMap,
+ homeBase: Coordinates,
+ targetBase: Coordinates,
+): MineConfig =>
+ mineConfig({
+ homeBase,
+ targetBase,
+ resource: mineableByCoordinates(map, targetBase).values().next().value,
+ })
diff --git a/src/main/basedbot/fsm/destruct.ts b/src/main/basedbot/fsm/destruct.ts
new file mode 100644
index 00000000..f9abddcc
--- /dev/null
+++ b/src/main/basedbot/fsm/destruct.ts
@@ -0,0 +1,176 @@
+import { Game } from '@staratlas/sage'
+import dayjs from 'dayjs'
+
+import { now } from '../../../dayjs'
+import { logger } from '../../../logger'
+import { disbandFleet } from '../lib/sage/act/disband-fleet'
+import { dock } from '../lib/sage/act/dock'
+import { endMine } from '../lib/sage/act/end-mine'
+import { endMove } from '../lib/sage/act/end-move'
+import { selfDestruct } from '../lib/sage/act/self-destruct'
+import { stopSubwarp } from '../lib/sage/act/stop-subwarp'
+import { undock } from '../lib/sage/act/undock'
+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 { mineableByCoordinates, WorldMap } from '../lib/sage/state/world-map'
+import { getName } from '../lib/sage/util'
+
+import { DisbandConfig } from './configs/disband-config'
+import { Strategy } from './strategy'
+
+// eslint-disable-next-line complexity
+const transition = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ config: DestructConfig,
+): Promise => {
+ const currentStarbase = await starbaseByCoordinates(fleetInfo.location)
+ const { fleetName, location } = fleetInfo
+ const homeBase = player.homeCoordinates
+ const isAtHomeBase = homeBase.equals(location)
+
+ switch (fleetInfo.fleetState.type) {
+ case 'Idle': {
+ logger.info(
+ `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`,
+ )
+
+ if (isAtHomeBase) {
+ logger.info(`${fleetName} is at home base, docking to disband`)
+
+ return dock(fleetInfo, location, player, game)
+ }
+
+ return selfDestruct(fleetInfo, player, game)
+ }
+ case 'StarbaseLoadingBay': {
+ logger.info(
+ `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`,
+ )
+
+ if (isAtHomeBase) {
+ logger.info(
+ `${fleetInfo.fleetName} is at home base, disbanding...`,
+ )
+
+ return disbandFleet(
+ player,
+ game,
+ player.homeStarbase,
+ fleetInfo,
+ )
+ }
+ logger.info(
+ `${fleetInfo.fleetName} is at ${location}, undocking...`,
+ )
+
+ return undock(fleetInfo.fleet, fleetInfo.location, player, game)
+ }
+ case 'MoveWarp': {
+ const { fromSector, toSector, warpFinish } =
+ fleetInfo.fleetState.data
+
+ if (!homeBase.equals(toSector)) {
+ logger.info(`Stopping fleet ${fleetInfo.fleetName}`)
+
+ return endMove(fleetInfo, player, game)
+ }
+
+ if (warpFinish.isBefore(now())) {
+ logger.info(
+ `${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 (!homeBase.equals(toSector)) {
+ logger.info(`Stopping fleet ${fleetInfo.fleetName}`)
+
+ return stopSubwarp(fleetInfo, player, game)
+ }
+
+ if (arrivalTime.isBefore(now())) {
+ logger.info(
+ `${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 } = fleetInfo.fleetState.data
+
+ if (end.isBefore(now())) {
+ logger.info(
+ `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`,
+ )
+ }
+
+ logger.info(
+ `${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Ending...`,
+ )
+ const resource = mineableByCoordinates(
+ config.worldMap,
+ fleetInfo.location,
+ )
+ .values()
+ .next().value
+
+ return endMine(fleetInfo, player, game, resource)
+ }
+ case 'Respawn': {
+ const { destructionTime, ETA } = fleetInfo.fleetState.data
+
+ if (ETA.isBefore(now())) {
+ logger.info(`${fleetInfo.fleetName} has respawned`)
+ } else {
+ logger.info(
+ `${fleetInfo.fleetName} respawning at ${fleetInfo.fleetState.data.sector}. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}. Destruction time: ${destructionTime}`,
+ )
+ }
+ break
+ }
+ default:
+ logger.info(
+ `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`,
+ )
+
+ return Promise.resolve()
+ }
+}
+
+export type DestructConfig = {
+ worldMap: WorldMap
+}
+
+export const destructConfig = (
+ config: Partial & {
+ worldMap: WorldMap
+ },
+): DestructConfig => ({
+ worldMap: config.worldMap,
+})
+
+export const createDestructStrategy = (
+ config: DestructConfig,
+ player: Player,
+ game: Game,
+): Strategy => {
+ return {
+ apply: (fleetInfo: FleetInfo): Promise =>
+ transition(fleetInfo, player, game, config),
+ }
+}
diff --git a/src/main/basedbot/fsm/disband.ts b/src/main/basedbot/fsm/disband.ts
new file mode 100644
index 00000000..0fca98be
--- /dev/null
+++ b/src/main/basedbot/fsm/disband.ts
@@ -0,0 +1,180 @@
+// eslint-disable-next-line filenames/match-regex
+import { Game } from '@staratlas/sage'
+import dayjs from 'dayjs'
+
+import { now } from '../../../dayjs'
+import { logger } from '../../../logger'
+import { disbandFleet } from '../lib/sage/act/disband-fleet'
+import { dock } from '../lib/sage/act/dock'
+import { endMine } from '../lib/sage/act/end-mine'
+import { endMove } from '../lib/sage/act/end-move'
+import { move } from '../lib/sage/act/move'
+import { selfDestruct } from '../lib/sage/act/self-destruct'
+import { stopSubwarp } from '../lib/sage/act/stop-subwarp'
+import { undock } from '../lib/sage/act/undock'
+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 { mineableByCoordinates } from '../lib/sage/state/world-map'
+import { getName } from '../lib/sage/util'
+
+import { DisbandConfig } from './configs/disband-config'
+import { Strategy } from './strategy'
+
+// eslint-disable-next-line complexity
+const transition = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ config: DisbandConfig,
+): Promise => {
+ const cargoLevelFuel = fleetInfo.cargoLevels.fuel
+ const currentStarbase = await starbaseByCoordinates(fleetInfo.location)
+ const { fleetName, location } = fleetInfo
+ const { homeBase, warpMode } = config
+ const isAtHomeBase = homeBase.equals(location)
+
+ 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(
+ `${fleetName} is out of fuel and not at a starbase, need self destruction`,
+ )
+
+ return selfDestruct(fleetInfo, player, game)
+ }
+ if (isAtHomeBase) {
+ logger.info(`${fleetName} is at home base, docking to disband`)
+
+ return dock(fleetInfo, location, player, game)
+ }
+
+ logger.info(`${fleetName} is at ${location} warping home`)
+
+ return move(fleetInfo, homeBase, player, game, warpMode)
+ }
+ case 'StarbaseLoadingBay': {
+ logger.info(
+ `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`,
+ )
+
+ if (isAtHomeBase) {
+ logger.info(
+ `${fleetInfo.fleetName} is at home base, disbanding...`,
+ )
+
+ return disbandFleet(
+ player,
+ game,
+ player.homeStarbase,
+ fleetInfo,
+ )
+ }
+ logger.info(
+ `${fleetInfo.fleetName} is at ${location}, undocking...`,
+ )
+
+ return undock(fleetInfo.fleet, fleetInfo.location, player, game)
+ }
+ case 'MoveWarp': {
+ const { fromSector, toSector, warpFinish } =
+ fleetInfo.fleetState.data
+
+ if (!homeBase.equals(toSector)) {
+ logger.info(
+ `Wrong direction, stopping fleet ${fleetInfo.fleetName}`,
+ )
+
+ return endMove(fleetInfo, player, game)
+ }
+
+ if (warpFinish.isBefore(now())) {
+ logger.info(
+ `${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 (!homeBase.equals(toSector)) {
+ logger.info(
+ `Wrong direction, stopping fleet ${fleetInfo.fleetName}`,
+ )
+
+ return stopSubwarp(fleetInfo, player, game)
+ }
+
+ if (arrivalTime.isBefore(now())) {
+ logger.info(
+ `${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 } = fleetInfo.fleetState.data
+
+ if (end.isBefore(now())) {
+ logger.info(
+ `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`,
+ )
+ }
+
+ logger.info(
+ `${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Ending...`,
+ )
+ const resource = mineableByCoordinates(
+ config.worldMap,
+ fleetInfo.location,
+ )
+ .values()
+ .next().value
+
+ return endMine(fleetInfo, player, game, resource)
+ }
+ case 'Respawn': {
+ const { destructionTime, ETA } = fleetInfo.fleetState.data
+
+ if (ETA.isBefore(now())) {
+ logger.info(`${fleetInfo.fleetName} has respawned`)
+ } else {
+ logger.info(
+ `${fleetInfo.fleetName} respawning at ${fleetInfo.fleetState.data.sector}. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}. Destruction time: ${destructionTime}`,
+ )
+ }
+ break
+ }
+ default:
+ logger.info(
+ `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`,
+ )
+
+ return Promise.resolve()
+ }
+}
+
+export const createDisbandStrategy = (
+ config: DisbandConfig,
+ player: Player,
+ game: Game,
+): Strategy => {
+ return {
+ apply: (fleetInfo: FleetInfo): Promise =>
+ transition(fleetInfo, player, game, config),
+ }
+}
diff --git a/src/main/basedbot/fsm/info.ts b/src/main/basedbot/fsm/info.ts
new file mode 100644
index 00000000..acb69cb0
--- /dev/null
+++ b/src/main/basedbot/fsm/info.ts
@@ -0,0 +1,86 @@
+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(
+ `${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(
+ `${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(
+ `${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(
+ `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`,
+ )
+ } else {
+ const log = endReason === 'FULL' ? logger.info : logger.warn
+
+ log(
+ `${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`,
+ )
+ }
+ break
+ }
+ default:
+ logger.info(
+ `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`,
+ )
+ }
+
+ return Promise.resolve()
+}
+
+export const createInfoStrategy = (): Strategy => ({
+ apply: (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..e201f2cb
--- /dev/null
+++ b/src/main/basedbot/fsm/mine.ts
@@ -0,0 +1,276 @@
+import { Game } from '@staratlas/sage'
+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 { selfDestruct } from '../lib/sage/act/self-destruct'
+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/mine-config'
+import { Strategy } from './strategy'
+
+// eslint-disable-next-line complexity
+const transition = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ config: MineConfig,
+): Promise => {
+ const cargoLoad = player.cargoTypes
+ .filter((ct) => !ct.data.mint.equals(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 / 20
+ 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)
+
+ logger.info(
+ `${fleetName} is mining ${getName(config.resource.mineItem)} resources from ${config.targetBase} to ${config.homeBase}`,
+ )
+
+ 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(
+ `${fleetName} is out of fuel and not at a starbase, need self destruction`,
+ )
+
+ return selfDestruct(fleetInfo, player, game)
+ }
+ if (isAtHomeBase) {
+ logger.info(`${fleetName} is at home base`)
+ if (hasCargo) {
+ logger.info(
+ `${fleetName} has ${cargoLoad} ${resourceName}, docking to unload`,
+ )
+
+ return dock(fleetInfo, location, player, game)
+ }
+ if (!hasEnoughFood || !hasEnoughFuel || !hasEnoughAmmo) {
+ logger.info(
+ `${fleetName} doesn't have enough resources, docking to resupply`,
+ )
+
+ return dock(fleetInfo, location, player, game)
+ }
+ if (isSameBase) {
+ logger.info(`${fleetName} is at home/target base, mining`)
+
+ return mine(fleetInfo, player, game, resource)
+ }
+ logger.info(
+ `${fleetName} is at home base, moving to target base`,
+ )
+
+ return move(fleetInfo, targetBase, player, game, warpMode)
+ }
+
+ if (isAtTargetBase && !isSameBase) {
+ logger.info(`${fleetName} is at target base`)
+ if (hasCargo) {
+ logger.info(
+ `${fleetName} has ${cargoLoad} ${resourceName}, returning home`,
+ )
+
+ return move(fleetInfo, homeBase, player, game, warpMode)
+ }
+ if (hasEnoughFood) {
+ logger.info(`${fleetName} has enough food, mining`)
+
+ return mine(fleetInfo, player, game, resource)
+ }
+ logger.info(
+ `${fleetName} doesn't have enough food, returning home`,
+ )
+
+ return move(fleetInfo, homeBase, player, game, warpMode)
+ }
+
+ logger.info(`${fleetName} is at ${location}`)
+
+ return move(
+ fleetInfo,
+ hasCargo || !hasEnoughFood ? homeBase : targetBase,
+ player,
+ game,
+ warpMode,
+ )
+ }
+ case 'StarbaseLoadingBay': {
+ logger.info(
+ `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`,
+ )
+
+ if (hasCargo) {
+ logger.info(
+ `${fleetInfo.fleetName} has ${cargoLoad} cargo, unloading`,
+ )
+
+ return unloadAllCargo(
+ fleetInfo,
+ fleetInfo.location,
+ player,
+ game,
+ )
+ }
+
+ if (!hasEnoughFuel) {
+ logger.info(`${fleetInfo.fleetName} is refueling`)
+
+ return loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.fuel,
+ fleetInfo.cargoStats.fuelCapacity - cargoLevelFuel,
+ )
+ }
+
+ if (!hasEnoughAmmo) {
+ logger.info(`${fleetInfo.fleetName} is rearming`)
+
+ return loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.ammo,
+ fleetInfo.cargoStats.ammoCapacity - cargoLevelAmmo,
+ )
+ }
+
+ if (!hasEnoughFood) {
+ logger.info(
+ `${fleetInfo.fleetName} is loading ${desiredFood - cargoLevelFood} food`,
+ )
+
+ return loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.food,
+ toLoad,
+ )
+ }
+
+ return undock(fleetInfo.fleet, fleetInfo.location, player, game)
+ }
+ case 'MoveWarp': {
+ const { fromSector, toSector, warpFinish } =
+ fleetInfo.fleetState.data
+
+ if (warpFinish.isBefore(now())) {
+ logger.info(
+ `${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(
+ `${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(
+ `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`,
+ )
+
+ return endMine(fleetInfo, player, game, config.resource)
+ }
+
+ const log = endReason === 'FULL' ? logger.info : logger.warn
+
+ log(
+ `${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(`${fleetInfo.fleetName} has respawned`)
+ } else {
+ logger.info(
+ `${fleetInfo.fleetName} respawning at ${fleetInfo.fleetState.data.sector}. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}. Destruction time: ${destructionTime}`,
+ )
+ }
+ break
+ }
+ default:
+ logger.info(
+ `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`,
+ )
+
+ return Promise.resolve()
+ }
+}
+
+export const createMiningStrategy = (
+ miningConfig: MineConfig,
+ player: Player,
+ game: Game,
+): Strategy => {
+ const config = miningConfig
+
+ return {
+ apply: (fleetInfo: FleetInfo): Promise =>
+ transition(fleetInfo, player, game, config),
+ }
+}
diff --git a/src/main/basedbot/fsm/strategy.ts b/src/main/basedbot/fsm/strategy.ts
new file mode 100644
index 00000000..5d6350de
--- /dev/null
+++ b/src/main/basedbot/fsm/strategy.ts
@@ -0,0 +1,7 @@
+import { FleetInfo } from '../lib/sage/state/user-fleets'
+
+export type Strategy = {
+ apply: (fleetInfo: FleetInfo) => Promise
+}
+
+export const noop = (): Promise => Promise.resolve()
diff --git a/src/main/basedbot/fsm/transport.ts b/src/main/basedbot/fsm/transport.ts
new file mode 100644
index 00000000..7ff514fa
--- /dev/null
+++ b/src/main/basedbot/fsm/transport.ts
@@ -0,0 +1,329 @@
+import { Game } from '@staratlas/sage'
+import dayjs from 'dayjs'
+
+import { now } from '../../../dayjs'
+import { logger } from '../../../logger'
+import { Resource } from '../../../service/wallet'
+import { getTokenBalance } from '../basedbot'
+import { dock } from '../lib/sage/act/dock'
+import { loadCargo } from '../lib/sage/act/load-cargo'
+import { move, WarpMode } from '../lib/sage/act/move'
+import { selfDestruct } from '../lib/sage/act/self-destruct'
+import { undock } from '../lib/sage/act/undock'
+import { getHold, unloadCargo } from '../lib/sage/act/unload-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 { WorldMap } from '../lib/sage/state/world-map'
+import { getName } from '../lib/sage/util'
+import { Coordinates } from '../lib/util/coordinates'
+
+import { Strategy } from './strategy'
+
+// eslint-disable-next-line complexity
+const transition = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ config: TransportConfig,
+): Promise => {
+ const cargoLoad = player.cargoTypes.reduce((acc, cargoType) => {
+ const load =
+ fleetInfo.cargoLevels.cargo.get(cargoType.data.mint.toBase58()) ?? 0
+
+ return acc + load
+ }, 0)
+
+ const { cargoCapacity } = fleetInfo.cargoStats
+ const cargoLevelFuel = fleetInfo.cargoLevels.fuel
+ const cargoLevelAmmo = fleetInfo.cargoLevels.ammo
+ const fuelReserve = fleetInfo.cargoStats.fuelCapacity / 10
+ const ammoReserve = 0
+ const hasEnoughFuel = cargoLevelFuel >= fuelReserve
+ const hasEnoughAmmo = cargoLevelAmmo >= ammoReserve
+ const hasCargo = cargoLoad > 0
+ const currentStarbase = await starbaseByCoordinates(fleetInfo.location)
+ const { fleetName, location } = fleetInfo
+ const { homeBase, targetBase, resources, warpMode } = config
+ const isAtHomeBase = homeBase.equals(location)
+ const isAtTargetBase = targetBase.equals(location)
+ const isSameBase = homeBase.equals(targetBase)
+
+ logger.info(
+ `${fleetName} is transporting ${config.resources.size} resources from ${config.homeBase} to ${config.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(
+ `${fleetName} is out of fuel and not at a starbase, need self destruction`,
+ )
+
+ return selfDestruct(fleetInfo, player, game)
+ }
+ if (isSameBase) {
+ logger.warn(
+ `${fleetName} is configured as transport fleet with home and target being the same. Idling....`,
+ )
+
+ return Promise.resolve()
+ }
+ if (isAtHomeBase) {
+ logger.info(`${fleetName} is at home base`)
+ if (hasEnoughAmmo && hasEnoughFuel && hasCargo) {
+ logger.info('Ready to go! Moving to target base')
+
+ return move(fleetInfo, targetBase, player, game, warpMode)
+ }
+ logger.info(`${fleetName} is docking to resupply`)
+
+ return dock(fleetInfo, location, player, game)
+ }
+
+ if (isAtTargetBase) {
+ logger.info(`${fleetName} is at target base`)
+
+ if (!hasCargo) {
+ logger.info('Ready to go! Moving to home base')
+
+ return move(fleetInfo, homeBase, player, game, warpMode)
+ }
+
+ logger.info(
+ `${fleetName} has ${cargoLoad} cargo, docking to unload.`,
+ )
+
+ return dock(fleetInfo, location, player, game)
+ }
+
+ logger.warn(`${fleetName} doesn't know what to do`)
+
+ return Promise.resolve()
+ }
+
+ case 'StarbaseLoadingBay': {
+ logger.info(
+ `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`,
+ )
+
+ if (isAtHomeBase) {
+ if (!hasEnoughFuel) {
+ logger.info(`${fleetInfo.fleetName} is refueling`)
+
+ await loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.fuel,
+ fleetInfo.cargoStats.fuelCapacity - cargoLevelFuel,
+ )
+ }
+ if (!hasEnoughAmmo) {
+ logger.info(`${fleetInfo.fleetName} is rearming`)
+
+ await loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.ammo,
+ fleetInfo.cargoStats.ammoCapacity - cargoLevelAmmo,
+ )
+ }
+
+ if (!hasCargo) {
+ logger.info(`Loading ${Array.from(resources).length} cargo`)
+ const cargoResources = Array.from(resources).filter(
+ (resource) =>
+ !resource.equals(game.data.mints.ammo) &&
+ !resource.equals(game.data.mints.fuel),
+ )
+
+ await Promise.all(
+ cargoResources.map((resource) => {
+ const count = Math.floor(
+ cargoCapacity /
+ Array.from(cargoResources).length,
+ )
+
+ logger.info(
+ `Loading ${count} ${resource.toBase58()}`,
+ )
+
+ return loadCargo(
+ fleetInfo,
+ player,
+ game,
+ resource,
+ count,
+ )
+ }),
+ )
+ }
+
+ logger.info(`${fleetName} is undocking...`)
+
+ return undock(fleetInfo.fleet, fleetInfo.location, player, game)
+ }
+
+ if (isAtTargetBase) {
+ if (hasCargo) {
+ logger.info(
+ `Unloading ${Array.from(resources).length} cargo`,
+ )
+
+ await Promise.all(
+ Array.from(resources).map(async (resource) => {
+ const fleetCargoPod = getHold(
+ resource,
+ game,
+ fleetInfo,
+ )
+ const amount = await getTokenBalance(
+ fleetCargoPod,
+ resource,
+ )
+
+ logger.info(
+ `Unloading ${amount} ${resource.toBase58()}`,
+ )
+
+ return unloadCargo(
+ fleetInfo,
+ player,
+ game,
+ resource,
+ amount,
+ )
+ }),
+ )
+ }
+
+ if (!hasEnoughFuel) {
+ logger.info(`${fleetInfo.fleetName} is refueling`)
+
+ await loadCargo(
+ fleetInfo,
+ player,
+ game,
+ game.data.mints.fuel,
+ fuelReserve - cargoLevelFuel,
+ )
+ }
+ }
+
+ logger.info(`${fleetName} is undocking for take off`)
+
+ return undock(fleetInfo.fleet, fleetInfo.location, player, game)
+ }
+ case 'MoveWarp': {
+ const { fromSector, toSector, warpFinish } =
+ fleetInfo.fleetState.data
+
+ if (warpFinish.isBefore(now())) {
+ logger.info(
+ `${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(
+ `${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': {
+ //TODO: Gather 'Mineable' in order to call `endMine`
+ // return endMine(fleetInfo, player, game, config.resource)
+ logger.warn(
+ `${fleetInfo.fleetName} is currently mining, need to end mine manually.`,
+ )
+
+ return Promise.resolve()
+ }
+ case 'Respawn': {
+ const { destructionTime, ETA } = fleetInfo.fleetState.data
+
+ if (ETA.isBefore(now())) {
+ logger.info(`${fleetInfo.fleetName} has respawned`)
+ } else {
+ logger.info(
+ `${fleetInfo.fleetName} respawning at ${fleetInfo.fleetState.data.sector}. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}. Destruction time: ${destructionTime}`,
+ )
+ }
+ break
+ }
+ default:
+ logger.info(
+ `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`,
+ )
+
+ return Promise.resolve()
+ }
+}
+
+export type TransportConfig = {
+ map: WorldMap
+ homeBase: Coordinates
+ targetBase: Coordinates
+ resources: Set
+ warpMode: WarpMode
+}
+
+export const transportConfig = (
+ config: Partial & {
+ map: WorldMap
+ homeBase: Coordinates
+ targetBase: Coordinates
+ resources: Set
+ },
+): TransportConfig => ({
+ map: config.map,
+ homeBase: config.homeBase,
+ targetBase: config.targetBase,
+ resources: config.resources,
+ warpMode: config.warpMode || 'auto',
+})
+
+export const transport = (
+ map: WorldMap,
+ homeBase: Coordinates,
+ targetBase: Coordinates,
+ resources: Set,
+): TransportConfig =>
+ transportConfig({
+ map,
+ homeBase,
+ targetBase,
+ resources,
+ warpMode: 'subwarp',
+ })
+
+export const createTransportStrategy = (
+ config: TransportConfig,
+ player: Player,
+ game: Game,
+): Strategy => {
+ return {
+ apply: (fleetInfo: FleetInfo): Promise =>
+ transition(fleetInfo, player, game, config),
+ }
+}
diff --git a/src/main/basedbot/index.ts b/src/main/basedbot/index.ts
new file mode 100644
index 00000000..6a5c0809
--- /dev/null
+++ b/src/main/basedbot/index.ts
@@ -0,0 +1,66 @@
+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..f260717b
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/fleet-state.ts
@@ -0,0 +1,286 @@
+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 {
+ EndReason,
+ 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)))
+}
+
+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')
+ }
+ const warpCooldownExpiry = transformTime(fleet.data.warpCooldownExpiresAt)
+ const scanCoolDownExpiry = transformTime(fleet.data.scanCooldownExpiresAt)
+
+ const baseData = {
+ warpCooldownExpiry,
+ scanCoolDownExpiry,
+ warpCooldown: warpCooldownExpiry.isAfter(now()),
+ scanCooldown: scanCoolDownExpiry.isAfter(now()),
+ }
+
+ switch (type) {
+ case 'Idle':
+ if (isIdleData(data)) {
+ return {
+ type,
+ data: {
+ sector: transformSector(data.sector),
+ ...baseData,
+ },
+ }
+ }
+ break
+ case 'StarbaseLoadingBay':
+ if (isStarbaseLoadingBayData(data)) {
+ const starbase = await starbaseByKey(data.starbase)
+
+ return {
+ type,
+ data: {
+ starbase,
+ lastUpdate: data.lastUpdate,
+ sector: transformSector(starbase.data.sector),
+ ...baseData,
+ },
+ }
+ }
+ 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),
+ resource: data.resource,
+ mineItem: map.mineItems.get(data.resource.toBase58())!,
+ start: transformTime(data.start),
+ endReason: miningStats.endReason,
+ ...baseData,
+ },
+ }
+ }
+ 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()),
+ ),
+ ...baseData,
+ },
+ }
+ }
+ 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()),
+ ),
+ ...baseData,
+ },
+ }
+ }
+ 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)),
+ ),
+ ...baseData,
+ },
+ }
+ }
+ 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..787a70f2
--- /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..7bc25312
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/idle.ts
@@ -0,0 +1,12 @@
+import BN from 'bn.js'
+
+import { RawIdleData } from '../types'
+
+export const isIdleData = (data: unknown): data is RawIdleData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'sector' in 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..28923611
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts
@@ -0,0 +1,22 @@
+import { PublicKey } from '@solana/web3.js'
+import BN from 'bn.js'
+
+import { RawMineAsteroidData } from '../types'
+
+export const isMineAsteroidData = (
+ data: unknown,
+): data is RawMineAsteroidData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'asteroid' in data &&
+ 'resource' in data &&
+ 'start' in data &&
+ 'end' in data &&
+ 'amountMined' in data &&
+ 'lastUpdate' in 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..e1385a2a
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts
@@ -0,0 +1,18 @@
+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: unknown): data is RawMoveSubwarpData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'fromSector' in data &&
+ 'toSector' in 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..d4ebc23a
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts
@@ -0,0 +1,18 @@
+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: unknown): data is RawMoveWarpData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'fromSector' in data &&
+ 'toSector' in 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..0a5a9291
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/respawn.ts
@@ -0,0 +1,12 @@
+import BN from 'bn.js'
+
+import { RawRespawnData } from '../types'
+
+export const isRespawnData = (data: unknown): data is RawRespawnData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'sector' in 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..f13a9457
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts
@@ -0,0 +1,14 @@
+import { PublicKey } from '@solana/web3.js'
+import BN from 'bn.js'
+
+import { RawStarbaseLoadingBayData } from '../types'
+
+export const isStarbaseLoadingBayData = (
+ data: unknown,
+): data is RawStarbaseLoadingBayData =>
+ data !== undefined &&
+ data instanceof Object &&
+ 'starbase' in data &&
+ 'lastUpdate' in 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..ce9bffd4
--- /dev/null
+++ b/src/main/basedbot/lib/fleet-state/types.ts
@@ -0,0 +1,119 @@
+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'
+
+export type EndReason = 'FULL' | 'AMMO' | 'FOOD'
+type BaseData = {
+ sector: Coordinates
+ warpCooldownExpiry: dayjs.Dayjs
+ scanCoolDownExpiry: dayjs.Dayjs
+ warpCooldown: boolean
+ scanCooldown: boolean
+}
+
+// Raw data types for incoming data
+export type RawIdleData = {
+ sector: [BN, BN]
+}
+
+export type IdleData = BaseData
+
+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
+ resource: PublicKey
+ 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..cb6d7387
--- /dev/null
+++ b/src/main/basedbot/lib/programs.ts
@@ -0,0 +1,95 @@
+// eslint-disable-next-line import/named
+import { Idl } from '@coral-xyz/anchor'
+import { PublicKey } from '@solana/web3.js'
+import { CargoProgram } from '@staratlas/cargo'
+import { Cargo } from '@staratlas/cargo/dist/src/idl/cargo'
+import { CraftingProgram } from '@staratlas/crafting'
+import { Crafting } from '@staratlas/crafting/dist/src/idl/crafting'
+import { ProgramMethods } from '@staratlas/data-source'
+import { PlayerProfileProgram } from '@staratlas/player-profile'
+import { PlayerProfile } from '@staratlas/player-profile/dist/src/idl/player_profile'
+import { PointsProgram } from '@staratlas/points'
+import { Points } from '@staratlas/points/dist/src/idl/points'
+import { ProfileFactionProgram } from '@staratlas/profile-faction'
+import { ProfileFaction } from '@staratlas/profile-faction/dist/src/idl/profile_faction'
+import { SageProgram } from '@staratlas/sage'
+import { Sage } from '@staratlas/sage/dist/src/idl/sage'
+
+import { config } from '../../../config'
+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 = config.sol.rpcEndpoint.includes('atlasnet')
+ ? {
+ dataRunningXpCategory: 'DXPsKQPMyaDtunxDWqiKTGWbQga3Wihck8zb8iSLATJQ',
+ councilRankXpCategory: 'CRXPW3csNpkEYU5U4DUp6Ln6aEEWq4PSUAwV8v6Ygcqg',
+ pilotingXpCategory: 'PXPfCZwu5Vuuj6aFdEUAXbxudDGeXVktTo6imwhZ5nC',
+ miningXpCategory: 'MXPkuZz7yXvqdEB8pGtyNknqhxbCzJNQzqixoEiW4Q7',
+ craftingXpCategory: 'CXPukKpixXCFPrfQmEUGR9VqnDvkUsKfPPLfdd4sKSH8',
+ loyalityCategory: 'LPpdwMuXRuGMz298EMbNcUioaARN8CUU6dA2qyq46g8',
+ }
+ : {
+ dataRunningXpCategory: 'DataJpxFgHhzwu4zYJeHCnAv21YqWtanEBphNxXBHdEY',
+ councilRankXpCategory: 'XPneyd1Wvoay3aAa24QiKyPjs8SUbZnGg5xvpKvTgN9',
+ pilotingXpCategory: 'PiLotBQoUBUvKxMrrQbuR3qDhqgwLJctWsXj3uR7fGs',
+ miningXpCategory: 'MineMBxARiRdMh7s1wdStSK4Ns3YfnLjBfvF5ZCnzuw',
+ craftingXpCategory: 'CraftndAV62acibnaW7TiwEYwu8MmJZBdyrfyN54nre7',
+ loyalityCategory: '',
+ }
+
+const programIds = config.sol.rpcEndpoint.includes('atlasnet')
+ ? {
+ sage: 'SaGeXGRK85wYGfDqnVDw2bZ61hpdYPFLn1HiRdnRhoC',
+ profile: 'PprofUW1pURCnMW2si88GWPXEEK3Bvh9Tksy8WtnoYJ',
+ cargo: 'CArGoi989iv3VL3xArrJXmYYDNhjwCX5ey5sY5KKwMG',
+ profileFaction: 'pFACzkX2eSpAjDyEohD6i3VRJvREtH9ynbtM1DwVFsj',
+ crafting: 'CRAFtUSjCW74gQtCS6LyJH33rhhVhdPhZxbPegE4Qwfq',
+ points: 'PointJfvuHi8DgGsPCy97EaZkQ6NvpghAAVkuquLf3w',
+ }
+ : {
+ sage: 'SAGE2HAwep459SNq61LHvjxPk4pLPEJLoMETef7f7EE',
+ profile: 'pprofELXjL5Kck7Jn5hCpwAL82DpTkSYBENzahVtbc9',
+ cargo: 'Cargo2VNTPPTi9c1vq1Jw5d3BWUNr18MjRtSupAghKEk',
+ profileFaction: 'pFACSRuobDmvfMKq1bAzwj27t6d2GJhSCHb1VcfnRmq',
+ crafting: 'CRAFT2RPXPJWCEix4WpJST3E7NLf79GTqZUL75wngXo5',
+ 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/create-fleet.ts b/src/main/basedbot/lib/sage/act/create-fleet.ts
new file mode 100644
index 00000000..8a59cc70
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/create-fleet.ts
@@ -0,0 +1,142 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source'
+import {
+ Game,
+ Ship,
+ Starbase,
+ StarbasePlayer,
+ WrappedShipEscrow,
+} from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { addShipToFleetIx } from '../ix/add-ship-to-fleet'
+import { createFleetIx } from '../ix/create-fleet'
+import { getCargoStatsDefinition } from '../state/cargo-stats-definition'
+import { getShipByMint, getStarbasePlayer } from '../state/starbase-player'
+import { Player } from '../state/user-account'
+
+export type FleetShips = Array
+export type FleetShip = {
+ shipMint: PublicKey
+ count: number
+}
+
+const getShipEscrowIndex = (
+ starbasePlayer: StarbasePlayer,
+ shipKey: PublicKey,
+) => {
+ const pred = (key: PublicKey) => (v: WrappedShipEscrow) =>
+ v.ship.equals(key)
+ const index = starbasePlayer.wrappedShipEscrows.findIndex(pred(shipKey))
+
+ if (index === -1) {
+ throw new Error('Ship not found')
+ }
+
+ return index
+}
+
+type ShipMintMap = {
+ mint: PublicKey
+ ship: Ship
+}
+
+export const createFleet = async (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ fleetShips: FleetShips,
+ name: string,
+): Promise => {
+ const instructions: InstructionReturn[] = []
+
+ const shipMints = (
+ await Promise.all(
+ fleetShips.map(async (fleetShip) => {
+ return {
+ mint: fleetShip.shipMint,
+ ship: await getShipByMint(
+ fleetShip.shipMint,
+ game,
+ programs,
+ ),
+ } as ShipMintMap
+ }),
+ )
+ ).reduce(
+ (acc, curr) => acc.set(curr.mint.toBase58(), curr.ship),
+ new Map(),
+ )
+
+ const [starbasePlayer, [cargoStatsDefinition]] = await Promise.all([
+ getStarbasePlayer(player, starbase, programs),
+ getCargoStatsDefinition(),
+ ])
+
+ const [head, ...tail] = fleetShips.sort(
+ (a, b) =>
+ getShipEscrowIndex(
+ starbasePlayer,
+ shipMints.get(a.shipMint.toBase58())!.key,
+ ) -
+ getShipEscrowIndex(
+ starbasePlayer,
+ shipMints.get(b.shipMint.toBase58())!.key,
+ ),
+ )
+
+ const shipKey = shipMints.get(head.shipMint.toBase58())?.key
+
+ if (!shipKey) throw new Error('No ship found')
+
+ const escrowIndex = getShipEscrowIndex(starbasePlayer, shipKey)
+
+ logger.info(`Escrow index ${escrowIndex} for ${head.shipMint.toBase58()}`)
+
+ const createFleetReturn = createFleetIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ programs,
+ shipKey,
+ cargoStatsDefinition.key,
+ head.count,
+ name,
+ escrowIndex,
+ )
+
+ instructions.push(createFleetReturn.instructions)
+
+ for (const fleetShip of tail) {
+ const shipKey2 = shipMints.get(fleetShip.shipMint.toBase58())?.key
+
+ if (!shipKey2) throw new Error('No ship found')
+
+ const escrowIndex2 = getShipEscrowIndex(starbasePlayer, shipKey2)
+
+ logger.info(
+ `Escrow index ${escrowIndex2} for ${fleetShip.shipMint.toBase58()}`,
+ )
+
+ instructions.push(
+ addShipToFleetIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ programs,
+ createFleetReturn.fleetKey[0],
+ shipKey2,
+ fleetShip.count,
+ escrowIndex2,
+ ),
+ )
+ }
+
+ await sendAndConfirmInstructions(
+ await ixReturnsToIxs(instructions, player.signer),
+ )
+}
diff --git a/src/main/basedbot/lib/sage/act/deposit-cargo.ts b/src/main/basedbot/lib/sage/act/deposit-cargo.ts
new file mode 100644
index 00000000..deda30f5
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/deposit-cargo.ts
@@ -0,0 +1,83 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import { PublicKey } from '@solana/web3.js'
+import {
+ createAssociatedTokenAccountIdempotent,
+ InstructionReturn,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game, Starbase } from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { getTokenBalance } from '../../../basedbot'
+import { programs } from '../../programs'
+import { depositCargoIx } from '../ix/import-cargo'
+import { getCargoType } from '../state/cargo-types'
+import {
+ getCargoPodsForStarbasePlayer,
+ getStarbasePlayer,
+} from '../state/starbase-player'
+import { Player } from '../state/user-account'
+
+export const depositCargo = async (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ mint: PublicKey,
+ amount: BN,
+ // eslint-disable-next-line max-params
+): Promise => {
+ const instructions: InstructionReturn[] = []
+
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+
+ const sourceTokenAccount = getAssociatedTokenAddressSync(
+ mint,
+ player.signer.publicKey(),
+ )
+
+ const cargoPodTo = await getCargoPodsForStarbasePlayer(
+ starbasePlayer,
+ programs,
+ )
+ const destinationTokenAccount = getAssociatedTokenAddressSync(
+ mint,
+ cargoPodTo.key,
+ true,
+ )
+
+ instructions.push(
+ createAssociatedTokenAccountIdempotent(mint, cargoPodTo.key, true)
+ .instructions,
+ )
+
+ const cargoType = getCargoType(player.cargoTypes, game, mint)
+
+ const amountAtOrigin = await getTokenBalance(
+ player.signer.publicKey(),
+ mint,
+ )
+
+ if (amountAtOrigin.lt(new BN(amount))) {
+ throw new Error('Not enough cargo available at origin')
+ }
+
+ instructions.push(
+ depositCargoIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ cargoPodTo.key,
+ sourceTokenAccount,
+ destinationTokenAccount,
+ cargoType.key,
+ programs,
+ amount,
+ ),
+ )
+
+ await sendAndConfirmInstructions(
+ await ixReturnsToIxs(instructions, player.signer),
+ )
+}
diff --git a/src/main/basedbot/lib/sage/act/deposit-ship.ts b/src/main/basedbot/lib/sage/act/deposit-ship.ts
new file mode 100644
index 00000000..ad2ab502
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/deposit-ship.ts
@@ -0,0 +1,112 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import {
+ createAssociatedTokenAccountIdempotent,
+ InstructionReturn,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import {
+ Game,
+ SagePlayerProfile,
+ Ship,
+ Starbase,
+ WrappedShipEscrow,
+} from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { connection } from '../../../../../service/sol'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { addShipEscrowIx } from '../ix/add-ship-escrow'
+import { getShipByMint, getStarbasePlayer } from '../state/starbase-player'
+import { Player } from '../state/user-account'
+
+import { FleetShips } from './create-fleet'
+
+export const depositShip = async (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ ship: Ship,
+ amount: BN,
+ // eslint-disable-next-line max-params,require-await
+): Promise => {
+ const instructions: InstructionReturn[] = []
+
+ const { mint } = ship.data
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+
+ const sourceTokenAccount = getAssociatedTokenAddressSync(
+ mint,
+ player.signer.publicKey(),
+ )
+
+ const [sagePlayerProfile] = SagePlayerProfile.findAddress(
+ programs.sage,
+ player.profile.key,
+ game.key,
+ )
+ const shipEscrowTokenAccountResult = createAssociatedTokenAccountIdempotent(
+ mint,
+ sagePlayerProfile,
+ true,
+ )
+
+ instructions.push(shipEscrowTokenAccountResult.instructions)
+
+ const info = await connection.getTokenAccountBalance(sourceTokenAccount)
+
+ if (info.value.uiAmount === null) throw new Error('No balance found')
+
+ const amountAtOrigin = new BN(info.value.uiAmount)
+
+ if (amountAtOrigin.lt(new BN(amount))) {
+ throw new Error('Not enough ships available at origin')
+ }
+
+ const pred = (v: WrappedShipEscrow) => v.ship.equals(ship.key)
+ // const shipEscrow = starbasePlayer.wrappedShipEscrows.find(pred)
+ const index = starbasePlayer.wrappedShipEscrows.findIndex(pred)
+
+ instructions.push(
+ addShipEscrowIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ sagePlayerProfile,
+ programs,
+ sourceTokenAccount,
+ ship.key,
+ shipEscrowTokenAccountResult.address,
+ amount,
+ index === -1 ? null : index,
+ ),
+ )
+
+ await sendAndConfirmInstructions(
+ await ixReturnsToIxs(instructions, player.signer),
+ )
+}
+export const ensureShips = async (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ fleetShips: FleetShips,
+): Promise => {
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+
+ for (const fleetShip of fleetShips) {
+ const desiredAmount = new BN(fleetShip.count)
+
+ const ship = await getShipByMint(fleetShip.shipMint, game, programs)
+ const pred = (v: WrappedShipEscrow) => v.ship.equals(ship.key)
+ const shipEscrow = starbasePlayer.wrappedShipEscrows.find(pred)
+ const needed = shipEscrow
+ ? desiredAmount.sub(shipEscrow.amount)
+ : desiredAmount
+
+ if (needed.gt(new BN(0))) {
+ await depositShip(player, game, starbase, ship, needed)
+ }
+ }
+}
diff --git a/src/main/basedbot/lib/sage/act/disband-fleet.ts b/src/main/basedbot/lib/sage/act/disband-fleet.ts
new file mode 100644
index 00000000..38cb7a05
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/disband-fleet.ts
@@ -0,0 +1,87 @@
+import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source'
+import { Game, Starbase, WrappedShipEscrow } from '@staratlas/sage'
+
+import dayjs from '../../../../../dayjs'
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { closeDisbandedFleetIx } from '../ix/close-disbanded-fleet'
+import { disbandFleetIx } from '../ix/disband-fleet'
+import { disbandedFleetToEscrowIx } from '../ix/disbanded-fleet-to-escrow'
+import { getFleetShips } from '../state/get-fleet-ships'
+import { getStarbasePlayer } from '../state/starbase-player'
+import { Player } from '../state/user-account'
+import { FleetInfo } from '../state/user-fleets'
+import { getName } from '../util'
+
+export const disbandFleet = async (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ fleetInfo: FleetInfo,
+): Promise => {
+ const ixs: InstructionReturn[] = []
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+ const { fleet } = fleetInfo
+
+ if (fleetInfo.fleetState.data.warpCooldown) {
+ const timeLeft = dayjs.duration(
+ dayjs().diff(fleetInfo.fleetState.data.warpCooldownExpiry),
+ )
+
+ logger.warn(
+ `Fleet is on warp cooldown, cannot disband. Retry in: ${timeLeft.humanize()}`,
+ )
+
+ return
+ }
+
+ const { disbandedFleetKey, instructions } = disbandFleetIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ programs,
+ fleet,
+ )
+
+ ixs.push(instructions)
+
+ const [fleetShips] = await getFleetShips(fleet)
+
+ for (const fleetShipInfo of fleetShips.fleetShips) {
+ const pred = (v: WrappedShipEscrow) => v.ship.equals(fleetShipInfo.ship)
+ // const shipEscrow = starbasePlayer.wrappedShipEscrows.find(pred)
+ const shipEscrowIndex =
+ starbasePlayer.wrappedShipEscrows.findIndex(pred)
+
+ ixs.push(
+ disbandedFleetToEscrowIx(
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ programs,
+ shipEscrowIndex === -1 ? null : shipEscrowIndex,
+ disbandedFleetKey[0],
+ fleet.data.fleetShips,
+ fleetShipInfo.ship,
+ fleetShipInfo.amount,
+ ),
+ )
+ }
+
+ ixs.push(
+ closeDisbandedFleetIx(
+ player,
+ programs,
+ disbandedFleetKey[0],
+ fleet.data.fleetShips,
+ ),
+ )
+ console.log(
+ `Added ${ixs.length} ixs for disbanding fleet ${getName(fleet)}`,
+ )
+
+ await sendAndConfirmInstructions(await ixReturnsToIxs(ixs, player.signer))
+}
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..9966e117
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/dock.ts
@@ -0,0 +1,38 @@
+import { ixReturnsToIxs } from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { Coordinates } from '../../util/coordinates'
+import { dockIx } from '../ix/dock'
+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,
+ game: Game,
+): 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,
+ game,
+ 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..670b7370
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/end-mine.ts
@@ -0,0 +1,99 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+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,
+ game: Game,
+ mineable: Mineable,
+): Promise => {
+ const { fleet } = fleetInfo
+
+ if (!fleet.state.MineAsteroid) {
+ logger.warn('Fleet is not mining, cannot End Mine')
+
+ return
+ }
+
+ const [
+ foodToken,
+ ammoToken,
+ resourceFromToken,
+ resourceToToken,
+ fuelToken,
+ ] = [
+ createAssociatedTokenAccountIdempotent(
+ game.data.mints.food,
+ fleet.data.cargoHold,
+ true,
+ ),
+ createAssociatedTokenAccountIdempotent(
+ game.data.mints.ammo,
+ fleet.data.ammoBank,
+ true,
+ ),
+ createAssociatedTokenAccountIdempotent(
+ mineable.mineItem.data.mint,
+ mineable.resource.data.mineItem,
+ true,
+ ),
+ createAssociatedTokenAccountIdempotent(
+ mineable.mineItem.data.mint,
+ fleet.data.cargoHold,
+ true,
+ ),
+ createAssociatedTokenAccountIdempotent(
+ game.data.mints.fuel,
+ fleet.data.fuelTank,
+ true,
+ ),
+ ]
+
+ await ixReturnsToIxs(
+ [
+ foodToken.instructions,
+ ammoToken.instructions,
+ resourceFromToken.instructions,
+ resourceToToken.instructions,
+ miningHandlerIx(
+ fleetInfo,
+ player,
+ mineable,
+ foodToken.address,
+ ammoToken.address,
+ resourceFromToken.address,
+ resourceToToken.address,
+ programs,
+ game,
+ ),
+ ],
+ player.signer,
+ ).then(sendAndConfirmInstructions)
+
+ await ixReturnsToIxs(
+ [
+ fuelToken.instructions,
+ stopMiningIx(
+ fleetInfo,
+ player,
+ game,
+ mineable,
+ fuelToken.address,
+ programs,
+ ),
+ ],
+ player.signer,
+ ).then(sendAndConfirmInstructions)
+}
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..97aa5d55
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/end-move.ts
@@ -0,0 +1,43 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { movementSubwarpHandlerIx } from '../ix/movement-subwarp-handler'
+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,
+ game: Game,
+): Promise => {
+ const { fleet } = fleetInfo
+
+ if (!fleet.state.MoveWarp && !fleet.state.MoveSubwarp) {
+ logger.warn('Fleet is not moving, cannot End Move')
+
+ return
+ }
+ const fuelTokenAccount = createAssociatedTokenAccountIdempotent(
+ game.data.mints.fuel,
+ fleet.data.fuelTank,
+ true,
+ )
+
+ const ix = (
+ fleet.state.MoveSubwarp ? movementSubwarpHandlerIx : stopWarpIx
+ )(fleetInfo, player, game, fuelTokenAccount.address, programs)
+
+ const instructions = await ixReturnsToIxs(
+ [fuelTokenAccount.instructions, 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..bc71b570
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/exit-respawn.ts
@@ -0,0 +1,77 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source'
+import { Game, Starbase } from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { connection } from '../../../../../service/sol'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { exitRespawnIx } from '../ix/exit-respawn'
+import { forceDropFleetCargoIx } from '../ix/force-drop-fleet-cargo'
+import { getCargoStatsDefinition } from '../state/cargo-stats-definition'
+import { getCargoType } from '../state/cargo-types'
+import { getStarbasePlayer } from '../state/starbase-player'
+import { Player } from '../state/user-account'
+import { FleetInfo } from '../state/user-fleets'
+
+import { getFleetCargoHold } from './load-cargo'
+
+export const exitRespawn = async (
+ fleetInfo: FleetInfo,
+ starbase: Starbase,
+ player: Player,
+ game: Game,
+): Promise => {
+ const { fleet } = fleetInfo
+
+ if (!fleet.state.Respawn) {
+ logger.warn('Fleet is not respawning, cannot Exit Respawn')
+
+ return
+ }
+
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+
+ const [cargoStatsDefinition] = await getCargoStatsDefinition()
+
+ const ixs: Array = []
+
+ const cargoMints = player.cargoTypes.map((ct) => ct.data.mint)
+
+ for (const key of cargoMints) {
+ const mint = new PublicKey(key)
+ const cargoType = getCargoType(player.cargoTypes, game, mint)
+
+ const cargoPod = getFleetCargoHold(mint, game, fleetInfo)
+ const tokenFrom = getAssociatedTokenAddressSync(mint, cargoPod, true)
+
+ const accountInfo = await connection.getAccountInfo(tokenFrom)
+
+ if (accountInfo) {
+ ixs.push(
+ forceDropFleetCargoIx(
+ fleetInfo,
+ game,
+ cargoStatsDefinition,
+ cargoPod,
+ cargoType.key,
+ tokenFrom,
+ mint,
+ programs,
+ ),
+ )
+ }
+ }
+ ixs.push(
+ exitRespawnIx(
+ fleetInfo,
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ programs,
+ ),
+ )
+ await ixReturnsToIxs(ixs, player.signer).then(sendAndConfirmInstructions)
+}
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..3b5ee5b7
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/load-cargo.ts
@@ -0,0 +1,113 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import { PublicKey } from '@solana/web3.js'
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { getTokenBalance } from '../../../basedbot'
+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'
+import { getName } from '../util'
+
+export const getFleetCargoHold = (
+ mint: PublicKey,
+ game: Game,
+ fleetInfo: FleetInfo,
+): PublicKey => {
+ switch (mint.toBase58()) {
+ case game.data.mints.fuel.toBase58():
+ return fleetInfo.fleet.data.fuelTank
+ case game.data.mints.ammo.toBase58():
+ return fleetInfo.fleet.data.ammoBank
+ default:
+ return fleetInfo.fleet.data.cargoHold
+ }
+}
+
+export const loadCargo = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ mint: PublicKey,
+ amount: number,
+ // eslint-disable-next-line max-params
+): Promise => {
+ const starbase = await starbaseByCoordinates(fleetInfo.location)
+
+ const hold = getFleetCargoHold(mint, game, fleetInfo)
+
+ if (!starbase) {
+ throw new Error(`No starbase found at ${fleetInfo.location}`)
+ }
+
+ const cargoType = getCargoType(player.cargoTypes, game, mint)
+ const fleetCargoTokenResult = createAssociatedTokenAccountIdempotent(
+ mint,
+ hold,
+ true,
+ )
+
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+ const cargoPodFrom = await getCargoPodsForStarbasePlayer(
+ starbasePlayer,
+ programs,
+ )
+
+ const cargoTokenAccountAddress = getAssociatedTokenAddressSync(
+ mint,
+ cargoPodFrom.key,
+ true,
+ )
+
+ const cargoAmountAtOrigin = await getTokenBalance(cargoPodFrom.key, mint)
+ const toLoad = cargoAmountAtOrigin.lt(new BN(amount))
+ ? cargoAmountAtOrigin
+ : new BN(amount)
+
+ if (toLoad.eq(new BN(0))) {
+ logger.warn(`No ${mint} available at ${getName(starbase)}...`)
+
+ return
+ }
+ if (cargoAmountAtOrigin.lt(new BN(amount))) {
+ logger.warn(
+ `Not enough cargo available at origin Starbase, loading ${cargoAmountAtOrigin} instead of ${amount}`,
+ )
+ }
+
+ const ix = loadCargoIx(
+ fleetInfo,
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ cargoPodFrom.key,
+ hold,
+ cargoTokenAccountAddress,
+ fleetCargoTokenResult.address,
+ mint,
+ cargoType.key,
+ programs,
+ toLoad,
+ )
+
+ 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..e75e3bcb
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/mine.ts
@@ -0,0 +1,74 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { startMiningIx } from '../ix/start-mining'
+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,
+ game: Game,
+ 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(
+ `${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`,
+ )
+
+ await undock(fleet, fleetInfo.location, player, game)
+ }
+
+ if (fleet.state.MoveSubwarp || fleet.state.MoveWarp) {
+ logger.info(`${fleetInfo.fleetName} is moving, cannot mine`)
+
+ return
+ }
+ const starbasePlayer = await getStarbasePlayer(
+ player,
+ mineable.starbase,
+ programs,
+ )
+ const fuelTokenAccount = createAssociatedTokenAccountIdempotent(
+ game.data.mints.fuel,
+ fleet.data.fuelTank,
+ true,
+ )
+
+ const ix = startMiningIx(
+ fleetInfo,
+ player,
+ game,
+ mineable,
+ starbasePlayer,
+ fuelTokenAccount.address,
+ programs,
+ )
+
+ const instructions = await ixReturnsToIxs(
+ [fuelTokenAccount.instructions, 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..3f40318c
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/move.ts
@@ -0,0 +1,89 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import dayjs from '../../../../../dayjs'
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { Coordinates } from '../../util/coordinates'
+import { subWarpIx } from '../ix/subwarp'
+import { warpIx } from '../ix/warp'
+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,
+ game: Game,
+ 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(
+ `${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`,
+ )
+
+ await undock(fleet, fleetInfo.location, player, game)
+ }
+
+ if (fleet.state.MineAsteroid) {
+ logger.info(`${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)
+
+ if (warp && fleetInfo.fleetState.data.warpCooldown) {
+ const timeLeft = dayjs.duration(
+ dayjs().diff(fleetInfo.fleetState.data.warpCooldownExpiry),
+ )
+
+ logger.warn(
+ `Fleet is on warp cooldown, cannot warp. Retry in: ${timeLeft.humanize()}`,
+ )
+
+ return
+ }
+
+ const fuelTokenAccount = createAssociatedTokenAccountIdempotent(
+ game.data.mints.fuel,
+ fleet.data.fuelTank,
+ true,
+ )
+
+ const ix = (warp ? warpIx : subWarpIx)(
+ fleetInfo,
+ coordinates,
+ fuelTokenAccount.address,
+ player,
+ game,
+ programs,
+ )
+
+ const instructions = await ixReturnsToIxs(
+ [fuelTokenAccount.instructions, 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..08d93c9d
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/rearm.ts
@@ -0,0 +1,89 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ getParsedTokenAccountsByOwner,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+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,
+ game: Game,
+): Promise => {
+ const starbase = await starbaseByCoordinates(coordinates)
+
+ if (!starbase) {
+ throw new Error(`No starbase found at ${coordinates}`)
+ }
+
+ const cargoType = getCargoType(
+ player.cargoTypes,
+ game,
+ game.data.mints.ammo,
+ )
+ const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent(
+ 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}`,
+ )
+
+ // TODO: Check if starbase has enough ammo balance
+
+ const ix = loadCargoIx(
+ fleetInfo,
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ cargoPodFrom.key,
+ fleetInfo.fleet.data.ammoBank,
+ starbaseTokenAccounts[0].address,
+ fleetFuelTokenResult.address,
+ 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..124e098c
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/refuel.ts
@@ -0,0 +1,89 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ getParsedTokenAccountsByOwner,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+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 refuel = async (
+ fleetInfo: FleetInfo,
+ coordinates: Coordinates,
+ player: Player,
+ game: Game,
+): Promise => {
+ const starbase = await starbaseByCoordinates(coordinates)
+
+ if (!starbase) {
+ throw new Error(`No starbase found at ${coordinates}`)
+ }
+
+ const cargoType = getCargoType(
+ player.cargoTypes,
+ game,
+ game.data.mints.fuel,
+ )
+ const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent(
+ 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}`,
+ )
+
+ // TODO: Check if starbase has enough ammo balance
+
+ const ix = loadCargoIx(
+ fleetInfo,
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ cargoPodFrom.key,
+ fleetInfo.fleet.data.fuelTank,
+ starbaseTokenAccounts[0].address,
+ fleetFuelTokenResult.address,
+ 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/self-destruct.ts b/src/main/basedbot/lib/sage/act/self-destruct.ts
new file mode 100644
index 00000000..808d93f4
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/self-destruct.ts
@@ -0,0 +1,34 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import { ixReturnsToIxs } from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { idleToRespawnIx } from '../ix/idle-to-respawn'
+import { Player } from '../state/user-account'
+import { FleetInfo } from '../state/user-fleets'
+
+export const selfDestruct = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+): Promise => {
+ const { fleet } = fleetInfo
+
+ // TODO: Also support self-destruct for mining fleets
+ if (!fleet.state.Idle) {
+ logger.warn('Only Idle Fleets can self destruct')
+
+ return
+ }
+ const atlasTokenFrom = getAssociatedTokenAddressSync(
+ game.data.mints.atlas,
+ player.signer.publicKey(),
+ )
+
+ await ixReturnsToIxs(
+ idleToRespawnIx(player, game, fleet, atlasTokenFrom, programs),
+ player.signer,
+ ).then(sendAndConfirmInstructions)
+}
diff --git a/src/main/basedbot/lib/sage/act/stop-subwarp.ts b/src/main/basedbot/lib/sage/act/stop-subwarp.ts
new file mode 100644
index 00000000..a03b6cec
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/stop-subwarp.ts
@@ -0,0 +1,52 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { stopSubWarpIx } from '../ix/stop-subwarp'
+import { getCargoStatsDefinition } from '../state/cargo-stats-definition'
+import { Player } from '../state/user-account'
+import { FleetInfo } from '../state/user-fleets'
+
+import { endMove } from './end-move'
+
+export const stopSubwarp = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+): Promise => {
+ const { fleet } = fleetInfo
+
+ if (!fleet.state.MoveSubwarp) {
+ logger.warn('Fleet is not subwarping, cannot End Subwarp')
+
+ return
+ }
+
+ const fuelToken = createAssociatedTokenAccountIdempotent(
+ game.data.mints.fuel,
+ fleet.data.fuelTank,
+ true,
+ )
+
+ await ixReturnsToIxs(
+ [
+ fuelToken.instructions,
+ stopSubWarpIx(
+ fleetInfo,
+ player,
+ game,
+ (await getCargoStatsDefinition())[0],
+ fuelToken.address,
+ game.data.mints.fuel,
+ programs,
+ ),
+ ],
+ player.signer,
+ ).then(sendAndConfirmInstructions)
+ await endMove(fleetInfo, player, game)
+}
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..249137f4
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/undock.ts
@@ -0,0 +1,41 @@
+import { ixReturnsToIxs } from '@staratlas/data-source'
+import { Fleet, Game } from '@staratlas/sage'
+
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { programs } from '../../programs'
+import { Coordinates } from '../../util/coordinates'
+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,
+ game: Game,
+): Promise => {
+ const starbase = await starbaseByCoordinates(coordinates)
+
+ if (!starbase) {
+ throw new Error(`No starbase found at ${coordinates}`)
+ }
+ const { sage } = programs
+ 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..234646f0
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/unload-all-cargo.ts
@@ -0,0 +1,94 @@
+import {
+ createAssociatedTokenAccountIdempotent,
+ getParsedTokenAccountsByOwner,
+ InstructionReturn,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+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,
+ game: Game,
+): Promise => {
+ const starbase = await starbaseByCoordinates(coordinates)
+
+ const hold = fleetInfo.fleet.data.cargoHold
+
+ 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,
+ game,
+ fleetTokenAccount.mint,
+ )
+
+ withdrawInstructions.push(
+ unloadCargoIx(
+ fleetInfo,
+ player,
+ game,
+ 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/act/unload-cargo.ts b/src/main/basedbot/lib/sage/act/unload-cargo.ts
new file mode 100644
index 00000000..75cae3ed
--- /dev/null
+++ b/src/main/basedbot/lib/sage/act/unload-cargo.ts
@@ -0,0 +1,108 @@
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import { PublicKey } from '@solana/web3.js'
+import {
+ createAssociatedTokenAccountIdempotent,
+ ixReturnsToIxs,
+} from '@staratlas/data-source'
+import { Game } from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { logger } from '../../../../../logger'
+import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx'
+import { getTokenBalance } from '../../../basedbot'
+import { programs } from '../../programs'
+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 getHold = (
+ mint: PublicKey,
+ game: Game,
+ fleetInfo: FleetInfo,
+): PublicKey => {
+ switch (mint.toBase58()) {
+ case game.data.mints.fuel.toBase58():
+ return fleetInfo.fleet.data.fuelTank
+ case game.data.mints.ammo.toBase58():
+ return fleetInfo.fleet.data.ammoBank
+ default:
+ return fleetInfo.fleet.data.cargoHold
+ }
+}
+
+export const unloadCargo = async (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ mint: PublicKey,
+ amount: BN,
+): Promise => {
+ const starbase = await starbaseByCoordinates(fleetInfo.location)
+
+ if (!starbase) {
+ throw new Error(`No starbase found at ${fleetInfo.location}`)
+ }
+
+ const cargoType = getCargoType(player.cargoTypes, game, mint)
+ const fleetCargoPod = getHold(mint, game, fleetInfo)
+
+ const starbasePlayer = await getStarbasePlayer(player, starbase, programs)
+ const cargoPodTo = await getCargoPodsForStarbasePlayer(
+ starbasePlayer,
+ programs,
+ )
+
+ const cargoPodTokenAccountAddress = getAssociatedTokenAddressSync(
+ mint,
+ cargoPodTo.key,
+ true,
+ )
+ const cargoFleetTokenAccountAddress = getAssociatedTokenAddressSync(
+ mint,
+ fleetCargoPod,
+ true,
+ )
+ const cargoPodTokenResult = createAssociatedTokenAccountIdempotent(
+ mint,
+ cargoPodTo.key,
+ true,
+ )
+
+ const fuelAmountAtOrigin = await getTokenBalance(fleetCargoPod, mint)
+
+ if (fuelAmountAtOrigin.lt(amount)) {
+ logger.warn(
+ `Requested ${amount.toNumber()} cargo to unload. can only unload ${fuelAmountAtOrigin.toNumber()}`,
+ )
+ }
+ const toUnload = fuelAmountAtOrigin.lt(amount) ? fuelAmountAtOrigin : amount
+
+ const ix = unloadCargoIx(
+ fleetInfo,
+ player,
+ game,
+ starbase,
+ starbasePlayer,
+ fleetCargoPod,
+ cargoPodTo.key,
+ cargoFleetTokenAccountAddress,
+ cargoPodTokenAccountAddress,
+ mint,
+ cargoType.key,
+ programs,
+ toUnload,
+ )
+
+ const instructions = await ixReturnsToIxs(
+ [cargoPodTokenResult.instructions, ix],
+ player.signer,
+ )
+
+ await sendAndConfirmInstructions(instructions)
+}
diff --git a/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts b/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts
new file mode 100644
index 00000000..2eeb6f5d
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts
@@ -0,0 +1,45 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import {
+ Game,
+ SagePlayerProfile,
+ Starbase,
+ StarbasePlayer,
+} from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const addShipEscrowIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ sagePlayerProfile: PublicKey,
+ programs: StarAtlasPrograms,
+ originTokenAccount: PublicKey,
+ ship: PublicKey,
+ shipEscrowTokenAccount: PublicKey,
+ shipAmount: BN,
+ escrowIndex: number | null,
+ // eslint-disable-next-line max-params
+): InstructionReturn =>
+ SagePlayerProfile.addShipEscrow(
+ programs.sage,
+ player.profile.key,
+ player.profileFaction.key,
+ sagePlayerProfile,
+ player.signer,
+ originTokenAccount,
+ ship,
+ shipEscrowTokenAccount,
+ starbasePlayer.key,
+ starbase.key,
+ game.key,
+ game.data.gameState,
+ {
+ shipAmount,
+ index: escrowIndex,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/add-ship-to-fleet.ts b/src/main/basedbot/lib/sage/ix/add-ship-to-fleet.ts
new file mode 100644
index 00000000..bf2cbedc
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/add-ship-to-fleet.ts
@@ -0,0 +1,36 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const addShipToFleetIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ programs: StarAtlasPrograms,
+ fleet: PublicKey,
+ ship: PublicKey,
+ shipAmount: number,
+ shipEscrowIndex: number,
+): InstructionReturn =>
+ Fleet.addShipToFleet(
+ programs.sage,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ fleet,
+ ship,
+ starbasePlayer.key,
+ starbase.key,
+ game.key,
+ game.data.gameState,
+ {
+ shipAmount,
+ shipEscrowIndex,
+ keyIndex: 0,
+ fleetShipInfoIndex: null,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/close-disbanded-fleet.ts b/src/main/basedbot/lib/sage/ix/close-disbanded-fleet.ts
new file mode 100644
index 00000000..520a3778
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/close-disbanded-fleet.ts
@@ -0,0 +1,22 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { DisbandedFleet } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const closeDisbandedFleetIx = (
+ player: Player,
+ programs: StarAtlasPrograms,
+ disbandedFleetKey: PublicKey,
+ fleetShipsKey: PublicKey,
+): InstructionReturn =>
+ DisbandedFleet.closeDisbandedFleet(
+ programs.sage,
+ player.signer,
+ player.profile.key,
+ 'funder',
+ disbandedFleetKey,
+ fleetShipsKey,
+ { keyIndex: 0 },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/create-fleet.ts b/src/main/basedbot/lib/sage/ix/create-fleet.ts
new file mode 100644
index 00000000..fa6c62d9
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/create-fleet.ts
@@ -0,0 +1,47 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn, stringToByteArray } from '@staratlas/data-source'
+import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+type CreateFleetReturn = {
+ fleetKey: [PublicKey, number]
+ cargoHoldKey: [PublicKey, number]
+ fuelTankKey: [PublicKey, number]
+ ammoBankKey: [PublicKey, number]
+ instructions: InstructionReturn
+}
+
+export const createFleetIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ programs: StarAtlasPrograms,
+ ship: PublicKey,
+ cargoStatsDefinition: PublicKey,
+ shipAmount: number,
+ name: string,
+ shipEscrowIndex: number,
+ // eslint-disable-next-line max-params
+): CreateFleetReturn =>
+ Fleet.createFleet(
+ programs.sage,
+ programs.cargo,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ ship,
+ starbasePlayer.key,
+ starbase.key,
+ game.key,
+ game.data.gameState,
+ cargoStatsDefinition,
+ {
+ shipAmount,
+ fleetLabel: stringToByteArray(name, 32),
+ shipEscrowIndex,
+ keyIndex: 0,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/disband-fleet.ts b/src/main/basedbot/lib/sage/ix/disband-fleet.ts
new file mode 100644
index 00000000..61762bda
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/disband-fleet.ts
@@ -0,0 +1,36 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+type DisbandFleetReturn = {
+ disbandedFleetKey: [PublicKey, number]
+ instructions: InstructionReturn
+}
+
+export const disbandFleetIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ programs: StarAtlasPrograms,
+ fleet: Fleet,
+ // eslint-disable-next-line max-params
+): DisbandFleetReturn =>
+ Fleet.disbandFleet(
+ programs.sage,
+ programs.cargo,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ fleet,
+ starbasePlayer.key,
+ starbase.key,
+ game.key,
+ game.data.gameState,
+ {
+ keyIndex: 0,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/disbanded-fleet-to-escrow.ts b/src/main/basedbot/lib/sage/ix/disbanded-fleet-to-escrow.ts
new file mode 100644
index 00000000..e006023f
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/disbanded-fleet-to-escrow.ts
@@ -0,0 +1,39 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { DisbandedFleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const disbandedFleetToEscrowIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ programs: StarAtlasPrograms,
+ shipEscrowIndex: number | null,
+ disbandedFleet: PublicKey,
+ fleetShips: PublicKey,
+ shipKey: PublicKey,
+ shipAmount: BN,
+): InstructionReturn =>
+ DisbandedFleet.disbandedFleetToEscrow(
+ programs.sage,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ disbandedFleet,
+ fleetShips,
+ shipKey,
+ starbasePlayer.key,
+ starbase.key,
+ game.key,
+ game.data.gameState,
+ {
+ fleetShipInfoIndex: 0,
+ keyIndex: 0,
+ shipAmount: shipAmount.toNumber(),
+ shipEscrowIndex,
+ },
+ )
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..4d327a5d
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/dock.ts
@@ -0,0 +1,27 @@
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ 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,
+ game.key,
+ 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..b65ea741
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/exit-respawn.ts
@@ -0,0 +1,32 @@
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ 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,
+ game.key,
+ 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..c7678036
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/fleet-state-handler.ts
@@ -0,0 +1,45 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ game: Game,
+ // 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, game, mineable.mineItem.data.mint).key,
+ game.data.cargo.statsDefinition,
+ game.data.gameState,
+ game.key,
+ foodTokenFrom,
+ ammoTokenFrom,
+ resourceTokenFrom,
+ resourceTokenTo,
+ game.data.mints.food,
+ game.data.mints.ammo,
+ )
diff --git a/src/main/basedbot/lib/sage/ix/force-drop-fleet-cargo.ts b/src/main/basedbot/lib/sage/ix/force-drop-fleet-cargo.ts
new file mode 100644
index 00000000..8f8e46e5
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/force-drop-fleet-cargo.ts
@@ -0,0 +1,29 @@
+import { PublicKey } from '@solana/web3.js'
+import { CargoStatsDefinition } from '@staratlas/cargo'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { FleetInfo } from '../state/user-fleets'
+
+export const forceDropFleetCargoIx = (
+ fleetInfo: FleetInfo,
+ game: Game,
+ cargoStatsDefinition: CargoStatsDefinition,
+ cargoPod: PublicKey,
+ cargoType: PublicKey,
+ tokenFrom: PublicKey,
+ tokenMint: PublicKey,
+ programs: StarAtlasPrograms,
+): InstructionReturn =>
+ Fleet.forceDropFleetCargo(
+ programs.sage,
+ programs.cargo,
+ fleetInfo.fleet.key,
+ cargoPod,
+ cargoType,
+ cargoStatsDefinition.key,
+ game.key,
+ tokenFrom,
+ tokenMint,
+ )
diff --git a/src/main/basedbot/lib/sage/ix/idle-to-respawn.ts b/src/main/basedbot/lib/sage/ix/idle-to-respawn.ts
new file mode 100644
index 00000000..2bfafea1
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/idle-to-respawn.ts
@@ -0,0 +1,26 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const idleToRespawnIx = (
+ player: Player,
+ game: Game,
+ fleet: Fleet,
+ atlasTokenFrom: PublicKey,
+ programs: StarAtlasPrograms,
+): InstructionReturn =>
+ Fleet.idleToRespawn(
+ programs.sage,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ fleet.key,
+ atlasTokenFrom,
+ game.data.vaults.atlas,
+ game.data.gameState,
+ game.key,
+ { keyIndex: 0 },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/import-cargo.ts b/src/main/basedbot/lib/sage/ix/import-cargo.ts
new file mode 100644
index 00000000..606f6cac
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/import-cargo.ts
@@ -0,0 +1,41 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Game, Starbase, StarbasePlayer } from '@staratlas/sage'
+import BN from 'bn.js'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+
+export const depositCargoIx = (
+ player: Player,
+ game: Game,
+ starbase: Starbase,
+ starbasePlayer: StarbasePlayer,
+ cargoPodTo: PublicKey,
+ tokenFrom: PublicKey,
+ tokenTo: PublicKey,
+ cargoType: PublicKey,
+ programs: StarAtlasPrograms,
+ amount: BN,
+ // eslint-disable-next-line max-params
+): InstructionReturn =>
+ StarbasePlayer.depositCargoToGame(
+ programs.sage,
+ programs.cargo,
+ starbasePlayer.key,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ starbase.key,
+ cargoPodTo,
+ cargoType,
+ game.data.cargo.statsDefinition,
+ tokenFrom,
+ tokenTo,
+ game.key,
+ game.data.gameState,
+ {
+ amount,
+ keyIndex: 0,
+ },
+ )
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..24f64fed
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/load-cargo.ts
@@ -0,0 +1,49 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ 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,
+ game.data.cargo.statsDefinition,
+ tokenFrom,
+ tokenTo,
+ tokenMint,
+ game.key,
+ game.data.gameState,
+ {
+ amount,
+ keyIndex: 0,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ix/movement-subwarp-handler.ts b/src/main/basedbot/lib/sage/ix/movement-subwarp-handler.ts
new file mode 100644
index 00000000..760d4326
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/movement-subwarp-handler.ts
@@ -0,0 +1,34 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } from '@staratlas/sage'
+
+import { StarAtlasPrograms } from '../../programs'
+import { Player } from '../state/user-account'
+import { FleetInfo } from '../state/user-fleets'
+
+export const movementSubwarpHandlerIx = (
+ fleetInfo: FleetInfo,
+ player: Player,
+ game: Game,
+ fuelTokenAccount: PublicKey,
+ programs: StarAtlasPrograms,
+): InstructionReturn =>
+ Fleet.movementSubwarpHandler(
+ programs.sage,
+ programs.cargo,
+ programs.points,
+ player.profile.key,
+ fleetInfo.fleet.key,
+ fleetInfo.fleet.data.fuelTank,
+ player.fuelCargoType.key,
+ game.data.cargo.statsDefinition,
+ fuelTokenAccount,
+ 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,
+ game.key,
+ )
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..a3d064bf
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/start-mining.ts
@@ -0,0 +1,37 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ mineable: Mineable,
+ starbasePlayer: StarbasePlayer,
+ fuelTokenAccount: PublicKey,
+ 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,
+ game.data.gameState,
+ game.key,
+ 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..5efb726c
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/stop-mining.ts
@@ -0,0 +1,49 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ game: Game,
+ mineable: Mineable,
+ fuelTokenAccount: PublicKey,
+ 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,
+ 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,
+ game.data.gameState,
+ game.key,
+ fuelTokenAccount,
+ 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..280a7bd5
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/stop-subwarp.ts
@@ -0,0 +1,43 @@
+import { PublicKey } from '@solana/web3.js'
+import { CargoStatsDefinition } from '@staratlas/cargo'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ game: Game,
+ cargoStatsDefinition: CargoStatsDefinition,
+ fuelTokenAccount: PublicKey,
+ fuelTokenMint: PublicKey,
+ programs: StarAtlasPrograms,
+): InstructionReturn =>
+ Fleet.stopSubwarp(
+ programs.sage,
+ programs.cargo,
+ programs.points,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ fleetInfo.fleet.key,
+ fleetInfo.fleet.data.fuelTank,
+ player.fuelCargoType.key,
+ cargoStatsDefinition.key,
+ fuelTokenAccount,
+ fuelTokenMint,
+ player.xpAccounts.piloting.userPointsAccount,
+ player.xpAccounts.piloting.pointsCategory,
+ player.xpAccounts.piloting.pointsModifierAccount,
+ player.xpAccounts.councilRank.userPointsAccount,
+ player.xpAccounts.councilRank.pointsCategory,
+ player.xpAccounts.councilRank.pointsModifierAccount,
+ game.key,
+ game.data.gameState,
+ {
+ keyIndex: 0,
+ },
+ )
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..865d9bcc
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/stop-warp.ts
@@ -0,0 +1,28 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ game: Game,
+ _fuelTokenAccount: PublicKey,
+ 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,
+ 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..073e4979
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/subwarp.ts
@@ -0,0 +1,30 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ _fuelTokenAccount: PublicKey,
+ player: Player,
+ game: Game,
+ programs: StarAtlasPrograms,
+): InstructionReturn =>
+ Fleet.startSubwarp(
+ programs.sage,
+ player.signer,
+ player.profile.key,
+ player.profileFaction.key,
+ fleetInfo.fleet.key,
+ game.key,
+ 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..2ef715bd
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/undock.ts
@@ -0,0 +1,27 @@
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ 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,
+ game.key,
+ 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..efb30b2c
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/unload-cargo.ts
@@ -0,0 +1,49 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game, 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,
+ game: Game,
+ 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,
+ game.data.cargo.statsDefinition,
+ tokenFrom,
+ tokenTo,
+ tokenMint,
+ game.key,
+ 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..8e828ba2
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ix/warp.ts
@@ -0,0 +1,36 @@
+import { PublicKey } from '@solana/web3.js'
+import { InstructionReturn } from '@staratlas/data-source'
+import { Fleet, Game } 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,
+ fuelTokenAccount: PublicKey,
+ player: Player,
+ game: Game,
+ 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,
+ game.data.cargo.statsDefinition,
+ fuelTokenAccount,
+ game.data.mints.fuel,
+ game.data.gameState,
+ game.key,
+ programs.cargo,
+ {
+ toSector: coordinates.toArray(),
+ keyIndex: player.keyIndex,
+ },
+ )
diff --git a/src/main/basedbot/lib/sage/ships.ts b/src/main/basedbot/lib/sage/ships.ts
new file mode 100644
index 00000000..a98a7a81
--- /dev/null
+++ b/src/main/basedbot/lib/sage/ships.ts
@@ -0,0 +1,354 @@
+import { PublicKey } from '@solana/web3.js'
+
+/* eslint-disable @typescript-eslint/naming-convention */
+export enum VZUS {
+ Opod = 'VZUSOP',
+ Ambwe = 'VZUSAM',
+ Ballad = 'VZUSBA',
+ Solos = 'VZUSSO',
+}
+
+export enum Pearce {
+ X5 = 'PX5',
+ F4 = 'PF4',
+ R8 = 'PR8',
+ C11 = 'PC11',
+ X6 = 'PX6',
+ X4 = 'PX4',
+ C9 = 'PC9',
+ R6 = 'PR6',
+ D9 = 'PD9',
+ T1 = 'T1TAN',
+}
+
+export enum Ogrika {
+ Thripid = 'OGKATP',
+ JodAsteris = 'OGKAJA',
+ Mik = 'OGKAMK',
+ Tursik = 'OGKATU',
+ Sunpaa = 'OGKASP',
+ Niruch = 'OGKANR',
+ Ruch = 'OGKARU',
+}
+
+export enum Opal {
+ JetJet = 'OPALJJ',
+ Jet = 'OPALJ',
+ Bitboat = 'OPALBB',
+ RayFam = 'OPALRF',
+}
+
+export enum Fimbul {
+ Airbike = 'FBLAIR',
+ Lowbie = 'FBLLOW',
+ Mamba = 'FBLMAM',
+ MambaEX = 'FBLMEX',
+ Sledbarge = 'FBLSLE',
+}
+
+export enum FimbulECOS {
+ Greenader = 'FBLEGR',
+ TreeArrow = 'FBLETR',
+ Bombarella = 'FBLEBO',
+ Unibomba = 'FBLEUN',
+ Superphoenix = 'SUPER',
+}
+
+export enum FimbulBYOS {
+ Earp = 'FBLBEA',
+ Packlite = 'FBLBPL',
+ Tankship = 'FBLBTA',
+ Butch = 'FBLBBU',
+}
+
+export enum Rainbow {
+ Chi = 'CHI',
+ Om = 'OM',
+ Arc = 'ARC',
+}
+
+export enum Calico {
+ Evac = 'CALEV',
+ Guardian = 'CALG',
+ CompaktHero = 'CALCH',
+ Medtech = 'CALMED',
+ AtsEnforcer = 'CALATS',
+ Scud = 'CALSCD',
+ Maxhog = 'CALMAX',
+}
+
+export enum Tufa {
+ Feist = 'TUFAFE',
+}
+
+export enum Busan {
+ ThrillOfLife = 'THRILL',
+ MaidenHeart = 'HEART',
+ TheLastStand = 'STAND',
+}
+
+export enum Armstrong {
+ Tip = 'IMP1',
+ Tap = 'IMP2',
+ IMP = 'IMP3',
+}
+
+export enum DevShip {
+ Test = 'Test',
+}
+
+export type ShipSymbol =
+ | VZUS
+ | Pearce
+ | Ogrika
+ | Opal
+ | Fimbul
+ | FimbulECOS
+ | FimbulBYOS
+ | Rainbow
+ | Calico
+ | Tufa
+ | Busan
+ | Armstrong
+ | DevShip
+
+interface ShipData {
+ name: string
+ mint: PublicKey
+}
+
+interface ShipMap {
+ [key: string]: ShipData
+}
+
+export const ships: ShipMap = {
+ [DevShip.Test]: {
+ name: 'A Test Ship',
+ mint: new PublicKey('testyj8KfG3VhmvyXALbQ9riiEG9UbWZ7xKQmkDwJDA'),
+ },
+ [Pearce.X4]: {
+ name: 'Pearce X4',
+ mint: new PublicKey('CWvsu7kmgCDhi1bWBqA9N4NKkhpDV6heTb5T3we54zrt'),
+ },
+ [Opal.JetJet]: {
+ name: 'Opal Jetjet',
+ mint: new PublicKey('5o635z1vCTUxQF5qVgsbSVB3EKMThGA1Y6giyrhTpLJj'),
+ },
+ [Rainbow.Om]: {
+ name: 'Rainbow Om',
+ mint: new PublicKey('7feVdv745gGxZYKb5JEe84EGCJ6nGyMYtPng2wQRfrdh'),
+ },
+ [Tufa.Feist]: {
+ name: 'Tufa Feist',
+ mint: new PublicKey('2K7rLcoTsx2pS5KV1LW6wmRdu7GKxUn9azx6iUXY87fS'),
+ },
+ [FimbulECOS.Greenader]: {
+ name: 'Fimbul ECOS Greenader',
+ mint: new PublicKey('2bkoL87tqVzgwGcRjqm5vk3RGjd5Mpay81dx88huU4e5'),
+ },
+ [FimbulECOS.Bombarella]: {
+ name: 'Fimbul ECOS Bombarella',
+ mint: new PublicKey('32K2GbxgmMWAvdztdY96NYD7eucpk95rbK2wVgh2vFRo'),
+ },
+ [FimbulBYOS.Tankship]: {
+ name: 'Fimbul BYOS Tankship',
+ mint: new PublicKey('7pW9NHhuHCP2rBhwE23B6v5bwrfmvGmgg1LgPSVpTxZv'),
+ },
+ [FimbulBYOS.Butch]: {
+ name: 'Fimbul BYOS Butch',
+ mint: new PublicKey('BBUPJXjgwBhHXrsQoLw4oCk2DrVR8sFYjCo7MjNjH2NE'),
+ },
+ [Fimbul.Mamba]: {
+ name: 'Fimbul Mamba',
+ mint: new PublicKey('En3uuAwxMFFKH382Xkr24LVKjs8e25NZXUV3FBkjnmuX'),
+ },
+ [Pearce.X5]: {
+ name: 'Pearce X5',
+ mint: new PublicKey('7CBrGuHSpRvLgQMwA2Vbe9ra9nsg2K9FUDH3TpMmjFdA'),
+ },
+ [Ogrika.Thripid]: {
+ name: 'Ogrika Thripid',
+ mint: new PublicKey('8Tt2ec8HKmwqr1wxKVwizmcvdJQos2B42faDyyinjFti'),
+ },
+ [Ogrika.Sunpaa]: {
+ name: 'Ogrika Sunpaa',
+ mint: new PublicKey('8mQpeQjw8zqxFDKV3iHdkec7DMh4uJD52kHESitKXs2f'),
+ },
+ [VZUS.Opod]: {
+ name: 'VZUS Opod',
+ mint: new PublicKey('4YRhx9VQR4TvscsAcvUjJZPjiT6vAjAQDrDo1pJR5RVF'),
+ },
+ [FimbulBYOS.Packlite]: {
+ name: 'Fimbul BYOS Packlite',
+ mint: new PublicKey('8BA19uTEFGPLHYHaGhJBd3y4rJtZgsA37HFDoPZ9wgzv'),
+ },
+ [Calico.Guardian]: {
+ name: 'Calico Guardian',
+ mint: new PublicKey('EyUcVag89Lx7rpJLZctPYwHiSJpk8TM411bqgpeKcS5n'),
+ },
+ [Calico.CompaktHero]: {
+ name: 'Calico Compakt Hero',
+ mint: new PublicKey('9jTjPB8rGNEBC5HzM49ZfqN1F8xQLUeuRVNeh3DFGHbh'),
+ },
+ [Opal.Jet]: {
+ name: 'Opal Jet',
+ mint: new PublicKey('5tyJwmNjzHusouZxsaMjSkLna9VeeJkg2FRaDXLRFQSk'),
+ },
+ [Pearce.C11]: {
+ name: 'Pearce C11',
+ mint: new PublicKey('6whMv9uPMG3mPXcCgDaqFg6TaN4KZM1m3BmjGJXJRvco'),
+ },
+ [Pearce.F4]: {
+ name: 'Pearce F4',
+ mint: new PublicKey('HUxaWGNGgZSEVpB2Epgx2zYHgi7KxP8atApkmdnsng2s'),
+ },
+ [VZUS.Ambwe]: {
+ name: 'VZUS Ambwe',
+ mint: new PublicKey('Dwf5wFNSuwtKC7xtvjGMpXPbRGqAf6WNmgKxXSNSqtaz'),
+ },
+ [Pearce.X6]: {
+ name: 'Pearce X6',
+ mint: new PublicKey('B8XeKhrKTKQTPePk87u6tA4KFd8oHzu1VqvbAuaDvLNv'),
+ },
+ [Pearce.R8]: {
+ name: 'Pearce R8',
+ mint: new PublicKey('9RGak4MwF9hH72ma42yuytDZsusEQQY57h9Ud9Zao1wY'),
+ },
+ [Pearce.C9]: {
+ name: 'Pearce C9',
+ mint: new PublicKey('G3NxTo8zMGMmDiADrWwpRdKz7Kbo2o5LgjCm4tKt2gxR'),
+ },
+ [Ogrika.JodAsteris]: {
+ name: 'Ogrika Jod Asteris',
+ mint: new PublicKey('6dUPLNJ6M1oorUyoH4QvHAdTQp4kViVwCNLUvnS9EHyN'),
+ },
+ [FimbulECOS.TreeArrow]: {
+ name: 'Fimbul ECOS Treearrow',
+ mint: new PublicKey('rcm1idAXHDYtcjtm56Q1HgFfJ2fnH9rUzEQg1WC74Ar'),
+ },
+ [Rainbow.Chi]: {
+ name: 'Rainbow Chi',
+ mint: new PublicKey('Ag97kh8ZNQp9aEB4ETX9kDsXDnBzrjXQccH42xdRnWkP'),
+ },
+ [FimbulBYOS.Earp]: {
+ name: 'Fimbul BYOS Earp',
+ mint: new PublicKey('Da6xRFv8W3JhQzmzYYACbxiADwdr3yDcwrBqo4WgjLBW'),
+ },
+ [Calico.Evac]: {
+ name: 'Calico Evac',
+ mint: new PublicKey('yMr4mABoBhzV948WzFuVDZPMdzJe8uL42v3RE5cdFqm'),
+ },
+ [Fimbul.Airbike]: {
+ name: 'Fimbul Airbike',
+ mint: new PublicKey('6N7zoDykqkbf4354jhHb6oD7zxqv6BZNtE77MyX99CUQ'),
+ },
+ [Ogrika.Mik]: {
+ name: 'Ogrika Mik',
+ mint: new PublicKey('HLAKintjFoH6dSwVDVV2a99tUV2r6CVR7hpKpaWUB6PU'),
+ },
+ [FimbulECOS.Unibomba]: {
+ name: 'Fimbul ECOS Unibomba',
+ mint: new PublicKey('5Uu2jCmS9mVBC7RyR9eMVDp79C9cocq6R31yCEYXt9zG'),
+ },
+ [Rainbow.Arc]: {
+ name: 'Rainbow Arc',
+ mint: new PublicKey('7BAvPzwjf6Y8hZpaweT4dobBT5nEy95HJJci3P6E3C8Y'),
+ },
+ [Fimbul.Lowbie]: {
+ name: 'Fimbul Lowbie',
+ mint: new PublicKey('68A55jmwJaiDULknycr7kRNi11XNyKSgotK6pVzJsSLe'),
+ },
+ [VZUS.Ballad]: {
+ name: 'VZUS Ballad',
+ mint: new PublicKey('9TTFMGM32UHvuoKnrFL5ECbG7vcYnwyhbT4gJAy56LNA'),
+ },
+ [Busan.ThrillOfLife]: {
+ name: 'Busan Thrill of Life',
+ mint: new PublicKey('ETadnNjotJA759Dn3SYa2ZN9TB6NF6hndF6ciajZVA4Y'),
+ },
+ [Opal.Bitboat]: {
+ name: 'Opal Bitboat',
+ mint: new PublicKey('Dni9ZeMpsUY825XvBdTLdjGLKCXuj2z1JYE769yrnu4L'),
+ },
+ [Calico.Medtech]: {
+ name: 'Calico Medtech',
+ mint: new PublicKey('EVyhcpWDbjEqUUadpv22WNxUx2z5kHwiYwoTvKiDhsVH'),
+ },
+ [Calico.AtsEnforcer]: {
+ name: 'Calico ATS Enforcer',
+ mint: new PublicKey('GAANgsxxkPMQ5rwP8qbXh3HifjFkBYM7MVciAsNBeL2S'),
+ },
+ [Pearce.R6]: {
+ name: 'Pearce R6',
+ mint: new PublicKey('427BaYEFGERXin2fxuRFGyUDzMoZYA9KTr9w7jZqRKqk'),
+ },
+ [Busan.MaidenHeart]: {
+ name: 'Busan Maiden Heart',
+ mint: new PublicKey('BA4SZjCZoPzPJb9oa7W8eFJbYDpYtD54ZAKPxE3GTWXM'),
+ },
+ [Ogrika.Tursik]: {
+ name: 'Ogrika Tursic',
+ mint: new PublicKey('EQYL2jczThWKRnydEExa4aYCKkXG7gAq3g8d7x85TQCR'),
+ },
+ [Pearce.D9]: {
+ name: 'Pearce D9',
+ mint: new PublicKey('6sPspaPk1UJDRPAASWhnwc8sghToVXBZAXskmHZG8M5v'),
+ },
+ [Pearce.T1]: {
+ name: 'Pearce T1',
+ mint: new PublicKey('HQayhtSnytPSTpb3LCJrxkECTDbxJZH1LKwPk9jRKRLf'),
+ },
+ [Busan.TheLastStand]: {
+ name: 'Busan The Last Stand mk. VIII',
+ mint: new PublicKey('9tGU2Mvtvvr2n7Fjmw3zbsdr5YrfGbLtPxR31bi5hTA4'),
+ },
+ [FimbulECOS.Superphoenix]: {
+ name: 'Fimbul ECOS Superphoenix',
+ mint: new PublicKey('BXtMBhvfeskw2YQAa4uB5WTBWZBhFn8imwQ24HHzjrPG'),
+ },
+ [Ogrika.Niruch]: {
+ name: 'Ogrika Niruch',
+ mint: new PublicKey('A94GocS3UK23zKVuwRqgcuyEbpuaNuWB5dK8fsiw6VJG'),
+ },
+ [Ogrika.Ruch]: {
+ name: 'Ogrika Ruch',
+ mint: new PublicKey('ruCHVupnE23CQzJ47STdKqLU2jVdFQ9FHEXj7Zvw8hJ'),
+ },
+ [Calico.Scud]: {
+ name: 'Calico Scud',
+ mint: new PublicKey('29uLEnHrLnuAJgrDsM66x4uhzwSi6w7bTsYjTGSf1KRk'),
+ },
+ [Calico.Maxhog]: {
+ name: 'Calico Maxhog',
+ mint: new PublicKey('6NeyGNP8tKX6V629kQNCa7Xxu8VKQRbGkMfy5477eKRs'),
+ },
+ [VZUS.Solos]: {
+ name: 'VZUS Solos',
+ mint: new PublicKey('DXRALgzwdJbKHatQLEyK59tq2sBTuvHPxmKamcrDnKSx'),
+ },
+ [Opal.RayFam]: {
+ name: 'Opal Rayfam',
+ mint: new PublicKey('6xS4TduL4hBsPS2ko21ynFqHf3BSAG5yq8MmV8sQoojU'),
+ },
+ [Fimbul.MambaEX]: {
+ name: 'Fimbul Mamba EX',
+ mint: new PublicKey('ErMXUtK3jiNMkxBh3Wd1pxXpNREdjp5w8LyofBp5PAvx'),
+ },
+ [Fimbul.Sledbarge]: {
+ name: 'Fimbul Sledbarge',
+ mint: new PublicKey('SLEDCrZ1tA6H21ez1uhxmK1jVYCyzCvDAUEbJxGm9qe'),
+ },
+ [Armstrong.Tip]: {
+ name: 'Armstrong IMP Tip',
+ mint: new PublicKey('1MP1J8CN5d1roJjsktiUbcTmHAeDYakfQ7eSRDLYmbG'),
+ },
+ [Armstrong.Tap]: {
+ name: 'Armstrong IMP Tap',
+ mint: new PublicKey('1MP2S5WNNFzf1MRLacPdAf8TVoebNtuuEm15xWmnpa7'),
+ },
+ [Armstrong.IMP]: {
+ name: 'Armstrong IMP',
+ mint: new PublicKey('1MP3RpP21TVoEWVnvNuG1piq32AnWtsxzd2fykS8yJe'),
+ },
+}
+/* eslint-enable @typescript-eslint/naming-convention */
diff --git a/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts b/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts
new file mode 100644
index 00000000..d70c426d
--- /dev/null
+++ b/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts
@@ -0,0 +1,19 @@
+import { CargoStatsDefinition } from '@staratlas/cargo'
+import { readAllFromRPC } from '@staratlas/data-source'
+
+import { connection } from '../../../../../service/sol'
+import { programs } from '../../programs'
+
+export const getCargoStatsDefinition = async (): Promise<
+ Array
+> => {
+ const cargoTypesAccountData = await readAllFromRPC(
+ connection,
+ programs.cargo,
+ CargoStatsDefinition,
+ )
+
+ return cargoTypesAccountData
+ .filter((f) => f.type === 'ok' && 'data' in f)
+ .map((f) => (f as any).data)
+}
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..9c2d8722
--- /dev/null
+++ b/src/main/basedbot/lib/sage/state/cargo-types.ts
@@ -0,0 +1,37 @@
+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..bb95632d
--- /dev/null
+++ b/src/main/basedbot/lib/sage/state/fleet-cargo.ts
@@ -0,0 +1,136 @@
+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 { sageGame } from './game'
+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[] = []
+
+ 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