diff --git a/.github/actions/docker-buildx/action.yml b/.github/actions/docker-buildx/action.yml index fd088610..c545be33 100644 --- a/.github/actions/docker-buildx/action.yml +++ b/.github/actions/docker-buildx/action.yml @@ -40,6 +40,9 @@ inputs: cache-strategy: default: 'registry' description: 'Cache strategy: gha or registry' + npm-token: + default: '' + description: 'NPM token for private registry access during build' runs: using: "composite" steps: @@ -77,4 +80,6 @@ runs: cache-to: ${{ inputs.cache-enabled == 'true' && (inputs.cache-strategy == 'gha' && 'type=gha,mode=max' || format('type=registry,ref={0}/{1}:cache,mode=max', inputs.repository, inputs.app-name)) || '' }} build-args: ${{ inputs.build-args }} ssh: ${{ inputs.ssh-enabled == 'true' && 'default' || '' }} - secrets: ${{ inputs.extra-secrets }} + secrets: | + ${{ inputs.extra-secrets }} + ${{ inputs.npm-token != '' && format('npmrc=//registry.npmjs.org/:_authToken={0}', inputs.npm-token) || '' }} diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 852f36c0..a753e084 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -1,6 +1,12 @@ name: 'Install cached modules' description: 'Run pnpm install with cached modules' +inputs: + npm-token: + description: 'NPM token for private registry' + required: false + default: '' + runs: using: 'composite' steps: @@ -28,6 +34,12 @@ runs: restore-keys: | pnpm-store- + - name: Configure npm auth + shell: bash + if: ${{ inputs.npm-token != '' }} + run: | + echo "//registry.npmjs.org/:_authToken=${{ inputs.npm-token }}" > ~/.npmrc + - name: Install dependencies shell: bash run: pnpm install --frozen-lockfile diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 85b6da53..84ef7ec3 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -12,6 +12,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install + with: + npm-token: ${{ secrets.NPM_TOKEN }} - run: pnpm run build lint: @@ -19,6 +21,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install + with: + npm-token: ${{ secrets.NPM_TOKEN }} - run: pnpm run lint release: @@ -26,6 +30,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/install + with: + npm-token: ${{ secrets.NPM_TOKEN }} - run: pnpm run build - run: pnpm install --frozen-lockfile --ignore-scripts --prod - uses: ./.github/actions/docker-buildx @@ -36,4 +42,5 @@ jobs: dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }} dockerhub-password: ${{ secrets.DOCKERHUB_TOKEN }} dockerfile: 'docker/Dockerfile' + npm-token: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 058bac83..ac97cc2d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,10 @@ /node_modules .env* !.env.example +.npmrc *.iml dist/ src/main/basedbot/lib/sage/ships.json package-lock.json +contrib/star-atlas-tech/ diff --git a/contrib/solana-dev-skill b/contrib/solana-dev-skill new file mode 160000 index 00000000..dcc633a3 --- /dev/null +++ b/contrib/solana-dev-skill @@ -0,0 +1 @@ +Subproject commit dcc633a384c033bb1ee20c26325caec6c611ecae diff --git a/docker/Dockerfile b/docker/Dockerfile index 83608ff6..4146daf7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -37,7 +37,6 @@ ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.s COPY --from=builder /app/node_modules /app/node_modules COPY --from=builder /app/dist/airdrop/index.js /app/airdrop.js -COPY --from=builder /app/dist/fleetbot/index.js /app/fleetbot.js COPY --from=builder /app/dist/basedbot/index.js /app/basedbot.js COPY --from=builder /app/dist/migrate/index.js /app/migrate.js COPY src/db/db-data-source.ts /app/db/db-data-source.ts diff --git a/docker/bin/fleetbot.sh b/docker/bin/fleetbot.sh deleted file mode 100755 index 2e60bb78..00000000 --- a/docker/bin/fleetbot.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -migrate-db.sh - -/app/fleetbot.js diff --git a/docs/C4_MIGRATION.md b/docs/C4_MIGRATION.md new file mode 100644 index 00000000..406eb106 --- /dev/null +++ b/docs/C4_MIGRATION.md @@ -0,0 +1,217 @@ +# Fleetbot C4 Migration Plan + +**Strategy:** Big-bang rewrite (no compat layer) + +**Scope:** Core automation only (mining, transport, docking) + +**Testing:** C4 devnet available + +## Progress Tracker + +- [x] **M1.1:** Update dependencies - add Kit/dev-sage, remove web3.js/anchor +- [x] **M1.2:** Rewrite RPC/Connection layer with solana-kite +- [x] **M1.3:** Rewrite transaction building/sending with Kit (incl priority fees) +- [x] **M1.4:** Migrate wallet/signer to KeyPairSigner +- [ ] **M2.1:** Migrate account types and discriminator-based decoding +- [ ] **M2.2:** Refactor Player context with Character account +- [ ] **M2.3:** Update Game/StarSystem/CelestialBody state structures +- [ ] **M2.4:** Update Fleet state machine (__kind discriminators) +- [x] **M3.1:** Rewrite dock/undock actions +- [x] **M3.2:** Rewrite warp/subwarp movement actions +- [x] **M3.3:** Rewrite mining start/stop actions +- [x] **M3.4:** Rewrite cargo load/unload actions +- [ ] **M4.1:** Update FSM strategies for C4 state patterns +- [ ] **M4.2:** Update mining configs for C4 resource IDs +- [ ] **M5:** Devnet integration testing + +## Current vs Target Stack + +| Layer | Current | Target | +|-------|---------|--------| +| Solana SDK | `@solana/web3.js` v1.x | `@solana/kit` + `solana-kite` | +| Program SDK | `@coral-xyz/anchor` v0.29 | Direct instruction fns (no Anchor) | +| Star Atlas | `@staratlas/sage` + `data-source` | `@staratlas/dev-sage` v0.38+ | +| Types | `PublicKey`, `Keypair`, `Connection` | `Address`, `KeyPairSigner`, `Connection` (kite) | +| Instructions | `Fleet.warpToCoordinate(...)` | `getWarpToCoordinateInstruction({...})` | + +--- + +## Milestone 1: Solana Foundation Layer + +Complete rewrite of core Solana infrastructure. + +### 1.1 Dependencies + +Remove legacy, add Kit stack: + +```diff +- "@solana/web3.js": "^1.98.2" +- "@coral-xyz/anchor": "^0.29.0" +- "@staratlas/sage": "^1.8.10" +- "@staratlas/cargo": "^1.1.0" +- "@staratlas/data-source": "^0.9.0" +- "@staratlas/player-profile": "^0.11.0" +- "@staratlas/profile-faction": "^0.6.0" ++ "@solana/kit": "^2.x" ++ "solana-kite": "^x.x" ++ "@staratlas/dev-sage": "^0.38.0" ++ "@staratlas/dev-player-profile": "^0.38.0" ++ "@staratlas/dev-profile-faction": "^0.38.0" +``` + +### 1.2 RPC/Connection Layer + +Rewrite `src/service/sol/const/connection.ts`: + +```typescript +// Target pattern from fc-app +import { connect } from 'solana-kite' +export const connection = connect(config.sol.rpcEndpoint, config.sol.wsEndpoint) +``` + +### 1.3 Transaction Sending + +Rewrite `src/service/sol/send-and-confirm-tx.ts` using Kit: + +- `pipe()`, `createTransactionMessage()`, `setTransactionMessageFeePayerSigner()` +- `signTransactionMessageWithSigners()`, `sendAndConfirmTransactionFactory()` +- Migrate priority fee logic to Kit (remove Anchor dependency) + +### 1.4 Wallet/Signer + +Rewrite `src/service/wallet/init-keypair.ts`: + +- `Keypair` to `KeyPairSigner` via `createKeyPairSignerFromBytes()` + +--- + +## Milestone 2: Account State Layer + +Rewrite account types and state management for C4. + +### 2.1 Account Decoding + +Delete `src/main/basedbot/lib/programs.ts` (no Anchor). + +Create discriminator-based account fetching (pattern from fc-app): + +```typescript +import { FLEET_DISCRIMINATOR, getFleetDecoder } from '@staratlas/dev-sage' +``` + +### 2.2 Player Context + +Rewrite `src/main/basedbot/lib/sage/state/user-account.ts`: + +- `PlayerProfile` -> `Profile` (dev-player-profile) +- Add `Character` account (new in C4, required for all actions) +- `AsyncSigner` -> `KeyPairSigner` + +### 2.3 Game/World State + +Rewrite `src/main/basedbot/lib/sage/state/game.ts`: + +- `Game` struct layout differs +- `StarSystem` replaces sector concepts +- `CelestialBody` for asteroid references +- `RegionTracker` required for movement/mining + +### 2.4 Fleet State Machine + +Rewrite `src/main/basedbot/lib/fleet-state/`: + +```typescript +// Current: fleet.state.MoveWarp +// Target: fleet.state.__kind === 'MoveWarp' +switch (fleet.state.__kind) { + case 'Idle': ... + case 'Docked': ... + case 'MoveWarp': ... + case 'MoveSubwarp': ... + case 'MineAsteroid': ... +} +``` + +--- + +## Milestone 3: Action Layer + +Rewrite fleet actions. Delete `src/main/basedbot/lib/sage/ix/` - inline direct dev-sage calls. + +### 3.1 Docking + +Rewrite `src/main/basedbot/lib/sage/act/dock.ts`: + +```typescript +import { getIdleToDockedInstruction, getDockedToIdleInstruction } from '@staratlas/dev-sage' +``` + +### 3.2 Movement + +Rewrite `src/main/basedbot/lib/sage/act/move.ts`: + +```typescript +import { getWarpToCoordinateInstruction, getStartSubwarpInstruction } from '@staratlas/dev-sage' +// Note: coordinates now [bigint, bigint] +``` + +### 3.3 Mining + +Rewrite `src/main/basedbot/lib/sage/act/mine.ts`: + +```typescript +import { getStartMiningAsteroidInstruction, getStopMiningAsteroidInstruction } from '@staratlas/dev-sage' +// Uses CelestialBody instead of mineItem +``` + +### 3.4 Cargo + +Rewrite `src/main/basedbot/lib/sage/act/load-cargo.ts`: + +```typescript +import { getTransferCargoToFleetInstruction } from '@staratlas/dev-sage' +// Uses cargoId-based transfers (fuelTank, ammoBank, cargoHold) +``` + +Reference: `contrib/star-atlas-tech/packages/fc-app/src/actions/FleetActions.ts` + +--- + +## Milestone 4: FSM Strategy Layer + +### 4.1 State Transitions + +Update `src/main/basedbot/fsm/mine.ts`: + +- `fleetInfo.fleetState.type` -> `fleet.state.__kind` +- Adapt timing checks for C4 mechanics + +### 4.2 Config Updates + +Update `src/main/basedbot/fsm/configs/mine/`: + +- New resource/cargoId mappings +- StarSystem coordinates + +--- + +## Milestone 5: Devnet Testing + +- Test full mining loop: undock -> move -> mine -> move -> dock -> unload +- Validate error handling + +--- + +## Files to Delete + +- `src/service/sol/anchor.ts` (no Anchor) +- `src/main/basedbot/lib/programs.ts` (no Anchor program wrappers) +- `src/main/basedbot/lib/sage/ix/*.ts` (inline into act layer) + +--- + +## Reference Resources + +- **C4 Implementation:** `contrib/star-atlas-tech/packages/fc-app/src/actions/` +- **Solana Kit Best Practices:** `contrib/solana-dev-skill/skill/` +- **Account Registry Pattern:** `contrib/star-atlas-tech/packages/fc-app/src/config/accountRegistry.ts` diff --git a/eslint.config.mjs b/eslint.config.mjs index 31e93b67..8a01805f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,7 +5,7 @@ import globals from 'globals' export default [ { - ignores: ['node_modules/', 'dist/'], + ignores: ['node_modules/', 'dist/', 'contrib/'], }, { files: ['src/**/*.ts', 'test/**/*.ts'], diff --git a/package.json b/package.json index a0c92c0a..7713b888 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "type": "module", "scripts": { "type-check": "tsc --noEmit", - "start:fleetbot": "tsx src/main/fleetbot/index.ts", "start:basedbot": "tsx src/main/basedbot/index.ts", "start:airdrop": "tsx src/main/airdrop/index.ts", "build": "pnpm run type-check && tsup", @@ -30,27 +29,21 @@ }, "homepage": "https://github.com/mindrunner/fleetbot#readme", "dependencies": { - "@coral-xyz/anchor": "^0.29.0", "@faker-js/faker": "^9.7.0", "@sentry/integrations": "^7.114.0", "@sentry/node": "^9.17.0", "@sentry/tracing": "^7.120.3", - "@solana/spl-token": "^0.4.13", - "@solana/web3.js": "^1.98.2", - "@staratlas/atlas-prime": "^0.13.1", - "@staratlas/cargo": "^1.1.0", - "@staratlas/claim-stake": "^0.12.0", - "@staratlas/crafting": "^1.1.0", - "@staratlas/data-source": "^0.9.0", - "@staratlas/factory": "^0.7.1", - "@staratlas/player-profile": "^0.11.0", - "@staratlas/points": "^1.1.0", - "@staratlas/profile-faction": "^0.6.0", - "@staratlas/sage": "^1.8.10", + "@solana/kit": "^5.4.0", + "@solana-program/compute-budget": "^0.9.0", + "@solana-program/system": "^0.9.0", + "@solana-program/token": "^0.9.0", + "@staratlas/dev-player-profile": "^0.38.0", + "@staratlas/dev-profile-faction": "^0.38.0", + "@staratlas/dev-sage": "^0.38.0", "big.js": "^7.0.1", "bip39": "^3.1.0", - "bn.js": "^5.2.2", "bs58": "^6.0.0", + "solana-kite": "^2.1.0", "chance": "^1.1.12", "cron": "^4.3.0", "dayjs": "^1.11.13", @@ -70,7 +63,6 @@ }, "devDependencies": { "@types/big.js": "^6.2.2", - "@types/bn.js": "^5.1.6", "@types/chance": "^1.1.6", "@types/module-alias": "^2.0.4", "@types/node": "^22.15.29", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 566d4f05..be9608d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ importers: .: dependencies: - '@coral-xyz/anchor': - specifier: ^0.29.0 - version: 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) '@faker-js/faker': specifier: ^9.7.0 version: 9.8.0 @@ -23,51 +20,33 @@ importers: '@sentry/tracing': specifier: ^7.120.3 version: 7.120.3 - '@solana/spl-token': - specifier: ^0.4.13 - version: 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': - specifier: ^1.98.2 - version: 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/atlas-prime': - specifier: ^0.13.1 - version: 0.13.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/cargo': - specifier: ^1.1.0 - version: 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/claim-stake': - specifier: ^0.12.0 - version: 0.12.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/crafting': - specifier: ^1.1.0 - version: 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': + '@solana-program/compute-budget': specifier: ^0.9.0 - version: 0.9.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/factory': - specifier: ^0.7.1 - version: 0.7.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': - specifier: ^0.11.0 - version: 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/points': - specifier: ^1.1.0 - version: 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/profile-faction': - specifier: ^0.6.0 - version: 0.6.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/sage': - specifier: ^1.8.10 - version: 1.8.10(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + version: 0.9.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/system': + specifier: ^0.9.0 + version: 0.9.1(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/token': + specifier: ^0.9.0 + version: 0.9.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana/kit': + specifier: ^5.4.0 + version: 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@staratlas/dev-player-profile': + specifier: ^0.38.0 + version: 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@staratlas/dev-profile-faction': + specifier: ^0.38.0 + version: 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@staratlas/dev-sage': + specifier: ^0.38.0 + version: 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) big.js: specifier: ^7.0.1 version: 7.0.1 bip39: specifier: ^3.1.0 version: 3.1.0 - bn.js: - specifier: ^5.2.2 - version: 5.2.2 bs58: specifier: ^6.0.0 version: 6.0.0 @@ -104,6 +83,9 @@ importers: reflect-metadata: specifier: ^0.2.2 version: 0.2.2 + solana-kite: + specifier: ^2.1.0 + version: 2.1.0(@solana/sysvars@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) superagent: specifier: ^10.2.1 version: 10.2.1 @@ -123,9 +105,6 @@ importers: '@types/big.js': specifier: ^6.2.2 version: 6.2.2 - '@types/bn.js': - specifier: ^5.1.6 - version: 5.1.6 '@types/chance': specifier: ^1.1.6 version: 1.1.6 @@ -336,10 +315,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} - engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -359,16 +334,6 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@coral-xyz/anchor@0.29.0': - resolution: {integrity: sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==} - engines: {node: '>=11'} - - '@coral-xyz/borsh@0.29.0': - resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.68.0 - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -564,15 +529,6 @@ packages: resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.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@9.8.0': resolution: {integrity: sha512-U9wpuSrJC93jZBxx/Qq2wPjCuYISBueyVUGK7qqdmj7r/nxaxwW8AQDCLeRO7wZnjj94sh3p246cAYjUKuqgfg==} engines: {node: '>=18.0.0', npm: '>=9.0.0'} @@ -696,129 +652,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@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.6': - resolution: {integrity: sha512-lgequ4N69A7QQjyVt0ViqdeXrnviihXZR+Y9rzAD79JtM3NdbKJ3/BTASgYdCm8oAsVBeLw1Wbf5NjCYdfgS8w==} - peerDependencies: - '@metaplex-foundation/umi': '>= 0.8.2 <= 1' - - '@metaplex-foundation/mpl-bubblegum@4.4.0': - resolution: {integrity: sha512-FiRlInn3ZAcdrJHKEWmVOrcQDI4CaME1w9DcxrlbYxbbpsSnO6of6g5zZ8SsimebGjDR+oxSkYO6LkcCPagvHQ==} - 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-token-metadata@3.4.0': - resolution: {integrity: sha512-AxBAYCK73JWxY3g9//z/C9krkR0t1orXZDknUPS4+GjwGH2vgPfsk04yfZ31Htka2AdS9YE/3wH7sMUBHKn9Rg==} - peerDependencies: - '@metaplex-foundation/umi': '>= 0.8.2 <= 1' - - '@metaplex-foundation/mpl-toolbox@0.10.0': - resolution: {integrity: sha512-84KD1L5cFyw5xnntHwL4uPwfcrkKSiwuDeypiVr92qCUFuF3ZENa2zlFVPu+pQcjTlod2LmEX3MhBmNjRMpdKg==} - 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.9.0': - resolution: {integrity: sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.4.0': - resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} - engines: {node: '>= 16'} - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -1039,12 +872,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.8 - '@project-serum/borsh@0.2.5': - resolution: {integrity: sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.2.0 - '@rollup/rollup-android-arm-eabi@4.41.1': resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==} cpu: [arm] @@ -1120,11 +947,6 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.40.2': - resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.41.1': resolution: {integrity: sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==} cpu: [x64] @@ -1153,15 +975,6 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@scure/base@1.1.9': - resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} - - '@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.120.3': resolution: {integrity: sha512-Ausx+Jw1pAMbIBHStoQ6ZqDZR60PsCByvHdw/jdH9AqPrNE9xlBSf9EwcycvmrzwyKspSLaB52grlje2cRIUMg==} engines: {node: '>=8'} @@ -1226,157 +1039,433 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@solana/buffer-layout-utils@0.2.0': - resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} - engines: {node: '>= 10'} + '@solana-program/compute-budget@0.11.0': + resolution: {integrity: sha512-7f1ePqB/eURkTwTOO9TNIdUXZcyrZoX3Uy2hNo7cXMfNhPFWp9AVgIyRNBc2jf15sdUa9gNpW+PfP2iV8AYAaw==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/compute-budget@0.9.0': + resolution: {integrity: sha512-on7Cs1V48X9E2x1yVmfM6N6Xv0r4oGruXPcWnI50D3D3CIsHNWJ4gsvL4qZ4iey7zAP73FdM21K2CZBi1a/jzg==} + peerDependencies: + '@solana/kit': ^3.0 - '@solana/buffer-layout@4.0.1': - resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} - engines: {node: '>=5.10'} + '@solana-program/memo@0.10.0': + resolution: {integrity: sha512-1FvQFenL3lzl5SpxhWV4QJCOLU/nvAOXGXjKjS7dprvG+0u971xoanApN7bM/a4NFZolp6S+lP2xVl6vTVIxbg==} + peerDependencies: + '@solana/kit': ^5.0 - '@solana/codecs-core@2.0.0-rc.1': - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} + '@solana-program/system@0.10.0': + resolution: {integrity: sha512-Go+LOEZmqmNlfr+Gjy5ZWAdY5HbYzk2RBewD9QinEU/bBSzpFfzqDRT55JjFRBGJUvMgf3C2vfXEGT4i8DSI4g==} peerDependencies: - typescript: '>=5' + '@solana/kit': ^5.0 - '@solana/codecs-core@2.1.0': - resolution: {integrity: sha512-SR7pKtmJBg2mhmkel2NeHA1pz06QeQXdMv8WJoIR9m8F/hw80K/612uaYbwTt2nkK0jg/Qn/rNSd7EcJ4SBGjw==} + '@solana-program/system@0.9.1': + resolution: {integrity: sha512-2N30CgYJw0qX8jKU8vW808yLmx5oRoDSM+FC6tqhsLQiph7agK9eRXJlnrq6OUfTAZd5yCYQHQvGtx0S8I9SAA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana-program/token-2022@0.6.1': + resolution: {integrity: sha512-Ex02cruDMGfBMvZZCrggVR45vdQQSI/unHVpt/7HPt/IwFYB4eTlXtO8otYZyqV/ce5GqZ8S6uwyRf0zy6fdbA==} + peerDependencies: + '@solana/kit': ^5.0 + '@solana/sysvars': ^5.0 + + '@solana-program/token@0.9.0': + resolution: {integrity: sha512-vnZxndd4ED4Fc56sw93cWZ2djEeeOFxtaPS8SPf5+a+JZjKA/EnKqzbE1y04FuMhIVrLERQ8uR8H2h72eZzlsA==} + peerDependencies: + '@solana/kit': ^5.0 + + '@solana/accounts@5.4.0': + resolution: {integrity: sha512-qHtAtwCcCFTXcya6JOOG1nzYicivivN/JkcYNHr10qOp9b4MVRkfW1ZAAG1CNzjMe5+mwtEl60RwdsY9jXNb+Q==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/codecs-data-structures@2.0.0-rc.1': - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} + '@solana/addresses@5.4.0': + resolution: {integrity: sha512-YRHiH30S8qDV4bZ+mtEk589PGfBuXHzD/fK2Z+YI5f/+s+yi/5le/fVw7PN6LxnnmVQKiRCDUiNF+WmFFKi6QQ==} + engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/codecs-numbers@2.0.0-rc.1': - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} + '@solana/assertions@3.0.3': + resolution: {integrity: sha512-2qspxdbWp2y62dfCIlqeWQr4g+hE8FYSSwcaP6itwMwGRb8393yDGCJfI/znuzJh6m/XVWhMHIgFgsBwnevCmg==} + engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: '>=5.3.3' - '@solana/codecs-numbers@2.1.0': - resolution: {integrity: sha512-XMu4yw5iCgQnMKsxSWPPOrGgtaohmupN3eyAtYv3K3C/MJEc5V90h74k5B1GUCiHvcrdUDO9RclNjD9lgbjFag==} + '@solana/assertions@5.4.0': + resolution: {integrity: sha512-8EP7mkdnrPc9y67FqWeAPzdWq2qAOkxsuo+ZBIXNWtIixDtXIdHrgjZ/wqbWxLgSTtXEfBCjpZU55Xw2Qfbwyg==} engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/codecs-strings@2.0.0-rc.1': - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} + '@solana/codecs-core@5.4.0': + resolution: {integrity: sha512-rQ5jXgiDe2vIU+mYCHDjgwMd9WdzZfh4sc5H6JgYleAUjeTUX6mx8hTV2+pcXvvn27LPrgrt9jfxswbDb8O8ww==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-data-structures@5.4.0': + resolution: {integrity: sha512-LVssbdQ1GfY6upnxW3mufYsNfvTWKnHNk5Hx2gHuOYJhm3HZlp+Y8zvuoY65G1d1xAXkPz5YVGxaSeVIRWLGWg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-numbers@5.4.0': + resolution: {integrity: sha512-z6LMkY+kXWx1alrvIDSAxexY5QLhsso638CjM7XI1u6dB7drTLWKhifyjnm1vOQc1VPVFmbYxTgKKpds8TY8tg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/codecs-strings@5.4.0': + resolution: {integrity: sha512-w0trrjfQDhkCVz7O1GTmHBk9m+MkljKx2uNBbQAD3/yW2Qn9dYiTrZ1/jDVq0/+lPPAUkbT3s3Yo7HUZ2QFmHw==} + engines: {node: '>=20.18.0'} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + fastestsmallesttextencoderdecoder: + optional: true + typescript: + optional: true - '@solana/codecs@2.0.0-rc.1': - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} + '@solana/codecs@5.4.0': + resolution: {integrity: sha512-IbDCUvNX0MrkQahxiXj9rHzkd/fYfp1F2nTJkHGH8v+vPfD+YPjl007ZBM38EnCeXj/Xn+hxqBBivPvIHP29dA==} + engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/errors@2.0.0-rc.1': - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} + '@solana/errors@3.0.3': + resolution: {integrity: sha512-1l84xJlHNva6io62PcYfUamwWlc0eM95nHgCrKX0g0cLoC6D6QHYPCEbEVkR+C5UtP9JDgyQM8MFiv+Ei5tO9Q==} + engines: {node: '>=20.18.0'} hasBin: true peerDependencies: - typescript: '>=5' + typescript: '>=5.3.3' - '@solana/errors@2.1.0': - resolution: {integrity: sha512-l+GxAv0Ar4d3c3PlZdA9G++wFYZREEbbRyAFP8+n8HSg0vudCuzogh/13io6hYuUhG/9Ve8ARZNamhV7UScKNw==} + '@solana/errors@5.4.0': + resolution: {integrity: sha512-hNoAOmlZAszaVBrAy1Jf7amHJ8wnUnTU0BqhNQXknbSvirvsYr81yEud2iq18YiCqhyJ9SuQ5kWrSAT0x7S0oA==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/options@2.0.0-rc.1': - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} + '@solana/fast-stable-stringify@5.4.0': + resolution: {integrity: sha512-KB7PUL7yalPvbWCezzyUDVRDp39eHLPH7OJ6S8VFT8YNIFUANwwj5ctui50Fim76kvSYDdYJOclXV45O2gfQ8Q==} + engines: {node: '>=20.18.0'} peerDependencies: - typescript: '>=5' + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/spl-account-compression@0.2.0': - resolution: {integrity: sha512-nHpa+hTUpjLdV9x4LXlp7k0WIkr8kUGjY/SPh+vuTUy4SEIIDjrGJ6/B0hUdd8+mFfrq2x4j/tgJvPsm4K5AJw==} - engines: {node: '>=16'} + '@solana/functional@5.4.0': + resolution: {integrity: sha512-32ghHO0bg6GgX/7++0/7Lps6RgeXD2gKF1okiuyEGuVfKENIapgaQdcGhUwb3q6D6fv6MRAVn/Yve4jopGVNMQ==} + engines: {node: '>=20.18.0'} peerDependencies: - '@solana/web3.js': ^1.50.1 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/spl-token-group@0.0.7': - resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} - engines: {node: '>=16'} + '@solana/instruction-plans@5.4.0': + resolution: {integrity: sha512-5xbJ+I/pP2aWECmK75bEM1zCnIITlohAK83dVN+t5X2vBFrr6M9gifo8r4Opdnibsgo6QVVkKPxRo5zow5j0ig==} + engines: {node: '>=20.18.0'} peerDependencies: - '@solana/web3.js': ^1.95.3 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/spl-token-metadata@0.1.6': - resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} - engines: {node: '>=16'} + '@solana/instructions@5.4.0': + resolution: {integrity: sha512-//a7jpHbNoAgTqy3YyqG1X6QhItJLKzJa6zuYJGCwaAAJye7BxS9pxJBgb2mUt7CGidhUksf+U8pmLlxCNWYyg==} + engines: {node: '>=20.18.0'} peerDependencies: - '@solana/web3.js': ^1.95.3 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/spl-token@0.3.11': - resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} - engines: {node: '>=16'} + '@solana/keys@5.4.0': + resolution: {integrity: sha512-zQVbAwdoXorgXjlhlVTZaymFG6N8n1zn2NT+xI6S8HtbrKIB/42xPdXFh+zIihGzRw+9k8jzU7Axki/IPm6qWQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/kit@5.4.0': + resolution: {integrity: sha512-aVjN26jOEzJA6UBYxSTQciZPXgTxWnO/WysHrw+yeBL/5AaTZnXEgb4j5xV6cUFzOlVxhJBrx51xtoxSqJ0u3g==} + engines: {node: '>=20.18.0'} peerDependencies: - '@solana/web3.js': ^1.88.0 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/spl-token@0.4.13': - resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} - engines: {node: '>=16'} + '@solana/nominal-types@5.4.0': + resolution: {integrity: sha512-h4dTRQwTerzksE5B1WmObN6TvLo8dYUd7kpUUynGd8WJjK0zz3zkDhq0MkA3aF6A1C2C82BSGqSsN9EN0E6Exg==} + engines: {node: '>=20.18.0'} peerDependencies: - '@solana/web3.js': ^1.95.5 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@solana/web3.js@1.98.2': - resolution: {integrity: sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==} + '@solana/offchain-messages@5.4.0': + resolution: {integrity: sha512-DjdlYJCcKfgh4dkdk+owH1bP+Q4BRqCs55mgWWp9PTwm/HHy/a5vcMtCi1GyIQXfhtNNvKBLbXrUE0Fxej8qlg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@sqltools/formatter@1.2.5': - resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + '@solana/options@5.4.0': + resolution: {integrity: sha512-h4vTWRChEXPhaHo9i1pCyQBWWs+NqYPQRXSAApqpUYvHb9Kct/C6KbHjfyaRMyqNQnDHLcJCX7oW9tk0iRDzIg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/plugin-core@5.4.0': + resolution: {integrity: sha512-e1aLGLldW7C5113qTOjFYSGq95a4QC9TWb77iq+8l6h085DcNj+195r4E2zKaINrevQjQTwvxo00oUyHP7hSJA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/programs@5.4.0': + resolution: {integrity: sha512-Sc90WK9ZZ7MghOflIvkrIm08JwsFC99yqSJy28/K+hDP2tcx+1x+H6OFP9cumW9eUA1+JVRDeKAhA8ak7e/kUA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/promises@3.0.3': + resolution: {integrity: sha512-K+UflGBVxj30XQMHTylHHZJdKH5QG3oj5k2s42GrZ/Wbu72oapVJySMBgpK45+p90t8/LEqV6rRPyTXlet9J+Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + + '@solana/promises@5.4.0': + resolution: {integrity: sha512-23mfgNBbuP6Q+4vsixGy+GkyZ7wBLrxTBNXqrG/XWrJhjuuSkjEUGaK4Fx5o7LIrBi6KGqPknKxmTlvqnJhy2Q==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@solana/rpc-api@5.4.0': + resolution: {integrity: sha512-FJL6KaAsQ4DhfhLKKMcqbTpToNFwHlABCemIpOunE3OSqJFDrmc/NbsEaLIoeHyIg3d1Imo49GIUOn2TEouFUA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/anchor@0.25.1': - resolution: {integrity: sha512-IDRKejAq+B+pZE+qnIj0qYbF8TBOyKofeY/zo6/Lk+PIAetareEZrF+avlDvkTeKIQS1t9fd0DOPcvbsV5ZS9A==} - engines: {node: '>=11'} + '@solana/rpc-parsed-types@5.4.0': + resolution: {integrity: sha512-IRQuSzx+Sj1A3XGiIzguNZlMjMMybXTTjV/RnTwBgnJQPd/H4us4pfPD94r+/yolWDVfGjJRm04hnKVMjJU8Rg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/atlas-prime@0.13.1': - resolution: {integrity: sha512-fFMjy0dFUgB+b9m+SZIIMSrrvB+8SVq/wJTFNVzDenr0YrXehbgtkfZWaxpffEAXdp433hbkP9yAArlf+weMPg==} + '@solana/rpc-spec-types@5.4.0': + resolution: {integrity: sha512-JU9hC5/iyJx30ym17gpoXDtT9rCbO6hLpB6UDhSFFoNeirxtTVb4OdnKtsjJDfXAiXsynJRsZRwfj3vGxRLgQw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/cargo@1.1.0': - resolution: {integrity: sha512-7td0gxru4dkygO+xv+RQs9bkizKj6+blAMRSPP5ozOZ9QQ35HwCBIDJXB6Kk8sfVcq2JOHiWyQtyjcAD6GHArQ==} + '@solana/rpc-spec@5.4.0': + resolution: {integrity: sha512-XMhxBb1GuZ3Kaeu5WNHB5KteCQ/aVuMByZmUKPqaanD+gs5MQZr0g62CvN7iwRlFU7GC18Q73ROWR3/JjzbXTA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/claim-stake@0.12.0': - resolution: {integrity: sha512-HbE2mJOMkMj+gNIQINkLj35elX0Oj772iegu+JcUIpsJRl/hRJh8F6NQt9iwnSYyrYQwBG+U4dpTpLgKAeQeHA==} + '@solana/rpc-subscriptions-api@5.4.0': + resolution: {integrity: sha512-euAFIG6ruEsqK+MsrL1tGSMbbOumm8UAyGzlD/kmXsAqqhcVsSeZdv5+BMIHIBsQ93GHcloA8UYw1BTPhpgl9w==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/crafting@1.1.0': - resolution: {integrity: sha512-lxvdGBZatVBL63o1EHcUAxk1k1jHwEv6dZsulpgXpsOfnBP7Btzt8Ky1knBD7HvM/gBQplxMKopolRGFCQekXQ==} + '@solana/rpc-subscriptions-channel-websocket@5.4.0': + resolution: {integrity: sha512-kWCmlW65MccxqXwKsIz+LkXUYQizgvBrrgYOkyclJHPa+zx4gqJjam87+wzvO9cfbDZRer3wtJBaRm61gTHNbw==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/crew@0.7.0': - resolution: {integrity: sha512-tr8RDDs1waOSVwVwR9WlD6Z6iDvRHprqQu4XQmSSy6oy5Sv2FNofFWENsC0dmysUl/n0OLzKfT8/Q4D3hKRlTA==} + '@solana/rpc-subscriptions-spec@5.4.0': + resolution: {integrity: sha512-ELaV9Z39GtKyUO0++he00ymWleb07QXYJhSfA0e1N5Q9hXu/Y366kgXHDcbZ/oUJkT3ylNgTupkrsdtiy8Ryow==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/data-source@0.7.7': - resolution: {integrity: sha512-DQfR/a9MXr1Gds/YTR9RylQZlx5gbCe4NjynQ/45je3rnnblNfhma0WoHrSHz/r/lWwJwN7yRwSJkfaDW8QhuQ==} + '@solana/rpc-subscriptions@5.4.0': + resolution: {integrity: sha512-051t1CEjjAzM9ohjj2zb3ED70yeS3ZY8J5wSytL6tthTGImw/JB2a0D9DWMOKriFKt496n95IC+IdpJ35CpBWA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/data-source@0.8.3': - resolution: {integrity: sha512-iM7EYTKUKLYUD90Xai2FufPdL/4raOizdfbJy1RWfZlzR77Jp0BRTi1Z92iSF1WMEXqRqWR2ZqU6hK0/8N3jyg==} + '@solana/rpc-transformers@5.4.0': + resolution: {integrity: sha512-dZ8keYloLW+eRAwAPb471uWCFs58yHloLoI+QH0FulYpsSJ7F2BNWYcdnjSS/WiggsNcU6DhpWzYAzlEY66lGQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/data-source@0.9.0': - resolution: {integrity: sha512-WLS6/Abq+0VJFZFQIE0pi2BsSDwU6OQ3c0BsQySIN/SZ38VGeBQLC0gbo0gktYomKDtawYB4qIB2JmrmgvkmQQ==} + '@solana/rpc-transport-http@5.4.0': + resolution: {integrity: sha512-vidA+Qtqrnqp3QSVumWHdWJ/986yCr5+qX3fbc9KPm9Ofoto88OMWB/oLJvi2Tfges1UBu/jl+lJdsVckCM1bA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/factory@0.7.1': - resolution: {integrity: sha512-agUbdiD6DsmMHOFA33mZp6rAvcjpFGeYWXHimi04++cVZK7N9cQ0S3YJsQOA0zCfgMbIoB6K5a6bcIl51gVVOA==} + '@solana/rpc-types@5.4.0': + resolution: {integrity: sha512-+C4N4/5AYzBdt3Y2yzkScknScy/jTx6wfvuJIY9XjOXtdDyZ8TmrnMwdPMTZPGLdLuHplJwlwy1acu/4hqmrBQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/player-profile@0.11.0': - resolution: {integrity: sha512-SQYysx0s0lD6J9Ln4vJQDa9TS/1rSy/FeKCexwzNiKm+S0fUFv6LKOKIPsGDQDQ2MIVfVqilVtIB2Nf3DuaXgg==} + '@solana/rpc@5.4.0': + resolution: {integrity: sha512-S6GRG+usnubDs0JSpgc0ZWEh9IPL5KPWMuBoD8ggGVOIVWntp53FpvhYslNzbxWBXlTvJecr2todBipGVM/AqQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/player-profile@0.9.1': - resolution: {integrity: sha512-bqPXn6fGl+gg5zZBEK4+/Cv9oriVzrNOTW1Z+6TMFSB/vMsE51DsdyN7BWMkvwfBg2XdHfL9VntlU8j8OYD8jQ==} + '@solana/signers@5.4.0': + resolution: {integrity: sha512-s+fZxpi6UPr6XNk2pH/R84WjNRoSktrgG8AGNfsj/V8MJ++eKX7hhIf4JsHZtnnQXXrHmS3ozB2oHlc8yEJvCQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/points-store@1.1.0': - resolution: {integrity: sha512-Rrbsnku5flephKSNkb6OYkUxGWmQAsHH76ZQKISBViXSPcQkpZ3lIYaqYaztEDx6rXSY1J5ftkJEAnxMVtkJ4A==} + '@solana/subscribable@5.4.0': + resolution: {integrity: sha512-72LmfNX7UENgA24sn/xjlWpPAOsrxkWb9DQhuPZxly/gq8rl/rvr7Xu9qBkvFF2po9XpdUrKlccqY4awvfpltA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/points@1.1.0': - resolution: {integrity: sha512-9XyG5i3xz4Om6Blq/HxYqnGGAnklyCLlokIXpLAxYWEU53JT7ldhLiXCgKuA8n/aWi0Y3YCOnmPuuQ5JwD+cZw==} + '@solana/sysvars@5.4.0': + resolution: {integrity: sha512-A5NES7sOlFmpnsiEts5vgyL3NXrt/tGGVSEjlEGvsgwl5EDZNv+xWnNA400uMDqd9O3a5PmH7p/6NsgR+kUzSg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/profile-faction@0.6.0': - resolution: {integrity: sha512-OLENOQ12cG12g8xkmZsf6oFvtCkTlsI/ljGGwpOzic0iCD282bydVStJ//N0nRxBfDBGYCyRI9TBkR3uF69D2g==} + '@solana/transaction-confirmation@5.4.0': + resolution: {integrity: sha512-EdSDgxs84/4gkjQw2r7N+Kgus8x9U+NFo0ufVG+48V8Hzy2t0rlBuXgIxwx0zZwUuTIgaKhpIutJgVncwZ5koA==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/profile-vault@0.9.1': - resolution: {integrity: sha512-nY6t1VTI/kRd/bQLSEeRTqwJ6QWRAC1du6vFN6zufJQyumuuZpmevKNZF1DkrSjZqhe3/2HgyR/ud7d62KGFxg==} + '@solana/transaction-messages@5.4.0': + resolution: {integrity: sha512-qd/3kZDaPiHM0amhn3vXnupfcsFTVz6CYuHXvq9HFv/fq32+5Kp1FMLnmHwoSxQxdTMDghPdOhC4vhNhuWmuVQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@staratlas/sage@1.8.10': - resolution: {integrity: sha512-e7UvoTXyF94HO3d4hjS04WlrUMChGow4/eW9Snw707snBHkmnNsiBZaLHQs+xU1knbISF2LZ3EtsvEfuy+DXCQ==} + '@solana/transactions@5.4.0': + resolution: {integrity: sha512-OuY4M4x/xna8KZQIrz8tSrI9EEul9Od97XejqFmGGkEjbRsUOfJW8705TveTW8jU3bd5RGecFYscPgS2F+m7jQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@sqltools/formatter@1.2.5': + resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} + + '@staratlas/dev-player-profile@0.38.0': + resolution: {integrity: sha512-Urf+wqD1qF7w0tQjyskxqv8xG9tvYazjsBIyVH4Ei2jgDK6q+5k30CFjfLqbfI05S6I3BmqB9OvTDH/jtHpOQQ==} + peerDependencies: + '@solana/kit': ^5.0.0 + + '@staratlas/dev-profile-faction@0.38.0': + resolution: {integrity: sha512-ZSFdvfETGSSpz3FELeU/1n0LiN/OE0OiGHQIpYdpt7HndMvu14B8HUlHwQnVx8/PdSHZtQz/CP/SK1DphQr5Sw==} + peerDependencies: + '@solana/kit': ^5.0.0 + + '@staratlas/dev-sage@0.38.0': + resolution: {integrity: sha512-FqbOG6673eXikgFvpX4r+vu0E/vkzGGv5GOaXbZN7JwrWK8S9fsg2vLVDUN1KGEb4RCd6prR0fb3StLaKdheeQ==} + peerDependencies: + '@solana/kit': ^5.0.0 '@swc/core-darwin-arm64@1.11.29': resolution: {integrity: sha512-whsCX7URzbuS5aET58c75Dloby3Gtj/ITk2vc4WW6pSDQKSPDuONsIcZ7B2ng8oz0K6ttbi4p3H/PNPQLJ4maQ==} @@ -1486,9 +1575,6 @@ packages: '@types/big.js@6.2.2': resolution: {integrity: sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==} - '@types/bn.js@5.1.6': - resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} - '@types/chance@1.1.6': resolution: {integrity: sha512-V+pm3stv1Mvz8fSKJJod6CglNGVqEQ6OyuqitoDkWywEODM/eJd1eSuIp9xt6DrX8BWZ2eDSIzbw1tPCUTvGbQ==} @@ -1519,9 +1605,6 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/lodash@4.17.16': - resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} - '@types/luxon@3.6.2': resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==} @@ -1534,12 +1617,6 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - '@types/node@20.17.46': - resolution: {integrity: sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==} - '@types/node@22.15.29': resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==} @@ -1567,15 +1644,6 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - '@types/uuid@8.3.4': - resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - - '@types/ws@7.4.7': - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1662,10 +1730,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1693,9 +1757,6 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - ansicolors@0.3.2: - resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} - ansis@3.17.0: resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==} engines: {node: '>=14'} @@ -1747,9 +1808,6 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -1764,9 +1822,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.9.0: - resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} - babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1795,49 +1850,21 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base-x@3.0.11: - resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - - base-x@4.0.1: - resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} - base-x@5.0.1: resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - big.js@6.2.2: - resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} - big.js@7.0.1: resolution: {integrity: sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg==} - bigint-buffer@1.1.5: - resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} - engines: {node: '>= 10.0.0'} - - bignumber.js@9.3.0: - resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} - - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - 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.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - borsh@0.7.0: - resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -1857,12 +1884,6 @@ packages: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} - bs58@4.0.1: - resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - - bs58@5.0.0: - resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} - bs58@6.0.0: resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} @@ -1881,13 +1902,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer-layout@1.2.2: - 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==} @@ -1929,14 +1943,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - - camelcase@8.0.0: - resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} - engines: {node: '>=16'} - caniuse-lite@1.0.30001720: resolution: {integrity: sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g==} @@ -1944,8 +1950,8 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} chance@1.1.13: @@ -2007,16 +2013,13 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} - engines: {node: '>=18'} + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} @@ -2059,20 +2062,10 @@ packages: resolution: {integrity: sha512-7x7DoEOxV11t3OPWWMjj1xrL1PGkTV5RV+/54IJTZD7gStiaMploY43EkeBSkDZTLRbUwk+OISbQ0TR133oXyA==} engines: {node: '>=18.x'} - cross-fetch@3.2.0: - resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crypto-hash@1.3.0: - 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==} @@ -2144,10 +2137,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - delay@5.0.0: - resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} - engines: {node: '>=10'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -2187,9 +2176,6 @@ packages: domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv@16.5.0: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} @@ -2259,12 +2245,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es6-promise@4.2.8: - resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - - es6-promisify@5.0.0: - resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} @@ -2394,26 +2374,10 @@ 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'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -2426,10 +2390,6 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - eyes@0.1.8: - resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} - engines: {node: '> 0.1.90'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2449,9 +2409,6 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-stable-stringify@1.0.0: - resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - fastestsmallesttextencoderdecoder@1.0.22: resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} @@ -2476,9 +2433,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -2507,15 +2461,6 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -2667,9 +2612,6 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2711,10 +2653,6 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2777,18 +2715,10 @@ packages: 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-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -2843,11 +2773,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -2880,11 +2805,6 @@ packages: engines: {node: '>=10'} hasBin: true - jayson@4.2.0: - resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} - engines: {node: '>=8'} - hasBin: true - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3018,12 +2938,6 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - 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==} @@ -3052,9 +2966,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -3135,9 +3046,6 @@ packages: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} - lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -3175,17 +3083,10 @@ 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.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3228,14 +3129,6 @@ packages: mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - mobx-utils@6.1.0: - resolution: {integrity: sha512-P3qUVDFp3Kv5HXD7EIGJn3zlgJJnN+/ZpFHWQ+u6YNN1xDxY53iMvsQ9fM8kauTVdDmt7ulDgDQtDrOxb1NS9Q==} - peerDependencies: - mobx: ^6.0.0 - - mobx@6.13.7: - resolution: {integrity: sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==} - module-alias@2.2.3: resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==} @@ -3255,16 +3148,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - neverthrow@6.2.2: - resolution: {integrity: sha512-POR1FACqdK9jH0S2kRPzaZEvzT11wsOxLW520PQV/+vKi9dQe+hXq19EiOvYx7lSRaF5VB9lYGsPInynrnN05w==} - - neverthrow@8.2.0: - resolution: {integrity: sha512-kOCT/1MCPAxY5iUV3wytNFUMUolzuwd/VF/1KCx7kf6CutrOsTie+84zTGTpgQycjvfLdBBdvBvFLqFD2c0wkQ==} - engines: {node: '>=18'} - - no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -3295,10 +3178,6 @@ 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'} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3307,10 +3186,6 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - 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'} @@ -3376,9 +3251,6 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pako@2.1.0: - resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3535,9 +3407,6 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3552,9 +3421,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3624,9 +3490,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rpc-websockets@9.1.1: - resolution: {integrity: sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -3725,8 +3588,8 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + solana-kite@2.1.0: + resolution: {integrity: sha512-I2wts94RErTUcRiYzK+ygNzniWH4lIeXZVqieXMUYgTS3tI1pGZ4FHh8yPGp+viNkVnesEEtuMaciO+GLRaihA==} source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -3757,12 +3620,6 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stream-chain@2.2.5: - resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} - - stream-json@1.9.1: - resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -3810,10 +3667,6 @@ 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@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -3827,13 +3680,6 @@ packages: resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} engines: {node: '>=14.18.0'} - superstruct@0.15.5: - resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} - - superstruct@2.0.2: - resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} - engines: {node: '>=14.0.0'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -3859,9 +3705,6 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-encoding-utf-8@1.0.2: - resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -3886,9 +3729,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toml@3.0.0: - resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -3899,10 +3739,6 @@ 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'} @@ -4078,9 +3914,6 @@ packages: typeorm-aurora-data-api-driver: optional: true - typescript-collections@1.3.3: - resolution: {integrity: sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ==} - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -4093,12 +3926,12 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@7.10.0: resolution: {integrity: sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==} engines: {node: '>=20.18.1'} @@ -4116,15 +3949,9 @@ packages: 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==} @@ -4132,10 +3959,6 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -4146,10 +3969,6 @@ packages: 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==} @@ -4210,20 +4029,8 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -4430,8 +4237,6 @@ snapshots: '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.1': {} - '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -4459,34 +4264,6 @@ snapshots: '@colors/colors@1.6.0': {} - '@coral-xyz/anchor@0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@noble/hashes': 1.8.0 - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - bs58: 4.0.1 - buffer-layout: 1.2.2 - camelcase: 6.3.0 - cross-fetch: 3.2.0 - crypto-hash: 1.3.0 - eventemitter3: 4.0.7 - pako: 2.1.0 - snake-case: 3.0.4 - superstruct: 0.15.5 - toml: 3.0.0 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - '@coral-xyz/borsh@0.29.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - buffer-layout: 1.2.2 - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -4617,14 +4394,6 @@ snapshots: '@eslint/core': 0.14.0 levn: 0.4.1 - '@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@9.8.0': {} '@humanfs/core@0.19.1': {} @@ -4844,162 +4613,6 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.0 optional: true - '@metaplex-foundation/beet-solana@0.4.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@metaplex-foundation/beet': 0.7.2 - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bs58: 5.0.0 - debug: 4.4.1 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - typescript - - utf-8-validate - - '@metaplex-foundation/beet@0.7.2': - dependencies: - ansicolors: 0.3.2 - assert: 2.1.0 - bn.js: 5.2.2 - debug: 4.4.1 - transitivePeerDependencies: - - supports-color - - '@metaplex-foundation/digital-asset-standard-api@1.0.6(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/mpl-bubblegum@4.4.0(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/digital-asset-standard-api': 1.0.6(@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.10.0(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi': 0.9.2 - '@noble/hashes': 1.8.0 - merkletreejs: 0.3.11 - - '@metaplex-foundation/mpl-token-metadata@3.2.1(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/mpl-toolbox': 0.9.4(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/mpl-token-metadata@3.4.0(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/mpl-toolbox': 0.10.0(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/mpl-toolbox@0.10.0(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/mpl-toolbox@0.9.4(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/umi-bundle-defaults@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - encoding - - '@metaplex-foundation/umi-downloader-http@0.9.2(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/umi-eddsa-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@noble/curves': 1.9.0 - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - - '@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@metaplex-foundation/umi-options@0.8.9': {} - - '@metaplex-foundation/umi-program-repository@0.9.2(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/umi-public-keys@0.8.9': - dependencies: - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - - '@metaplex-foundation/umi-rpc-chunk-get-accounts@0.9.2(@metaplex-foundation/umi@0.9.2)': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - - '@metaplex-foundation/umi-rpc-web3js@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - - '@metaplex-foundation/umi-web3js-adapters@0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@metaplex-foundation/umi': 0.9.2 - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(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.9.0': - dependencies: - '@noble/hashes': 1.8.0 - - '@noble/hashes@1.4.0': {} - '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': @@ -5272,12 +4885,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@project-serum/borsh@0.2.5(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))': - dependencies: - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - buffer-layout: 1.2.2 - '@rollup/rollup-android-arm-eabi@4.41.1': optional: true @@ -5323,9 +4930,6 @@ snapshots: '@rollup/rollup-linux-s390x-gnu@4.41.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.2': - optional: true - '@rollup/rollup-linux-x64-gnu@4.41.1': optional: true @@ -5343,19 +4947,6 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@scure/base@1.1.9': {} - - '@scure/bip32@1.4.0': - dependencies: - '@noble/curves': 1.4.2 - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - - '@scure/bip39@1.3.0': - dependencies: - '@noble/hashes': 1.4.0 - '@scure/base': 1.1.9 - '@sentry-internal/tracing@7.120.3': dependencies: '@sentry/core': 7.120.3 @@ -5456,464 +5047,491 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana-program/compute-budget@0.11.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bigint-buffer: 1.1.5 - bignumber.js: 9.3.0 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/buffer-layout@4.0.1': + '@solana-program/compute-budget@0.9.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - buffer: 6.0.3 + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/codecs-core@2.0.0-rc.1(typescript@5.8.3)': + '@solana-program/memo@0.10.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) - typescript: 5.8.3 + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@solana-program/system@0.10.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@solana-program/system@0.9.1(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@solana-program/token-2022@0.6.1(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))(@solana/sysvars@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/sysvars': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + + '@solana-program/token@0.9.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/codecs-core@2.1.0(typescript@5.8.3)': + '@solana/accounts@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/errors': 2.1.0(typescript@5.8.3) + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder - '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.3)': + '@solana/addresses@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + '@solana/assertions': 5.4.0(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder - '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.3)': + '@solana/assertions@3.0.3(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 3.0.3(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-numbers@2.1.0(typescript@5.8.3)': + '@solana/assertions@5.4.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.1.0(typescript@5.8.3) - '@solana/errors': 2.1.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 - '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + '@solana/codecs-core@5.4.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) - fastestsmallesttextencoderdecoder: 1.0.22 + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 - '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + '@solana/codecs-data-structures@5.4.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - '@solana/errors@2.0.0-rc.1(typescript@5.8.3)': + '@solana/codecs-numbers@5.4.0(typescript@5.8.3)': dependencies: - chalk: 5.4.1 - commander: 12.1.0 + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 - '@solana/errors@2.1.0(typescript@5.8.3)': + '@solana/codecs-strings@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - chalk: 5.4.1 - commander: 13.1.0 + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: + fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.8.3 - '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + '@solana/codecs@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-data-structures': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/options': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/spl-account-compression@0.2.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/errors@3.0.3(typescript@5.8.3)': dependencies: - '@metaplex-foundation/beet': 0.7.2 - '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bn.js: 5.2.2 - borsh: 0.7.0 - js-sha3: 0.8.0 - typescript-collections: 1.3.3 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - typescript - - utf-8-validate + chalk: 5.6.2 + commander: 14.0.0 + typescript: 5.8.3 - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + '@solana/errors@5.4.0(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript + chalk: 5.6.2 + commander: 14.0.2 + optionalDependencies: + typescript: 5.8.3 + + '@solana/fast-stable-stringify@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 + + '@solana/functional@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + '@solana/instruction-plans@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/instructions': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 5.4.0(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - - typescript - '@solana/spl-token@0.3.11(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/instructions@5.4.0(typescript@5.8.3)': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 - '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/keys@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - buffer: 6.0.3 + '@solana/assertions': 5.4.0(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@babel/runtime': 7.27.1 - '@noble/curves': 1.9.0 - '@noble/hashes': 1.8.0 - '@solana/buffer-layout': 4.0.1 - '@solana/codecs-numbers': 2.1.0(typescript@5.8.3) - agentkeepalive: 4.6.0 - bn.js: 5.2.2 - borsh: 0.7.0 - bs58: 4.0.1 - buffer: 6.0.3 - fast-stable-stringify: 1.0.0 - jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - node-fetch: 2.7.0 - rpc-websockets: 9.1.1 - superstruct: 2.0.2 + '@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/instruction-plans': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/instructions': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/offchain-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/plugin-core': 5.4.0(typescript@5.8.3) + '@solana/programs': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-api': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-subscriptions': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/signers': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/sysvars': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-confirmation': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - - encoding - - typescript + - fastestsmallesttextencoderdecoder - utf-8-validate - '@sqltools/formatter@1.2.5': {} + '@solana/nominal-types@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 - '@staratlas/anchor@0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/offchain-messages@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@project-serum/borsh': 0.2.5(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - base64-js: 1.5.1 - bn.js: 5.2.2 - bs58: 4.0.1 - buffer-layout: 1.2.2 - camelcase: 5.3.1 - cross-fetch: 3.2.0 - crypto-hash: 1.3.0 - eventemitter3: 4.0.7 - js-sha256: 0.9.0 - pako: 2.1.0 - snake-case: 3.0.4 - superstruct: 0.15.5 - toml: 3.0.0 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - '@staratlas/atlas-prime@0.13.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.7(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.9.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/profile-vault': 0.9.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@types/node': 20.17.46 - axios: 1.9.0 - bs58: 5.0.0 - neverthrow: 6.2.2 + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-data-structures': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - debug - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/cargo@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/options@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-data-structures': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/claim-stake@0.12.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/plugin-core@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 + + '@solana/programs@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/crafting@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/promises@3.0.3(typescript@5.8.3)': dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@types/lodash': 4.17.16 - lodash: 4.17.21 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate + typescript: 5.8.3 + + '@solana/promises@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 - '@staratlas/crew@0.7.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@metaplex-foundation/mpl-bubblegum': 4.4.0(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/mpl-token-metadata': 3.4.0(@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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@project-serum/anchor': '@staratlas/anchor@0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)' - '@solana/spl-account-compression': 0.2.0(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/rpc-api@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-transformers': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - supports-color - - typescript - - utf-8-validate - '@staratlas/data-source@0.7.7(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/rpc-parsed-types@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 + + '@solana/rpc-spec-types@5.4.0(typescript@5.8.3)': + optionalDependencies: + typescript: 5.8.3 + + '@solana/rpc-spec@5.4.0(typescript@5.8.3)': dependencies: - '@noble/curves': 1.9.0 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bs58: 5.0.0 - camelcase: 7.0.1 - lodash: 4.17.21 - neverthrow: 6.2.2 + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + + '@solana/rpc-subscriptions-api@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-transformers': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/data-source@0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions-channel-websocket@5.4.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@noble/curves': 1.9.0 - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bs58: 6.0.0 - camelcase: 8.0.0 - lodash: 4.17.21 - neverthrow: 8.2.0 + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.8.3) + '@solana/subscribable': 5.4.0(typescript@5.8.3) + ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - utf-8-validate - '@staratlas/data-source@0.9.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions-spec@5.4.0(typescript@5.8.3)': dependencies: - '@noble/curves': 1.9.0 - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - bs58: 6.0.0 - camelcase: 8.0.0 - lodash: 4.17.21 - neverthrow: 8.2.0 + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/promises': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + '@solana/subscribable': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + + '@solana/rpc-subscriptions@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/promises': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-subscriptions-api': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-channel-websocket': 5.4.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-transformers': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/subscribable': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - utf-8-validate - '@staratlas/factory@0.7.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/rpc-transformers@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@coral-xyz/anchor': 0.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - big.js: 6.2.2 - lodash: 4.17.21 - mobx: 6.13.7 - mobx-utils: 6.1.0(mobx@6.13.7) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/player-profile@0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/rpc-transport-http@5.4.0(typescript@5.8.3)': + dependencies: + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + undici-types: 7.18.2 + optionalDependencies: + typescript: 5.8.3 + + '@solana/rpc-types@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/player-profile@0.9.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.7(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/rpc@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/rpc-api': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-spec': 5.4.0(typescript@5.8.3) + '@solana/rpc-spec-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-transformers': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-transport-http': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/points-store@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/points': 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/signers@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/instructions': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + '@solana/offchain-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/points@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + '@solana/subscribable@5.4.0(typescript@5.8.3)': + dependencies: + '@solana/errors': 5.4.0(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + + '@solana/sysvars@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/accounts': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/profile-faction@0.6.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/transaction-confirmation@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 5.4.0(typescript@5.8.3) + '@solana/rpc': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - utf-8-validate - '@staratlas/profile-vault@0.9.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.7.7(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.9.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-data-structures': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/instructions': 5.4.0(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - '@staratlas/sage@1.8.10(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: - '@metaplex-foundation/mpl-bubblegum': 4.4.0(@metaplex-foundation/umi@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.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2) - '@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)) - '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/anchor': 0.25.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/cargo': 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/crafting': 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/crew': 0.7.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/data-source': 0.8.3(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/player-profile': 0.11.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/points': 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/points-store': 1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@staratlas/profile-faction': 0.6.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) - '@types/lodash': 4.17.16 - lodash: 4.17.21 + '@solana/transactions@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 5.4.0(typescript@5.8.3) + '@solana/codecs-data-structures': 5.4.0(typescript@5.8.3) + '@solana/codecs-numbers': 5.4.0(typescript@5.8.3) + '@solana/codecs-strings': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 5.4.0(typescript@5.8.3) + '@solana/functional': 5.4.0(typescript@5.8.3) + '@solana/instructions': 5.4.0(typescript@5.8.3) + '@solana/keys': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 5.4.0(typescript@5.8.3) + '@solana/rpc-types': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 transitivePeerDependencies: - - bufferutil - - encoding - fastestsmallesttextencoderdecoder - - supports-color - - typescript - - utf-8-validate + + '@sqltools/formatter@1.2.5': {} + + '@staratlas/dev-player-profile@0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@staratlas/dev-profile-faction@0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@staratlas/dev-player-profile': 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + + '@staratlas/dev-sage@0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@staratlas/dev-player-profile': 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@staratlas/dev-profile-faction': 0.38.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) '@swc/core-darwin-arm64@1.11.29': optional: true @@ -5969,6 +5587,7 @@ snapshots: '@swc/helpers@0.5.17': dependencies: tslib: 2.8.1 + optional: true '@swc/types@0.1.21': dependencies: @@ -6012,10 +5631,6 @@ snapshots: '@types/big.js@6.2.2': {} - '@types/bn.js@5.1.6': - dependencies: - '@types/node': 22.15.29 - '@types/chance@1.1.6': {} '@types/connect@3.4.38': @@ -6044,8 +5659,6 @@ snapshots: '@types/json5@0.0.29': {} - '@types/lodash@4.17.16': {} - '@types/luxon@3.6.2': {} '@types/methods@1.1.4': {} @@ -6056,12 +5669,6 @@ snapshots: dependencies: '@types/node': 22.15.29 - '@types/node@12.20.55': {} - - '@types/node@20.17.46': - dependencies: - undici-types: 6.19.8 - '@types/node@22.15.29': dependencies: undici-types: 6.21.0 @@ -6099,16 +5706,6 @@ snapshots: '@types/triple-beam@1.3.5': {} - '@types/uuid@8.3.4': {} - - '@types/ws@7.4.7': - dependencies: - '@types/node': 22.15.29 - - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.15.29 - '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.33': @@ -6226,10 +5823,6 @@ snapshots: acorn@8.14.1: {} - agentkeepalive@4.6.0: - dependencies: - humanize-ms: 1.2.1 - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -6253,8 +5846,6 @@ snapshots: ansi-styles@6.2.1: {} - ansicolors@0.3.2: {} - ansis@3.17.0: {} any-promise@1.3.0: {} @@ -6325,14 +5916,6 @@ snapshots: asap@2.0.6: {} - assert@2.1.0: - dependencies: - call-bind: 1.0.8 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.7 - util: 0.12.5 - async-function@1.0.0: {} async@3.2.6: {} @@ -6343,14 +5926,6 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.9.0: - dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.2 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - babel-jest@29.7.0(@babel/core@7.27.4): dependencies: '@babel/core': 7.27.4 @@ -6408,46 +5983,18 @@ snapshots: balanced-match@1.0.2: {} - base-x@3.0.11: - dependencies: - safe-buffer: 5.2.1 - - base-x@4.0.1: {} - base-x@5.0.1: {} base64-js@1.5.1: {} - big.js@6.2.2: {} - big.js@7.0.1: {} - bigint-buffer@1.1.5: - dependencies: - bindings: 1.5.0 - - bignumber.js@9.3.0: {} - - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - bip39@3.1.0: dependencies: '@noble/hashes': 1.8.0 - bn.js@4.11.6: {} - - bn.js@5.2.2: {} - boolbase@1.0.0: {} - borsh@0.7.0: - dependencies: - bn.js: 5.2.2 - bs58: 4.0.1 - text-encoding-utf-8: 1.0.2 - brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -6472,14 +6019,6 @@ snapshots: dependencies: fast-json-stable-stringify: 2.1.0 - bs58@4.0.1: - dependencies: - base-x: 3.0.11 - - bs58@5.0.0: - dependencies: - base-x: 4.0.1 - bs58@6.0.0: dependencies: base-x: 5.0.1 @@ -6499,10 +6038,6 @@ snapshots: buffer-from@1.1.2: {} - buffer-layout@1.2.2: {} - - buffer-reverse@1.0.1: {} - buffer@6.0.3: dependencies: base64-js: 1.5.1 @@ -6543,10 +6078,6 @@ snapshots: camelcase@6.3.0: {} - camelcase@7.0.1: {} - - camelcase@8.0.0: {} - caniuse-lite@1.0.30001720: {} chalk@4.1.2: @@ -6554,7 +6085,7 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.4.1: {} + chalk@5.6.2: {} chance@1.1.13: {} @@ -6614,11 +6145,9 @@ snapshots: dependencies: delayed-stream: 1.0.0 - commander@12.1.0: {} - - commander@13.1.0: {} + commander@14.0.0: {} - commander@2.20.3: {} + commander@14.0.2: {} commander@4.1.1: {} @@ -6674,22 +6203,12 @@ snapshots: '@types/luxon': 3.6.2 luxon: 3.6.1 - cross-fetch@3.2.0: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - crypto-hash@1.3.0: {} - - crypto-js@4.2.0: {} - css-select@4.3.0: dependencies: boolbase: 1.0.0 @@ -6750,8 +6269,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - delay@5.0.0: {} - delayed-stream@1.0.0: {} detect-newline@3.1.0: {} @@ -6792,11 +6309,6 @@ snapshots: domelementtype: 2.3.0 domhandler: 4.3.1 - dot-case@3.0.4: - dependencies: - no-case: 3.0.4 - tslib: 2.8.1 - dotenv@16.5.0: {} dunder-proto@1.0.1: @@ -6911,12 +6423,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es6-promise@4.2.8: {} - - es6-promisify@5.0.0: - dependencies: - es6-promise: 4.2.8 - esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -7093,28 +6599,8 @@ snapshots: esutils@2.0.3: {} - ethereum-bloom-filters@1.2.0: - dependencies: - '@noble/hashes': 1.8.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: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -7137,8 +6623,6 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - eyes@0.1.8: {} - fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -7157,9 +6641,8 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-stable-stringify@1.0.0: {} - - fastestsmallesttextencoderdecoder@1.0.22: {} + fastestsmallesttextencoderdecoder@1.0.22: + optional: true fastq@1.19.1: dependencies: @@ -7179,8 +6662,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-uri-to-path@1.0.0: {} - filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -7214,8 +6695,6 @@ snapshots: fn.name@1.1.0: {} - follow-redirects@1.15.9: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -7376,10 +6855,6 @@ snapshots: human-signals@2.1.0: {} - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - ieee754@1.2.1: {} ignore@5.3.2: {} @@ -7420,11 +6895,6 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -7490,15 +6960,8 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-hex-prefixed@1.0.0: {} - is-map@2.0.3: {} - is-nan@1.3.2: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -7551,10 +7014,6 @@ snapshots: isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): - dependencies: - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: @@ -7609,24 +7068,6 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 - jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@types/connect': 3.4.38 - '@types/node': 12.20.55 - '@types/ws': 7.4.7 - commander: 2.20.3 - delay: 5.0.0 - es6-promisify: 5.0.0 - eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - json-stringify-safe: 5.0.1 - stream-json: 1.9.1 - uuid: 8.3.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -7938,10 +7379,6 @@ snapshots: joycon@3.1.1: {} - js-sha256@0.9.0: {} - - js-sha3@0.8.0: {} - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -7963,8 +7400,6 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - json-stringify-safe@5.0.1: {} - json5@1.0.2: dependencies: minimist: 1.2.8 @@ -8033,10 +7468,6 @@ snapshots: safe-stable-stringify: 2.5.0 triple-beam: 1.4.1 - lower-case@2.0.2: - dependencies: - tslib: 2.8.1 - lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -8071,18 +7502,8 @@ snapshots: merge2@1.4.1: {} - merkletreejs@0.3.11: - dependencies: - bignumber.js: 9.3.0 - 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.8: dependencies: braces: 3.0.3 @@ -8121,12 +7542,6 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 - mobx-utils@6.1.0(mobx@6.13.7): - dependencies: - mobx: 6.13.7 - - mobx@6.13.7: {} - module-alias@2.2.3: {} module-details-from-path@1.0.4: {} @@ -8143,17 +7558,6 @@ snapshots: natural-compare@1.4.0: {} - neverthrow@6.2.2: {} - - neverthrow@8.2.0: - optionalDependencies: - '@rollup/rollup-linux-x64-gnu': 4.40.2 - - no-case@3.0.4: - dependencies: - lower-case: 2.0.2 - tslib: 2.8.1 - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -8175,20 +7579,10 @@ snapshots: dependencies: boolbase: 1.0.0 - number-to-bn@1.7.0: - dependencies: - bn.js: 4.11.6 - strip-hex-prefix: 1.0.0 - object-assign@4.1.1: {} object-inspect@1.13.4: {} - object-is@1.1.6: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - object-keys@1.1.1: {} object.assign@4.1.7: @@ -8269,8 +7663,6 @@ snapshots: package-json-from-dist@1.0.1: {} - pako@2.1.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -8396,8 +7788,6 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - proxy-from-env@1.1.0: {} - punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -8408,10 +7798,6 @@ snapshots: queue-microtask@1.2.3: {} - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - react-is@18.3.1: {} readable-stream@3.6.2: @@ -8513,19 +7899,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.41.1 fsevents: 2.3.3 - rpc-websockets@9.1.1: - dependencies: - '@swc/helpers': 0.5.17 - '@types/uuid': 8.3.4 - '@types/ws': 8.18.1 - buffer: 6.0.3 - eventemitter3: 5.0.1 - uuid: 8.3.2 - ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -8638,10 +8011,26 @@ snapshots: slash@3.0.0: {} - snake-case@3.0.4: - dependencies: - dot-case: 3.0.4 - tslib: 2.8.1 + solana-kite@2.1.0(@solana/sysvars@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10): + dependencies: + '@solana-program/compute-budget': 0.11.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/memo': 0.10.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/system': 0.10.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@solana-program/token-2022': 0.6.1(@solana/kit@5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10))(@solana/sysvars@5.4.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)) + '@solana/assertions': 3.0.3(typescript@5.8.3) + '@solana/errors': 3.0.3(typescript@5.8.3) + '@solana/kit': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/promises': 3.0.3(typescript@5.8.3) + '@solana/transaction-confirmation': 5.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + dotenv: 16.5.0 + ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - '@solana/sysvars' + - bufferutil + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate source-map-support@0.5.13: dependencies: @@ -8666,12 +8055,6 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 - stream-chain@2.2.5: {} - - stream-json@1.9.1: - dependencies: - stream-chain: 2.2.5 - string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -8730,10 +8113,6 @@ snapshots: strip-final-newline@2.0.0: {} - strip-hex-prefix@1.0.0: - dependencies: - is-hex-prefixed: 1.0.0 - strip-json-comments@3.1.1: {} sucrase@3.35.0: @@ -8760,10 +8139,6 @@ snapshots: transitivePeerDependencies: - supports-color - superstruct@0.15.5: {} - - superstruct@2.0.2: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -8798,8 +8173,6 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - text-encoding-utf-8@1.0.2: {} - text-hex@1.0.0: {} thenify-all@1.6.0: @@ -8823,8 +8196,6 @@ snapshots: dependencies: is-number: 7.0.0 - toml@3.0.0: {} - tr46@0.0.3: {} tr46@1.0.1: @@ -8833,8 +8204,6 @@ snapshots: tree-kill@1.2.2: {} - treeify@1.1.0: {} - triple-beam@1.4.1: {} ts-api-utils@2.1.0(typescript@5.8.3): @@ -8998,8 +8367,6 @@ snapshots: - babel-plugin-macros - supports-color - typescript-collections@1.3.3: {} - typescript@5.8.3: {} ufo@1.6.1: {} @@ -9011,10 +8378,10 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.19.8: {} - undici-types@6.21.0: {} + undici-types@7.18.2: {} + undici@7.10.0: {} update-browserslist-db@1.1.3(browserslist@4.25.0): @@ -9032,24 +8399,12 @@ snapshots: node-gyp-build: 4.8.4 optional: true - utf8@3.0.0: {} - util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.0 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - utila@0.4.0: {} uuid@11.1.0: {} - uuid@8.3.2: {} - v8-compile-cache-lib@3.0.1: optional: true @@ -9063,17 +8418,6 @@ snapshots: dependencies: makeerror: 1.0.12 - web3-utils@1.10.4: - dependencies: - '@ethereumjs/util': 8.1.0 - bn.js: 5.2.2 - 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: {} webidl-conversions@4.0.2: {} @@ -9175,12 +8519,7 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 diff --git a/src/db/entities/tx-cache.ts b/src/db/entities/tx-cache.ts index fb548754..b51536fe 100644 --- a/src/db/entities/tx-cache.ts +++ b/src/db/entities/tx-cache.ts @@ -1,11 +1,14 @@ -import type { ParsedTransactionWithMeta } from '@solana/web3.js' import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm' +/** + * Legacy R4 transaction cache entity + * Kept for database compatibility - no longer actively used + */ @Entity() export class TxCache extends BaseEntity { @PrimaryColumn({ type: 'varchar' }) id!: string @Column('jsonb', { nullable: false }) - tx!: ParsedTransactionWithMeta + tx!: Record } diff --git a/src/lib/airdrop.ts b/src/lib/airdrop.ts deleted file mode 100644 index e7fc6881..00000000 --- a/src/lib/airdrop.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import superagent from 'superagent' -import { logger } from '../logger.js' - -export const airdrop = async ( - baseUrl: string, - token: string, - address: PublicKey, -) => { - logger.info(`Airdropping to: ${address.toBase58()}`) - - const res = await superagent.get(`${baseUrl}/airdrop/sage`).query({ - token, - address: address.toBase58(), - }) - - logger.info(`Airdropped to ${address.toBase58()}: ${res.statusCode}`) -} - -export const airdropCrew = async ( - baseUrl: string, - token: string, - address: PublicKey, - amount: number, -) => { - logger.info(`Airdropping Crew to: ${address.toBase58()}`) - - const res = await superagent.get(`${baseUrl}/airdrop/crew`).query({ - token, - address: address.toBase58(), - amount, - }) - - logger.info(`Airdropped Crew to ${address.toBase58()}: ${res.statusCode}`) -} - -export const airdropSol = async ( - baseUrl: string, - token: string, - address: PublicKey, - amount: number, -) => { - logger.info(`Airdropping SOL to: ${address.toBase58()}`) - - const res = await superagent.get(`${baseUrl}/airdrop/sol`).query({ - token, - address: address.toBase58(), - amount, - }) - - logger.info(`Airdropped SOL to ${address.toBase58()}: ${res.statusCode}`) -} diff --git a/src/lib/check-atlas-transactions.ts b/src/lib/check-atlas-transactions.ts deleted file mode 100644 index c797b2b6..00000000 --- a/src/lib/check-atlas-transactions.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { - ParsedInstruction, - ParsedTransactionWithMeta, - SignaturesForAddressOptions, -} from '@solana/web3.js' - -import dayjs from '../dayjs.js' -import { Transaction } from '../db/entities/transaction.js' -import { logger } from '../logger.js' -import { connection } from '../service/sol/index.js' -import { keyPair, resource } from '../service/wallet/index.js' - -import { ensureWallet } from './ensure-wallet.js' - -export const checkAtlasTransactions = async ( - options?: SignaturesForAddressOptions, -): Promise => { - const atlasTokenAccount = getAssociatedTokenAddressSync( - resource.atlas, - keyPair.publicKey, - true, - ) - const signatureList = await connection.getSignaturesForAddress( - atlasTokenAccount, - options, - ) - - const transactionList: ParsedTransactionWithMeta[] = [] - - for (const signature of signatureList) { - // https://docs.solana.com/developing/versioned-transactions#max-supported-transaction-version - const parsedSignature = await connection.getParsedTransaction( - signature.signature, - { - maxSupportedTransactionVersion: 0, - }, - ) - - if (parsedSignature) { - transactionList.push(parsedSignature) - } - } - - const txList: ParsedTransactionWithMeta[] = transactionList.filter( - (tx): tx is ParsedTransactionWithMeta => tx !== null, - ) - - 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}`, - ) - } 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() - } - } - } - }), - ), - ) -} diff --git a/src/lib/check-r4-transactions.ts b/src/lib/check-r4-transactions.ts deleted file mode 100644 index 3df90ad4..00000000 --- a/src/lib/check-r4-transactions.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { - ParsedInstruction, - PublicKey, - SignaturesForAddressOptions, -} from '@solana/web3.js' -import Big from 'big.js' - -import dayjs from '../dayjs.js' -import { Transaction } from '../db/entities/transaction.js' -import { Wallet } from '../db/entities/wallet.js' -import { TxCache } from '../db/entities/tx-cache.js' -import { logger } from '../logger.js' -import { Amounts } from '../service/fleet/const/index.js' -import { AD, connection } from '../service/sol/index.js' -import { keyPair } from '../service/wallet/index.js' - -import { ensureWallet } from './ensure-wallet.js' -import { getPrice } from './get-price.js' - -type ResourceName = 'food' | 'tool' | 'fuel' | 'ammo' - -export const checkR4Transactions = async ( - wallet: Wallet, - resourceName: ResourceName, - resource: PublicKey, - prices: Amounts, - options?: SignaturesForAddressOptions, -): Promise => { - const sourceTokenAccount = getAssociatedTokenAddressSync( - resource, - new PublicKey(wallet.publicKey), - true, - ) - const destTokenAccount = getAssociatedTokenAddressSync( - resource, - new PublicKey(keyPair.publicKey), - true, - ) - - const signatureList = await connection.getSignaturesForAddress( - sourceTokenAccount, - options, - ) - - logger.info( - `${signatureList.length} transactions found for ${resourceName} on ${wallet.publicKey}`, - ) - - for (const signatureInfo of signatureList) { - const { signature } = signatureInfo - - let tx = (await TxCache.findOneBy({ id: signature }))?.tx ?? null - - if (!tx) { - // https://docs.solana.com/developing/versioned-transactions#max-supported-transaction-version - tx = await connection.getParsedTransaction(signature, { - maxSupportedTransactionVersion: 0, - }) - if (tx) { - await TxCache.create({ id: signature, tx }).save() - } - } - - if (!tx) { - throw new Error('tx is null') - } - - const balanceChangeLength = tx?.meta?.postTokenBalances?.length - - if (balanceChangeLength && balanceChangeLength >= 2) { - for (const instr of tx.transaction.message.instructions) { - const instruction: ParsedInstruction = - instr as ParsedInstruction - - if ( - instruction.program === 'spl-token' && - instruction.parsed.info.source === - sourceTokenAccount.toBase58() && - instruction.parsed.info.destination === - destTokenAccount.toBase58() - ) { - const { info } = instruction.parsed - - const sender = info.authority ?? info.multisigAuthority - const originalAmount = - info.tokenAmount?.uiAmount || info.amount - const blockTime = tx.blockTime || 0 - const time = dayjs.unix(blockTime).toDate() - - const transaction = await Transaction.findOneBy({ - signature, - resource: resourceName.toUpperCase(), - }) - const log = transaction ? logger.debug : logger.info - - const amounts: Amounts = { - ammo: Big(0), - food: Big(0), - fuel: Big(0), - tool: Big(0), - } - - amounts[resourceName] = Big(originalAmount) - - const price = getPrice(amounts, prices) - - if (sender === keyPair.publicKey.toString()) { - const receiver = tx.meta?.postTokenBalances?.filter( - (tb) => - tb.owner?.toString() !== - keyPair.publicKey.toString(), - )[0].owner as string - - log( - `${receiver} -${originalAmount} ${resourceName.toUpperCase()} worth ${price.toFixed(AD)} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`, - ) - - if (!transaction) { - await Transaction.create({ - wallet: await ensureWallet(receiver), - amount: price.mul(-1).toNumber(), - originalAmount: Big(originalAmount) - .mul(-1) - .toNumber(), - resource: resourceName.toUpperCase(), - signature, - time, - }).save() - } - } else { - log( - `${sender} +${originalAmount} ${resourceName.toUpperCase()} worth ${price.toFixed(AD)} ATLAS ${dayjs.duration(dayjs().diff(time)).humanize(false)} ago`, - ) - - if (!transaction) { - await Transaction.create({ - wallet: await ensureWallet(sender), - amount: price.toNumber(), - originalAmount, - resource: resourceName.toUpperCase(), - signature, - time, - }).save() - } - } - } - } - } - } -} diff --git a/src/lib/check-transactions.ts b/src/lib/check-transactions.ts deleted file mode 100644 index 8ce63cdc..00000000 --- a/src/lib/check-transactions.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { LessThan, MoreThan } from 'typeorm' - -import dayjs from '../dayjs.js' -import { Transaction } from '../db/entities/transaction.js' -import { Wallet } from '../db/entities/wallet.js' -import { logger } from '../logger.js' -import { - getBalanceAtlas, - getResourceBalances, - getResourcePrices, -} from '../service/gm/index.js' -import { AD } from '../service/sol/index.js' -import { keyPair, resource } from '../service/wallet/index.js' - -import { checkAtlasTransactions } from './check-atlas-transactions.js' -import { checkR4Transactions } from './check-r4-transactions.js' - -export const checkTransactions = async (): Promise => { - const atlasBalance = await getBalanceAtlas(keyPair.publicKey) - const prices = getResourcePrices() - 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)`, - ) - - 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, - }), - ]) - - let getSigOptions - - if (lastInTx && lastOutTx) { - const until = dayjs(lastInTx.time).isBefore(lastOutTx.time) - ? lastInTx.signature - : lastOutTx.signature - - getSigOptions = { until } - } - - logger.info(`Total balance: ${total.toFixed(AD)} ATLAS`) - - await checkAtlasTransactions(getSigOptions) - - const wallets = await Wallet.findBy({ enabled: true }) - - for (const wallet of wallets) { - await checkR4Transactions( - wallet, - 'tool', - resource.tool, - prices, - getSigOptions, - ) - - await checkR4Transactions( - wallet, - 'ammo', - resource.ammo, - prices, - getSigOptions, - ) - - await checkR4Transactions( - wallet, - 'food', - resource.food, - prices, - getSigOptions, - ) - - await checkR4Transactions( - wallet, - 'fuel', - resource.fuel, - prices, - getSigOptions, - ) - } -} diff --git a/src/lib/const/fleet-refill.ts b/src/lib/const/fleet-refill.ts deleted file mode 100644 index 14bd89f4..00000000 --- a/src/lib/const/fleet-refill.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ShipStakingInfo } from '@staratlas/factory' -import Big from 'big.js' - -import { Amounts } from '../../service/fleet/const/index.js' - -export interface FleetRefill { - shipStakingInfo: ShipStakingInfo - amount: Amounts - price: Big -} diff --git a/src/lib/const/index.ts b/src/lib/const/index.ts deleted file mode 100644 index e644cd82..00000000 --- a/src/lib/const/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './fleet-refill.js' -export * from './max.js' diff --git a/src/lib/const/max.ts b/src/lib/const/max.ts deleted file mode 100644 index 3fd992bd..00000000 --- a/src/lib/const/max.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Big from 'big.js' - -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 deleted file mode 100644 index ebe69bc5..00000000 --- a/src/lib/ensure-wallet.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Wallet } from '../db/entities/wallet.js' - -export const ensureWallet = async (publicKey: string): Promise => { - 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 deleted file mode 100644 index cc5fb1a8..00000000 --- a/src/lib/fleet-depletion-info.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { getScoreVarsShipInfo, ShipStakingInfo } from '@staratlas/factory' - -import dayjs from '../dayjs.js' -import { getFleetRemainingResources } from '../service/fleet/index.js' -import { connection, fleetProgram } from '../service/sol/index.js' - -type FleetDepletionInfo = { - seconds: number - human: string -} - -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, - ) - - return { - seconds: secondsLeft, - human: dayjs.duration(secondsLeft, 'seconds').humanize(false), - } -} diff --git a/src/lib/get-price.ts b/src/lib/get-price.ts deleted file mode 100644 index 27d0b7e8..00000000 --- a/src/lib/get-price.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Big from 'big.js' - -import { Amounts } from '../service/fleet/const/index.js' -import { getResourcePrices } from '../service/gm/index.js' - -export const getPrice = (amount: Amounts, price?: Amounts): Big => { - const p: Amounts = price ? price : getResourcePrices() - const totalFuelPrice = amount.fuel.mul(p.fuel) - const totalFoodPrice = amount.food.mul(p.food) - const totalAmmoPrice = amount.ammo.mul(p.ammo) - const totalToolPrice = amount.tool.mul(p.tool) - - return totalFuelPrice - .add(totalFoodPrice) - .add(totalAmmoPrice) - .add(totalToolPrice) -} diff --git a/src/lib/index.ts b/src/lib/index.ts deleted file mode 100644 index b4fb8da0..00000000 --- a/src/lib/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from './check-transactions.js' -export * from './const/index.js' -export * from './fleet-depletion-info.js' -export * from './get-price.js' -export * from './refill.js' -export * from './refill-player.js' -export * from './refill-strategy/index.js' -export * from './stock-resources.js' -export * from './telegram/index.js' diff --git a/src/lib/refill-player.ts b/src/lib/refill-player.ts deleted file mode 100644 index cf960f63..00000000 --- a/src/lib/refill-player.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { getAllFleetsForUserPublicKey } from '@staratlas/factory' -import Big from 'big.js' - -import { Sentry } from '../sentry.js' - -import dayjs from '../dayjs.js' -import { Refill } from '../db/entities/refill.js' -import { Wallet } from '../db/entities/wallet.js' -import { logger } from '../logger.js' -import { refillFleet } from '../service/fleet/index.js' -import { AD, connection, fleetProgram } from '../service/sol/index.js' - -import { FleetRefill } from './const/index.js' -import { fleetDepletionInfo } from './fleet-depletion-info.js' -import { RefillStrategy } from './refill-strategy/index.js' -import { getShipName } from './stock-resources.js' - -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`) - - return [] - } - - 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) - - const totalCost = refills.reduce((acc, curr) => acc.add(curr.price), Big(0)) - 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() }, - ) - - 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 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).flat() -} diff --git a/src/lib/refill-strategy/full-refill-strategy.ts b/src/lib/refill-strategy/full-refill-strategy.ts deleted file mode 100644 index d9a4d7ed..00000000 --- a/src/lib/refill-strategy/full-refill-strategy.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { getScoreVarsShipInfo } from '@staratlas/factory' -import Big from 'big.js' - -import { getFleetRemainingResources } from '../../service/fleet/index.js' -import { Amounts } from '../../service/fleet/const/index.js' -import { connection, fleetProgram } from '../../service/sol/index.js' -import { FleetRefill } from '../const/index.js' -import { getPrice } from '../get-price.js' - -import { RefillStrategy } from './refill-strategy.js' - -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 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 - }), - ) diff --git a/src/lib/refill-strategy/index.ts b/src/lib/refill-strategy/index.ts deleted file mode 100644 index f0883129..00000000 --- a/src/lib/refill-strategy/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './full-refill-strategy.js' -export * from './optimal-refill-strategy.js' -export * from './refill-strategy.js' diff --git a/src/lib/refill-strategy/optimal-refill-strategy.ts b/src/lib/refill-strategy/optimal-refill-strategy.ts deleted file mode 100644 index f40abb4d..00000000 --- a/src/lib/refill-strategy/optimal-refill-strategy.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { getScoreVarsShipInfo } from '@staratlas/factory' -import Big from 'big.js' - -import { getFleetRemainingResources } from '../../service/fleet/index.js' -import { Amounts } from '../../service/fleet/const/index.js' -import { connection, fleetProgram } from '../../service/sol/index.js' -import { FleetRefill, max } from '../const/index.js' -import { getPrice } from '../get-price.js' - -import { RefillStrategy } from './refill-strategy.js' - -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, - ) - - 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 - }), - ) -} diff --git a/src/lib/refill-strategy/refill-strategy.ts b/src/lib/refill-strategy/refill-strategy.ts deleted file mode 100644 index 4dff1ce6..00000000 --- a/src/lib/refill-strategy/refill-strategy.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ShipStakingInfo } from '@staratlas/factory' - -import { FleetRefill } from '../const/index.js' - -export type RefillStrategy = ( - shipStakingInfos: ShipStakingInfo[], -) => Promise diff --git a/src/lib/refill.ts b/src/lib/refill.ts deleted file mode 100644 index e5570f47..00000000 --- a/src/lib/refill.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { getAllFleetsForUserPublicKey } from '@staratlas/factory' -import Big from 'big.js' - -import dayjs from '../dayjs.js' -import { Wallet } from '../db/entities/wallet.js' -import { logger } from '../logger.js' -import { getResourcePrices } from '../service/gm/index.js' -import { AD, connection, fleetProgram } from '../service/sol/index.js' - -import { max } from './const/index.js' -import { refillPlayer } from './refill-player.js' -import { optimalRefillStrategy } from './refill-strategy/index.js' -import { getDailyBurnRate } from './stock-resources.js' - -export const refill = async (): Promise => { - const players = await Wallet.findBy({ enabled: true }) - - for (const player of players) { - if (dayjs().isAfter(player.nextRefill)) { - await refillPlayer( - new PublicKey(player.publicKey), - optimalRefillStrategy, - ) - } - - 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 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(`Total Tipped: ${(await player.totalTipped()).toFixed(AD)}`) - logger.info('-----------------------------------------------------') - } -} diff --git a/src/lib/stock-resources.ts b/src/lib/stock-resources.ts deleted file mode 100644 index 33cc3ca9..00000000 --- a/src/lib/stock-resources.ts +++ /dev/null @@ -1,266 +0,0 @@ -import { AnchorProvider, Idl, Program } from '@coral-xyz/anchor' -import { Connection, PublicKey } from '@solana/web3.js' -import { - ShipStakingInfo, - getScoreIDL, - getScoreVarsShipInfo, - getShipStakingAccount, -} from '@staratlas/factory' -import Big from 'big.js' -import superagent from 'superagent' - -import dayjs from '../dayjs.js' -import { ShipInfo } from '../db/entities/ship-info.js' -import { Wallet } from '../db/entities/wallet.js' -import { logger } from '../logger.js' -import { - getFleetRemainingResources, - getTimePass, -} from '../service/fleet/index.js' -import { Amounts } from '../service/fleet/const/index.js' -import { - buyResources, - getResourceBalances, - getResourcePrices, -} from '../service/gm/index.js' -import { AD, connection, fleetProgram } from '../service/sol/index.js' -import { keyPair } from '../service/wallet/index.js' - -/** - * Returns a list of player deployed fleets to the SCORE program - * - * @param conn - web3.Connection object - * @param playerPublicKey - Player's public key - * @param programId - Deployed program ID for the SCORE program - * @returns - [Ship Staking Account Infos] - */ -const getAllFleetsForUserPublicKey = async ( - conn: Connection, - playerPublicKey: PublicKey, - programId: PublicKey, -): Promise => { - const idl = getScoreIDL(programId) - // @ts-expect-error 123 - const provider = new AnchorProvider(conn, null, null) - const program = new Program(idl, programId, provider) - - const shipsRegistered = await program.account.scoreVarsShip.all() - - const playerShipStakingAccounts = [] - - for (const ship of shipsRegistered) { - const [playerShipStakingAccount] = await getShipStakingAccount( - programId, - ship.account.shipMint as PublicKey, - playerPublicKey, - ) - - playerShipStakingAccounts.push(playerShipStakingAccount) - } - - const playerFleets: ShipStakingInfo[] = [] - - for (const acc of playerShipStakingAccounts) { - try { - const fleet = await program.account.shipStaking.fetchNullable(acc) - - if (fleet) { - playerFleets.push(fleet) - } - } catch (e) { - logger.error(e) - } - } - logger.info( - `Found ${playerFleets.length} fleets for ${playerPublicKey.toString()}`, - ) - - return playerFleets -} - -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 urlSplit = shipNftInfo.body.image.slice(0, -4).split('/') - const imageName = urlSplit[urlSplit.length - 1] - - shipInfo = await ShipInfo.create({ - mint, - name: shipNftInfo.body.name, - imageName, - }).save() - } catch { - return 'n/a' - } - } - - return shipInfo.name -} - -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 => { - const dayInSeconds = 86400 - - const resourcePerDay: Amounts = { - food: Big(0), - tool: Big(0), - ammo: 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), - ) - }), - ) - - 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()}`, - ) -} - -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 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), - } - - 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) - - logger.info( - `Buying Resources...${JSON.stringify(amount)} for ${totalPrice.toFixed(AD)} ATLAS`, - ) - - const txs = await buyResources(amount) - - 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 deleted file mode 100644 index abadf592..00000000 --- a/src/lib/telegram/commands/disable-notify.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const disableNotify = (bot: Telegraf): void => { - bot.command(['disable-notify'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - ctx.user.notify = false - await ctx.user.save() - await ctx.reply( - 'I will stay silent unless there is something urgent happening.', - ) - }) - }) -} diff --git a/src/lib/telegram/commands/disable.ts b/src/lib/telegram/commands/disable.ts deleted file mode 100644 index 0404631f..00000000 --- a/src/lib/telegram/commands/disable.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const disable = (bot: Telegraf): void => { - bot.command(['disable'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - ctx.user.enabled = false - await ctx.user.save() - await ctx.reply('Refilling is disabled!') - }) - }) -} diff --git a/src/lib/telegram/commands/enable-notify.ts b/src/lib/telegram/commands/enable-notify.ts deleted file mode 100644 index 456b4b6f..00000000 --- a/src/lib/telegram/commands/enable-notify.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const enableNotify = (bot: Telegraf): void => { - bot.command(['enable-notify'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - ctx.user.notify = true - await ctx.user.save() - await ctx.reply('I will notify you after I refilled your fleets.') - }) - }) -} diff --git a/src/lib/telegram/commands/enable.ts b/src/lib/telegram/commands/enable.ts deleted file mode 100644 index df10c613..00000000 --- a/src/lib/telegram/commands/enable.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const enable = (bot: Telegraf): void => { - bot.command(['enable'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - ctx.user.enabled = true - await ctx.user.save() - await ctx.reply('Refilling is enabled!') - }) - }) -} diff --git a/src/lib/telegram/commands/help.ts b/src/lib/telegram/commands/help.ts deleted file mode 100644 index 74810be8..00000000 --- a/src/lib/telegram/commands/help.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { config } from '../../../config/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' - -export const help = (bot: Telegraf): void => { - bot.command(['start', 'help'], async (ctx: ContextMessageUpdate) => { - await ctx.persistentChatAction('typing', async () => { - await ctx.replyWithHTML(` -Welcome to fleetbot! -I can automatically refill your Star Atlas enlisted fleets. - -Prerequisites: -- Your wallet has enlisted Ships to the Faction Fleet. You can do that on https://play.staratlas.com -- You have refilled your ships at least once. This creates escrow accounts for the supplies. Unfortunately, I cannot do that for you. - -Last but not least, you have to send me some ATLAS which I will use as your credits for the supplies. - -You can send it to one of the following addresses: -- ${config.user.address1} -- ${config.user.address2} - -As soon as I receive the ATLAS, I will start refilling your ships in regular intervals. I use an optimized refilling strategy, so that you are always covered. -However, to save you on commission, I will not fully load the ship. You can always refill your fleet by yourself. I will only jump in if you are running low. - -Since it is now possible to stake your claim stakes and generate R4, I decided to accept this as a payment method as well. Please first set everything up -by using ATLAS as explained above. As soon as everything is set, you can also send me your R4 (tool, fuel, ammo, food). I am not keeping track of individual R4 account balances. -Instead, each R4 you send me will credit your internal ATLAS balance by the current market price. - -Withdrawals are only possible in ATLAS. - -For the hard work of refilling all the fleets, I keep 15% of the ATLAS I spend for myself. -You can change the commission at any time to any value. - -Bare in mind that I check the ledger in 10 minute intervals. So your deposits will be recognized by me a little bit delayed. Dont' panic. :) -If something does not work as expected, you can always use the /support command to get a human fixing the issues. - -Commands: - -/help Prints this message -/support Talk to a human -/verify {publicKey} Connect your Telegram Account to a wallet - -Commands for verified users: - -/enable Enable automatic fleet refilling -/disable Disable automatic fleet refilling -/refill Trigger an immediate refill -/stats Query some stats -/transactions Query transactions associated to your wallet -/refills Query refill activity -/withdraw {amount} Withdraw ATLAS back to your wallet -/logout Disconnects Telegram Account from wallet -/tip Set or query tip setting (default 15%) - `) - }) - }) -} diff --git a/src/lib/telegram/commands/index.ts b/src/lib/telegram/commands/index.ts deleted file mode 100644 index 29d8f607..00000000 --- a/src/lib/telegram/commands/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export * from './disable.js' -export * from './disable-notify.js' -export * from './enable.js' -export * from './enable-notify.js' -export * from './help.js' -export * from './logout.js' -export * from './meme/index.js' -export * from './refill.js' -export * from './refills.js' -export * from './stats.js' -export * from './support.js' -export * from './tip.js' -export * from './transactions.js' -export * from './verify.js' -export * from './withdraw.js' diff --git a/src/lib/telegram/commands/logout.ts b/src/lib/telegram/commands/logout.ts deleted file mode 100644 index 43bf618c..00000000 --- a/src/lib/telegram/commands/logout.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { Wallet } from '../../../db/entities/wallet.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const logout = (bot: Telegraf): void => { - bot.command(['logout'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (ctx.user.enabled) { - await ctx.reply( - 'Your wallet is currently enabled, toggle with /disable command', - ) - - return - } - - ctx.user.authed = false - ctx.user.telegramId = null - ctx.user.authTxAmount = null - ctx.user.authExpire = null - - await Wallet.save(ctx.user) - - ctx.reply('Logged out successfully') - }) - }) -} diff --git a/src/lib/telegram/commands/meme/index.ts b/src/lib/telegram/commands/meme/index.ts deleted file mode 100644 index 9664b7fc..00000000 --- a/src/lib/telegram/commands/meme/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './kitten.js' -export * from './porn.js' -export * from './wen.js' diff --git a/src/lib/telegram/commands/meme/kitten.ts b/src/lib/telegram/commands/meme/kitten.ts deleted file mode 100644 index 5303511a..00000000 --- a/src/lib/telegram/commands/meme/kitten.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { faker } from '@faker-js/faker' -import { Telegraf } from 'telegraf' - -import { logger } from '../../../../logger.js' -import { ContextMessageUpdate } from '../../context-message-update.js' - -export const kitten = (bot: Telegraf): void => { - bot.command(['kitten'], async (ctx) => { - await ctx.persistentChatAction('upload_photo', async () => { - const x = faker.number.int({ min: 128, max: 2048 }) - - try { - await ctx.replyWithPhoto(`https://placekitten.com/${x}/${x}`) - } 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 deleted file mode 100644 index 83d621bf..00000000 --- a/src/lib/telegram/commands/meme/porn.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { ShipInfo } from '../../../../db/entities/ship-info.js' -import { logger } from '../../../../logger.js' -import { ContextMessageUpdate } from '../../context-message-update.js' - -export const porn = (bot: Telegraf): void => { - bot.command(['porn'], async (ctx) => { - await ctx.persistentChatAction('upload_photo', async () => { - try { - const randomShip = await ShipInfo.getRepository() - .createQueryBuilder('ship_info') - .select() - .orderBy('RANDOM()') - .getOne() - - const imageName = randomShip?.imageName || '' - const imageUrl = `https://storage.googleapis.com/nft-assets/items/${imageName}.jpg` - - await ctx.replyWithPhoto(imageUrl) - } 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 deleted file mode 100644 index f7e2bef2..00000000 --- a/src/lib/telegram/commands/meme/wen.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { logger } from '../../../../logger.js' -import { ContextMessageUpdate } from '../../context-message-update.js' - -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) { - 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 deleted file mode 100644 index 3f41f580..00000000 --- a/src/lib/telegram/commands/refill.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { Telegraf } from 'telegraf' - -import { AD } from '../../../service/sol/index.js' -import { refillPlayer } from '../../refill-player.js' -import { - fullRefillStrategy, - optimalRefillStrategy, -} from '../../refill-strategy/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const refill = (bot: Telegraf): void => { - bot.command(['refill'], async (ctx: ContextMessageUpdate) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (!ctx.user.enabled) { - 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' - - 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) { - await ctx.replyWithHTML(` -Signature: click -Time: ${userRefill.time.toLocaleDateString()} ${userRefill.time.toLocaleTimeString()} -Fleet: ${userRefill.fleet} -Food: ${userRefill.food} -Tool: ${userRefill.tool} -Fuel: ${userRefill.fuel} -Ammo ${userRefill.ammo} -Price: ${userRefill.price.toFixed(AD)} ATLAS`) - } - - if (strategyName === 'optimal') { - 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 deleted file mode 100644 index ffe77f46..00000000 --- a/src/lib/telegram/commands/refills.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { Refill } from '../../../db/entities/refill.js' -import { AD } from '../../../service/sol/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const refills = (bot: Telegraf): void => { - bot.command(['refills'], async (ctx: ContextMessageUpdate) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (!ctx.user.enabled) { - await ctx.reply( - 'Your wallet is currently disabled, toggle with /enable command', - ) - - return - } - - 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' }, - }) - - for (const refill of userRefills) { - await ctx.replyWithHTML(` -Signature: click -Time: ${refill.time.toLocaleDateString()} ${refill.time.toLocaleTimeString()} -Fleet: ${refill.fleet} -Food: ${refill.food} -Tool: ${refill.tool} -Fuel: ${refill.fuel} -Ammo ${refill.ammo} -Price: ${refill.price.toFixed(AD)} ATLAS`) - } - }) - }) -} diff --git a/src/lib/telegram/commands/stats.ts b/src/lib/telegram/commands/stats.ts deleted file mode 100644 index 0e7fefde..00000000 --- a/src/lib/telegram/commands/stats.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { getAllFleetsForUserPublicKey } from '@staratlas/factory' -import Big from 'big.js' -import { Telegraf } from 'telegraf' -import { Between, MoreThanOrEqual } from 'typeorm' - -import dayjs from '../../../dayjs.js' -import { Refill } from '../../../db/entities/refill.js' -import { getResourcePrices } from '../../../service/gm/index.js' -import { AD, connection, fleetProgram } from '../../../service/sol/index.js' -import { getDailyBurnRate, getPendingRewards } from '../../stock-resources.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const stats = (bot: Telegraf): void => { - bot.command(['stats'], async (ctx: ContextMessageUpdate) => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (!ctx.user.enabled) { - await ctx.reply( - 'Your wallet is currently disabled, toggle with /enable command', - ) - - return - } - - await ctx.reply('Gathering statistics, hold on...') - await ctx.persistentChatAction('typing', async () => { - const player = ctx.user - - if (player) { - 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), - ) - - const yesterdayRefills = await Refill.findBy({ - walletPublicKey: player.publicKey, - time: Between( - dayjs().subtract(1, 'day').startOf('day').toDate(), - dayjs().subtract(1, 'day').endOf('day').toDate(), - ), - }) - 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 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 burnAvg7 = burn7d.div(7) - const burnAvg30 = burn30d.div(30) - - const burnAvg = avg([burnAvg7, burnAvg30]) - - const drift = burnAvg.sub(burnPerDay) - - await ctx.replyWithMarkdownV2( - ` -*Next refill in:* ${dayjs.duration(diffToNextRefill, 'second').humanize()} -*Balance:* ${playerBalance.toFixed(AD)} ATLAS -*Burn \\(estimate\\):* ${burnPerDay.toFixed(AD)} ATLAS per day -*Burn \\(today\\):* ${burnToday.toFixed(AD)} ATLAS \\(${todayRefills.length} refills\\) -*Burn \\(yesterday\\):* ${burnYesterday.toFixed(AD)} ATLAS \\(${yesterdayRefills.length} refills\\) -*Burn \\(7d\\):* ${burn7d.toFixed(AD)} ATLAS \\(${refills7d.length} refills\\) \\[avg.: ${burnAvg7.toFixed(AD)} / day\\] -*Burn \\(30d\\):* ${burn30d.toFixed(AD)} ATLAS \\(${refills30d.length} refills\\) \\[avg.: ${burnAvg30.toFixed(AD)} / day\\] -*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, '\\.'), - ) - } - }) - }) -} diff --git a/src/lib/telegram/commands/support.ts b/src/lib/telegram/commands/support.ts deleted file mode 100644 index dedb1afe..00000000 --- a/src/lib/telegram/commands/support.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { config } from '../../../config/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' - -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}`, - ) - }) - }) -} diff --git a/src/lib/telegram/commands/tip.ts b/src/lib/telegram/commands/tip.ts deleted file mode 100644 index bb5a24e4..00000000 --- a/src/lib/telegram/commands/tip.ts +++ /dev/null @@ -1,42 +0,0 @@ -import Big from 'big.js' -import { Telegraf } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const tip = (bot: Telegraf): void => { - bot.command(['tip'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (ctx.params.length !== 1) { - 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 - } - - let [tipPercent] = ctx.params - - tipPercent = tipPercent.replace('%', '') - try { - ctx.user.tip = Big(tipPercent).div(100).abs().toNumber() - await ctx.user.save() - } 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()} %`, - ) - }) - }) -} diff --git a/src/lib/telegram/commands/transactions.ts b/src/lib/telegram/commands/transactions.ts deleted file mode 100644 index 105fa5ea..00000000 --- a/src/lib/telegram/commands/transactions.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { Transaction } from '../../../db/entities/transaction.js' -import { AD } from '../../../service/sol/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized } from '../response/index.js' - -export const transactions = (bot: Telegraf): void => { - bot.command(['transactions'], async (ctx: ContextMessageUpdate) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (!ctx.user.enabled) { - await ctx.reply( - 'Your wallet is currently disabled, toggle with /enable command', - ) - - return - } - - 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' }, - }) - - for (const transaction of userTransactions) { - await ctx.replyWithHTML(` -Signature: click -Time: ${transaction.time.toLocaleDateString()} ${transaction.time.toLocaleTimeString()} -Amount: ${transaction.amount.toFixed(AD)} ATLAS`) - } - }) - }) -} diff --git a/src/lib/telegram/commands/verify.ts b/src/lib/telegram/commands/verify.ts deleted file mode 100644 index 9c602358..00000000 --- a/src/lib/telegram/commands/verify.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { faker } from '@faker-js/faker' -import dayjs from 'dayjs' -import { Telegraf } from 'telegraf' - -import { Wallet } from '../../../db/entities/wallet.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { - alreadyRegistered, - authPending, - unknownWallet, - wrongParamCount, -} from '../response/index.js' - -export const verify = (bot: Telegraf): void => { - bot.command(['verify'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (ctx.user) { - if (ctx.authed) { - await alreadyRegistered(ctx) - - return - } - - const telegramId = ctx.from.id - const wallet = await Wallet.findOneBy({ telegramId }) - - if (wallet && dayjs().isBefore(wallet.authExpire)) { - await authPending(ctx, wallet) - - return - } - } - - if (ctx.params.length !== 1) { - await wrongParamCount(ctx, 'Usage: /verify {publicKey}') - - return - } - const [publicKey] = ctx.params - const wallet = await Wallet.findOneBy({ publicKey }) - - if (!wallet) { - await unknownWallet(ctx) - - 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.", - ) - wallet.telegramId = ctx.from.id - wallet.authExpire = dayjs().add(1, 'hour').toDate() - wallet.authTxAmount = faker.number.float({ - min: 0.1, - max: 0.999, - fractionDigits: 3, - }) - await wallet.save() - await authPending(ctx, wallet) - 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 deleted file mode 100644 index 87a10d94..00000000 --- a/src/lib/telegram/commands/withdraw.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import Big from 'big.js' -import { Telegraf } from 'telegraf' - -import dayjs from '../../../dayjs.js' -import { Transaction } from '../../../db/entities/transaction.js' -import { sendAtlas } from '../../../service/gm/index.js' -import { AD } from '../../../service/sol/index.js' -import { ContextMessageUpdate } from '../context-message-update.js' -import { unauthorized, wrongParamCount } from '../response/index.js' - -export const withdraw = (bot: Telegraf): void => { - bot.command(['withdraw'], async (ctx) => { - await ctx.persistentChatAction('typing', async () => { - if (!ctx.user || !ctx.authed) { - await unauthorized(ctx) - - return - } - - if (ctx.params.length !== 1) { - await wrongParamCount(ctx, 'Usage: /withdraw {amount}|all') - - return - } - - const wallet = ctx.user - const userBalance = await wallet.getBalance() - 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)}`, - ) - - return - } - - await ctx.reply( - `Sending ${withdrawAmount} ATLAS to ${ctx.user.publicKey}`, - ) - const signatures = await sendAtlas( - new PublicKey(ctx.user.publicKey), - withdrawAmount.toNumber(), - ) - - const amount = -withdrawAmount - - 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/context-message-update.ts b/src/lib/telegram/context-message-update.ts deleted file mode 100644 index ee768fb2..00000000 --- a/src/lib/telegram/context-message-update.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Context } from 'telegraf' -import { Update } from 'telegraf/types' -import { Wallet } from '../../db/entities/wallet' - -export interface ContextMessageUpdate extends Context { - user: Wallet | null - authed: boolean - params: string[] -} diff --git a/src/lib/telegram/index.ts b/src/lib/telegram/index.ts deleted file mode 100644 index 3d0ae021..00000000 --- a/src/lib/telegram/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './context-message-update.js' -export * from './telegram-bot.js' diff --git a/src/lib/telegram/middleware/auth.ts b/src/lib/telegram/middleware/auth.ts deleted file mode 100644 index d41c5d15..00000000 --- a/src/lib/telegram/middleware/auth.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Middleware } from 'telegraf' -import { Wallet } from '../../../db/entities/wallet.js' - -import { ContextMessageUpdate } from '../context-message-update.js' - -export const auth: Middleware = async ( - ctx: ContextMessageUpdate, - next: any, -) => { - if (ctx.from?.id) { - const wallet = await Wallet.findOneBy({ telegramId: ctx.from.id }) - - ctx.authed = wallet?.authed || false - ctx.user = wallet - } - - await next() -} diff --git a/src/lib/telegram/middleware/index.ts b/src/lib/telegram/middleware/index.ts deleted file mode 100644 index 9401a10c..00000000 --- a/src/lib/telegram/middleware/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './auth.js' diff --git a/src/lib/telegram/middleware/params.ts b/src/lib/telegram/middleware/params.ts deleted file mode 100644 index 4e6c4a04..00000000 --- a/src/lib/telegram/middleware/params.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Middleware } from 'telegraf' - -import { ContextMessageUpdate } from '../context-message-update.js' - -export const params: Middleware = ( - ctx: ContextMessageUpdate, - next: any, -) => { - if (ctx.message && 'text' in ctx.message) { - ctx.params = ctx.message.text.split(' ').splice(1) - } - - return next() -} diff --git a/src/lib/telegram/response/already-registered.ts b/src/lib/telegram/response/already-registered.ts deleted file mode 100644 index 019e88f5..00000000 --- a/src/lib/telegram/response/already-registered.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Message } from 'telegraf/types' - -import { ContextMessageUpdate } from '../context-message-update.js' - -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 deleted file mode 100644 index 215b3cec..00000000 --- a/src/lib/telegram/response/auth-pending.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Message } from 'telegraf/types' - -import dayjs from '../../../dayjs.js' -import { Wallet } from '../../../db/entities/wallet' -import { ContextMessageUpdate } from '../context-message-update.js' - -export const authPending = ( - ctx: ContextMessageUpdate, - wallet: Wallet, -): Promise => { - const authExpire = dayjs().diff(wallet.authExpire, 'second') - - return ctx.reply(` -Please send exactly ${wallet.authTxAmount} ATLAS to fleetbot.sol. -You have ${dayjs.duration(authExpire, 'seconds').humanize(false)} left! -`) -} diff --git a/src/lib/telegram/response/index.ts b/src/lib/telegram/response/index.ts deleted file mode 100644 index 74ca5ee7..00000000 --- a/src/lib/telegram/response/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './already-registered.js' -export * from './auth-pending.js' -export * from './unauthorized.js' -export * from './unknown-wallet.js' -export * from './wrong-param-count.js' diff --git a/src/lib/telegram/response/unauthorized.ts b/src/lib/telegram/response/unauthorized.ts deleted file mode 100644 index e18e371c..00000000 --- a/src/lib/telegram/response/unauthorized.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Message } from 'telegraf/types' - -import { ContextMessageUpdate } from '../context-message-update.js' - -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 deleted file mode 100644 index a9dc9f84..00000000 --- a/src/lib/telegram/response/unknown-wallet.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Message } from 'telegraf/types' - -import { ContextMessageUpdate } from '../context-message-update.js' - -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 deleted file mode 100644 index e3cd080a..00000000 --- a/src/lib/telegram/response/wrong-param-count.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Message } from 'telegraf/types' - -import { ContextMessageUpdate } from '../context-message-update.js' - -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 deleted file mode 100644 index ab14b8d1..00000000 --- a/src/lib/telegram/telegram-bot.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Telegraf } from 'telegraf' - -import { config } from '../../config/index.js' - -import * as commands from './commands/index.js' -import { ContextMessageUpdate } from './context-message-update.js' -import { auth } from './middleware/index.js' -import { params } from './middleware/params.js' - -const telegramBot: Telegraf = new Telegraf( - config.bot.telegramToken, - { handlerTimeout: 360_000 }, -) - -telegramBot.use(auth) -telegramBot.use(params) - -for (const command of Object.values(commands)) { - command(telegramBot) -} - -process.once('SIGINT', () => telegramBot.stop('SIGINT')) -process.once('SIGTERM', () => telegramBot.stop('SIGTERM')) - -export { telegramBot } diff --git a/src/main/airdrop/airdrop.ts b/src/main/airdrop/airdrop.ts index a61832d8..9903ffb8 100644 --- a/src/main/airdrop/airdrop.ts +++ b/src/main/airdrop/airdrop.ts @@ -1,24 +1,49 @@ -import { Keypair, PublicKey } from '@solana/web3.js' -import { readAllFromRPC } from '@staratlas/data-source' -import { PlayerProfile } from '@staratlas/player-profile' +/** + * Airdrop module for C4 devnet testing + * Requests airdrops from Star Atlas devnet faucet + */ +import type { Address, KeyPairSigner } from '@solana/kit' import { CronJob } from 'cron' import * as fs from 'node:fs' import path from 'node:path' +import superagent from 'superagent' import { config } from '../../config/index.js' -import { airdrop } from '../../lib/airdrop.js' import { logger } from '../../logger.js' -import { connection } from '../../service/sol/index.js' -import { loadKeypairFromFile } from '../../service/wallet' -import { programs } from '../basedbot/lib/programs.js' +import { loadKeypairFromFile } from '../../service/wallet/init-keypair.js' import { sageGame } from '../basedbot/lib/sage/state/game.js' -import { Faction } from '../basedbot/lib/util/galaxy-sectors-data' -import { createAndInitializeCharacter } from '../basedbot/lib/util/profile' +import { getPlayerContext } from '../basedbot/lib/sage/state/user-account.js' +import { Faction } from '../basedbot/lib/util/galaxy-sectors-data.js' +import { createAndInitializeCharacter } from '../basedbot/lib/util/profile.js' let airdropCronJob: CronJob | undefined -const findKeyFiles = (dirPath: string): Map => { - let keyMap = new Map() +/** + * Request airdrop from Star Atlas devnet faucet + */ +const requestAirdrop = async ( + baseUrl: string, + token: string, + address: Address, +): Promise => { + logger.info(`Airdropping to: ${address}`) + + const res = await superagent.get(`${baseUrl}/airdrop/sage`).query({ + token, + address, + }) + + logger.info(`Airdropped to ${address}: ${res.statusCode}`) +} + +/** + * Find key files in a directory + */ +const findKeyFiles = async ( + dirPath: string, +): Promise> => { + const keyMap = new Map() + if (!fs.existsSync(dirPath)) { logger.warn(`${dirPath} does not exist`) return keyMap @@ -31,18 +56,21 @@ const findKeyFiles = (dirPath: string): Map => { const fullPath = path.join(dirPath, entry.name) if (entry.isDirectory()) { - keyMap = new Map([...keyMap, ...findKeyFiles(fullPath)]) + const subKeys = await findKeyFiles(fullPath) + for (const [addr, signer] of subKeys) { + keyMap.set(addr, signer) + } } else if (entry.isFile() && entry.name.endsWith('.json')) { try { - const keyPair = loadKeypairFromFile(fullPath) - keyMap.set(keyPair.publicKey.toBase58(), keyPair) + const signer = await loadKeypairFromFile(fullPath) + keyMap.set(signer.address, signer) } catch { - logger.warn(`${entry.name} is not a Key`) + logger.warn(`${entry.name} is not a valid key file`) } } } } catch (error) { - console.error(`Error reading directory: ${dirPath}`, error) + logger.error(`Error reading directory: ${dirPath}`, error) } return keyMap @@ -63,60 +91,55 @@ export const stop = async (): Promise => { } } -const airdropOrCreateProfile = async (user: PublicKey): Promise => { - const myProfiles = await readAllFromRPC( - connection, - programs.playerProfile, - PlayerProfile, - 'processed', - [ - { - memcmp: { - offset: PlayerProfile.MIN_DATA_SIZE + 2, - bytes: user.toBase58(), - }, - }, - ], - ) - const game = await sageGame() +/** + * Airdrop to a wallet, creating profile if needed + */ +const airdropOrCreateProfile = async ( + walletAddress: Address, + keys: Map, +): Promise => { + const signer = keys.get(walletAddress) + + if (!signer) { + logger.warn(`No key found for ${walletAddress}, skipping`) + return + } + + try { + // Check if player has a profile + const player = await getPlayerContext(walletAddress, signer) - if (myProfiles.length > 0) { logger.info( - `Airdropping to ${user.toBase58()} with profile ${myProfiles[0].key.toBase58()}`, + `Airdropping to ${walletAddress} with profile ${player.profile.key}`, ) - await airdrop( + await requestAirdrop( config.app.airdropUrl, config.app.airdropToken, - new PublicKey(user), + walletAddress, + ) + } catch { + // No profile - create one + logger.info(`Creating profile for ${walletAddress}`) + const game = await sageGame() + + const addrStr = walletAddress as string + await createAndInitializeCharacter( + game, + `${addrStr.slice(0, 4)}...${addrStr.slice(-4)}`, + Faction.ONI, + signer, ) - } else { - logger.info(`Creating profile for ${user.toBase58()}`) - const keyDir = process.env.KEY_DIR || '/tmp/keys' - logger.info(`Looking for keys in ${path.dirname(keyDir)}`) - const keys = findKeyFiles(keyDir) - - const keyPair = keys.get(user.toBase58()) - - if (keyPair) { - const pubStr = keyPair.publicKey.toBase58() - await createAndInitializeCharacter( - game, - `${pubStr.slice(0, 4)}...${pubStr.slice(-4)}`, - Faction.ONI, - keyPair, - ) - } else { - logger.warn( - `No key found for ${user.toBase58()}, cannot create profile`, - ) - } } } const airdropTick = async (): Promise => { + const keyDir = process.env.KEY_DIR || '/tmp/keys' + logger.info(`Looking for keys in ${keyDir}`) + const keys = await findKeyFiles(keyDir) + await Promise.all( config.app.airdropWallets.map((w) => - airdropOrCreateProfile(new PublicKey(w)), + airdropOrCreateProfile(w as Address, keys), ), ) } diff --git a/src/main/basedbot/basedbot.ts b/src/main/basedbot/basedbot.ts index fc32ac81..1f6fbda4 100644 --- a/src/main/basedbot/basedbot.ts +++ b/src/main/basedbot/basedbot.ts @@ -1,29 +1,15 @@ -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, Starbase } from '@staratlas/sage' -import BN from 'bn.js' -import { config } from '../../config/index.js' +import type { Address } from '@solana/kit' +import type { Fleet, Game, StarSystem } from '@staratlas/dev-sage' +import { config } from '../../config/index.js' import { logger } from '../../logger.js' import { Sentry } from '../../sentry.js' import { sleep } from '../../service/sleep.js' -import { connection } from '../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../service/sol/send-and-confirm-tx.js' import { keyPair } from '../../service/wallet/index.js' import { StrategyConfig } from './fleet-strategies/strategy-config.js' import { createInfoStrategy } from './fsm/info.js' -import { programs } from './lib/programs.js' -import { createFleet, FleetShip } from './lib/sage/act/create-fleet.js' -import { depositCargo } from './lib/sage/act/deposit-cargo.js' +import { createFleet, type FleetShip } from './lib/sage/act/create-fleet.js' import { ensureShips } from './lib/sage/act/deposit-ship.js' -import { getCargoStatsDefinition } from './lib/sage/state/cargo-stats-definition.js' import { sageGame } from './lib/sage/state/game.js' import { settleFleet } from './lib/sage/state/settle-fleet.js' import { getStarbasePlayer } from './lib/sage/state/starbase-player.js' @@ -36,10 +22,7 @@ import { } from './lib/sage/state/user-fleets.js' import { getMapContext, WorldMap } from './lib/sage/state/world-map.js' import { getName } from './lib/sage/util.js' -import { - getCleanPodsByStarbasePlayerAccounts, - getPodCleanupInstructions, -} from './lib/util/pod-cleanup.js' +import type { AccountWithKey } from './lib/types.js' import { getFleetStrategy } from './fleet-strategies/get-fleet-strategy.js' export const create = async (): Promise => { @@ -73,66 +56,39 @@ const applyStrategy = ( return strategy.apply(fleetInfo) } +/** + * Get token balance for an account + * TODO: Implement with C4 token account fetching + */ 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.debug('Token account not found, assuming empty balance.') - } - - return new BN(mintTokenAccount ? mintTokenAccount.amount.toString() : 0) + _account: Address, + _mint: Address, +): Promise => { + // TODO: Implement using solana-kite token account queries + logger.warn('getTokenBalance not yet implemented for C4') + return 0n } -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, - ) - } - }), - ) +/** + * Import R4 resources from wallet to home starbase + * TODO: Implement with C4 cargo/mint handling + */ +const importR4 = async ( + _player: Player, + _game: AccountWithKey, +): Promise => { + // TODO: Implement R4 import for C4 + // C4 Game structure differs - need to map mints correctly + logger.debug('importR4 not yet implemented for C4') } +/** + * Ensure required fleets exist + */ const ensureFleets = async ( player: Player, - game: Game, - fleets: Array, + game: AccountWithKey, + fleets: Array>, fleetStrategies: StrategyConfig, ): Promise => { const existingFleets = fleets.map(getName) @@ -149,13 +105,13 @@ const ensureFleets = async ( 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, - ) + fleetStrategy.fleet?.forEach((fleetShip: FleetShip) => { + const mintKey = + typeof fleetShip.shipMint === 'string' + ? fleetShip.shipMint + : String(fleetShip.shipMint) + const curr = neededShips.get(mintKey) ?? 0 + neededShips.set(mintKey, curr + fleetShip.count) }) }) @@ -163,12 +119,12 @@ const ensureFleets = async ( .map((mint) => [ { count: neededShips.get(mint) ?? 0, - shipMint: new PublicKey(mint), + shipMint: mint as Address, } as FleetShip, ]) .flat() - await ensureShips(player, game, player.homeStarbase, shipMints) + await ensureShips(player, game, player.homeSystem, shipMints) await Promise.all( neededFleets.map((fleetName) => { @@ -176,14 +132,13 @@ const ensureFleets = async ( if (!fleetStrategy.fleet) { logger.info('Cannot ensure fleet without config.') - return Promise.resolve() } return createFleet( player, game, - player.homeStarbase, + player.homeSystem, fleetStrategy.fleet!, fleetName, ) @@ -191,50 +146,32 @@ const ensureFleets = async ( ) } -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( - game.data.cargo.statsDefinition, - ) - - if (!podCleanup) { - logger.info('Nothing to Clean up') +/** + * Cleanup empty cargo pods at a starbase + * TODO: Implement for C4 cargo system + */ +const cleanupPods = async ( + player: Player, + _game: AccountWithKey, + system: AccountWithKey, +): Promise => { + const starbasePlayer = await getStarbasePlayer(player, system) + if (!starbasePlayer) { + logger.debug('No starbase player found, 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, - ) - - logger.info(`Pod Cleanup Instructions: ${ixs.length}`) - - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs(ixs, player.signer), - ) + // TODO: Implement pod cleanup for C4 + // C4 cargo system differs from legacy - may not need pod cleanup + logger.debug('Pod cleanup not yet implemented for C4') } const basedbot = async (botConfig: BotConfig) => { logger.info( '-------------------------------------------------------------------------------------', ) - const { player, map } = botConfig + const { player } = botConfig const [fleets, disbandedFleets, game] = await Promise.all([ getUserFleets(player), getUserDisbandedFleets(player), @@ -247,15 +184,16 @@ const basedbot = async (botConfig: BotConfig) => { ) } + // getFleetInfo now takes only fleet arg const fleetInfos = ( - await Promise.all(fleets.map((f) => getFleetInfo(f, player, map))) + await Promise.all(fleets.map((f) => getFleetInfo(f))) ).filter((fn) => config.app.fleetFilter ? fn.fleetName.includes(config.app.fleetFilter) : true, ) - await cleanupPods(player, game, player.homeStarbase) + await cleanupPods(player, game, player.homeSystem) await Promise.all([ importR4(player, game), @@ -270,18 +208,17 @@ const basedbot = async (botConfig: BotConfig) => { applyStrategy(fleetInfo, botConfig.fleetStrategies), ), ) - // for (const fleetInfo of fleetInfos) { - // await applyStrategy(fleetInfo, botConfig.fleetStrategies) - // } logger.info( '-------------------------------------------------------------------------------------', ) } export const start = async (): Promise => { - const player = await getPlayerContext(keyPair.publicKey, keyPair) + // C4: Use signer.address instead of keypair.publicKey + const player = await getPlayerContext(keyPair.address, keyPair) const game = await sageGame() - const map = await getMapContext(game) + // C4: getMapContext takes no args + const map = await getMapContext() const fleetStrategies = getFleetStrategy(map, player, game) while (true) { diff --git a/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts index 89c07441..0adf3f05 100644 --- a/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts +++ b/src/main/basedbot/fleet-strategies/atlasnet-fc-strategy.ts @@ -1,11 +1,13 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { Chance } from 'chance' +import { resource } from '../../../service/wallet/resource.js' import { mine } from '../fsm/configs/mine/mine.js' import { createInfoStrategy } from '../fsm/info.js' import { createMiningStrategy } from '../fsm/mine.js' import { createTransportStrategy, transport } from '../fsm/transport.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { galaxySectorsData, @@ -29,7 +31,7 @@ export const atlasnetFcStrategy = ( map: WorldMap, player: Player, - game: Game, + game: AccountWithKey, seed: string = 'basedbot', ): StrategyConfig => { const strategyMap: StrategyMap = makeStrategyMap() @@ -62,10 +64,10 @@ export const atlasnetFcStrategy = player.homeCoordinates, home, new Set([ - game.data.mints.fuel, - game.data.mints.ammo, - game.data.mints.food, - game.data.mints.repairKit, + resource.fuel, + resource.ammo, + resource.food, + resource.tool, ]), ), player, diff --git a/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts index 6437c73c..bb93df1f 100644 --- a/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts +++ b/src/main/basedbot/fleet-strategies/atlasnet-lu-strategy.ts @@ -1,5 +1,6 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' +import type { AccountWithKey } from '../lib/types.js' import { mineBiomass } from '../fsm/configs/mine/mine-biomass.js' import { mineCarbon } from '../fsm/configs/mine/mine-carbon.js' import { mineCopperOre } from '../fsm/configs/mine/mine-copper-ore.js' @@ -21,7 +22,7 @@ import { StrategyConfig } from './strategy-config.js' export const atlasnetLuStrategy = ( map: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { return { match: nameMapMatcher(createInfoStrategy()), diff --git a/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts b/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts index 76320eb5..e0465389 100644 --- a/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts +++ b/src/main/basedbot/fleet-strategies/atlasnet-qt-strategy.ts @@ -1,11 +1,13 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { Chance } from 'chance' +import { resource } from '../../../service/wallet/resource.js' import { mine } from '../fsm/configs/mine/mine.js' import { createInfoStrategy } from '../fsm/info.js' import { createMiningStrategy } from '../fsm/mine.js' import { createTransportStrategy, transport } from '../fsm/transport.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { galaxySectorsData, @@ -29,7 +31,7 @@ export const atlasnetQtStrategy = ( map: WorldMap, player: Player, - game: Game, + game: AccountWithKey, seed: string = 'basedbot', ): StrategyConfig => { const strategyMap: StrategyMap = makeStrategyMap() @@ -78,10 +80,10 @@ export const atlasnetQtStrategy = player.homeCoordinates, home, new Set([ - game.data.mints.fuel, - game.data.mints.ammo, - game.data.mints.food, - game.data.mints.repairKit, + resource.fuel, + resource.ammo, + resource.food, + resource.tool, ]), ), player, diff --git a/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts b/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts index 29808969..2a29e4f6 100644 --- a/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts +++ b/src/main/basedbot/fleet-strategies/destruct-all-strategy.ts @@ -1,7 +1,8 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { createDestructStrategy, destructConfig } from '../fsm/destruct.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { nameMapMatcher } from './name-map-matcher.js' @@ -10,7 +11,7 @@ import { makeStrategyMap, StrategyConfig } from './strategy-config.js' export const destructAllStrategy = ( worldMap: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { return { match: nameMapMatcher( diff --git a/src/main/basedbot/fleet-strategies/disband-all-strategy.ts b/src/main/basedbot/fleet-strategies/disband-all-strategy.ts index f7f4c97c..137ac0cc 100644 --- a/src/main/basedbot/fleet-strategies/disband-all-strategy.ts +++ b/src/main/basedbot/fleet-strategies/disband-all-strategy.ts @@ -1,8 +1,9 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { disbandConfig } from '../fsm/configs/disband-config.js' import { createDisbandStrategy } from '../fsm/disband.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { nameMapMatcher } from './name-map-matcher.js' @@ -11,7 +12,7 @@ import { makeStrategyMap, StrategyConfig } from './strategy-config.js' export const disbandAllStrategy = ( worldMap: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { return { match: nameMapMatcher( diff --git a/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts b/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts index 57126623..f1b4fd2f 100644 --- a/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts +++ b/src/main/basedbot/fleet-strategies/get-fleet-strategy.ts @@ -1,7 +1,8 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { config } from '../../../config/index.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { atlasnetFcStrategy } from './atlasnet-fc-strategy.js' @@ -14,9 +15,9 @@ import { StrategyConfig } from './strategy-config.js' export const getFleetStrategy = ( map: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { - switch (player.publicKey.toString()) { + switch (player.address) { case '4GZeR3hQdQXgoaEG22Gj4egAPX3db7So41rsHtBhHBk8': return mainnetGellsnStrategy(map, player, game) case 'k49Y5xwN7Nyi19TqDR4zbCFuAt8kgy6qMaJ6Kj1wHrn': @@ -30,7 +31,7 @@ export const getFleetStrategy = ( map, player, game, - player.profile.key.toBase58(), + player.profile.key, ) } } diff --git a/src/main/basedbot/fleet-strategies/mainnet-gellsn-strategy.ts b/src/main/basedbot/fleet-strategies/mainnet-gellsn-strategy.ts index ac5e24af..1f218f97 100644 --- a/src/main/basedbot/fleet-strategies/mainnet-gellsn-strategy.ts +++ b/src/main/basedbot/fleet-strategies/mainnet-gellsn-strategy.ts @@ -1,9 +1,10 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { mineHydrogen } from '../fsm/configs/mine/mine-hydrogen.js' import { createInfoStrategy } from '../fsm/info.js' import { createMiningStrategy } from '../fsm/mine.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { nameMapMatcher } from './name-map-matcher.js' @@ -12,7 +13,7 @@ import { StrategyConfig } from './strategy-config.js' export const mainnetGellsnStrategy = ( worldMap: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { return { match: nameMapMatcher(createInfoStrategy()), diff --git a/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts b/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts index 648b019a..34f177e4 100644 --- a/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts +++ b/src/main/basedbot/fleet-strategies/mainnet-lu-strategy.ts @@ -1,10 +1,11 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import { mineCarbon } from '../fsm/configs/mine/mine-carbon.js' import { mineHydrogen } from '../fsm/configs/mine/mine-hydrogen.js' import { createInfoStrategy } from '../fsm/info.js' import { createMiningStrategy } from '../fsm/mine.js' import { Player } from '../lib/sage/state/user-account.js' +import type { AccountWithKey } from '../lib/types.js' import { WorldMap } from '../lib/sage/state/world-map.js' import { nameMapMatcher } from './name-map-matcher.js' @@ -13,7 +14,7 @@ import { StrategyConfig } from './strategy-config.js' export const mainnetLuStrategy = ( worldMap: WorldMap, player: Player, - game: Game, + game: AccountWithKey, ): StrategyConfig => { return { match: nameMapMatcher(createInfoStrategy()), diff --git a/src/main/basedbot/fleet-strategies/strategy-config.ts b/src/main/basedbot/fleet-strategies/strategy-config.ts index d9e71ac2..a791668a 100644 --- a/src/main/basedbot/fleet-strategies/strategy-config.ts +++ b/src/main/basedbot/fleet-strategies/strategy-config.ts @@ -1,8 +1,8 @@ import { Strategy } from '../fsm/strategy.js' -import { FleetShips } from '../lib/sage/act/create-fleet.js' +import type { FleetShip } from '../lib/sage/act/create-fleet.js' export type FleetStrategy = { - fleet: FleetShips | null + fleet: FleetShip[] | null strategy: Strategy } export type StrategyMap = Map diff --git a/src/main/basedbot/fsm/configs/mine/mine-biomass.ts b/src/main/basedbot/fsm/configs/mine/mine-biomass.ts index 80c9542d..956a1750 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-biomass.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-biomass.ts @@ -1,19 +1,27 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineBiomass = (map: WorldMap): MineConfig => - mineConfig({ +export const mineBiomass = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-42, 35) + const resource = mineableByCoordinates( + map, + Coordinates.fromNumber(-30, 30), + 'Biomass', + ) + + if (!resource) { + throw new Error(`No Biomass mineable found`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-42, 35), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-30, 30), - 'Biomass', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-carbon.ts b/src/main/basedbot/fsm/configs/mine/mine-carbon.ts index e4cd28ce..13cc85ea 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-carbon.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-carbon.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineCarbon = (map: WorldMap): MineConfig => - mineConfig({ +export const mineCarbon = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-30, 30) + const resource = mineableByCoordinates(map, targetBase, 'Carbon') + + if (!resource) { + throw new Error(`No Carbon mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-30, 30), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-30, 30), - 'Carbon', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts index f222dd77..0bb3d74e 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-copper-ore.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineCopperOre = (map: WorldMap): MineConfig => - mineConfig({ +export const mineCopperOre = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-47, 30) + const resource = mineableByCoordinates(map, targetBase, 'Copper Ore') + + if (!resource) { + throw new Error(`No Copper Ore mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-47, 30), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-47, 30), - 'Copper Ore', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-diamond.ts b/src/main/basedbot/fsm/configs/mine/mine-diamond.ts index 630877be..695e7206 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-diamond.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-diamond.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineDiamond = (map: WorldMap): MineConfig => - mineConfig({ +export const mineDiamond = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-16, 0) + const resource = mineableByCoordinates(map, targetBase, 'Diamond') + + if (!resource) { + throw new Error(`No Diamond mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-16, 0), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-16, 0), - 'Diamond', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts b/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts index a9df04c9..2fdd4cdb 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-hydrogen.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineHydrogen = (map: WorldMap): MineConfig => - mineConfig({ +export const mineHydrogen = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-40, 30) + const resource = mineableByCoordinates(map, targetBase, 'Hydrogen') + + if (!resource) { + throw new Error(`No Hydrogen mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-40, 30), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-40, 30), - 'Hydrogen', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts index 3e81f47b..38173c97 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-iron-ore.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineIronOre = (map: WorldMap): MineConfig => - mineConfig({ +export const mineIronOre = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-38, 25) + const resource = mineableByCoordinates(map, targetBase, 'Iron Ore') + + if (!resource) { + throw new Error(`No Iron Ore mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-38, 25), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-38, 25), - 'Iron Ore', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts b/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts index 94dc3d6a..580714ff 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-lumanite.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineLumanite = (map: WorldMap): MineConfig => - mineConfig({ +export const mineLumanite = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-23, 4) + const resource = mineableByCoordinates(map, targetBase, 'Lumanite') + + if (!resource) { + throw new Error(`No Lumanite mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-23, 4), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-23, 4), - 'Lumanite', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts b/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts index 27a1e387..e8861bba 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-nitrogen.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineNitrogen = (map: WorldMap): MineConfig => - mineConfig({ +export const mineNitrogen = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-45, 15) + const resource = mineableByCoordinates(map, targetBase, 'Nitrogen') + + if (!resource) { + throw new Error(`No Nitrogen mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-45, 15), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-45, 15), - 'Nitrogen', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts b/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts index db17b9b4..780c75c7 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-rochinol.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineRochinol = (map: WorldMap): MineConfig => - mineConfig({ +export const mineRochinol = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(0, 16) + const resource = mineableByCoordinates(map, targetBase, 'Rochinol') + + if (!resource) { + throw new Error(`No Rochinol mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(0, 16), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(0, 16), - 'Rochinol', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-silicia.ts b/src/main/basedbot/fsm/configs/mine/mine-silicia.ts index a08fc06c..3157fc61 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-silicia.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-silicia.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineSilicia = (map: WorldMap): MineConfig => - mineConfig({ +export const mineSilicia = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-22, 32) + const resource = mineableByCoordinates(map, targetBase, 'Silicia') + + if (!resource) { + throw new Error(`No Silicia mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-22, 32), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-22, 32), - 'Silicia', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts b/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts index 96aa4ddd..5458d03b 100644 --- a/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts +++ b/src/main/basedbot/fsm/configs/mine/mine-titanium-ore.ts @@ -1,19 +1,23 @@ import { mineableByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { Coordinates } from '../../../lib/util/coordinates.js' -import { MineConfig, mineConfig } from './mine-config.js' +import { type MineConfig, mineConfig } from './mine-config.js' -export const mineTitaniumOre = (map: WorldMap): MineConfig => - mineConfig({ +export const mineTitaniumOre = (map: WorldMap): MineConfig => { + const targetBase = Coordinates.fromNumber(-8, 35) + const resource = mineableByCoordinates(map, targetBase, 'Titanium Ore') + + if (!resource) { + throw new Error(`No Titanium Ore mineable found at ${targetBase}`) + } + + return mineConfig({ homeBase: Coordinates.fromNumber(-40, 30), - targetBase: Coordinates.fromNumber(-8, 35), - resource: mineableByCoordinates( - map, - Coordinates.fromNumber(-8, 35), - 'Titanium Ore', - ), + targetBase, + resource, worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/configs/mine/mine.ts b/src/main/basedbot/fsm/configs/mine/mine.ts index f71e1e0d..2187505a 100644 --- a/src/main/basedbot/fsm/configs/mine/mine.ts +++ b/src/main/basedbot/fsm/configs/mine/mine.ts @@ -1,25 +1,33 @@ import { mineablesByCoordinates, - WorldMap, + type WorldMap, } from '../../../lib/sage/state/world-map.js' import { getName } from '../../../lib/sage/util.js' -import { Coordinates } from '../../../lib/util/coordinates.js' +import type { Coordinates } from '../../../lib/util/coordinates.js' -import { mineConfig, MineConfig } from './mine-config.js' +import { mineConfig, type MineConfig } from './mine-config.js' export const mine = ( map: WorldMap, homeBase: Coordinates, targetBase: Coordinates, chance: Chance.Chance, -): MineConfig => - mineConfig({ +): MineConfig => { + const mineables = mineablesByCoordinates(map, targetBase) + + if (mineables.length === 0) { + throw new Error(`No mineables found at ${targetBase}`) + } + + // C4: Use celestialBody instead of mineItem + const sortedMineables = mineables.sort((m, n) => + getName(m.celestialBody).localeCompare(getName(n.celestialBody)), + ) + + return mineConfig({ homeBase, targetBase, - resource: chance.pickone( - Array.from(mineablesByCoordinates(map, targetBase)).sort((m, n) => - getName(m.mineItem).localeCompare(getName(n.mineItem)), - ), - ), + resource: chance.pickone(sortedMineables), worldMap: map, }) +} diff --git a/src/main/basedbot/fsm/destruct.ts b/src/main/basedbot/fsm/destruct.ts index 2caf41c6..f1956e74 100644 --- a/src/main/basedbot/fsm/destruct.ts +++ b/src/main/basedbot/fsm/destruct.ts @@ -1,28 +1,34 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import dayjs from 'dayjs' import { now } from '../../../dayjs.js' import { logger } from '../../../logger.js' import { disbandFleet } from '../lib/sage/act/disband-fleet.js' -import { dock } from '../lib/sage/act/dock.js' -import { endMine } from '../lib/sage/act/end-mine.js' -import { endMove } from '../lib/sage/act/end-move.js' -import { selfDestruct } from '../lib/sage/act/self-destruct.js' -import { stopSubwarp } from '../lib/sage/act/stop-subwarp.js' -import { undock } from '../lib/sage/act/undock.js' import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates.js' -import { Player } from '../lib/sage/state/user-account.js' -import { FleetInfo } from '../lib/sage/state/user-fleets.js' -import { mineableByCoordinates, WorldMap } from '../lib/sage/state/world-map.js' +import type { Player } from '../lib/sage/state/user-account.js' +import type { FleetInfo } from '../lib/sage/state/user-fleets.js' +import { + mineableByCoordinates, + type WorldMap, +} from '../lib/sage/state/world-map.js' import { getName } from '../lib/sage/util.js' - -import { DisbandConfig } from './configs/disband-config.js' -import { Strategy } from './strategy.js' +import type { AccountWithKey } from '../lib/types.js' + +import type { DisbandConfig } from './configs/disband-config.js' +import type { Strategy } from './strategy.js' +import { + fsmDock, + fsmEndMine, + fsmEndMove, + fsmSelfDestruct, + fsmStopSubwarp, + fsmUndock, +} from './fsm-actions.js' const transition = async ( fleetInfo: FleetInfo, player: Player, - game: Game, + game: AccountWithKey, config: DestructConfig, ): Promise => { const currentStarbase = await starbaseByCoordinates(fleetInfo.location) @@ -30,104 +36,104 @@ const transition = async ( const homeBase = player.homeCoordinates const isAtHomeBase = homeBase.equals(location) - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { logger.info( - `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`, + `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase.system) : 'N/A'}]`, ) if (isAtHomeBase) { logger.info(`${fleetName} is at home base, docking to disband`) - - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } - return selfDestruct(fleetInfo, player, game) + return fsmSelfDestruct(fleetInfo, player, game) } - case 'StarbaseLoadingBay': { + case 'Docked': { logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${getName(fleetInfo.fleetState.data.system)}`, ) if (isAtHomeBase) { logger.info( `${fleetInfo.fleetName} is at home base, disbanding...`, ) - - return disbandFleet( - player, - game, - player.homeStarbase, - fleetInfo, - ) + return disbandFleet(player, game, player.homeSystem, fleetInfo) } logger.info( `${fleetInfo.fleetName} is at ${location}, undocking...`, ) - - return undock(fleetInfo.fleet, fleetInfo.location, player, game) + return fsmUndock(fleetInfo, fleetInfo.location, player, game) } case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (!homeBase.equals(toSector)) { + if (!homeBase.equals(toCoordinates)) { logger.info(`Stopping fleet ${fleetInfo.fleetName}`) - - return endMove(fleetInfo, player, game) + return fsmEndMove(fleetInfo, player, game) } - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (!homeBase.equals(toSector)) { + if (!homeBase.equals(toCoordinates)) { logger.info(`Stopping fleet ${fleetInfo.fleetName}`) - - return stopSubwarp(fleetInfo, player, game) + return fsmStopSubwarp(fleetInfo, player, game) } if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MineAsteroid': { - const { mineItem, end, amountMined } = fleetInfo.fleetState.data + const { celestialBody, end, amountMined } = + fleetInfo.fleetState.data + const celestialBodyName = getName(celestialBody) if (end.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`, + `${fleetInfo.fleetName} has finished mining ${celestialBodyName} for ${amountMined}`, ) } logger.info( - `${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Ending...`, + `${fleetInfo.fleetName} mining ${celestialBodyName} for ${amountMined}. Ending...`, ) - const resource = mineableByCoordinates( + + const mineable = mineableByCoordinates( config.worldMap, fleetInfo.location, - getName(mineItem), + celestialBodyName, ) - return endMine(fleetInfo, player, game, resource) + if (!mineable) { + logger.error( + `No mineable found at ${fleetInfo.location} for ${celestialBodyName}`, + ) + return Promise.resolve() + } + + return fsmEndMine(fleetInfo, player, game, mineable) } case 'Respawn': { const { destructionTime, ETA } = fleetInfo.fleetState.data @@ -142,10 +148,7 @@ const transition = async ( break } default: - logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, - ) - + logger.info(`${fleetInfo.fleetName} is in unknown state`) return Promise.resolve() } } @@ -165,7 +168,7 @@ export const destructConfig = ( export const createDestructStrategy = ( config: DestructConfig, player: Player, - game: Game, + game: AccountWithKey, ): Strategy => { return { apply: (fleetInfo: FleetInfo): Promise => diff --git a/src/main/basedbot/fsm/disband.ts b/src/main/basedbot/fsm/disband.ts index b9df4f08..1519f45d 100644 --- a/src/main/basedbot/fsm/disband.ts +++ b/src/main/basedbot/fsm/disband.ts @@ -1,148 +1,149 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import dayjs from 'dayjs' import { now } from '../../../dayjs.js' import { logger } from '../../../logger.js' import { disbandFleet } from '../lib/sage/act/disband-fleet.js' -import { dock } from '../lib/sage/act/dock.js' -import { endMine } from '../lib/sage/act/end-mine.js' -import { endMove } from '../lib/sage/act/end-move.js' -import { move } from '../lib/sage/act/move.js' -import { selfDestruct } from '../lib/sage/act/self-destruct.js' -import { stopSubwarp } from '../lib/sage/act/stop-subwarp.js' -import { undock } from '../lib/sage/act/undock.js' import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates.js' -import { Player } from '../lib/sage/state/user-account.js' -import { FleetInfo } from '../lib/sage/state/user-fleets.js' +import type { Player } from '../lib/sage/state/user-account.js' +import type { FleetInfo } from '../lib/sage/state/user-fleets.js' import { mineableByCoordinates } from '../lib/sage/state/world-map.js' import { getName } from '../lib/sage/util.js' - -import { DisbandConfig } from './configs/disband-config.js' -import { Strategy } from './strategy.js' +import type { AccountWithKey } from '../lib/types.js' + +import type { DisbandConfig } from './configs/disband-config.js' +import type { Strategy } from './strategy.js' +import { + fsmDock, + fsmEndMine, + fsmEndMove, + fsmMove, + fsmSelfDestruct, + fsmStopSubwarp, + fsmUndock, +} from './fsm-actions.js' const transition = async ( fleetInfo: FleetInfo, player: Player, - game: Game, + game: AccountWithKey, config: DisbandConfig, ): Promise => { - const cargoLevelFuel = fleetInfo.cargoLevels.fuel + const cargoLevelFuel = Number(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) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { logger.info( - `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`, + `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase.system) : '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) + return fsmSelfDestruct(fleetInfo, player, game) } if (isAtHomeBase) { logger.info(`${fleetName} is at home base, docking to disband`) - - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } logger.info(`${fleetName} is at ${location} warping home`) - - return move(fleetInfo, homeBase, player, game, warpMode) + return fsmMove(fleetInfo, homeBase, player, game, warpMode) } - case 'StarbaseLoadingBay': { + case 'Docked': { logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${getName(fleetInfo.fleetState.data.system)}`, ) if (isAtHomeBase) { logger.info( `${fleetInfo.fleetName} is at home base, disbanding...`, ) - - return disbandFleet( - player, - game, - player.homeStarbase, - fleetInfo, - ) + return disbandFleet(player, game, player.homeSystem, fleetInfo) } logger.info( `${fleetInfo.fleetName} is at ${location}, undocking...`, ) - - return undock(fleetInfo.fleet, fleetInfo.location, player, game) + return fsmUndock(fleetInfo, fleetInfo.location, player, game) } case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (!homeBase.equals(toSector)) { + if (!homeBase.equals(toCoordinates)) { logger.info( `Wrong direction, stopping fleet ${fleetInfo.fleetName}`, ) - - return endMove(fleetInfo, player, game) + return fsmEndMove(fleetInfo, player, game) } - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (!homeBase.equals(toSector)) { + if (!homeBase.equals(toCoordinates)) { logger.info( `Wrong direction, stopping fleet ${fleetInfo.fleetName}`, ) - - return stopSubwarp(fleetInfo, player, game) + return fsmStopSubwarp(fleetInfo, player, game) } if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MineAsteroid': { - const { mineItem, end, amountMined } = fleetInfo.fleetState.data + const { celestialBody, end, amountMined } = + fleetInfo.fleetState.data + const celestialBodyName = getName(celestialBody) if (end.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`, + `${fleetInfo.fleetName} has finished mining ${celestialBodyName} for ${amountMined}`, ) } logger.info( - `${fleetInfo.fleetName} mining ${getName(mineItem)} for ${amountMined}. Ending...`, + `${fleetInfo.fleetName} mining ${celestialBodyName} for ${amountMined}. Ending...`, ) - const resource = mineableByCoordinates( + + const mineable = mineableByCoordinates( config.worldMap, fleetInfo.location, - getName(mineItem), + celestialBodyName, ) - return endMine(fleetInfo, player, game, resource) + if (!mineable) { + logger.error( + `No mineable found at ${fleetInfo.location} for ${celestialBodyName}`, + ) + return Promise.resolve() + } + + return fsmEndMine(fleetInfo, player, game, mineable) } case 'Respawn': { const { destructionTime, ETA } = fleetInfo.fleetState.data @@ -157,10 +158,7 @@ const transition = async ( break } default: - logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, - ) - + logger.info(`${fleetInfo.fleetName} is in unknown state`) return Promise.resolve() } } @@ -168,7 +166,7 @@ const transition = async ( export const createDisbandStrategy = ( config: DisbandConfig, player: Player, - game: Game, + game: AccountWithKey, ): Strategy => { return { apply: (fleetInfo: FleetInfo): Promise => diff --git a/src/main/basedbot/fsm/fsm-actions.ts b/src/main/basedbot/fsm/fsm-actions.ts new file mode 100644 index 00000000..6d272691 --- /dev/null +++ b/src/main/basedbot/fsm/fsm-actions.ts @@ -0,0 +1,288 @@ +/** + * FSM Action Adapters for C4 + * Bridges FSM layer to C4 action layer with proper params construction + */ +import type { Game } from '@staratlas/dev-sage' + +import type { AccountWithKey } from '../lib/types.js' +import type { Coordinates } from '../lib/util/coordinates.js' +import type { Player } from '../lib/sage/state/user-account.js' +import type { FleetInfo } from '../lib/sage/state/user-fleets.js' +import type { Mineable } from '../lib/sage/state/world-map.js' +import { systemByCoordinates } from '../lib/sage/state/starbase-by-coordinates.js' +import { getStarbasePlayer } from '../lib/sage/state/starbase-player.js' +import { fetchRegionTracker } from '../lib/account-fetcher.js' + +import { dock as dockAction } from '../lib/sage/act/dock.js' +import { undock as undockAction } from '../lib/sage/act/undock.js' +import { move as moveAction, type WarpMode } from '../lib/sage/act/move.js' +import { mine as mineAction } from '../lib/sage/act/mine.js' +import { endMine as endMineAction } from '../lib/sage/act/end-mine.js' +import { selfDestruct as selfDestructAction } from '../lib/sage/act/self-destruct.js' +import { endMove as endMoveAction } from '../lib/sage/act/end-move.js' +import { stopSubwarp as stopSubwarpAction } from '../lib/sage/act/stop-subwarp.js' +import { + loadCargo as loadCargoAction, + getFuelCargoId, + getAmmoCargoId, +} from '../lib/sage/act/load-cargo.js' +import { unloadAllCargo as unloadAllCargoAction } from '../lib/sage/act/unload-all-cargo.js' + +/** + * FSM dock adapter - bridges old signature to new params + */ +export async function fsmDock( + fleetInfo: FleetInfo, + _location: Coordinates, + player: Player, + game: AccountWithKey, +): Promise { + const system = await systemByCoordinates(fleetInfo.location) + if (!system) { + throw new Error(`No system found at ${fleetInfo.location}`) + } + + const starbasePlayer = await getStarbasePlayer(player, system) + + return dockAction({ + game, + fleet: fleetInfo.fleet, + profile: player.profile, + profileFaction: player.profileFaction, + character: player.character, + system, + starbasePlayer: starbasePlayer?.key, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * FSM undock adapter + */ +export async function fsmUndock( + fleetInfo: FleetInfo, + _location: Coordinates, + player: Player, + game: AccountWithKey, +): Promise { + if (fleetInfo.fleetState.kind !== 'Docked') { + throw new Error(`Cannot undock: fleet is not docked`) + } + + const system = fleetInfo.fleetState.data.system + + const starbasePlayer = await getStarbasePlayer(player, system) + + return undockAction({ + game, + fleet: fleetInfo.fleet, + profile: player.profile, + profileFaction: player.profileFaction, + character: player.character, + system, + starbasePlayer: starbasePlayer?.key, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * FSM move adapter + */ +export async function fsmMove( + fleetInfo: FleetInfo, + destination: Coordinates, + player: Player, + game: AccountWithKey, + warpMode: WarpMode = 'auto', +): Promise { + const regionTracker = await fetchRegionTracker() + + return moveAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + regionTracker, + destination: [destination.xBigInt, destination.yBigInt], + signer: player.signer, + keyIndex: player.keyIndex, + warpMode, + }) +} + +/** + * FSM mine adapter + */ +export async function fsmMine( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, + mineable: Mineable, +): Promise { + const regionTracker = await fetchRegionTracker() + + return mineAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + regionTracker, + system: mineable.system, + asteroid: mineable.celestialBody, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * FSM end mine adapter + */ +export async function fsmEndMine( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, + mineable: Mineable, +): Promise { + return endMineAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + asteroid: mineable.celestialBody, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * FSM self destruct adapter + */ +export async function fsmSelfDestruct( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, +): Promise { + return selfDestructAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * FSM end move adapter + */ +export async function fsmEndMove( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, +): Promise { + return endMoveAction({ + game, + fleet: fleetInfo.fleet, + signer: player.signer, + character: player.character, + }) +} + +/** + * FSM stop subwarp adapter + */ +export async function fsmStopSubwarp( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, +): Promise { + const regionTracker = await fetchRegionTracker() + + return stopSubwarpAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + regionTracker, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +/** + * Get cargo ID for a cargo type string + */ +function resolveCargoId(cargoType: string, fleetInfo: FleetInfo): number { + switch (cargoType) { + case 'fuel': + return getFuelCargoId(fleetInfo.fleet) + case 'ammo': + return getAmmoCargoId(fleetInfo.fleet) + case 'food': + // TODO: Food cargo ID should come from game config + return 4 // Placeholder + default: + // Assume it's a cargoId number + return parseInt(cargoType, 10) || 0 + } +} + +/** + * FSM load cargo adapter + */ +export async function fsmLoadCargo( + fleetInfo: FleetInfo, + player: Player, + game: AccountWithKey, + cargoType: string, + amount: number, + _maxAmount?: boolean, +): Promise { + if (fleetInfo.fleetState.kind !== 'Docked') { + throw new Error('Fleet must be docked to load cargo') + } + + const system = fleetInfo.fleetState.data.system + const starbasePlayer = await getStarbasePlayer(player, system) + const cargoId = resolveCargoId(cargoType, fleetInfo) + + return loadCargoAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + profileFaction: player.profileFaction, + system, + starbasePlayer: starbasePlayer?.key, + signer: player.signer, + keyIndex: player.keyIndex, + transfers: [{ cargoId, amount }], + }) +} + +/** + * FSM unload all cargo adapter + */ +export async function fsmUnloadAllCargo( + fleetInfo: FleetInfo, + _location: Coordinates, + player: Player, + game: AccountWithKey, +): Promise { + if (fleetInfo.fleetState.kind !== 'Docked') { + throw new Error('Fleet must be docked to unload cargo') + } + + const system = fleetInfo.fleetState.data.system + const starbasePlayer = await getStarbasePlayer(player, system) + + return unloadAllCargoAction({ + game, + fleet: fleetInfo.fleet, + character: player.character, + profileFaction: player.profileFaction, + system, + starbasePlayer: starbasePlayer?.key, + signer: player.signer, + keyIndex: player.keyIndex, + }) +} + +// Re-export for FSM files to import +export { type WarpMode } from '../lib/sage/act/move.js' diff --git a/src/main/basedbot/fsm/info.ts b/src/main/basedbot/fsm/info.ts index 75203f21..ba03fe5a 100644 --- a/src/main/basedbot/fsm/info.ts +++ b/src/main/basedbot/fsm/info.ts @@ -10,7 +10,7 @@ import { getName } from '../lib/sage/util.js' import { Strategy } from './strategy.js' const transition = async (fleetInfo: FleetInfo): Promise => { - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { const baseStation = await starbaseByCoordinates(fleetInfo.location) const planets = await planetsByCoordinates(fleetInfo.location) @@ -20,61 +20,61 @@ const transition = async (fleetInfo: FleetInfo): Promise => { ) break } - case 'StarbaseLoadingBay': + case 'Docked': logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${getName(fleetInfo.fleetState.data.system)}`, ) break case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MineAsteroid': { - const { mineItem, end, amountMined, endReason } = + const { celestialBody, end, amountMined, endReason } = fleetInfo.fleetState.data if (end.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`, + `${fleetInfo.fleetName} has finished mining ${getName(celestialBody)} 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}`, + `${fleetInfo.fleetName} mining ${getName(celestialBody)} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`, ) } break } default: logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, + `${fleetInfo.fleetName} is ${fleetInfo.fleetState.kind}`, ) } diff --git a/src/main/basedbot/fsm/mine.ts b/src/main/basedbot/fsm/mine.ts index 2571c983..4c9a17da 100644 --- a/src/main/basedbot/fsm/mine.ts +++ b/src/main/basedbot/fsm/mine.ts @@ -1,47 +1,47 @@ -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' import dayjs from 'dayjs' import { now } from '../../../dayjs.js' import { logger } from '../../../logger.js' -import { dock } from '../lib/sage/act/dock.js' -import { endMine } from '../lib/sage/act/end-mine.js' -import { loadCargo } from '../lib/sage/act/load-cargo.js' -import { mine } from '../lib/sage/act/mine.js' -import { move } from '../lib/sage/act/move.js' -import { selfDestruct } from '../lib/sage/act/self-destruct.js' -import { undock } from '../lib/sage/act/undock.js' -import { unloadAllCargo } from '../lib/sage/act/unload-all-cargo.js' import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates.js' -import { Player } from '../lib/sage/state/user-account.js' -import { FleetInfo } from '../lib/sage/state/user-fleets.js' +import type { Player } from '../lib/sage/state/user-account.js' +import type { FleetInfo } from '../lib/sage/state/user-fleets.js' import { mineableByCoordinates } from '../lib/sage/state/world-map.js' import { getName } from '../lib/sage/util.js' - -import { MineConfig } from './configs/mine/mine-config.js' -import { Strategy } from './strategy.js' +import type { AccountWithKey } from '../lib/types.js' + +import type { MineConfig } from './configs/mine/mine-config.js' +import type { Strategy } from './strategy.js' +import { + fsmDock, + fsmEndMine, + fsmLoadCargo, + fsmMine, + fsmMove, + fsmSelfDestruct, + fsmUndock, + fsmUnloadAllCargo, +} from './fsm-actions.js' const transition = async ( fleetInfo: FleetInfo, player: Player, - game: Game, + game: AccountWithKey, 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 + // C4: cargo levels are bigint, convert to number for comparisons + const cargoEntries = Array.from(fleetInfo.cargoLevels.cargo.entries()) + const cargoLoad = cargoEntries.reduce( + (acc, [_cargoId, amount]) => acc + Number(amount), + 0, + ) - return acc + load - }, 0) const { homeBase, targetBase, resource, warpMode } = config const { cargoCapacity } = fleetInfo.cargoStats - const cargoLevelFood = fleetInfo.cargoLevels.food - const cargoLevelAmmo = fleetInfo.cargoLevels.ammo - const cargoLevelFuel = fleetInfo.cargoLevels.fuel + const cargoLevelFood = 0 // TODO: Get food level from cargo by cargoId + const cargoLevelAmmo = Number(fleetInfo.cargoLevels.ammo) + const cargoLevelFuel = Number(fleetInfo.cargoLevels.fuel) const desiredFood = cargoCapacity / 20 const fuelReserve = fleetInfo.cargoStats.fuelCapacity const toLoad = desiredFood - cargoLevelFood @@ -52,27 +52,27 @@ const transition = async ( const hasCargo = cargoLoad > 0 const currentStarbase = await starbaseByCoordinates(fleetInfo.location) const { fleetName, location } = fleetInfo - const resourceName = getName(resource.mineItem) + + const resourceName = getName(resource.celestialBody) 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}`, + `${fleetName} is mining ${resourceName} resources from ${config.targetBase} to ${config.homeBase}`, ) - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { logger.info( - `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`, + `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase.system) : '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) + return fsmSelfDestruct(fleetInfo, player, game) } if (isAtHomeBase) { logger.info(`${fleetName} is at home base`) @@ -80,26 +80,22 @@ const transition = async ( logger.info( `${fleetName} has ${cargoLoad} ${resourceName}, docking to unload`, ) - - return dock(fleetInfo, location, player, game) + return fsmDock(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) + return fsmDock(fleetInfo, location, player, game) } if (isSameBase) { logger.info(`${fleetName} is at home/target base, mining`) - - return mine(fleetInfo, player, game, resource) + return fsmMine(fleetInfo, player, game, resource) } logger.info( `${fleetName} is at home base, moving to target base`, ) - - return move(fleetInfo, targetBase, player, game, warpMode) + return fsmMove(fleetInfo, targetBase, player, game, warpMode) } if (isAtTargetBase && !isSameBase) { @@ -108,30 +104,26 @@ const transition = async ( logger.info( `${fleetName} doesn't have enough fuel, docking`, ) - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } if (hasCargo) { logger.info( `${fleetName} has ${cargoLoad} ${resourceName}, returning home`, ) - - return move(fleetInfo, homeBase, player, game, warpMode) + return fsmMove(fleetInfo, homeBase, player, game, warpMode) } if (hasEnoughFood) { logger.info(`${fleetName} has enough food, mining`) - - return mine(fleetInfo, player, game, resource) + return fsmMine(fleetInfo, player, game, resource) } logger.info( `${fleetName} doesn't have enough food, returning home`, ) - - return move(fleetInfo, homeBase, player, game, warpMode) + return fsmMove(fleetInfo, homeBase, player, game, warpMode) } logger.info(`${fleetName} is at ${location}`) - - return move( + return fsmMove( fleetInfo, hasCargo || !hasEnoughFood ? homeBase : targetBase, player, @@ -139,17 +131,16 @@ const transition = async ( warpMode, ) } - case 'StarbaseLoadingBay': { + case 'Docked': { logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${getName(fleetInfo.fleetState.data.system)}`, ) if (hasCargo) { logger.info( `${fleetInfo.fleetName} has ${cargoLoad} cargo, unloading`, ) - - return unloadAllCargo( + return fsmUnloadAllCargo( fleetInfo, fleetInfo.location, player, @@ -161,97 +152,85 @@ const transition = async ( const neededFuel = fleetInfo.cargoStats.fuelCapacity - cargoLevelFuel logger.info(`${fleetInfo.fleetName} is refueling ${neededFuel}`) - - return loadCargo( - fleetInfo, - player, - game, - game.data.mints.fuel, - neededFuel, - ) + return fsmLoadCargo(fleetInfo, player, game, 'fuel', neededFuel) } if (!hasEnoughAmmo && isAtHomeBase) { const neededAmmo = fleetInfo.cargoStats.ammoCapacity - cargoLevelAmmo logger.info(`${fleetInfo.fleetName} is rearming ${neededAmmo}`) - - return loadCargo( - fleetInfo, - player, - game, - game.data.mints.ammo, - neededAmmo, - ) + return fsmLoadCargo(fleetInfo, player, game, 'ammo', neededAmmo) } if (!hasEnoughFood && isAtHomeBase) { logger.info( `${fleetInfo.fleetName} is loading ${desiredFood - cargoLevelFood} food`, ) - - return loadCargo( - fleetInfo, - player, - game, - game.data.mints.food, - toLoad, - ) + return fsmLoadCargo(fleetInfo, player, game, 'food', toLoad) } - return undock(fleetInfo.fleet, fleetInfo.location, player, game) + return fsmUndock(fleetInfo, fleetInfo.location, player, game) } case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MineAsteroid': { - const { mineItem, end, amountMined, endReason } = + const { celestialBody, end, amountMined, endReason } = fleetInfo.fleetState.data + const celestialBodyName = getName(celestialBody) + if (end.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`, + `${fleetInfo.fleetName} has finished mining ${celestialBodyName} for ${amountMined}`, ) - const resource = mineableByCoordinates( + const mineable = mineableByCoordinates( config.worldMap, fleetInfo.location, - getName(mineItem), + celestialBodyName, ) - return endMine(fleetInfo, player, game, resource) + if (!mineable) { + logger.error( + `No mineable found at ${fleetInfo.location} for ${celestialBodyName}`, + ) + return Promise.resolve() + } + + return fsmEndMine(fleetInfo, player, game, mineable) } 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}`, + `${fleetInfo.fleetName} mining ${celestialBodyName} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`, ) break @@ -269,10 +248,7 @@ const transition = async ( break } default: - logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, - ) - + logger.info(`${fleetInfo.fleetName} is in unknown state`) return Promise.resolve() } } @@ -280,7 +256,7 @@ const transition = async ( export const createMiningStrategy = ( miningConfig: MineConfig, player: Player, - game: Game, + game: AccountWithKey, ): Strategy => { const config = miningConfig diff --git a/src/main/basedbot/fsm/transport.ts b/src/main/basedbot/fsm/transport.ts index be809553..9ea63ed9 100644 --- a/src/main/basedbot/fsm/transport.ts +++ b/src/main/basedbot/fsm/transport.ts @@ -1,49 +1,49 @@ -import { PublicKey } from '@solana/web3.js' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' +import type { Game } from '@staratlas/dev-sage' import dayjs from 'dayjs' import { now } from '../../../dayjs.js' import { logger } from '../../../logger.js' -import { Resource } from '../../../service/wallet/index.js' -import { getTokenBalance } from '../basedbot.js' -import { dock } from '../lib/sage/act/dock.js' -import { loadCargo } from '../lib/sage/act/load-cargo.js' -import { move, WarpMode } from '../lib/sage/act/move.js' -import { selfDestruct } from '../lib/sage/act/self-destruct.js' -import { undock } from '../lib/sage/act/undock.js' -import { getHold, unloadCargo } from '../lib/sage/act/unload-cargo.js' +import type { Resource } from '../../../service/wallet/index.js' import { starbaseByCoordinates } from '../lib/sage/state/starbase-by-coordinates.js' -import { Player } from '../lib/sage/state/user-account.js' -import { FleetInfo } from '../lib/sage/state/user-fleets.js' -import { WorldMap } from '../lib/sage/state/world-map.js' +import type { Player } from '../lib/sage/state/user-account.js' +import type { FleetInfo } from '../lib/sage/state/user-fleets.js' +import type { WorldMap } from '../lib/sage/state/world-map.js' import { getName } from '../lib/sage/util.js' -import { Coordinates } from '../lib/util/coordinates.js' +import type { AccountWithKey } from '../lib/types.js' +import type { Coordinates } from '../lib/util/coordinates.js' import { getFuelConsumption } from '../lib/util/fuel-consumption.js' -import { Strategy } from './strategy.js' +import type { Strategy } from './strategy.js' +import { + fsmDock, + fsmLoadCargo, + fsmMove, + fsmSelfDestruct, + fsmUndock, + type WarpMode, +} from './fsm-actions.js' const transition = async ( fleetInfo: FleetInfo, player: Player, - game: Game, + game: AccountWithKey, 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) + // C4: Convert bigint to number for comparisons + const cargoEntries = Array.from(fleetInfo.cargoLevels.cargo.entries()) + const cargoLoad = cargoEntries.reduce( + (acc, [_cargoId, amount]) => acc + Number(amount), + 0, + ) const { homeBase, targetBase, resources, warpMode } = config const fuelConsumption = getFuelConsumption(homeBase, targetBase, fleetInfo) const { cargoCapacity } = fleetInfo.cargoStats - const cargoLevelFuel = fleetInfo.cargoLevels.fuel - const cargoLevelAmmo = fleetInfo.cargoLevels.ammo + const cargoLevelFuel = Number(fleetInfo.cargoLevels.fuel) + const cargoLevelAmmo = Number(fleetInfo.cargoLevels.ammo) const cargoLevel = Array.from(fleetInfo.cargoLevels.cargo.values()).reduce( - (acc, curr) => acc + curr, + (acc, curr) => acc + Number(curr), 0, ) @@ -62,36 +62,38 @@ const transition = async ( `${fleetName} is transporting ${config.resources.size} resources from ${config.homeBase} to ${config.targetBase}`, ) - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { logger.info( - `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase) : 'N/A'}]`, + `${fleetName} is idle at ${fleetInfo.fleetState.data.sector} [Starbase: ${currentStarbase ? getName(currentStarbase.system) : '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) + return fsmSelfDestruct(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) + return fsmMove( + fleetInfo, + targetBase, + player, + game, + warpMode, + ) } logger.info(`${fleetName} is docking to resupply`) - - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } if (isAtTargetBase) { @@ -101,30 +103,27 @@ const transition = async ( logger.info( `${fleetName} docking to unload ${cargoLoad} cargo`, ) - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } if (!hasEnoughFuel) { logger.info(`${fleetName} docking to refuel.`) - - return dock(fleetInfo, location, player, game) + return fsmDock(fleetInfo, location, player, game) } if (hasEnoughFuel && !hasCargo) { - logger.info('Ready to go! Moving to target base') - - return move(fleetInfo, homeBase, player, game, warpMode) + logger.info('Ready to go! Moving home') + return fsmMove(fleetInfo, homeBase, player, game, warpMode) } } logger.warn(`${fleetName} doesn't know what to do`) - return Promise.resolve() } - case 'StarbaseLoadingBay': { + case 'Docked': { logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${getName(fleetInfo.fleetState.data.system)}`, ) if (isAtHomeBase) { @@ -134,12 +133,11 @@ const transition = async ( logger.info( `${fleetInfo.fleetName} is refueling ${neededFuel}`, ) - - await loadCargo( + await fsmLoadCargo( fleetInfo, player, game, - game.data.mints.fuel, + 'fuel', neededFuel, ) } @@ -149,21 +147,18 @@ const transition = async ( logger.info( `${fleetInfo.fleetName} is rearming ${neededAmmo}`, ) - - await loadCargo( + await fsmLoadCargo( fleetInfo, player, game, - game.data.mints.ammo, + 'ammo', neededAmmo, ) } if (!hasCargo) { logger.info(`Loading ${Array.from(resources).length} cargo`) - const cargoResources = Array.from(resources).filter( - (resource) => !resource.equals(game.data.mints.fuel), - ) + const cargoResources = Array.from(resources) await Promise.all( cargoResources.map((resource) => { @@ -171,16 +166,13 @@ const transition = async ( (cargoCapacity - cargoLevel) / Array.from(cargoResources).length, ) - - logger.info( - `Loading ${count} ${resource.toBase58()}`, - ) - - return loadCargo( + logger.info(`Loading ${count} of resource`) + // TODO: Map Resource to cargoId + return fsmLoadCargo( fleetInfo, player, game, - resource, + String(resource), count, true, ) @@ -189,138 +181,63 @@ const transition = async ( } logger.info(`${fleetName} is undocking...`) - - return undock(fleetInfo.fleet, fleetInfo.location, player, game) + return fsmUndock(fleetInfo, fleetInfo.location, player, game) } if (isAtTargetBase) { - if (hasCargo) { - logger.info( - `Unloading ${Array.from(resources).length} cargo`, - ) - - //Unload Fuel - - const unload = async (mint: PublicKey): Promise => { - const fleetCargoPod = getHold(mint, game, fleetInfo) - const amount = await getTokenBalance( - fleetCargoPod, - mint, - ) - - const toUnload = amount.sub(new BN(fuelReserve)) - - if (toUnload.lte(new BN(0))) { - return Promise.resolve() - } - - logger.info(`Unloading ${toUnload} ${mint}`) - - await unloadCargo( - fleetInfo, - player, - game, - mint, - toUnload, - ) - } - - await Promise.all([ - unload(game.data.mints.ammo), - unload(game.data.mints.fuel), - ]) - - const cargoResources = Array.from(resources).filter( - (resource) => !resource.equals(game.data.mints.fuel), - ) - - await Promise.all( - Array.from(cargoResources).map(async (resource) => { - const fleetCargoPod = fleetInfo.fleet.data.cargoHold - const amount = await getTokenBalance( - fleetCargoPod, - resource, - ) - - logger.info(`Unloading ${amount} ${resource}`) - - if (amount.eq(new BN(0))) { - return Promise.resolve() - } - - logger.info( - `Unloading ${amount} ${resource.toBase58()}`, - ) - - await unloadCargo( - fleetInfo, - player, - game, - resource, - amount, - true, - ) - }), - ) - } - + // TODO: Implement cargo unloading at target if (!hasEnoughFuel) { const neededFuel = fuelReserve - cargoLevelFuel logger.info( `${fleetInfo.fleetName} is refueling ${neededFuel}`, ) - - await loadCargo( + await fsmLoadCargo( fleetInfo, player, game, - game.data.mints.fuel, + 'fuel', neededFuel, ) } } logger.info(`${fleetName} is undocking for take off`) - - return undock(fleetInfo.fleet, fleetInfo.location, player, game) + return fsmUndock(fleetInfo, fleetInfo.location, player, game) } case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. 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': { @@ -336,10 +253,7 @@ const transition = async ( break } default: - logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, - ) - + logger.info(`${fleetInfo.fleetName} is in unknown state`) return Promise.resolve() } } @@ -384,7 +298,7 @@ export const transport = ( export const createTransportStrategy = ( config: TransportConfig, player: Player, - game: Game, + game: AccountWithKey, ): Strategy => { return { apply: (fleetInfo: FleetInfo): Promise => diff --git a/src/main/basedbot/lib/account-fetcher.ts b/src/main/basedbot/lib/account-fetcher.ts new file mode 100644 index 00000000..ccb7d498 --- /dev/null +++ b/src/main/basedbot/lib/account-fetcher.ts @@ -0,0 +1,99 @@ +import type { Address } from '@solana/kit' + +import { connection } from '../../../service/sol/index.js' +import { + ACCOUNT_REGISTRY, + type AccountConfig, + type AccountTypeMap, + type AccountTypeName, +} from './account-registry.js' +import type { AccountWithKey } from './types.js' + +/** + * Fetch all accounts of a specific type using discriminator-based filtering + */ +export async function fetchAllAccounts( + accountType: T, +): Promise[]> { + const config = ACCOUNT_REGISTRY[accountType] as AccountConfig< + AccountTypeMap[T] + > + + const factoryFn = connection.getAccountsFactory( + config.programAddress, + config.discriminator, + config.decoder(), + ) + + const accounts = await factoryFn() + + return accounts + .filter((acc) => acc.exists) + .map((acc) => ({ + ...(acc.data as AccountTypeMap[T]), + key: acc.address as Address, + })) +} + +/** + * Fetch a single account by address + */ +export async function fetchAccount( + accountType: T, + address: Address, +): Promise | null> { + const config = ACCOUNT_REGISTRY[accountType] as AccountConfig< + AccountTypeMap[T] + > + + // Use getAccountsFactory with filter to find specific account + const factoryFn = connection.getAccountsFactory( + config.programAddress, + config.discriminator, + config.decoder(), + ) + + const accounts = await factoryFn() + const account = accounts.find( + (acc) => acc.exists && acc.address === address, + ) + + if (!account || !account.exists) { + return null + } + + return { + ...(account.data as AccountTypeMap[T]), + key: account.address as Address, + } +} + +/** + * Fetch the Game account (singleton) + */ +export async function fetchGame(): Promise< + AccountWithKey +> { + const games = await fetchAllAccounts('Game') + + if (games.length === 0) { + throw new Error('No Game account found') + } + + return games[0] +} + +/** + * Fetch RegionTracker account (singleton) + */ +export async function fetchRegionTracker(): Promise< + AccountWithKey +> { + const trackers = await fetchAllAccounts('RegionTracker') + + if (trackers.length === 0) { + throw new Error('No RegionTracker account found') + } + + return trackers[0] +} diff --git a/src/main/basedbot/lib/account-registry.ts b/src/main/basedbot/lib/account-registry.ts new file mode 100644 index 00000000..4c86bd64 --- /dev/null +++ b/src/main/basedbot/lib/account-registry.ts @@ -0,0 +1,123 @@ +import type { Address, Decoder } from '@solana/kit' +import { + PLAYER_NAME_DISCRIMINATOR, + PLAYER_PROFILE_PROGRAM_ADDRESS, + PROFILE_DISCRIMINATOR, + type PlayerName, + type Profile, + getPlayerNameDecoder, + getProfileDecoder, +} from '@staratlas/dev-player-profile' +import { + PROFILE_FACTION_ACCOUNT_DISCRIMINATOR, + PROFILE_FACTION_PROGRAM_ADDRESS, + type ProfileFactionAccount, + getProfileFactionAccountDecoder, +} from '@staratlas/dev-profile-faction' +import { + CELESTIAL_BODY_DISCRIMINATOR, + CHARACTER_DISCRIMINATOR, + FLEET_DISCRIMINATOR, + GAME_DISCRIMINATOR, + REGION_TRACKER_DISCRIMINATOR, + SAGE_PROGRAM_ADDRESS, + STARBASE_PLAYER_DISCRIMINATOR, + STAR_SYSTEM_DISCRIMINATOR, + type CelestialBody, + type Character, + type Fleet, + type Game, + type RegionTracker, + type StarbasePlayer, + type StarSystem, + getCelestialBodyDecoder, + getCharacterDecoder, + getFleetDecoder, + getGameDecoder, + getRegionTrackerDecoder, + getStarbasePlayerDecoder, + getStarSystemDecoder, +} from '@staratlas/dev-sage' + +export interface AccountConfig { + programAddress: Address + discriminator: Uint8Array + decoder: () => Decoder +} + +export const ACCOUNT_REGISTRY = { + Profile: { + programAddress: PLAYER_PROFILE_PROGRAM_ADDRESS, + discriminator: PROFILE_DISCRIMINATOR, + decoder: getProfileDecoder, + }, + + PlayerName: { + programAddress: PLAYER_PROFILE_PROGRAM_ADDRESS, + discriminator: PLAYER_NAME_DISCRIMINATOR, + decoder: getPlayerNameDecoder, + }, + + ProfileFactionAccount: { + programAddress: PROFILE_FACTION_PROGRAM_ADDRESS, + discriminator: PROFILE_FACTION_ACCOUNT_DISCRIMINATOR, + decoder: getProfileFactionAccountDecoder, + }, + + Character: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: CHARACTER_DISCRIMINATOR, + decoder: getCharacterDecoder, + }, + + StarbasePlayer: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: STARBASE_PLAYER_DISCRIMINATOR, + decoder: getStarbasePlayerDecoder, + }, + + Game: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: GAME_DISCRIMINATOR, + decoder: getGameDecoder, + }, + + RegionTracker: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: REGION_TRACKER_DISCRIMINATOR, + decoder: getRegionTrackerDecoder, + }, + + StarSystem: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: STAR_SYSTEM_DISCRIMINATOR, + decoder: getStarSystemDecoder, + }, + + CelestialBody: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: CELESTIAL_BODY_DISCRIMINATOR, + decoder: getCelestialBodyDecoder, + }, + + Fleet: { + programAddress: SAGE_PROGRAM_ADDRESS, + discriminator: FLEET_DISCRIMINATOR, + decoder: getFleetDecoder, + }, +} as const + +export type AccountTypeMap = { + Profile: Profile + PlayerName: PlayerName + ProfileFactionAccount: ProfileFactionAccount + Character: Character + StarbasePlayer: StarbasePlayer + Game: Game + RegionTracker: RegionTracker + StarSystem: StarSystem + CelestialBody: CelestialBody + Fleet: Fleet +} + +export type AccountTypeName = keyof typeof ACCOUNT_REGISTRY diff --git a/src/main/basedbot/lib/fleet-state/fleet-state.ts b/src/main/basedbot/lib/fleet-state/fleet-state.ts index c4f72c5b..b61e0058 100644 --- a/src/main/basedbot/lib/fleet-state/fleet-state.ts +++ b/src/main/basedbot/lib/fleet-state/fleet-state.ts @@ -1,168 +1,77 @@ -import { CargoStats, Fleet, MiscStats } from '@staratlas/sage' -import Big from 'big.js' -import BN from 'bn.js' +import type { Fleet } from '@staratlas/dev-sage' import { now } from '../../../../dayjs.js' -import { FleetCargo } from '../sage/state/fleet-cargo.js' -import { planetByKey } from '../sage/state/planet-by-key.js' -import { starbaseByKey } from '../sage/state/starbase-by-key.js' -import { WorldMap } from '../sage/state/world-map.js' +import { fetchAccount } from '../account-fetcher.js' +import type { AccountWithKey } from '../types.js' import { Coordinates } from '../util/coordinates.js' import { transformSector } from './transform/transform-sector.js' import { transformTime } from './transform/transform-time.js' -import { isIdleData } from './type-guard/idle.js' -import { isMineAsteroidData } from './type-guard/mine-asteroid.js' -import { isMoveSubWarpData } from './type-guard/move-sub-warp.js' -import { isMoveWarpData } from './type-guard/move-warp.js' -import { isRespawnData } from './type-guard/respawn.js' -import { isStarbaseLoadingBayData } from './type-guard/starbase-loading-bay.js' import { - EndReason, - FleetState, - FleetStateType, - RawMineAsteroidData, + type EndReason, + type FleetState, + type FleetStateKind, } from './types.js' -const toBig = (bn: BN): Big => new Big(bn.toString()) -const toBN = (bigInt: Big): BN => new BN(bigInt.toString()) - +/** + * Calculate current position during movement + */ const calculateCurrentPosition = ( startPos: Coordinates, destPos: Coordinates, - startTime: BN, - endTime: BN, - currentTime: BN, + startTime: bigint, + endTime: bigint, + currentTime: bigint, ): Coordinates => { - if (currentTime.gte(endTime)) { + if (currentTime >= endTime) { return destPos - } else if (currentTime.lte(startTime)) { + } else if (currentTime <= 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 totalTime = Number(endTime - startTime) + const elapsedTime = Number(currentTime - startTime) + const ratio = elapsedTime / totalTime - const xt = toBig(startPos.xBN).add(xDifference.mul(ratio)) - const yt = toBig(startPos.yBN).add(yDifference.mul(ratio)) + const xDiff = destPos.x - startPos.x + const yDiff = destPos.y - startPos.y - return Coordinates.fromBN(toBN(xt.round(0, 1)), toBN(yt.round(0, 1))) -} + const newX = Math.round(startPos.x + xDiff * ratio) + const newY = Math.round(startPos.y + yDiff * ratio) -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 + return Coordinates.fromNumber(newX, newY) } -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, +/** + * Get the fleet state kind from C4 __kind discriminator + */ +function getStateKind(fleet: Fleet): FleetStateKind { + const kind = fleet.state.__kind + // Map C4 state names to our normalized names + switch (kind) { + case 'Idle': + case 'Docked': + case 'MoveWarp': + case 'MoveSubwarp': + case 'MineAsteroid': + case 'Respawn': + return kind + default: + throw new Error(`Unknown fleet state kind: ${kind}`) } } +/** + * Get processed fleet state from C4 Fleet account + */ export const getFleetState = async ( - fleet: Fleet, - map: WorldMap, - cargoLevels: FleetCargo, + fleet: AccountWithKey, ): Promise => { - const fleetStateKeys = Object.keys(fleet.state) as Array - const miscStats = fleet.data.stats.miscStats as unknown as MiscStats + const kind = getStateKind(fleet) + const stateFields = fleet.state.fields[0] as Record - 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 warpCooldownExpiry = transformTime(fleet.warpCooldownExpiresAt) + const scanCoolDownExpiry = transformTime(fleet.scanCooldownExpiresAt) const baseData = { warpCooldownExpiry, @@ -171,116 +80,195 @@ export const getFleetState = async ( scanCooldown: scanCoolDownExpiry.isAfter(now()), } - switch (type) { - case 'Idle': - if (isIdleData(data)) { - return { - type, - data: { - sector: transformSector(data.sector), - ...baseData, - }, - } + const currentUnix = BigInt(now().unix()) + + switch (kind) { + case 'Idle': { + const coordinates = stateFields.coordinates as [ + { raw: bigint }, + { raw: bigint }, + ] + return { + kind: 'Idle', + data: { + sector: transformSector(coordinates), + ...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, - }, - } + case 'Docked': { + const systemKey = stateFields.system as string + const system = await fetchAccount('StarSystem', systemKey as never) + if (!system) { + throw new Error(`StarSystem not found: ${systemKey}`) } - 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, - }, - } + return { + kind: 'Docked', + data: { + system, + lastUpdate: stateFields.lastUpdate as bigint, + sector: transformSector([ + system.coordinates[0], + system.coordinates[1], + ]), + ...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, - }, - } + } + + case 'MoveWarp': { + const journey = (stateFields.journey || stateFields) as Record< + string, + unknown + > + const from = journey.fromCoordinates as [ + { raw: bigint }, + { raw: bigint }, + ] + const to = journey.toCoordinates as [ + { raw: bigint }, + { raw: bigint }, + ] + const departureTime = journey.departureTime as bigint + const arrivalTime = journey.arrivalTime as bigint + + const fromCoords = transformSector(from) + const toCoords = transformSector(to) + + return { + kind: 'MoveWarp', + data: { + fromCoordinates: fromCoords, + toCoordinates: toCoords, + departureTime: transformTime(departureTime), + arrivalTime: transformTime(arrivalTime), + sector: calculateCurrentPosition( + fromCoords, + toCoords, + departureTime, + arrivalTime, + currentUnix, + ), + ...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, - }, - } + } + + case 'MoveSubwarp': { + const journey = (stateFields.journey || stateFields) as Record< + string, + unknown + > + const from = journey.fromCoordinates as [ + { raw: bigint }, + { raw: bigint }, + ] + const to = journey.toCoordinates as [ + { raw: bigint }, + { raw: bigint }, + ] + const departureTime = journey.departureTime as bigint + const arrivalTime = journey.arrivalTime as bigint + const fuelExpenditure = journey.fuelExpenditure as bigint + const lastUpdate = journey.lastUpdate as bigint + + const fromCoords = transformSector(from) + const toCoords = transformSector(to) + + return { + kind: 'MoveSubwarp', + data: { + fromCoordinates: fromCoords, + toCoordinates: toCoords, + departureTime: transformTime(departureTime), + arrivalTime: transformTime(arrivalTime), + fuelExpenditure, + lastUpdate: transformTime(lastUpdate), + sector: calculateCurrentPosition( + fromCoords, + toCoords, + departureTime, + arrivalTime, + currentUnix, + ), + ...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, - }, - } + } + + case 'MineAsteroid': { + const asteroidKey = stateFields.asteroid as string + const celestialBody = await fetchAccount( + 'CelestialBody', + asteroidKey as never, + ) + if (!celestialBody) { + throw new Error(`CelestialBody not found: ${asteroidKey}`) } - break - } - throw new Error('Data does not match expected type for the fleet state') + // Get coordinates from the parent star system + const systemKey = celestialBody.system + const system = await fetchAccount('StarSystem', systemKey as never) + if (!system) { + throw new Error(`StarSystem not found: ${systemKey}`) + } + + const start = stateFields.start as bigint + const lastUpdate = stateFields.lastUpdate as bigint + + // Calculate mining end time based on resources + // TODO: Implement proper mining calculation with fleet stats + const miningDuration = 3600n // Placeholder: 1 hour + const end = start + miningDuration + const endReason: EndReason = 'FULL' // Placeholder + + return { + kind: 'MineAsteroid', + data: { + asteroid: asteroidKey as never, + celestialBody, + start: transformTime(start), + end: transformTime(end), + amountMined: (stateFields.amountMined as bigint) ?? 0n, + lastUpdate: transformTime(lastUpdate), + endReason, + sector: transformSector([ + system.coordinates[0], + system.coordinates[1], + ]), + ...baseData, + }, + } + } + + case 'Respawn': { + const start = stateFields.start as bigint + // C4 uses respawnTimeWithoutFee for base respawn time + const respawnTime = + ((fleet.stats.miscStats as Record) + .respawnTimeWithoutFee as bigint) ?? 0n + const eta = start + respawnTime + + // Get coordinates from respawn location + const coordinates = stateFields.coordinates as + | [{ raw: bigint }, { raw: bigint }] + | undefined + + const sector = coordinates + ? transformSector(coordinates) + : Coordinates.fromNumber(0, 0) + + return { + kind: 'Respawn', + data: { + destructionTime: transformTime(start), + ETA: transformTime(eta), + sector, + ...baseData, + }, + } + } + } } diff --git a/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts b/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts index 9b22f9a2..f497d674 100644 --- a/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts +++ b/src/main/basedbot/lib/fleet-state/transform/transform-sector.ts @@ -1,6 +1,29 @@ -import BN from 'bn.js' - import { Coordinates } from '../../util/coordinates.js' -export const transformSector = (sector: BN[]): Coordinates => - Coordinates.fromBN(sector[0], sector[1]) +type RawCoord = { raw: bigint } +type CoordInput = + | readonly [RawCoord, RawCoord] + | [RawCoord, RawCoord] + | [bigint, bigint] + +/** + * Transform C4 coordinate array to Coordinates + * C4 uses [{raw: bigint}, {raw: bigint}] format + */ +export const transformSector = (coords: CoordInput): Coordinates => { + if (Array.isArray(coords) && coords.length === 2) { + const first = coords[0] + const second = coords[1] + + // Check if it's the object format with .raw + if (typeof first === 'object' && first !== null && 'raw' in first) { + const x = (first as RawCoord).raw + const y = (second as RawCoord).raw + return Coordinates.fromBigInt(x, y) + } + + // Raw bigint tuple + return Coordinates.fromBigInt(first as bigint, second as bigint) + } + throw new Error('Invalid coordinate format') +} diff --git a/src/main/basedbot/lib/fleet-state/transform/transform-time.ts b/src/main/basedbot/lib/fleet-state/transform/transform-time.ts index 650789ac..1e288277 100644 --- a/src/main/basedbot/lib/fleet-state/transform/transform-time.ts +++ b/src/main/basedbot/lib/fleet-state/transform/transform-time.ts @@ -1,6 +1,10 @@ -import BN from 'bn.js' +import dayjs, { type Dayjs } from '../../../../../dayjs.js' -import dayjs from '../../../../../dayjs.js' - -export const transformTime = (time: BN): dayjs.Dayjs => - dayjs.unix(time.toNumber()) +/** + * Transform C4 timestamp (bigint seconds) to Dayjs + */ +export const transformTime = (timestamp: bigint | number): Dayjs => { + const seconds = + typeof timestamp === 'bigint' ? Number(timestamp) : timestamp + return dayjs.unix(seconds) +} diff --git a/src/main/basedbot/lib/fleet-state/type-guard/idle.ts b/src/main/basedbot/lib/fleet-state/type-guard/idle.ts deleted file mode 100644 index e68afc00..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/idle.ts +++ /dev/null @@ -1,12 +0,0 @@ -import BN from 'bn.js' - -import { RawIdleData } from '../types.js' - -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 deleted file mode 100644 index 2cdaf723..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/mine-asteroid.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import BN from 'bn.js' - -import { RawMineAsteroidData } from '../types.js' - -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 deleted file mode 100644 index c598fa4e..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/move-sub-warp.ts +++ /dev/null @@ -1,18 +0,0 @@ -import BN from 'bn.js' - -import { RawMoveSubwarpData } from '../types.js' - -// 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 deleted file mode 100644 index f14cea6a..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/move-warp.ts +++ /dev/null @@ -1,18 +0,0 @@ -import BN from 'bn.js' - -import { RawMoveWarpData } from '../types.js' - -// 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 deleted file mode 100644 index 085324d2..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/respawn.ts +++ /dev/null @@ -1,12 +0,0 @@ -import BN from 'bn.js' - -import { RawRespawnData } from '../types.js' - -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 deleted file mode 100644 index 998a4eaf..00000000 --- a/src/main/basedbot/lib/fleet-state/type-guard/starbase-loading-bay.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import BN from 'bn.js' - -import { RawStarbaseLoadingBayData } from '../types.js' - -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 index 2af1db4c..2b6f37b0 100644 --- a/src/main/basedbot/lib/fleet-state/types.ts +++ b/src/main/basedbot/lib/fleet-state/types.ts @@ -1,118 +1,108 @@ -import { PublicKey } from '@solana/web3.js' -import { MineItem, Starbase } from '@staratlas/sage' -import BN from 'bn.js' +import type { Address } from '@solana/kit' +import type { CelestialBody, StarSystem } from '@staratlas/dev-sage' -import dayjs from '../../../../dayjs.js' -import { Coordinates } from '../util/coordinates.js' +import type { Dayjs } from '../../../../dayjs.js' +import type { AccountWithKey } from '../types.js' +import type { Coordinates } from '../util/coordinates.js' export type EndReason = 'FULL' | 'AMMO' | 'FOOD' + +/** + * Base data included in all fleet states + */ type BaseData = { sector: Coordinates - warpCooldownExpiry: dayjs.Dayjs - scanCoolDownExpiry: dayjs.Dayjs + warpCooldownExpiry: Dayjs + scanCoolDownExpiry: Dayjs warpCooldown: boolean scanCooldown: boolean } -// Raw data types for incoming data -export type RawIdleData = { - sector: [BN, BN] -} +/** + * Fleet state types matching C4 __kind discriminators + */ +export type FleetStateKind = + | 'Idle' + | 'Docked' + | 'MoveWarp' + | 'MoveSubwarp' + | 'MineAsteroid' + | 'Respawn' +/** + * Idle state data + */ export type IdleData = BaseData -export type RawStarbaseLoadingBayData = { starbase: PublicKey; lastUpdate: BN } -export type StarbaseLoadingBayData = { - starbase: Starbase - lastUpdate: BN +/** + * Docked state data (C4: was StarbaseLoadingBay) + */ +export type DockedData = { + system: AccountWithKey + lastUpdate: bigint } & BaseData -export type RawMineAsteroidData = { - asteroid: PublicKey - resource: PublicKey - start: BN - end: BN - amountMined: BN - lastUpdate: BN - sector: BN[] -} +/** + * Mining state data + */ export type MineAsteroidData = { - asteroid: PublicKey - mineItem: MineItem - resource: PublicKey - start: dayjs.Dayjs - end: dayjs.Dayjs - amountMined: BN - lastUpdate: dayjs.Dayjs + asteroid: Address + celestialBody: AccountWithKey + start: Dayjs + end: Dayjs + amountMined: bigint + lastUpdate: Dayjs endReason: EndReason } & BaseData -export type RawMoveWarpData = { - fromSector: BN[] - toSector: BN[] - warpStart: BN - warpFinish: BN -} +/** + * Warp movement state data + */ export type MoveWarpData = { - fromSector: Coordinates - toSector: Coordinates - warpStart: dayjs.Dayjs - warpFinish: dayjs.Dayjs + fromCoordinates: Coordinates + toCoordinates: Coordinates + departureTime: Dayjs + arrivalTime: Dayjs } & BaseData -export type RawMoveSubwarpData = { - fromSector: BN[] - toSector: BN[] - currentSector: BN[] - departureTime: BN - arrivalTime: BN - fuelExpenditure: BN - lastUpdate: BN -} +/** + * Subwarp movement state data + */ export type MoveSubwarpData = { - fromSector: Coordinates - toSector: Coordinates - departureTime: dayjs.Dayjs - arrivalTime: dayjs.Dayjs - fuelExpenditure: BN - lastUpdate: dayjs.Dayjs + fromCoordinates: Coordinates + toCoordinates: Coordinates + departureTime: Dayjs + arrivalTime: Dayjs + fuelExpenditure: bigint + lastUpdate: Dayjs } & BaseData -export type RawRespawnData = { - sector: [BN, BN] - start: BN -} +/** + * Respawn state data + */ export type RespawnData = { - destructionTime: dayjs.Dayjs - ETA: dayjs.Dayjs + destructionTime: Dayjs + ETA: Dayjs } & BaseData -export type StarbaseUpgradeData = BaseData -export type StarbaseRepairData = BaseData - -export type FleetStateType = - | 'StarbaseLoadingBay' - | 'Idle' - | 'MineAsteroid' - | 'MoveWarp' - | 'MoveSubwarp' - | 'Respawn' - | 'StarbaseUpgrade' - | 'StarbaseRepair' +/** + * Map of state kinds to their data types + */ export type FleetStateDataMap = { - StarbaseLoadingBay: StarbaseLoadingBayData Idle: IdleData - MineAsteroid: MineAsteroidData + Docked: DockedData MoveWarp: MoveWarpData MoveSubwarp: MoveSubwarpData + MineAsteroid: MineAsteroidData Respawn: RespawnData - StarbaseUpgrade: StarbaseUpgradeData - StarbaseRepair: StarbaseRepairData } +/** + * Discriminated union of all fleet states + */ export type FleetState = { - [K in FleetStateType]: { - type: K + [K in FleetStateKind]: { + kind: K data: FleetStateDataMap[K] } -}[FleetStateType] +}[FleetStateKind] diff --git a/src/main/basedbot/lib/programs.ts b/src/main/basedbot/lib/programs.ts deleted file mode 100644 index e5ad0c5c..00000000 --- a/src/main/basedbot/lib/programs.ts +++ /dev/null @@ -1,107 +0,0 @@ -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/index.js' -import { anchorProvider } from '../../../service/sol/anchor.js' - -// @ts-expect-error -export type StarAtlasProgram = ProgramMethods - -export const xpCategoryIds = - config.sol.rpcEndpoint.includes('devnet') || - config.sol.rpcEndpoint.includes('validator') || - config.sol.rpcEndpoint.includes('universe') || - config.sol.rpcEndpoint.includes('localhost') - ? { - 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('devnet') || - config.sol.rpcEndpoint.includes('validator') || - config.sol.rpcEndpoint.includes('universe') || - config.sol.rpcEndpoint.includes('localhost') - ? { - sage: 'sAgezwJpDb1aHvzNr3o24cKjsETmFEKghBEyJ1askDi', - 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 index 06b8991e..06c5f42a 100644 --- a/src/main/basedbot/lib/sage/act/create-fleet.ts +++ b/src/main/basedbot/lib/sage/act/create-fleet.ts @@ -1,143 +1,40 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source' -import { - Game, - Ship, - Starbase, - StarbasePlayer, - WrappedShipEscrow, -} from '@staratlas/sage' +/** + * Create fleet action - C4 migration stub + * TODO: Implement when needed + */ +import type { Address, KeyPairSigner } from '@solana/kit' +import type { Game, StarSystem } from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { addShipToFleetIx } from '../ix/add-ship-to-fleet.js' -import { createFleetIx } from '../ix/create-fleet.js' -import { getCargoStatsDefinition } from '../state/cargo-stats-definition.js' -import { getShipByMint, getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' +import type { AccountWithKey, Character } from '../../types.js' +import type { Player } from '../state/user-account.js' -export type FleetShips = Array export type FleetShip = { - shipMint: PublicKey + shipMint: Address 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 +export type CreateFleetParams = { + game: AccountWithKey + character: AccountWithKey + system: AccountWithKey + ships: FleetShip[] + fleetName: string + signer: KeyPairSigner + keyIndex?: number } -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(game.data.cargo.statsDefinition), - ]) - - 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.debug(`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([keyPair])( - await ixReturnsToIxs(instructions, player.signer), - ) +/** + * Execute create fleet action + * TODO: Implement with C4 create fleet instruction + */ +export async function createFleet( + _player: Player, + _game: AccountWithKey, + _system: AccountWithKey, + _ships: FleetShip[], + _fleetName: string, +): Promise { + logger.warn('createFleet not yet implemented for C4') + // TODO: Implement using getCreateFleetInstruction from dev-sage } diff --git a/src/main/basedbot/lib/sage/act/deposit-cargo.ts b/src/main/basedbot/lib/sage/act/deposit-cargo.ts index d4599b4e..6e88b45a 100644 --- a/src/main/basedbot/lib/sage/act/deposit-cargo.ts +++ b/src/main/basedbot/lib/sage/act/deposit-cargo.ts @@ -1,83 +1,35 @@ -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.js' -import { keyPair } from '../../../../../service/wallet' -import { getTokenBalance } from '../../../basedbot.js' -import { programs } from '../../programs.js' -import { depositCargoIx } from '../ix/deposit-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' - -export const depositCargo = async ( - player: Player, - game: Game, - starbase: Starbase, - mint: PublicKey, - amount: BN, -): 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, - ), - ) +/** + * Deposit cargo action - C4 migration stub + * TODO: Implement when needed + */ +import type { Address, KeyPairSigner } from '@solana/kit' +import type { Game, StarSystem } from '@staratlas/dev-sage' + +import { logger } from '../../../../../logger.js' +import type { AccountWithKey, Character } from '../../types.js' +import type { Player } from '../state/user-account.js' + +export type DepositCargoParams = { + game: AccountWithKey + character: AccountWithKey + system: AccountWithKey + mint: Address + amount: bigint + signer: KeyPairSigner + keyIndex?: number +} - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs(instructions, player.signer), - ) +/** + * Execute deposit cargo action (from wallet to starbase) + * TODO: Implement with C4 cargo instruction + */ +export async function depositCargo( + _player: Player, + _game: AccountWithKey, + _system: AccountWithKey, + _mint: Address, + _amount: bigint, +): Promise { + logger.warn('depositCargo not yet implemented for C4') + // TODO: Implement using cargo instructions from dev-sage } diff --git a/src/main/basedbot/lib/sage/act/deposit-ship.ts b/src/main/basedbot/lib/sage/act/deposit-ship.ts index e78c3965..66ec4e79 100644 --- a/src/main/basedbot/lib/sage/act/deposit-ship.ts +++ b/src/main/basedbot/lib/sage/act/deposit-ship.ts @@ -1,119 +1,35 @@ -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/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { addShipEscrowIx } from '../ix/add-ship-escrow.js' -import { getShipByMint, getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' - -import { FleetShips } from './create-fleet.js' - -export const depositShip = async ( - player: Player, - game: Game, - starbase: Starbase, - ship: Ship, - amount: BN, -): 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) - - let uiAmount = 0 - try { - const info = await connection.getTokenAccountBalance(sourceTokenAccount) - if (info.value.uiAmount === null) uiAmount = 0 - else uiAmount = info.value.uiAmount - } catch (_) { - uiAmount = 0 - } - - const amountAtOrigin = new BN(uiAmount) - - if (amountAtOrigin.lt(new BN(amount))) { - throw new Error( - `Not enough ships available at origin ${ship.data.mint.toBase58()}`, - ) - } - - 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([keyPair])( - await ixReturnsToIxs(instructions, player.signer), - ) +/** + * Deposit/ensure ships action - C4 migration stub + * TODO: Implement when needed + */ +import type { Address, KeyPairSigner } from '@solana/kit' +import type { Game, StarSystem } from '@staratlas/dev-sage' + +import { logger } from '../../../../../logger.js' +import type { AccountWithKey, Character } from '../../types.js' +import type { Player } from '../state/user-account.js' +import type { FleetShip } from './create-fleet.js' + +export type DepositShipParams = { + game: AccountWithKey + character: AccountWithKey + system: AccountWithKey + shipMint: Address + count: number + signer: KeyPairSigner + keyIndex?: number } -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) - } - } +/** + * Ensure ships are deposited at starbase + * TODO: Implement with C4 ship deposit instruction + */ +export async function ensureShips( + _player: Player, + _game: AccountWithKey, + _system: AccountWithKey, + _ships: FleetShip[], +): Promise { + logger.warn('ensureShips not yet implemented for C4') + // TODO: Implement using ship deposit instructions from dev-sage } diff --git a/src/main/basedbot/lib/sage/act/disband-fleet.ts b/src/main/basedbot/lib/sage/act/disband-fleet.ts index 032ea455..172cd736 100644 --- a/src/main/basedbot/lib/sage/act/disband-fleet.ts +++ b/src/main/basedbot/lib/sage/act/disband-fleet.ts @@ -1,92 +1,34 @@ -import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source' -import { Game, Starbase, WrappedShipEscrow } from '@staratlas/sage' +/** + * Disband fleet action - C4 migration stub + * TODO: Implement when needed + */ +import type { KeyPairSigner } from '@solana/kit' +import type { Game, StarSystem } from '@staratlas/dev-sage' -import dayjs from '../../../../../dayjs.js' import { logger } from '../../../../../logger.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { closeDisbandedFleetIx } from '../ix/close-disbanded-fleet.js' -import { disbandFleetIx } from '../ix/disband-fleet.js' -import { disbandedFleetToEscrowIx } from '../ix/disbanded-fleet-to-escrow.js' -import { getFleetShips } from '../state/get-fleet-ships.js' -import { getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { getName } from '../util.js' - -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 (let i = fleetShips.fleetShips.length - 1; i >= 0; --i) { - const fleetShipInfo = fleetShips.fleetShips[i] - - const pred = (v: WrappedShipEscrow) => v.ship.equals(fleetShipInfo.ship) - const shipEscrowIndex = - starbasePlayer.wrappedShipEscrows.findIndex(pred) - - ixs.push( - disbandedFleetToEscrowIx( - player, - game, - starbase, - starbasePlayer, - programs, - shipEscrowIndex === -1 ? null : shipEscrowIndex, - i, - disbandedFleetKey[0], - fleet.data.fleetShips, - fleetShipInfo.ship, - fleetShipInfo.amount, - ), - ) - } - - ixs.push( - closeDisbandedFleetIx( - player, - programs, - disbandedFleetKey[0], - fleet.data.fleetShips, - ), - ) - logger.debug( - `Added ${ixs.length} ixs for disbanding fleet ${getName(fleet)}`, - ) +import type { AccountWithKey, Character, Fleet } from '../../types.js' +import type { FleetInfo } from '../state/user-fleets.js' +import type { Player } from '../state/user-account.js' + +export type DisbandFleetParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + system: AccountWithKey + signer: KeyPairSigner + keyIndex?: number +} - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs(ixs, player.signer), - ) +/** + * Execute disband fleet action + * TODO: Implement with C4 disband instruction + */ +export async function disbandFleet( + _player: Player, + _game: AccountWithKey, + _system: AccountWithKey, + _fleetInfo: FleetInfo, +): Promise { + logger.warn('disbandFleet not yet implemented for C4') + // TODO: Implement using getDisbandFleetInstruction from dev-sage } diff --git a/src/main/basedbot/lib/sage/act/dock.ts b/src/main/basedbot/lib/sage/act/dock.ts index 610706a6..be2221e5 100644 --- a/src/main/basedbot/lib/sage/act/dock.ts +++ b/src/main/basedbot/lib/sage/act/dock.ts @@ -1,39 +1,108 @@ -import { ixReturnsToIxs } from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +/** + * Dock fleet action - C4 migration + * Transitions fleet from Idle to Docked state + */ +import type { Address, Instruction, KeyPairSigner } from '@solana/kit' +import { + findStarbasePlayerPda, + getIdleToDockedInstruction, + getRegisterStarbasePlayerInstruction, +} from '@staratlas/dev-sage' +import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { dockIx } from '../ix/dock.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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}`) +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' +import type { Profile } from '@staratlas/dev-player-profile' + +export type DockParams = { + game: AccountWithKey + fleet: AccountWithKey + profile: AccountWithKey + profileFaction: AccountWithKey + character: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + keyIndex?: number +} + +/** + * Creates dock instruction (Idle -> Docked) + * Returns instructions array (may include starbase player registration if needed) + */ +export async function createDockInstructions({ + game, + fleet, + profile, + profileFaction, + character, + system, + starbasePlayer, + signer, + keyIndex = 0, +}: DockParams): Promise { + const instructions: Instruction[] = [] + + // Check fleet state + if (fleet.state.__kind !== 'Idle') { + throw new Error( + `Cannot dock: fleet is in ${fleet.state.__kind} state, must be Idle`, + ) + } + + let starbasePlayerAddress = starbasePlayer + + // Register starbase player if needed + if (!starbasePlayerAddress) { + const [pda] = await findStarbasePlayerPda({ + system: system.key, + character: character.key, + }) + starbasePlayerAddress = pda + + logger.info(`Registering new starbase player at system: ${system.key}`) + + instructions.push( + getRegisterStarbasePlayerInstruction({ + game: game.key, + character: character.key, + profileFaction: profileFaction.key, + funder: signer, + system: system.key, + starbasePlayer: starbasePlayerAddress, + }), + ) } - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - - const ix = dockIx( - fleetInfo, - player, - game, - starbase, - starbasePlayer, - programs, + + // Add dock instruction + instructions.push( + getIdleToDockedInstruction({ + game: game.key, + systemAndStarbasePlayerSystem: system.key, + systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: profile.key, + keyIndex, + }), ) - const instructions = await ixReturnsToIxs(ix, player.signer) + return instructions +} + +/** + * Execute dock action - sends transaction + */ +export async function dock(params: DockParams): Promise { + const instructions = await createDockInstructions(params) - await sendAndConfirmInstructions([keyPair])(instructions) + logger.info(`Docking fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)(instructions) } diff --git a/src/main/basedbot/lib/sage/act/end-mine.ts b/src/main/basedbot/lib/sage/act/end-mine.ts index 35e54a5d..5a03e32a 100644 --- a/src/main/basedbot/lib/sage/act/end-mine.ts +++ b/src/main/basedbot/lib/sage/act/end-mine.ts @@ -1,100 +1,139 @@ +/** + * End mining action - C4 migration + * Transitions fleet from MineAsteroid to Idle state + */ +import type { Instruction, KeyPairSigner } from '@solana/kit' import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' + getStopMiningAsteroidInstruction, + getTheOneFleetStateHandlerInstruction, +} from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { miningHandlerIx } from '../ix/fleet-state-handler.js' -import { stopMiningIx } from '../ix/stop-mining.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { Mineable } from '../state/world-map.js' - -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 +import type { + AccountWithKey, + CelestialBody, + Character, + Fleet, + Game, +} from '../../types.js' + +export type EndMineParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + asteroid: AccountWithKey + signer: KeyPairSigner + keyIndex?: number +} + +/** + * Creates stop mining instruction + */ +export function createStopMiningInstruction({ + game, + fleet, + character, + asteroid, + signer, + keyIndex = 0, +}: EndMineParams): Instruction { + // Check fleet state + if (fleet.state.__kind !== 'MineAsteroid') { + throw new Error( + `Cannot stop mining: fleet is in ${fleet.state.__kind} state, must be MineAsteroid`, + ) + } + + return getStopMiningAsteroidInstruction({ + profileValidationSigner: signer, + profileValidationProfile: character.playerProfile, + character: character.key, + fleet: fleet.key, + asteroid: asteroid.key, + game: game.key, + keyIndex, + }) +} + +/** + * Creates mining handler instruction (processes mining rewards) + * This is typically called before stopMining to collect resources + */ +export function createMiningHandlerInstruction({ + game, + fleet, + asteroid, + signer, +}: Pick): Instruction { + return getTheOneFleetStateHandlerInstruction({ + game: game.key, + fleet: fleet.key, + asteroid: asteroid.key, + funder: signer, + }) +} + +/** + * Execute end mining action - sends transaction + * Stops mining and returns fleet to Idle state + */ +export async function endMine(params: EndMineParams): Promise { + const instruction = createStopMiningInstruction(params) + + logger.info(`Stopping mining for fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)([instruction]) +} + +/** + * Execute full mining cycle completion: + * 1. Process mining rewards (handler) + * 2. Stop mining + */ +export async function completeMining(params: EndMineParams): Promise { + const handlerIx = createMiningHandlerInstruction(params) + const stopIx = createStopMiningInstruction(params) + + logger.info(`Completing mining cycle for fleet ${params.fleet.key}`) + + // Send handler first to process rewards + await sendAndConfirmInstructions(params.signer)([handlerIx]) + + // Then stop mining + await sendAndConfirmInstructions(params.signer)([stopIx]) +} + +/** + * Check if mining is complete (end time reached) + */ +export function isMiningComplete(fleet: AccountWithKey): boolean { + const state = fleet.state + + if (state.__kind === 'MineAsteroid') { + const mineState = state.fields[0] as unknown as { end: bigint } + return BigInt(Date.now() / 1000) >= mineState.end } - 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([keyPair])) - - await ixReturnsToIxs( - [ - fuelToken.instructions, - stopMiningIx( - fleetInfo, - player, - game, - mineable, - fuelToken.address, - programs, - ), - ], - player.signer, - ).then(sendAndConfirmInstructions([keyPair])) + return false } + +/** + * Get remaining mining time in seconds (0 if complete) + */ +export function getRemainingMiningTime(fleet: AccountWithKey): number { + const state = fleet.state + + if (state.__kind === 'MineAsteroid') { + const mineState = state.fields[0] as unknown as { end: bigint } + const endTime = Number(mineState.end) + const now = Math.floor(Date.now() / 1000) + return Math.max(0, endTime - now) + } + + return 0 +} + +/** + * Alias for endMine() + */ +export const stopMining = endMine diff --git a/src/main/basedbot/lib/sage/act/end-move.ts b/src/main/basedbot/lib/sage/act/end-move.ts index 999c1d18..d77f7e8c 100644 --- a/src/main/basedbot/lib/sage/act/end-move.ts +++ b/src/main/basedbot/lib/sage/act/end-move.ts @@ -1,44 +1,114 @@ -import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +/** + * End movement action - C4 migration + * Completes warp or subwarp movement (MoveWarp/MoveSubwarp -> Idle) + */ +import type { Instruction, KeyPairSigner } from '@solana/kit' +import { getTheOneFleetStateHandlerInstruction } from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { movementSubwarpHandlerIx } from '../ix/movement-subwarp-handler.js' -import { stopWarpIx } from '../ix/stop-warp.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 +import type { + AccountWithKey, + Character, + Fleet, + Game, + RegionTracker, +} from '../../types.js' + +export type EndMoveParams = { + game: AccountWithKey + fleet: AccountWithKey + signer: KeyPairSigner + character?: AccountWithKey + regionTracker?: AccountWithKey +} + +/** + * Creates end movement instruction + * Uses TheOneFleetStateHandler to transition from movement states + */ +export function createEndMoveInstruction({ + game, + fleet, + signer, + character, + regionTracker, +}: EndMoveParams): Instruction { + // Check fleet state + if ( + fleet.state.__kind !== 'MoveWarp' && + fleet.state.__kind !== 'MoveSubwarp' + ) { + throw new Error( + `Cannot end move: fleet is in ${fleet.state.__kind} state, must be MoveWarp or MoveSubwarp`, + ) } - 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([keyPair])(instructions) + + return getTheOneFleetStateHandlerInstruction({ + game: game.key, + fleet: fleet.key, + funder: signer, + ...(character && { character: character.key }), + ...(regionTracker && { regionTracker: regionTracker.key }), + }) +} + +/** + * Execute end move action - sends transaction + */ +export async function endMove(params: EndMoveParams): Promise { + const instruction = createEndMoveInstruction(params) + + logger.info(`Ending movement for fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)([instruction]) +} + +/** + * Check if fleet movement is complete (arrival time passed) + */ +export function isMovementComplete(fleet: AccountWithKey): boolean { + const state = fleet.state + + if (state.__kind === 'MoveWarp') { + const moveState = state.fields[0] as unknown as { + journey?: { arrivalTime: bigint } + arrivalTime?: bigint + } + const arrivalTime = + moveState.journey?.arrivalTime ?? moveState.arrivalTime ?? 0n + return BigInt(Date.now() / 1000) >= arrivalTime + } + + if (state.__kind === 'MoveSubwarp') { + const moveState = state.fields[0] as unknown as { + journey?: { arrivalTime: bigint } + arrivalTime?: bigint + } + const arrivalTime = + moveState.journey?.arrivalTime ?? moveState.arrivalTime ?? 0n + return BigInt(Date.now() / 1000) >= arrivalTime + } + + return false +} + +/** + * Get remaining movement time in seconds (0 if complete) + */ +export function getRemainingMovementTime(fleet: AccountWithKey): number { + const state = fleet.state + + if (state.__kind === 'MoveWarp' || state.__kind === 'MoveSubwarp') { + const moveState = state.fields[0] as unknown as { + journey?: { arrivalTime: bigint } + arrivalTime?: bigint + } + const arrivalTime = Number( + moveState.journey?.arrivalTime ?? moveState.arrivalTime ?? 0n, + ) + const now = Math.floor(Date.now() / 1000) + return Math.max(0, arrivalTime - now) + } + + return 0 } diff --git a/src/main/basedbot/lib/sage/act/exit-respawn.ts b/src/main/basedbot/lib/sage/act/exit-respawn.ts deleted file mode 100644 index 0cf62f14..00000000 --- a/src/main/basedbot/lib/sage/act/exit-respawn.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' -import { CargoType } from '@staratlas/cargo' -import { InstructionReturn, ixReturnsToIxs } from '@staratlas/data-source' -import { Game, Starbase } from '@staratlas/sage' - -import { logger } from '../../../../../logger.js' -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { exitRespawnIx } from '../ix/exit-respawn.js' -import { forceDropFleetCargoIx } from '../ix/force-drop-fleet-cargo.js' -import { getCargoStatsDefinition } from '../state/cargo-stats-definition.js' -import { getCargoType } from '../state/cargo-types.js' -import { getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -import { getFleetCargoHold } from './load-cargo.js' - -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( - game.data.cargo.statsDefinition, - ) - - const ixs: Array = [] - - const cargoMints = player.cargoTypes.map((ct) => ct.data.mint) - const uniqPublicKeys = (keys: PublicKey[]): PublicKey[] => { - const uniqueStrings = [...new Set(keys.map((key) => key.toString()))] - - return uniqueStrings.map((str) => new PublicKey(str)) - } - - for (const key of uniqPublicKeys(cargoMints)) { - const mint = new PublicKey(key) - let cargoType: CargoType | undefined - try { - cargoType = getCargoType(player.cargoTypes, game, mint) - } catch (_) { - continue - } - - const cargoPod = getFleetCargoHold(mint, game, fleetInfo) - const tokenFrom = getAssociatedTokenAddressSync(mint, cargoPod, true) - - const accountInfo = await connection.getAccountInfo(tokenFrom) - - if (accountInfo && cargoType) { - 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([keyPair]), - ) -} diff --git a/src/main/basedbot/lib/sage/act/load-cargo.ts b/src/main/basedbot/lib/sage/act/load-cargo.ts index c87021b1..72b573bb 100644 --- a/src/main/basedbot/lib/sage/act/load-cargo.ts +++ b/src/main/basedbot/lib/sage/act/load-cargo.ts @@ -1,120 +1,219 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' +/** + * Load cargo to fleet action - C4 migration + * Transfers cargo from starbase to fleet using cargoId system + */ +import type { Address, Instruction, KeyPairSigner } from '@solana/kit' import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' + findStarbasePlayerPda, + getRegisterStarbasePlayerInstruction, + getTransferCargoToFleetInstruction, +} from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { getTokenBalance } from '../../../basedbot.js' -import { programs } from '../../programs.js' -import { loadCargoIx } from '../ix/load-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { getName } from '../util.js' - -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 - } +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' + +export type CargoTransfer = { + cargoId: number + amount: number // positive to load, negative to unload +} + +export type LoadCargoParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + profileFaction: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + transfers: CargoTransfer[] + keyIndex?: number +} + +/** + * Get fuel tank cargo ID from fleet + */ +export function getFuelCargoId(fleet: AccountWithKey): number { + return fleet.fuelTank?.cargoId ?? 1 } -export const loadCargo = async ( - fleetInfo: FleetInfo, - player: Player, - game: Game, - mint: PublicKey, - amount: number, - forceCargoHold: boolean = false, -): Promise => { - if (amount < 1) { - logger.warn(`Cannot load amount less than 1 (${amount})`) - return +/** + * Get ammo bank cargo ID from fleet + */ +export function getAmmoCargoId(fleet: AccountWithKey): number { + return fleet.ammoBank?.cargoId ?? 2 +} + +/** + * Creates transfer cargo instruction + * Handles routing to fuel tank, ammo bank, or cargo hold automatically + */ +export async function createTransferCargoInstructions({ + game, + fleet, + character, + profileFaction, + system, + starbasePlayer, + signer, + transfers, + keyIndex = 0, +}: LoadCargoParams): Promise { + const instructions: Instruction[] = [] + + // Check fleet state - must be docked + if (fleet.state.__kind !== 'Docked') { + throw new Error( + `Cannot transfer cargo: fleet is in ${fleet.state.__kind} state, must be Docked`, + ) } - const starbase = await starbaseByCoordinates(fleetInfo.location) - const hold = forceCargoHold - ? fleetInfo.fleet.data.cargoHold - : getFleetCargoHold(mint, game, fleetInfo) + let starbasePlayerAddress = starbasePlayer - if (!starbase) { - throw new Error(`No starbase found at ${fleetInfo.location}`) + // Register starbase player if needed + if (!starbasePlayerAddress) { + const [pda] = await findStarbasePlayerPda({ + system: system.key, + character: character.key, + }) + starbasePlayerAddress = pda + + logger.info(`Registering new starbase player at system: ${system.key}`) + + instructions.push( + getRegisterStarbasePlayerInstruction({ + game: game.key, + character: character.key, + profileFaction: profileFaction.key, + funder: signer, + system: system.key, + starbasePlayer: starbasePlayerAddress, + }), + ) } - const cargoType = getCargoType(player.cargoTypes, game, mint) - const fleetCargoTokenResult = createAssociatedTokenAccountIdempotent( - mint, - hold, - true, - ) + // Extract fuel and ammo cargo IDs from fleet + const fuelCargoId = getFuelCargoId(fleet) + const ammoCargoId = getAmmoCargoId(fleet) - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - const cargoPodFrom = await getCargoPodsForStarbasePlayer( - starbasePlayer, - programs, - ) + // Get current amounts and capacities + const currentFuel = Number(fleet.fuelTank?.amount ?? 0n) + const currentAmmo = Number(fleet.ammoBank?.amount ?? 0n) + const fuelCapacity = Number(fleet.stats?.cargoStats?.fuelCapacity ?? 0) + const ammoCapacity = Number(fleet.stats?.cargoStats?.ammoCapacity ?? 0) - const cargoTokenAccountAddress = getAssociatedTokenAddressSync( - mint, - cargoPodFrom.key, - true, - ) + let fuelTank: bigint | null = null + let ammoBank: bigint | null = null + const cargoHoldToLoad: Array = [] + const cargoHoldToUnload: Array = [] - const cargoAmountAtOrigin = await getTokenBalance(cargoPodFrom.key, mint) - const toLoad = cargoAmountAtOrigin.lt(new BN(amount)) - ? cargoAmountAtOrigin - : new BN(amount) + for (const { cargoId, amount } of transfers) { + if (amount === 0) continue - if (toLoad.eq(new BN(0))) { - logger.warn(`No ${mint} available at ${getName(starbase)}...`) + const isWithdrawal = amount < 0 + const absAmount = Math.abs(amount) - return - } - if (cargoAmountAtOrigin.lt(new BN(amount))) { - logger.warn( - `Not enough cargo available at origin Starbase, loading ${cargoAmountAtOrigin} instead of ${amount}`, - ) + if (cargoId === fuelCargoId) { + if (isWithdrawal) { + fuelTank = BigInt(amount) + } else { + // Fill tank first, overflow to cargo hold + const spaceInTank = fuelCapacity - currentFuel + const toTank = Math.min(absAmount, spaceInTank) + const overflow = absAmount - toTank + + if (toTank > 0) { + fuelTank = BigInt(toTank) + } + if (overflow > 0) { + cargoHoldToLoad.push([cargoId, BigInt(overflow)] as const) + } + } + } else if (cargoId === ammoCargoId) { + if (isWithdrawal) { + ammoBank = BigInt(amount) + } else { + // Fill bank first, overflow to cargo hold + const spaceInBank = ammoCapacity - currentAmmo + const toBank = Math.min(absAmount, spaceInBank) + const overflow = absAmount - toBank + + if (toBank > 0) { + ammoBank = BigInt(toBank) + } + if (overflow > 0) { + cargoHoldToLoad.push([cargoId, BigInt(overflow)] as const) + } + } + } else { + // Regular cargo goes to/from cargo hold + if (isWithdrawal) { + cargoHoldToUnload.push([cargoId, BigInt(absAmount)] as const) + } else { + cargoHoldToLoad.push([cargoId, BigInt(absAmount)] as const) + } + } } - const ix = loadCargoIx( - fleetInfo, - player, - game, - starbase, - starbasePlayer, - cargoPodFrom.key, - hold, - cargoTokenAccountAddress, - fleetCargoTokenResult.address, - mint, - cargoType.key, - programs, - toLoad, + // Create transfer instruction + instructions.push( + getTransferCargoToFleetInstruction({ + game: game.key, + character: character.key, + systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress, + systemAndStarbasePlayerSystem: system.key, + fleet: fleet.key, + profileValidationProfile: character.playerProfile, + profileValidationSigner: signer, + fuelTank, + ammoBank, + cargoHold: { toLoad: cargoHoldToLoad, toUnload: cargoHoldToUnload }, + keyIndex, + }), ) - const instructions = await ixReturnsToIxs( - [fleetCargoTokenResult.instructions, ix], - player.signer, - ) + return instructions +} + +/** + * Execute load cargo action - sends transaction + */ +export async function loadCargo(params: LoadCargoParams): Promise { + const instructions = await createTransferCargoInstructions(params) + + logger.info(`Loading cargo to fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)(instructions) +} + +/** + * Convenience function: load specific amount of fuel + */ +export async function loadFuel( + params: Omit & { amount: number }, +): Promise { + const fuelCargoId = getFuelCargoId(params.fleet) + return loadCargo({ + ...params, + transfers: [{ cargoId: fuelCargoId, amount: params.amount }], + }) +} - await sendAndConfirmInstructions([keyPair])(instructions) +/** + * Convenience function: load specific amount of ammo + */ +export async function loadAmmo( + params: Omit & { amount: number }, +): Promise { + const ammoCargoId = getAmmoCargoId(params.fleet) + return loadCargo({ + ...params, + transfers: [{ cargoId: ammoCargoId, amount: params.amount }], + }) } diff --git a/src/main/basedbot/lib/sage/act/mine.ts b/src/main/basedbot/lib/sage/act/mine.ts index ae486b1d..a72fd814 100644 --- a/src/main/basedbot/lib/sage/act/mine.ts +++ b/src/main/basedbot/lib/sage/act/mine.ts @@ -1,75 +1,79 @@ -import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +/** + * Start mining action - C4 migration + * Transitions fleet from Idle to MineAsteroid state + */ +import type { Instruction, KeyPairSigner } from '@solana/kit' +import { getStartMiningAsteroidInstruction } from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { startMiningIx } from '../ix/start-mining.js' -import { getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { Mineable } from '../state/world-map.js' +import type { + AccountWithKey, + CelestialBody, + Character, + Fleet, + Game, + RegionTracker, + StarSystem, +} from '../../types.js' -import { undock } from './undock.js' - -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 - } +export type MineParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + regionTracker: AccountWithKey + system: AccountWithKey + asteroid: AccountWithKey + signer: KeyPairSigner + keyIndex?: number +} - if (fleet.state.StarbaseLoadingBay) { - logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`, +/** + * Creates start mining instruction + */ +export function createStartMiningInstruction({ + game, + fleet, + character, + regionTracker, + system, + asteroid, + signer, + keyIndex = 0, +}: MineParams): Instruction { + // Check fleet state + if (fleet.state.__kind !== 'Idle') { + throw new Error( + `Cannot start mining: fleet is in ${fleet.state.__kind} state, must be Idle`, ) - - 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, - ) + return getStartMiningAsteroidInstruction({ + game: game.key, + asteroid: asteroid.key, + starSystem: system.key, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: character.playerProfile, + regionTracker: regionTracker.key, + character: character.key, + keyIndex, + }) +} - const ix = startMiningIx( - fleetInfo, - player, - game, - mineable, - starbasePlayer, - fuelTokenAccount.address, - programs, - ) +/** + * Execute start mining action - sends transaction + */ +export async function mine(params: MineParams): Promise { + const instruction = createStartMiningInstruction(params) - const instructions = await ixReturnsToIxs( - [fuelTokenAccount.instructions, ix], - player.signer, + logger.info( + `Starting mining at asteroid ${params.asteroid.key} for fleet ${params.fleet.key}`, ) - - await sendAndConfirmInstructions([keyPair])(instructions) + await sendAndConfirmInstructions(params.signer)([instruction]) } + +/** + * Alias for mine() + */ +export const startMining = mine diff --git a/src/main/basedbot/lib/sage/act/move.ts b/src/main/basedbot/lib/sage/act/move.ts index 94cc4bf3..fa15331e 100644 --- a/src/main/basedbot/lib/sage/act/move.ts +++ b/src/main/basedbot/lib/sage/act/move.ts @@ -1,133 +1,200 @@ +/** + * Movement actions - C4 migration + * Warp and subwarp fleet movement + */ +import type { Instruction, KeyPairSigner } from '@solana/kit' import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' + getStartSubwarpInstruction, + getWarpToCoordinateInstruction, +} from '@staratlas/dev-sage' -import dayjs from '../../../../../dayjs.js' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { getFuelConsumption } from '../../util/fuel-consumption.js' -import { subWarpIx } from '../ix/subwarp.js' -import { warpIx } from '../ix/warp.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -import { undock } from './undock.js' +import type { + AccountWithKey, + Character, + Fleet, + Game, + RegionTracker, +} from '../../types.js' 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') +export type MoveParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + regionTracker: AccountWithKey + destination: [bigint, bigint] + signer: KeyPairSigner + keyIndex?: number + warpMode?: WarpMode +} - return - } +/** + * Calculate if fleet can warp given distance and stats + */ +function canWarpDistance( + fleet: AccountWithKey, + fromCoords: [bigint, bigint], + toCoords: [bigint, bigint], +): boolean { + // Get max warp distance from fleet stats + // Handle FixedPoint type by converting to number first + const maxWarpValue = fleet.stats?.movementStats?.maxWarpDistance + const maxWarpDistance = BigInt( + typeof maxWarpValue === 'object' && maxWarpValue !== null + ? Number((maxWarpValue as { raw?: bigint }).raw ?? 0) + : Number(maxWarpValue ?? 0), + ) - if (fleet.state.StarbaseLoadingBay) { - logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${fleet.state.StarbaseLoadingBay.starbase}, undocking...`, - ) + // Calculate distance (using bigint math) + const dx = toCoords[0] - fromCoords[0] + const dy = toCoords[1] - fromCoords[1] + const distanceSquared = dx * dx + dy * dy - await undock(fleet, fleetInfo.location, player, game) - } + // maxWarpDistance is in units * 100 (e.g., 50 = 0.5 units) + // distance is in same units, so compare squared values + const maxWarpSquared = maxWarpDistance * maxWarpDistance - if (fleet.state.MineAsteroid) { - logger.info(`${fleetInfo.fleetName} is mining an asteroid, cannot move`) + return distanceSquared <= maxWarpSquared +} - return +/** + * Get current fleet coordinates from state + */ +function getCurrentCoordinates(fleet: AccountWithKey): [bigint, bigint] { + const state = fleet.state + + switch (state.__kind) { + case 'Idle': { + const idleState = state.fields[0] as unknown as { + sector: [bigint, bigint] + } + return [idleState.sector[0], idleState.sector[1]] + } + case 'Docked': { + // Docked fleets are at the system coordinates + const dockedState = state.fields[0] as unknown as { + sector: [bigint, bigint] + } + return [dockedState.sector[0], dockedState.sector[1]] + } + default: + throw new Error( + `Cannot get coordinates from state: ${state.__kind}`, + ) } +} - const { maxWarpDistance } = fleetInfo.movementStats - - const desiredDistance = fleetInfo.location.distanceFrom(coordinates) * 100 - - const fuelConsumption = getFuelConsumption( - fleetInfo.location, - coordinates, - fleetInfo, - ) - - const subWarpFuelConsumptionRatePerSecond = fuelConsumption.subwarp / 1000 - const warpFuelConsumptionRatePerSecond = fuelConsumption.warp / 1000 - - logger.info(`Distance to Travel: ${desiredDistance}`) - logger.info( - `Subwarp Fuel Consumption per sec: ${subWarpFuelConsumptionRatePerSecond}`, - ) - logger.info( - `Warp Fuel Consumption per sec: ${warpFuelConsumptionRatePerSecond}`, - ) - - logger.info(`Fuel level: ${fleetInfo.cargoLevels.fuel}`) +/** + * Creates warp instruction + */ +export function createWarpInstruction({ + game, + fleet, + character, + regionTracker, + destination, + signer, + keyIndex = 0, +}: Omit): Instruction { + return getWarpToCoordinateInstruction({ + game: game.key, + destination, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: character.playerProfile, + character: character.key, + regionTracker: regionTracker.key, + keyIndex, + }) +} - const canWarp = desiredDistance <= maxWarpDistance - const warp = warpMode === 'warp' || (warpMode === 'auto' && canWarp) +/** + * Creates subwarp instruction + */ +export function createSubwarpInstruction({ + game, + fleet, + character, + regionTracker, + destination, + signer, + keyIndex = 0, +}: Omit): Instruction { + return getStartSubwarpInstruction({ + game: game.key, + toCoordinate: destination, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: character.playerProfile, + character: character.key, + regionTracker: regionTracker.key, + keyIndex, + }) +} - if (warp && fleetInfo.fleetState.data.warpCooldown) { - const timeLeft = dayjs.duration( - dayjs().diff(fleetInfo.fleetState.data.warpCooldownExpiry), - ) +/** + * Creates move instruction (warp or subwarp based on mode/distance) + */ +export function createMoveInstruction(params: MoveParams): Instruction { + const { fleet, destination, warpMode = 'auto' } = params - logger.warn( - `Fleet is on warp cooldown, cannot warp. Retry in: ${timeLeft.humanize()}`, + // Check fleet state + if (fleet.state.__kind !== 'Idle') { + throw new Error( + `Cannot move: fleet is in ${fleet.state.__kind} state, must be Idle`, ) - - return } - const estimatedConsumption = warp - ? fuelConsumption.warp - : fuelConsumption.subwarp - - logger.info(`Estimated fuel consumption: ${estimatedConsumption}`) - - const hasEnoughFuel = fleetInfo.cargoLevels.fuel >= estimatedConsumption + const currentCoords = getCurrentCoordinates(fleet) + const canWarp = canWarpDistance(fleet, currentCoords, destination) - const hasEnoughFuelForRoundTrip = - fleetInfo.cargoLevels.fuel >= estimatedConsumption * 2 + // Determine movement type + const useWarp = warpMode === 'warp' || (warpMode === 'auto' && canWarp) - if (!hasEnoughFuel) { - logger.warn('Not enough fuel to move') - return + if (useWarp && !canWarp) { + throw new Error('Distance exceeds maximum warp distance') } - if (!hasEnoughFuelForRoundTrip) { - logger.warn( - 'Not enough fuel for the round trip. Need Fuel at destination Starbase', + // TODO: Check warp cooldown if warping + // TODO: Check fuel levels + + if (useWarp) { + logger.info(`Warping fleet to [${destination[0]}, ${destination[1]}]`) + return createWarpInstruction(params) + } else { + logger.info( + `Subwarping fleet to [${destination[0]}, ${destination[1]}]`, ) + return createSubwarpInstruction(params) } +} - const fuelTokenAccount = createAssociatedTokenAccountIdempotent( - game.data.mints.fuel, - fleet.data.fuelTank, - true, - ) +/** + * Execute move action - sends transaction + */ +export async function move(params: MoveParams): Promise { + const instruction = createMoveInstruction(params) - const ix = (warp ? warpIx : subWarpIx)( - fleetInfo, - coordinates, - fuelTokenAccount.address, - player, - game, - programs, - ) + await sendAndConfirmInstructions(params.signer)([instruction]) +} - const instructions = await ixReturnsToIxs( - [fuelTokenAccount.instructions, ix], - player.signer, - ) +/** + * Execute warp action - sends transaction + */ +export async function warp( + params: Omit, +): Promise { + return move({ ...params, warpMode: 'warp' }) +} - await sendAndConfirmInstructions([keyPair])(instructions) +/** + * Execute subwarp action - sends transaction + */ +export async function subwarp( + params: Omit, +): Promise { + return move({ ...params, warpMode: 'subwarp' }) } diff --git a/src/main/basedbot/lib/sage/act/rearm.ts b/src/main/basedbot/lib/sage/act/rearm.ts index 93766dcf..79dee6ee 100644 --- a/src/main/basedbot/lib/sage/act/rearm.ts +++ b/src/main/basedbot/lib/sage/act/rearm.ts @@ -1,91 +1,64 @@ -import { - createAssociatedTokenAccountIdempotent, - getParsedTokenAccountsByOwner, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' -import { logger } from '../../../../../logger' +/** + * Rearm fleet action - C4 migration + * Fills fleet ammo bank from starbase inventory + */ +import type { Address, KeyPairSigner } from '@solana/kit' -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { loadCargoIx } from '../ix/load-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' +import { logger } from '../../../../../logger.js' +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' +import { getAmmoCargoId, loadCargo } from './load-cargo.js' -export const rearm = async ( - fleetInfo: FleetInfo, - coordinates: Coordinates, - player: Player, - game: Game, -): Promise => { - const starbase = await starbaseByCoordinates(coordinates) +export type RearmParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + profileFaction: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + keyIndex?: number +} - if (!starbase) { - throw new Error(`No starbase found at ${coordinates}`) - } +/** + * Calculate ammo needed to fill bank + */ +export function getAmmoNeeded(fleet: AccountWithKey): number { + const currentAmmo = Number(fleet.ammoBank?.amount ?? 0n) + const maxAmmo = Number(fleet.stats?.cargoStats?.ammoCapacity ?? 0) + return Math.max(0, maxAmmo - currentAmmo) +} - const cargoType = getCargoType( - player.cargoTypes, - game, - game.data.mints.ammo, - ) - const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( - game.data.mints.ammo, - fleetInfo.fleet.data.ammoBank, - true, - ) +/** + * Rearm fleet - fills ammo bank to capacity + */ +export async function rearm(params: RearmParams): Promise { + const { fleet } = params - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - const cargoPodFrom = await getCargoPodsForStarbasePlayer( - starbasePlayer, - programs, - ) + const ammoNeeded = getAmmoNeeded(fleet) - const starbaseTokenAccounts = await getParsedTokenAccountsByOwner( - connection, - cargoPodFrom.key, - ) + if (ammoNeeded <= 0) { + logger.info('Fleet ammo bank is already full') + return + } - const currentAmmo = fleetInfo.cargoLevels.ammo - const maxAmmo = fleetInfo.cargoStats.ammoCapacity - const ammoNeeded = maxAmmo - currentAmmo + const currentAmmo = Number(fleet.ammoBank?.amount ?? 0n) + const maxAmmo = Number(fleet.stats?.cargoStats?.ammoCapacity ?? 0) logger.info( - `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), + `Rearming: current=${currentAmmo}, max=${maxAmmo}, needed=${ammoNeeded}`, ) - const instructions = await ixReturnsToIxs( - [fleetFuelTokenResult.instructions, ix], - player.signer, - ) + const ammoCargoId = getAmmoCargoId(fleet) - await sendAndConfirmInstructions([keyPair])(instructions) + await loadCargo({ + ...params, + transfers: [{ cargoId: ammoCargoId, amount: ammoNeeded }], + }) } diff --git a/src/main/basedbot/lib/sage/act/refuel.ts b/src/main/basedbot/lib/sage/act/refuel.ts index 955f743e..d571c28d 100644 --- a/src/main/basedbot/lib/sage/act/refuel.ts +++ b/src/main/basedbot/lib/sage/act/refuel.ts @@ -1,91 +1,64 @@ -import { - createAssociatedTokenAccountIdempotent, - getParsedTokenAccountsByOwner, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' -import { logger } from '../../../../../logger' +/** + * Refuel fleet action - C4 migration + * Fills fleet fuel tank from starbase inventory + */ +import type { Address, KeyPairSigner } from '@solana/kit' -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { loadCargoIx } from '../ix/load-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' +import { logger } from '../../../../../logger.js' +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' +import { getFuelCargoId, loadCargo } from './load-cargo.js' -export const refuel = async ( - fleetInfo: FleetInfo, - coordinates: Coordinates, - player: Player, - game: Game, -): Promise => { - const starbase = await starbaseByCoordinates(coordinates) +export type RefuelParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + profileFaction: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + keyIndex?: number +} - if (!starbase) { - throw new Error(`No starbase found at ${coordinates}`) - } +/** + * Calculate fuel needed to fill tank + */ +export function getFuelNeeded(fleet: AccountWithKey): number { + const currentFuel = Number(fleet.fuelTank?.amount ?? 0n) + const maxFuel = Number(fleet.stats?.cargoStats?.fuelCapacity ?? 0) + return Math.max(0, maxFuel - currentFuel) +} - const cargoType = getCargoType( - player.cargoTypes, - game, - game.data.mints.fuel, - ) - const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( - game.data.mints.fuel, - fleetInfo.fleet.data.fuelTank, - true, - ) +/** + * Refuel fleet - fills fuel tank to capacity + */ +export async function refuel(params: RefuelParams): Promise { + const { fleet } = params - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - const cargoPodFrom = await getCargoPodsForStarbasePlayer( - starbasePlayer, - programs, - ) + const fuelNeeded = getFuelNeeded(fleet) - const starbaseTokenAccounts = await getParsedTokenAccountsByOwner( - connection, - cargoPodFrom.key, - ) + if (fuelNeeded <= 0) { + logger.info('Fleet fuel tank is already full') + return + } - const currentFuel = fleetInfo.cargoLevels.fuel - const maxFuel = fleetInfo.cargoStats.fuelCapacity - const fuelNeeded = maxFuel - currentFuel + const currentFuel = Number(fleet.fuelTank?.amount ?? 0n) + const maxFuel = Number(fleet.stats?.cargoStats?.fuelCapacity ?? 0) logger.info( - `Current Fuel: ${currentFuel}, Max Fuel: ${maxFuel}, Fuel Needed: ${fuelNeeded}`, - ) - - // TODO: Check if starbase has enough fuel 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), + `Refueling: current=${currentFuel}, max=${maxFuel}, needed=${fuelNeeded}`, ) - const instructions = await ixReturnsToIxs( - [fleetFuelTokenResult.instructions, ix], - player.signer, - ) + const fuelCargoId = getFuelCargoId(fleet) - await sendAndConfirmInstructions([keyPair])(instructions) + await loadCargo({ + ...params, + transfers: [{ cargoId: fuelCargoId, amount: fuelNeeded }], + }) } diff --git a/src/main/basedbot/lib/sage/act/self-destruct.ts b/src/main/basedbot/lib/sage/act/self-destruct.ts index cc8a6b1f..7a9ef687 100644 --- a/src/main/basedbot/lib/sage/act/self-destruct.ts +++ b/src/main/basedbot/lib/sage/act/self-destruct.ts @@ -1,35 +1,26 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { ixReturnsToIxs } from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +/** + * Self-destruct fleet action - C4 migration stub + * TODO: Implement when needed + */ +import type { KeyPairSigner } from '@solana/kit' +import type { Game } from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { idleToRespawnIx } from '../ix/idle-to-respawn.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' +import type { AccountWithKey, Character, Fleet } from '../../types.js' -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(), - ) +export type SelfDestructParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + signer: KeyPairSigner + keyIndex?: number +} - await ixReturnsToIxs( - idleToRespawnIx(player, game, fleet, atlasTokenFrom, programs), - player.signer, - ).then(sendAndConfirmInstructions([keyPair])) +/** + * Execute self-destruct action + * TODO: Implement with C4 self-destruct instruction + */ +export async function selfDestruct(_params: SelfDestructParams): Promise { + logger.warn('selfDestruct not yet implemented for C4') + // TODO: Implement using getSelfDestructInstruction from dev-sage } diff --git a/src/main/basedbot/lib/sage/act/stop-subwarp.ts b/src/main/basedbot/lib/sage/act/stop-subwarp.ts index 34a8f9cf..777ddd6a 100644 --- a/src/main/basedbot/lib/sage/act/stop-subwarp.ts +++ b/src/main/basedbot/lib/sage/act/stop-subwarp.ts @@ -1,53 +1,64 @@ -import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +/** + * Stop subwarp action - C4 migration + * Stops subwarp movement before arrival (MoveSubwarp -> Idle) + */ +import type { Instruction, KeyPairSigner } from '@solana/kit' +import { getStopSubwarpInstruction } from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { stopSubWarpIx } from '../ix/stop-subwarp.js' -import { getCargoStatsDefinition } from '../state/cargo-stats-definition.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' +import type { + AccountWithKey, + Character, + Fleet, + Game, + RegionTracker, +} from '../../types.js' -import { endMove } from './end-move.js' - -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') +export type StopSubwarpParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + regionTracker: AccountWithKey + signer: KeyPairSigner + keyIndex?: number +} - return +/** + * Creates stop subwarp instruction + */ +export function createStopSubwarpInstruction({ + game, + fleet, + character, + regionTracker, + signer, + keyIndex = 0, +}: StopSubwarpParams): Instruction { + // Check fleet state + if (fleet.state.__kind !== 'MoveSubwarp') { + throw new Error( + `Cannot stop subwarp: fleet is in ${fleet.state.__kind} state, must be MoveSubwarp`, + ) } - const fuelToken = createAssociatedTokenAccountIdempotent( - game.data.mints.fuel, - fleet.data.fuelTank, - true, - ) + return getStopSubwarpInstruction({ + game: game.key, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: character.playerProfile, + character: character.key, + regionTracker: regionTracker.key, + keyIndex, + }) +} + +/** + * Execute stop subwarp action - sends transaction + */ +export async function stopSubwarp(params: StopSubwarpParams): Promise { + const instruction = createStopSubwarpInstruction(params) - await ixReturnsToIxs( - [ - fuelToken.instructions, - stopSubWarpIx( - fleetInfo, - player, - game, - await getCargoStatsDefinition(game.data.cargo.statsDefinition), - fuelToken.address, - game.data.mints.fuel, - programs, - ), - ], - player.signer, - ).then(sendAndConfirmInstructions([keyPair])) - await endMove(fleetInfo, player, game) + logger.info(`Stopping subwarp for fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)([instruction]) } diff --git a/src/main/basedbot/lib/sage/act/undock.ts b/src/main/basedbot/lib/sage/act/undock.ts index 765821e0..c7951790 100644 --- a/src/main/basedbot/lib/sage/act/undock.ts +++ b/src/main/basedbot/lib/sage/act/undock.ts @@ -1,42 +1,108 @@ -import { ixReturnsToIxs } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' +/** + * Undock fleet action - C4 migration + * Transitions fleet from Docked to Idle state + */ +import type { Address, Instruction, KeyPairSigner } from '@solana/kit' +import { + findStarbasePlayerPda, + getDockedToIdleInstruction, + getRegisterStarbasePlayerInstruction, +} from '@staratlas/dev-sage' +import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { getStarbasePlayer } from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' - -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}`) +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' +import type { Profile } from '@staratlas/dev-player-profile' + +export type UndockParams = { + game: AccountWithKey + fleet: AccountWithKey + profile: AccountWithKey + profileFaction: AccountWithKey + character: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + keyIndex?: number +} + +/** + * Creates undock instruction (Docked -> Idle) + * Returns instructions array (may include starbase player registration if needed) + */ +export async function createUndockInstructions({ + game, + fleet, + profile, + profileFaction, + character, + system, + starbasePlayer, + signer, + keyIndex = 0, +}: UndockParams): Promise { + const instructions: Instruction[] = [] + + // Check fleet state + if (fleet.state.__kind !== 'Docked') { + throw new Error( + `Cannot undock: fleet is in ${fleet.state.__kind} state, must be Docked`, + ) + } + + let starbasePlayerAddress = starbasePlayer + + // Register starbase player if needed + if (!starbasePlayerAddress) { + const [pda] = await findStarbasePlayerPda({ + system: system.key, + character: character.key, + }) + starbasePlayerAddress = pda + + logger.info(`Registering new starbase player at system: ${system.key}`) + + instructions.push( + getRegisterStarbasePlayerInstruction({ + game: game.key, + character: character.key, + profileFaction: profileFaction.key, + funder: signer, + system: system.key, + starbasePlayer: starbasePlayerAddress, + }), + ) } - 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, + + // Add undock instruction + instructions.push( + getDockedToIdleInstruction({ + game: game.key, + systemAndStarbasePlayerSystem: system.key, + systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress, + fleet: fleet.key, + profileValidationSigner: signer, + profileValidationProfile: profile.key, + keyIndex, + }), ) - const instructions = await ixReturnsToIxs(ix, player.signer) + return instructions +} + +/** + * Execute undock action - sends transaction + */ +export async function undock(params: UndockParams): Promise { + const instructions = await createUndockInstructions(params) - await sendAndConfirmInstructions([keyPair])(instructions) + logger.info(`Undocking fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)(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 index 2141814b..2184e318 100644 --- a/src/main/basedbot/lib/sage/act/unload-all-cargo.ts +++ b/src/main/basedbot/lib/sage/act/unload-all-cargo.ts @@ -1,95 +1,5 @@ -import { - createAssociatedTokenAccountIdempotent, - getParsedTokenAccountsByOwner, - InstructionReturn, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' - -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { unloadCargoIx } from '../ix/unload-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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([keyPair])(instructions) -} +/** + * Unload all cargo action - C4 migration + * Re-exports from unload-cargo.ts for backwards compatibility + */ +export { unloadAllCargo } from './unload-cargo.js' diff --git a/src/main/basedbot/lib/sage/act/unload-cargo.ts b/src/main/basedbot/lib/sage/act/unload-cargo.ts index a232c6f0..92db1678 100644 --- a/src/main/basedbot/lib/sage/act/unload-cargo.ts +++ b/src/main/basedbot/lib/sage/act/unload-cargo.ts @@ -1,114 +1,194 @@ -import { getAssociatedTokenAddressSync } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' +/** + * Unload cargo from fleet action - C4 migration + * Transfers cargo from fleet to starbase using cargoId system + */ +import type { Address, Instruction, KeyPairSigner } from '@solana/kit' import { - createAssociatedTokenAccountIdempotent, - ixReturnsToIxs, -} from '@staratlas/data-source' -import { Game } from '@staratlas/sage' -import BN from 'bn.js' + findStarbasePlayerPda, + getRegisterStarbasePlayerInstruction, + getTransferCargoToFleetInstruction, +} from '@staratlas/dev-sage' import { logger } from '../../../../../logger.js' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { getTokenBalance } from '../../../basedbot.js' -import { programs } from '../../programs.js' -import { unloadCargoIx } from '../ix/unload-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates.js' +import type { + AccountWithKey, + Character, + Fleet, + Game, + ProfileFactionAccount, + StarSystem, +} from '../../types.js' import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { getFleetCargoHold } from './load-cargo.js' - -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 - } + getFuelCargoId, + getAmmoCargoId, + type CargoTransfer, +} from './load-cargo.js' + +export type UnloadCargoParams = { + game: AccountWithKey + fleet: AccountWithKey + character: AccountWithKey + profileFaction: AccountWithKey + system: AccountWithKey + starbasePlayer: Address | undefined + signer: KeyPairSigner + transfers: CargoTransfer[] + keyIndex?: number } -export const unloadCargo = async ( - fleetInfo: FleetInfo, - player: Player, - game: Game, - mint: PublicKey, - amount: BN, - forceCargoHold: boolean = false, -): Promise => { - const starbase = await starbaseByCoordinates(fleetInfo.location) - - if (!starbase) { - throw new Error(`No starbase found at ${fleetInfo.location}`) +/** + * Creates unload cargo instruction + * Uses negative amounts to transfer from fleet to starbase + */ +export async function createUnloadCargoInstructions({ + game, + fleet, + character, + profileFaction, + system, + starbasePlayer, + signer, + transfers, + keyIndex = 0, +}: UnloadCargoParams): Promise { + const instructions: Instruction[] = [] + + // Check fleet state - must be docked + if (fleet.state.__kind !== 'Docked') { + throw new Error( + `Cannot unload cargo: fleet is in ${fleet.state.__kind} state, must be Docked`, + ) } - const cargoType = getCargoType(player.cargoTypes, game, mint) + let starbasePlayerAddress = starbasePlayer - const fleetCargoPod = forceCargoHold - ? fleetInfo.fleet.data.cargoHold - : getFleetCargoHold(mint, game, fleetInfo) + // Register starbase player if needed + if (!starbasePlayerAddress) { + const [pda] = await findStarbasePlayerPda({ + system: system.key, + character: character.key, + }) + starbasePlayerAddress = pda - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - const cargoPodTo = await getCargoPodsForStarbasePlayer( - starbasePlayer, - programs, - ) + logger.info(`Registering new starbase player at system: ${system.key}`) - const cargoPodTokenAccountAddress = getAssociatedTokenAddressSync( - mint, - cargoPodTo.key, - true, - ) - const cargoFleetTokenAccountAddress = getAssociatedTokenAddressSync( - mint, - fleetCargoPod, - true, - ) - const cargoPodTokenResult = createAssociatedTokenAccountIdempotent( - mint, - cargoPodTo.key, - true, - ) + instructions.push( + getRegisterStarbasePlayerInstruction({ + game: game.key, + character: character.key, + profileFaction: profileFaction.key, + funder: signer, + system: system.key, + starbasePlayer: starbasePlayerAddress, + }), + ) + } - const amountAtOrigin = await getTokenBalance(fleetCargoPod, mint) + const fuelCargoId = getFuelCargoId(fleet) + const ammoCargoId = getAmmoCargoId(fleet) - if (amountAtOrigin.lt(amount)) { - logger.warn( - `Requested ${amount.toNumber()} cargo to unload. can only unload ${amountAtOrigin.toNumber()}`, - ) + let fuelTank: bigint | null = null + let ammoBank: bigint | null = null + const cargoHoldToUnload: Array = [] + + for (const { cargoId, amount } of transfers) { + if (amount === 0) continue + + // For unloading, we use negative values + const unloadAmount = Math.abs(amount) + + if (cargoId === fuelCargoId) { + fuelTank = BigInt(-unloadAmount) + } else if (cargoId === ammoCargoId) { + ammoBank = BigInt(-unloadAmount) + } else { + cargoHoldToUnload.push([cargoId, BigInt(unloadAmount)] as const) + } } - const toUnload = amountAtOrigin.lt(amount) ? amountAtOrigin : 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, + // Create transfer instruction + instructions.push( + getTransferCargoToFleetInstruction({ + game: game.key, + character: character.key, + systemAndStarbasePlayerStarbasePlayer: starbasePlayerAddress, + systemAndStarbasePlayerSystem: system.key, + fleet: fleet.key, + profileValidationProfile: character.playerProfile, + profileValidationSigner: signer, + fuelTank, + ammoBank, + cargoHold: { toLoad: [], toUnload: cargoHoldToUnload }, + keyIndex, + }), ) - await sendAndConfirmInstructions([keyPair])(instructions) + return instructions +} + +/** + * Execute unload cargo action - sends transaction + */ +export async function unloadCargo(params: UnloadCargoParams): Promise { + const instructions = await createUnloadCargoInstructions(params) + + logger.info(`Unloading cargo from fleet ${params.fleet.key}`) + await sendAndConfirmInstructions(params.signer)(instructions) +} + +/** + * Unload all cargo from fleet cargo hold + */ +export async function unloadAllCargo( + params: Omit, +): Promise { + const { fleet } = params + + // Get all cargo in cargo hold + const cargoAmounts = fleet.cargoHold?.amounts + if (!cargoAmounts || cargoAmounts.size === 0) { + logger.info('No cargo to unload') + return + } + + const transfers: CargoTransfer[] = [] + for (const [cargoId, amount] of cargoAmounts) { + if (Number(amount) > 0) { + transfers.push({ cargoId: Number(cargoId), amount: Number(amount) }) + } + } + + if (transfers.length === 0) { + logger.info('No cargo to unload') + return + } + + return unloadCargo({ ...params, transfers }) +} + +/** + * Convenience function: unload specific amount of fuel + */ +export async function unloadFuel( + params: Omit & { amount: number }, +): Promise { + const fuelCargoId = getFuelCargoId(params.fleet) + return unloadCargo({ + ...params, + transfers: [{ cargoId: fuelCargoId, amount: params.amount }], + }) +} + +/** + * Convenience function: unload specific amount of ammo + */ +export async function unloadAmmo( + params: Omit & { amount: number }, +): Promise { + const ammoCargoId = getAmmoCargoId(params.fleet) + return unloadCargo({ + ...params, + transfers: [{ cargoId: ammoCargoId, amount: params.amount }], + }) } diff --git a/src/main/basedbot/lib/sage/act/withdraw-cargo.ts b/src/main/basedbot/lib/sage/act/withdraw-cargo.ts deleted file mode 100644 index 443a2b02..00000000 --- a/src/main/basedbot/lib/sage/act/withdraw-cargo.ts +++ /dev/null @@ -1,84 +0,0 @@ -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.js' -import { keyPair } from '../../../../../service/wallet' -import { getTokenBalance } from '../../../basedbot.js' -import { programs } from '../../programs.js' -import { withdrawCargoIx } from '../ix/withdraw-cargo.js' -import { getCargoType } from '../state/cargo-types.js' -import { - getCargoPodsForStarbasePlayer, - getStarbasePlayer, -} from '../state/starbase-player.js' -import { Player } from '../state/user-account.js' - -export const withdrawCargo = async ( - player: Player, - game: Game, - starbase: Starbase, - mint: PublicKey, - amount: BN, -): Promise => { - const instructions: InstructionReturn[] = [] - - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) - - const destinationTokenAccount = getAssociatedTokenAddressSync( - mint, - player.signer.publicKey(), - ) - - const cargoPodFrom = await getCargoPodsForStarbasePlayer( - starbasePlayer, - programs, - ) - const sourceTokenAccount = getAssociatedTokenAddressSync( - mint, - cargoPodFrom.key, - true, - ) - - instructions.push( - createAssociatedTokenAccountIdempotent( - mint, - destinationTokenAccount, - true, - ).instructions, - ) - - const cargoType = getCargoType(player.cargoTypes, game, mint) - - const amountAtOrigin = await getTokenBalance(cargoPodFrom.key, mint) - - if (amountAtOrigin.lt(new BN(amount))) { - throw new Error('Not enough cargo available at origin') - } - - instructions.push( - withdrawCargoIx( - player, - game, - starbase, - starbasePlayer, - cargoPodFrom.key, - sourceTokenAccount, - destinationTokenAccount, - cargoType.key, - mint, - programs, - amount, - ), - ) - - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs(instructions, player.signer), - ) -} diff --git a/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts b/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts deleted file mode 100644 index 3c60a126..00000000 --- a/src/main/basedbot/lib/sage/ix/add-ship-escrow.ts +++ /dev/null @@ -1,44 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' - -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, -): 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 deleted file mode 100644 index 1a4b31de..00000000 --- a/src/main/basedbot/lib/sage/ix/add-ship-to-fleet.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' - -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 deleted file mode 100644 index dc871b58..00000000 --- a/src/main/basedbot/lib/sage/ix/close-disbanded-fleet.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { DisbandedFleet } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' - -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 deleted file mode 100644 index ac40bc51..00000000 --- a/src/main/basedbot/lib/sage/ix/create-fleet.ts +++ /dev/null @@ -1,46 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' - -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, -): 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/deposit-cargo.ts b/src/main/basedbot/lib/sage/ix/deposit-cargo.ts deleted file mode 100644 index d9373846..00000000 --- a/src/main/basedbot/lib/sage/ix/deposit-cargo.ts +++ /dev/null @@ -1,40 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' - -export const depositCargoIx = ( - player: Player, - game: Game, - starbase: Starbase, - starbasePlayer: StarbasePlayer, - cargoPodTo: PublicKey, - tokenFrom: PublicKey, - tokenTo: PublicKey, - cargoType: PublicKey, - programs: StarAtlasPrograms, - amount: BN, -): 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/disband-fleet.ts b/src/main/basedbot/lib/sage/ix/disband-fleet.ts deleted file mode 100644 index a4592466..00000000 --- a/src/main/basedbot/lib/sage/ix/disband-fleet.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' - -type DisbandFleetReturn = { - disbandedFleetKey: [PublicKey, number] - instructions: InstructionReturn -} - -export const disbandFleetIx = ( - player: Player, - game: Game, - starbase: Starbase, - starbasePlayer: StarbasePlayer, - programs: StarAtlasPrograms, - fleet: Fleet, -): 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 deleted file mode 100644 index 615e0ce3..00000000 --- a/src/main/basedbot/lib/sage/ix/disbanded-fleet-to-escrow.ts +++ /dev/null @@ -1,40 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' - -export const disbandedFleetToEscrowIx = ( - player: Player, - game: Game, - starbase: Starbase, - starbasePlayer: StarbasePlayer, - programs: StarAtlasPrograms, - shipEscrowIndex: number | null, - fleetShipInfoIndex: number, - 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, - 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 deleted file mode 100644 index 7175d4f9..00000000 --- a/src/main/basedbot/lib/sage/ix/dock.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 57824e25..00000000 --- a/src/main/basedbot/lib/sage/ix/exit-respawn.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 07c9b32c..00000000 --- a/src/main/basedbot/lib/sage/ix/fleet-state-handler.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { getCargoType } from '../state/cargo-types.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { Mineable } from '../state/world-map.js' - -export const miningHandlerIx = ( - fleetInfo: FleetInfo, - player: Player, - mineable: Mineable, - foodTokenFrom: PublicKey, - ammoTokenFrom: PublicKey, - resourceTokenFrom: PublicKey, - resourceTokenTo: PublicKey, - programs: StarAtlasPrograms, - game: Game, -): 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 deleted file mode 100644 index 1f73aa09..00000000 --- a/src/main/basedbot/lib/sage/ix/force-drop-fleet-cargo.ts +++ /dev/null @@ -1,29 +0,0 @@ -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.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 3c165a69..00000000 --- a/src/main/basedbot/lib/sage/ix/idle-to-respawn.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' - -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/load-cargo.ts b/src/main/basedbot/lib/sage/ix/load-cargo.ts deleted file mode 100644 index e18c1bcd..00000000 --- a/src/main/basedbot/lib/sage/ix/load-cargo.ts +++ /dev/null @@ -1,48 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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, -): 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 deleted file mode 100644 index 9c089f07..00000000 --- a/src/main/basedbot/lib/sage/ix/movement-subwarp-handler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 30ebc794..00000000 --- a/src/main/basedbot/lib/sage/ix/start-mining.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { Mineable } from '../state/world-map.js' - -export const startMiningIx = ( - fleetInfo: FleetInfo, - player: Player, - game: Game, - mineable: Mineable, - starbasePlayer: StarbasePlayer, - fuelTokenAccount: PublicKey, - programs: StarAtlasPrograms, -): 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 deleted file mode 100644 index bb0c4adc..00000000 --- a/src/main/basedbot/lib/sage/ix/stop-mining.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' -import { Mineable } from '../state/world-map.js' - -export const stopMiningIx = ( - fleetInfo: FleetInfo, - player: Player, - game: Game, - mineable: Mineable, - fuelTokenAccount: PublicKey, - programs: StarAtlasPrograms, -): 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 deleted file mode 100644 index 96738a3b..00000000 --- a/src/main/basedbot/lib/sage/ix/stop-subwarp.ts +++ /dev/null @@ -1,43 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index ec2e10a7..00000000 --- a/src/main/basedbot/lib/sage/ix/stop-warp.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 470334de..00000000 --- a/src/main/basedbot/lib/sage/ix/subwarp.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index b15074c6..00000000 --- a/src/main/basedbot/lib/sage/ix/undock.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game, Starbase, StarbasePlayer } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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 deleted file mode 100644 index 727f6ffd..00000000 --- a/src/main/basedbot/lib/sage/ix/unload-cargo.ts +++ /dev/null @@ -1,48 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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, -): 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 deleted file mode 100644 index cbccc326..00000000 --- a/src/main/basedbot/lib/sage/ix/warp.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { InstructionReturn } from '@staratlas/data-source' -import { Fleet, Game } from '@staratlas/sage' - -import { StarAtlasPrograms } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' -import { Player } from '../state/user-account.js' -import { FleetInfo } from '../state/user-fleets.js' - -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/ix/withdraw-cargo.ts b/src/main/basedbot/lib/sage/ix/withdraw-cargo.ts deleted file mode 100644 index db70af72..00000000 --- a/src/main/basedbot/lib/sage/ix/withdraw-cargo.ts +++ /dev/null @@ -1,43 +0,0 @@ -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.js' -import { Player } from '../state/user-account.js' - -export const withdrawCargoIx = ( - player: Player, - game: Game, - starbase: Starbase, - starbasePlayer: StarbasePlayer, - cargoPodTo: PublicKey, - tokenFrom: PublicKey, - tokenTo: PublicKey, - cargoType: PublicKey, - mint: PublicKey, - programs: StarAtlasPrograms, - amount: BN, -): InstructionReturn => - StarbasePlayer.withdrawCargoFromGame( - programs.sage, - programs.cargo, - starbasePlayer.key, - player.signer, - player.publicKey, - player.profile.key, - player.profileFaction.key, - starbase.key, - cargoPodTo, - cargoType, - game.data.cargo.statsDefinition, - tokenFrom, - tokenTo, - mint, - game.key, - game.data.gameState, - { - amount, - keyIndex: 0, - }, - ) diff --git a/src/main/basedbot/lib/sage/ships.ts b/src/main/basedbot/lib/sage/ships.ts index a932b1bb..c6511198 100644 --- a/src/main/basedbot/lib/sage/ships.ts +++ b/src/main/basedbot/lib/sage/ships.ts @@ -1,10 +1,14 @@ -import { PublicKey } from '@solana/web3.js' -import { Game, Ship } from '@staratlas/sage' +/** + * Ship data utilities for C4 + * TODO: Implement ship config loading for C4 + * In C4, ship configs may be accessed differently + */ + +import type { Address } from '@solana/kit' import superagent from 'superagent' + import { config } from '../../../../config/index.js' import { logger } from '../../../../logger.js' -import { programs } from '../programs.js' -import { getShipByMint } from './state/starbase-player.js' interface ApiItem { _id: string @@ -22,18 +26,6 @@ interface ApiItem { mint: string } -interface ShipData { - role: ShipRole - mint: PublicKey - make: ShipMake - model: ShipModel -} - -export interface ExtShipData extends ShipData { - ship: Ship - size: number -} - type ShipRole = | 'fighter' | 'multi-role' @@ -79,6 +71,19 @@ type ShipModel = | 'MiG' | 'Tree Arrow' +interface ShipData { + name: string + role: ShipRole + mint: Address + make: ShipMake + model: ShipModel | 'Unknown' +} + +export interface ExtShipData extends ShipData { + configId?: number + size: number +} + const problematicMints = new Set([ 'RNGRjeGyFeyFT4k5aTJXKZukVx3GbG215fcSQJxg64G', 'phi4PYgmxeTMLLpGkU87T16VUZ6AjWZESkfT1JGJ635', @@ -92,13 +97,13 @@ const parseShips = (items: ApiItem[]): ShipData[] => { .map((item) => ({ name: item.name, role: item.attributes.spec as ShipRole, - mint: new PublicKey(item.mint), + mint: item.mint as Address, make: item.attributes.make as ShipMake, model: (item.attributes.model as ShipModel) || 'Unknown', })) } -export const fetchGalaxyData = async (baseUrl: string) => { +export const fetchGalaxyData = async (baseUrl: string): Promise => { logger.info('Fetching galaxy data') const res = await superagent.get(`${baseUrl}/items`) @@ -107,22 +112,15 @@ export const fetchGalaxyData = async (baseUrl: string) => { return res.body } -export const getShipData = async (game: Game): Promise> => { +/** + * Get ship data from API + * TODO: Enhance with on-chain ship config data for C4 + */ +export const getShipData = async (): Promise => { const shipData = parseShips(await fetchGalaxyData(config.app.airdropUrl)) - return ( - await Promise.all( - shipData.map(async (value): Promise => { - const ship = await getShipByMint(value.mint, game, programs) - const size = ship.data.sizeClass ** 2 - return { - make: value.make, - model: value.model, - role: value.role, - mint: value.mint, - ship: ship, - size, - } - }), - ) - ).sort((a, b) => a.mint.toBase58().localeCompare(b.mint.toBase58())) + + return shipData.map((ship) => ({ + ...ship, + size: 1, // TODO: Get actual size from ship config + })) } diff --git a/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts b/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts deleted file mode 100644 index 25bde93e..00000000 --- a/src/main/basedbot/lib/sage/state/cargo-stats-definition.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { PublicKey } from '@solana/web3.js' -import { CargoStatsDefinition } from '@staratlas/cargo' -import { readAllFromRPC } from '@staratlas/data-source' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const getCargoStatsDefinition = async ( - statsDefinitionKey: PublicKey, -): Promise => { - const cargoTypesAccountData = await readAllFromRPC( - connection, - programs.cargo, - CargoStatsDefinition, - ) - - return cargoTypesAccountData - .filter((f) => f.type === 'ok' && 'data' in f) - .map((f) => (f as any).data) - .find((f) => f.key.toString() === statsDefinitionKey.toString()) -} diff --git a/src/main/basedbot/lib/sage/state/cargo-types.ts b/src/main/basedbot/lib/sage/state/cargo-types.ts deleted file mode 100644 index d7de2368..00000000 --- a/src/main/basedbot/lib/sage/state/cargo-types.ts +++ /dev/null @@ -1,37 +0,0 @@ -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/index.js' -import { programs } from '../../programs.js' - -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 index 7eccc3e4..fe12eca0 100644 --- a/src/main/basedbot/lib/sage/state/fleet-cargo.ts +++ b/src/main/basedbot/lib/sage/state/fleet-cargo.ts @@ -1,137 +1,79 @@ -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' +/** + * Fleet cargo utilities for C4 + * In C4, cargo amounts are directly on the Fleet struct + */ -import { logger } from '../../../../../logger.js' -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' +import type { Fleet } from '@staratlas/dev-sage' -import { sageGame } from './game.js' -import { Player } from './user-account.js' +import type { AccountWithKey } from '../../types.js' -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.subarray(64, 72), 'le').toNumber(), - mint: bs58.encode(accountData.subarray(0, 32)), - owner: bs58.encode(accountData.subarray(32, 64)), - tokenAccount: accountKey, - delegate: bs58.encode(accountData.subarray(76, 108)), - delegatedAmount: new BN( - accountData.slice(121, 129), - 'le', - ).toNumber(), - }) - } - - return result +export type FleetCargo = { + fuel: bigint + ammo: bigint + /** Map of cargoId -> amount for cargo hold items */ + cargo: Map + /** Total storage cost of cargo hold */ + storageCost: bigint } -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.debug( - `No balance found for ${mint.toBase58()} at ${bank.toBase58()} creating new account`, - ) - const fleetFuelTokenResult = createAssociatedTokenAccountIdempotent( - mint, - bank, - true, - ) - - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs( - fleetFuelTokenResult.instructions, - player.signer, - ), - ) - - return 0 +/** + * Get cargo levels directly from Fleet struct (C4) + * No RPC calls needed - data is on the Fleet account + */ +export const getFleetCargoBalance = ( + fleet: AccountWithKey, +): FleetCargo => { + const cargoMap = new Map() + + // In C4, cargoHold.amounts is a Map + const amounts = fleet.cargoHold?.amounts + if (amounts) { + for (const [cargoId, amount] of amounts.entries()) { + // Handle both array keys [number] and number keys + const id = Array.isArray(cargoId) + ? (cargoId[0] as number) + : (cargoId as number) + cargoMap.set(id, amount as bigint) } } - return 0 + return { + fuel: (fleet.fuelTank?.amount as bigint) ?? 0n, + ammo: (fleet.ammoBank?.amount as bigint) ?? 0n, + cargo: cargoMap, + storageCost: (fleet.cargoHold?.storageCost as bigint) ?? 0n, + } } -const getFleetCargoBalances = async ( - fleet: Fleet, -): Promise> => { - const cargoHoldBalances = await getTokenAccountForKey( - new PublicKey(fleet.data.cargoHold), - ) - - return new Map( - cargoHoldBalances.map((tokenAccount) => [ - tokenAccount.mint, - tokenAccount.delegatedAmount, - ]), - ) +/** + * Get cargo amount by cargoId from cargo hold + */ +export const getCargoAmount = (cargo: FleetCargo, cargoId: number): bigint => { + return cargo.cargo.get(cargoId) ?? 0n } -export type FleetCargo = { - ammo: number - cargo: Map - food: number - fuel: number - toolkit: number +/** + * Get fuel/ammo cargo IDs from fleet + */ +export const getFleetCargoIds = ( + fleet: AccountWithKey, +): { fuelCargoId: number; ammoCargoId: number } => { + return { + fuelCargoId: (fleet.fuelTank?.cargoId as number) ?? 0, + ammoCargoId: (fleet.ammoBank?.cargoId as number) ?? 0, + } } -export const getFleetCargoBalance = async ( - fleet: Fleet, - player: Player, -): Promise => { - const game = await sageGame() - const [ammo, fuel, cargo] = await Promise.all([ - getBalance(game.data.mints.ammo, fleet.data.ammoBank, player), - getBalance(game.data.mints.fuel, fleet.data.fuelTank, player), - getFleetCargoBalances(fleet), - ]) - +/** + * Get cargo capacity stats from fleet + */ +export const getFleetCargoCapacity = ( + fleet: AccountWithKey, +): { fuelCapacity: bigint; ammoCapacity: bigint; cargoCapacity: bigint } => { + const stats = fleet.stats?.cargoStats as Record | undefined return { - ammo, - cargo, - food: cargo.get(game.data.mints.food.toBase58()) ?? 0, - fuel, - toolkit: cargo.get(game.data.mints.repairKit.toBase58()) ?? 0, + fuelCapacity: (stats?.fuelCapacity as bigint) ?? 0n, + ammoCapacity: (stats?.ammoCapacity as bigint) ?? 0n, + cargoCapacity: (stats?.cargoCapacity as bigint) ?? 0n, } } diff --git a/src/main/basedbot/lib/sage/state/game.ts b/src/main/basedbot/lib/sage/state/game.ts index dc0de0bf..775edee6 100644 --- a/src/main/basedbot/lib/sage/state/game.ts +++ b/src/main/basedbot/lib/sage/state/game.ts @@ -1,21 +1,12 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Game } from '@staratlas/sage' +import type { Game } from '@staratlas/dev-sage' -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' +import { fetchGame } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' -export const sageGame = async (): Promise => { - const [game] = await readAllFromRPC( - connection, - programs.sage, - Game, - 'processed', - [], - ) - - if (game.type === 'error') { - throw new Error('Error reading game account') - } - - return game.data +/** + * Fetch the SAGE Game account + * Note: This is a singleton - there's only one Game account per program deployment + */ +export const sageGame = async (): Promise> => { + return fetchGame() } diff --git a/src/main/basedbot/lib/sage/state/get-fleet-ships.ts b/src/main/basedbot/lib/sage/state/get-fleet-ships.ts deleted file mode 100644 index 42f32df0..00000000 --- a/src/main/basedbot/lib/sage/state/get-fleet-ships.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Fleet, FleetShips } from '@staratlas/sage' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const getFleetShips = async ( - fleet: Fleet, -): Promise> => { - const resources = await readAllFromRPC( - connection, - programs.sage, - FleetShips, - 'processed', - [ - { - memcmp: { - offset: 8 + 1, - bytes: fleet.key.toBase58(), - }, - }, - ], - ) - - return resources - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) -} diff --git a/src/main/basedbot/lib/sage/state/mine-items.ts b/src/main/basedbot/lib/sage/state/mine-items.ts index 671cb04f..836e0b87 100644 --- a/src/main/basedbot/lib/sage/state/mine-items.ts +++ b/src/main/basedbot/lib/sage/state/mine-items.ts @@ -1,26 +1,50 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Game, MineItem } from '@staratlas/sage' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const getMineItems = async (game: Game): Promise> => { - const mineItems = await readAllFromRPC( - connection, - programs.sage, - MineItem, - 'processed', - [ - { - memcmp: { - offset: 8 + 1, - bytes: game.key.toBase58(), - }, - }, - ], - ) - - return mineItems - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) +/** + * Mine items utilities for C4 + * In C4, there are no separate MineItem accounts + * Mining is done directly on CelestialBody (asteroids) + * Resource type is identified by cargoId + */ + +import type { CelestialBody } from '@staratlas/dev-sage' + +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' + +import { type ResourceInfo, getResourceFromCelestialBody } from './resources.js' + +/** + * Get all minable asteroids + */ +export const getMinableAsteroids = async (): Promise => { + const celestialBodies = await fetchAllAccounts('CelestialBody') + + return celestialBodies + .map((cb) => getResourceFromCelestialBody(cb)) + .filter((r): r is ResourceInfo => r !== null) +} + +/** + * Get minable asteroids at a specific star system + */ +export const getMinableAsteroidsBySystem = async ( + systemKey: string, +): Promise => { + const celestialBodies = await fetchAllAccounts('CelestialBody') + + return celestialBodies + .filter((cb) => cb.system === systemKey) + .map((cb) => getResourceFromCelestialBody(cb)) + .filter((r): r is ResourceInfo => r !== null) +} + +/** + * Find asteroid by cargoId (resource type) + */ +export const findAsteroidByCargoId = async ( + systemKey: string, + cargoId: number, +): Promise | null> => { + const resources = await getMinableAsteroidsBySystem(systemKey) + const match = resources.find((r) => r.cargoId === cargoId) + return match?.celestialBody ?? null } diff --git a/src/main/basedbot/lib/sage/state/planet-by-key.ts b/src/main/basedbot/lib/sage/state/planet-by-key.ts index 9748b71d..d0ef0e38 100644 --- a/src/main/basedbot/lib/sage/state/planet-by-key.ts +++ b/src/main/basedbot/lib/sage/state/planet-by-key.ts @@ -1,26 +1,27 @@ -import { PublicKey } from '@solana/web3.js' -import { readFromRPC } from '@staratlas/data-source' -import { Planet } from '@staratlas/sage' +/** + * CelestialBody by key utility for C4 + */ -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' +import type { Address } from '@solana/kit' +import type { CelestialBody } from '@staratlas/dev-sage' -export const planetByKey = async (key: PublicKey): Promise => { - const planet = await readFromRPC( - connection, - programs.sage, - key, - Planet, - 'processed', - ) +import { fetchAccount } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' - if (!planet) { - throw new Error('no planet found') - } +/** + * Get a celestial body by address + */ +export const celestialBodyByKey = async ( + key: Address, +): Promise> => { + const celestialBody = await fetchAccount('CelestialBody', key) - if (planet.type === 'error') { - throw new Error('Error reading planet account') + if (!celestialBody) { + throw new Error(`CelestialBody not found: ${key}`) } - return planet.data + return celestialBody } + +// Legacy alias +export const planetByKey = celestialBodyByKey diff --git a/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts b/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts index 7d32a265..2d8ee640 100644 --- a/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts +++ b/src/main/basedbot/lib/sage/state/planets-by-coordinates.ts @@ -1,35 +1,41 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Planet } from '@staratlas/sage' +/** + * CelestialBodies by system coordinates for C4 + * In C4, celestial bodies are associated with a StarSystem, not coordinates + */ -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' +import type { CelestialBody } from '@staratlas/dev-sage' -export const planetsByCoordinates = async ( +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' +import type { Coordinates } from '../../util/coordinates.js' + +/** + * Get celestial bodies at a star system matching coordinates + */ +export const celestialBodiesByCoordinates = async ( coordinates: Coordinates, -): Promise> => { - const planets = await readAllFromRPC( - connection, - programs.sage, - Planet, - 'processed', - [ - { - memcmp: { - offset: 105, - bytes: coordinates.xB58, - }, - }, - { - memcmp: { - offset: 113, - bytes: coordinates.yB58, - }, - }, - ], - ) +): Promise[]> => { + // First find the star system at these coordinates + const systems = await fetchAllAccounts('StarSystem') + const system = systems.find((s) => { + const sysCoords = s.coordinates as unknown as [ + { raw: bigint }, + { raw: bigint }, + ] + return ( + sysCoords[0].raw === coordinates.xBigInt && + sysCoords[1].raw === coordinates.yBigInt + ) + }) + + if (!system) { + return [] + } - return planets - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) + // Then get all celestial bodies for that system + const celestialBodies = await fetchAllAccounts('CelestialBody') + return celestialBodies.filter((cb) => cb.system === system.key) } + +// Legacy alias +export const planetsByCoordinates = celestialBodiesByCoordinates diff --git a/src/main/basedbot/lib/sage/state/planets.ts b/src/main/basedbot/lib/sage/state/planets.ts index f8d301ba..5c14c722 100644 --- a/src/main/basedbot/lib/sage/state/planets.ts +++ b/src/main/basedbot/lib/sage/state/planets.ts @@ -1,26 +1,31 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Game, Planet } from '@staratlas/sage' +/** + * CelestialBody (planets/asteroids) utilities for C4 + * In C4, Planet is renamed to CelestialBody + */ -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' +import type { CelestialBody } from '@staratlas/dev-sage' -export const getPlanets = async (game: Game): Promise> => { - const planets = await readAllFromRPC( - connection, - programs.sage, - Planet, - 'processed', - [ - { - memcmp: { - offset: 8 + 1 + 64, - bytes: game.key.toBase58(), - }, - }, - ], - ) +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' - return planets - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) +/** + * Get all celestial bodies + */ +export const getCelestialBodies = async (): Promise< + AccountWithKey[] +> => { + return fetchAllAccounts('CelestialBody') } + +/** + * Get celestial bodies for a specific star system + */ +export const getCelestialBodiesBySystem = async ( + systemKey: string, +): Promise[]> => { + const all = await fetchAllAccounts('CelestialBody') + return all.filter((cb) => cb.system === systemKey) +} + +// Legacy alias +export const getPlanets = getCelestialBodies diff --git a/src/main/basedbot/lib/sage/state/resources.ts b/src/main/basedbot/lib/sage/state/resources.ts index 3f547388..66bf0863 100644 --- a/src/main/basedbot/lib/sage/state/resources.ts +++ b/src/main/basedbot/lib/sage/state/resources.ts @@ -1,26 +1,85 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Game, Resource } from '@staratlas/sage' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const getResources = async (game: Game): Promise> => { - const resources = await readAllFromRPC( - connection, - programs.sage, - Resource, - 'processed', - [ - { - memcmp: { - offset: 8 + 1, - bytes: game.key.toBase58(), - }, - }, - ], - ) - - return resources - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) +/** + * Resource utilities for C4 + * In C4, resources are embedded in CelestialBody.celestialBodyType.fields + * as cargoId references + */ + +import type { CelestialBody } from '@staratlas/dev-sage' + +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' + +/** + * Resource info extracted from CelestialBody + */ +export type ResourceInfo = { + celestialBody: AccountWithKey + cargoId: number + richness: number + amountMined: bigint +} + +/** + * Extract resource info from a CelestialBody (asteroid) + */ +export const getResourceFromCelestialBody = ( + celestialBody: AccountWithKey, +): ResourceInfo | null => { + const cbType = celestialBody.celestialBodyType as unknown as { + __kind: string + fields?: unknown[] + } + + if (cbType.__kind !== 'Asteroid' || !cbType.fields?.[0]) { + return null + } + + const asteroidData = cbType.fields[0] as { + resource?: [number] + richness?: { toNumber?: () => number } | number + amountMined?: bigint + } + + const resourceArr = asteroidData.resource + if (!resourceArr || !Array.isArray(resourceArr)) { + return null + } + + const richnessVal = asteroidData.richness + const richness = + typeof richnessVal === 'object' && richnessVal?.toNumber + ? richnessVal.toNumber() + : Number(richnessVal ?? 0) + + return { + celestialBody, + cargoId: resourceArr[0], + richness, + amountMined: asteroidData.amountMined ?? 0n, + } +} + +/** + * Get all resources (asteroids with resource data) + */ +export const getResources = async (): Promise => { + const celestialBodies = await fetchAllAccounts('CelestialBody') + + return celestialBodies + .map((cb) => getResourceFromCelestialBody(cb)) + .filter((r): r is ResourceInfo => r !== null) +} + +/** + * Get resources at a specific star system + */ +export const getResourcesBySystem = async ( + systemKey: string, +): Promise => { + const celestialBodies = await fetchAllAccounts('CelestialBody') + + return celestialBodies + .filter((cb) => cb.system === systemKey) + .map((cb) => getResourceFromCelestialBody(cb)) + .filter((r): r is ResourceInfo => r !== null) } diff --git a/src/main/basedbot/lib/sage/state/settle-fleet.ts b/src/main/basedbot/lib/sage/state/settle-fleet.ts index d78832b5..a44bf174 100644 --- a/src/main/basedbot/lib/sage/state/settle-fleet.ts +++ b/src/main/basedbot/lib/sage/state/settle-fleet.ts @@ -1,24 +1,35 @@ -import { Game } from '@staratlas/sage' +/** + * Fleet settlement utilities for C4 + * Handles completing in-progress fleet actions + */ + +import type { Game } from '@staratlas/dev-sage' import { now } from '../../../../../dayjs.js' import { logger } from '../../../../../logger.js' -import { endMove } from '../act/end-move.js' -import { exitRespawn } from '../act/exit-respawn.js' +import type { AccountWithKey } from '../../types.js' -import { Player } from './user-account.js' -import { FleetInfo } from './user-fleets.js' +import type { Player } from './user-account.js' +import type { FleetInfo } from './user-fleets.js' +/** + * Settle (complete) any in-progress fleet actions + * TODO: Implement action completions using M3 act layer + */ export const settleFleet = async ( fleetInfo: FleetInfo, - player: Player, - game: Game, + _player: Player, + _game: AccountWithKey, ): Promise => { - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'MoveWarp': { - const { warpFinish } = fleetInfo.fleetState.data + const { arrivalTime } = fleetInfo.fleetState.data - if (warpFinish.isBefore(now())) { - await endMove(fleetInfo, player, game) + if (arrivalTime.isBefore(now())) { + // TODO: Call endMove from act layer + logger.info( + `${fleetInfo.fleetName} warp complete, needs settlement`, + ) } break } @@ -26,7 +37,10 @@ export const settleFleet = async ( const { arrivalTime } = fleetInfo.fleetState.data if (arrivalTime.isBefore(now())) { - await endMove(fleetInfo, player, game) + // TODO: Call endMove from act layer + logger.info( + `${fleetInfo.fleetName} subwarp complete, needs settlement`, + ) } break } @@ -34,13 +48,16 @@ export const settleFleet = async ( const { ETA } = fleetInfo.fleetState.data if (ETA.isBefore(now())) { - await exitRespawn(fleetInfo, player.homeStarbase, player, game) + // TODO: Call exitRespawn from act layer + logger.info( + `${fleetInfo.fleetName} respawn complete, needs settlement`, + ) } break } default: logger.debug( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, + `${fleetInfo.fleetName} is ${fleetInfo.fleetState.kind}`, ) } } diff --git a/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts b/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts index a7a86130..95792a2b 100644 --- a/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts +++ b/src/main/basedbot/lib/sage/state/show-fleet-cargo-info.ts @@ -1,16 +1,23 @@ +/** + * Fleet cargo info display utilities for C4 + */ + import { logger } from '../../../../../logger.js' -import { FleetInfo } from './user-fleets.js' +import type { FleetInfo } from './user-fleets.js' export const showFleetCargoInfo = (fleetInfo: FleetInfo): void => { - const { ammo, cargo, food, fuel, toolkit } = fleetInfo.cargoLevels + const { fuel, ammo, cargo, storageCost } = fleetInfo.cargoLevels logger.info(`${fleetInfo.fleetName} cargo levels:`) - logger.info(`Ammo: ${ammo}`) - logger.info(`Food: ${food}`) - logger.info(`Fuel: ${fuel}`) - logger.info(`Toolkit: ${toolkit}`) - for (const [mint, amount] of cargo.entries()) { - logger.info(`${mint}: ${amount}`) + logger.info(` Fuel: ${fuel}`) + logger.info(` Ammo: ${ammo}`) + logger.info(` Storage Cost: ${storageCost}`) + + if (cargo.size > 0) { + logger.info(' Cargo Hold:') + for (const [cargoId, amount] of cargo.entries()) { + logger.info(` CargoId ${cargoId}: ${amount}`) + } } } diff --git a/src/main/basedbot/lib/sage/state/show-fleet-info.ts b/src/main/basedbot/lib/sage/state/show-fleet-info.ts index d35c2c23..fd463ca0 100644 --- a/src/main/basedbot/lib/sage/state/show-fleet-info.ts +++ b/src/main/basedbot/lib/sage/state/show-fleet-info.ts @@ -1,77 +1,91 @@ +/** + * Fleet info display utilities for C4 + */ + import dayjs, { now } from '../../../../../dayjs.js' import { logger } from '../../../../../logger.js' -import { getName } from '../util.js' -import { planetsByCoordinates } from './planets-by-coordinates.js' -import { starbaseByCoordinates } from './starbase-by-coordinates.js' -import { FleetInfo } from './user-fleets.js' +import { celestialBodiesByCoordinates } from './planets-by-coordinates.js' +import { systemByCoordinates } from './starbase-by-coordinates.js' +import type { FleetInfo } from './user-fleets.js' export const showFleetInfo = async (fleetInfo: FleetInfo): Promise => { - switch (fleetInfo.fleetState.type) { + switch (fleetInfo.fleetState.kind) { case 'Idle': { - const baseStation = await starbaseByCoordinates(fleetInfo.location) - const planets = await planetsByCoordinates(fleetInfo.location) + const system = await systemByCoordinates(fleetInfo.location) + const celestialBodies = await celestialBodiesByCoordinates( + fleetInfo.location, + ) logger.info( - `${fleetInfo.fleetName} is idle at ${fleetInfo.fleetState.data.sector} [BaseStation: ${baseStation ? getName(baseStation) : 'N/A'} / Planets: ${planets.length}]`, + `${fleetInfo.fleetName} is idle at ${fleetInfo.fleetState.data.sector} [System: ${system?.name ?? 'N/A'} / Bodies: ${celestialBodies.length}]`, ) break } - case 'StarbaseLoadingBay': + case 'Docked': { + const { system } = fleetInfo.fleetState.data logger.info( - `${fleetInfo.fleetName} is in the loading bay at ${getName(fleetInfo.fleetState.data.starbase)}`, + `${fleetInfo.fleetName} is docked at ${system.name ?? system.key}`, ) break + } case 'MoveWarp': { - const { fromSector, toSector, warpFinish } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data - if (warpFinish.isBefore(now())) { + if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} warping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(warpFinish.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} warping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MoveSubwarp': { - const { fromSector, toSector, arrivalTime } = + const { fromCoordinates, toCoordinates, arrivalTime } = fleetInfo.fleetState.data if (arrivalTime.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has arrived at ${fleetInfo.fleetState.data.toSector}`, + `${fleetInfo.fleetName} has arrived at ${toCoordinates}`, ) } else { logger.info( - `${fleetInfo.fleetName} subwarping from ${fromSector} to ${toSector}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, + `${fleetInfo.fleetName} subwarping from ${fromCoordinates} to ${toCoordinates}. Arrival in ${dayjs.duration(arrivalTime.diff(now())).humanize(false)}. Current Position: ${fleetInfo.location}`, ) } break } case 'MineAsteroid': { - const { mineItem, end, amountMined, endReason } = + const { celestialBody, end, amountMined, endReason } = fleetInfo.fleetState.data if (end.isBefore(now())) { logger.info( - `${fleetInfo.fleetName} has finished mining ${getName(mineItem)} for ${amountMined}`, + `${fleetInfo.fleetName} has finished mining at ${celestialBody.key} 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}`, + `${fleetInfo.fleetName} mining at ${celestialBody.key} for ${amountMined}. Time remaining: ${dayjs.duration(end.diff(now())).humanize(false)} until ${endReason}`, ) } break } + case 'Respawn': { + const { ETA } = fleetInfo.fleetState.data + logger.info( + `${fleetInfo.fleetName} respawning. ETA: ${dayjs.duration(ETA.diff(now())).humanize(false)}`, + ) + break + } default: logger.info( - `${fleetInfo.fleetName} is ${fleetInfo.fleetState.type}`, + `${fleetInfo.fleetName} is ${(fleetInfo.fleetState as { kind: string }).kind}`, ) } } diff --git a/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts b/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts index ad3c975a..e3ec9eab 100644 --- a/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts +++ b/src/main/basedbot/lib/sage/state/starbase-by-coordinates.ts @@ -1,41 +1,44 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Starbase } from '@staratlas/sage' +/** + * Find star system with starbase by coordinates (C4) + */ -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' +import type { StarSystem } from '@staratlas/dev-sage' -export const starbaseByCoordinates = async ( - coordinates: Coordinates, -): Promise => { - const [starbase] = await readAllFromRPC( - connection, - programs.sage, - Starbase, - 'processed', - [ - { - memcmp: { - offset: 41, - bytes: coordinates.xB58, - }, - }, - { - memcmp: { - offset: 49, - bytes: coordinates.yB58, - }, - }, - ], - ) +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' +import type { Coordinates } from '../../util/coordinates.js' - if (!starbase) { - return null - } +import { type StarbaseData, getStarbaseFromSystem } from './starbases.js' - if (starbase.type === 'error') { - throw new Error('Error reading starbase account') - } +/** + * Find a star system by coordinates + */ +export const systemByCoordinates = async ( + coordinates: Coordinates, +): Promise | null> => { + const systems = await fetchAllAccounts('StarSystem') - return starbase.data + return ( + systems.find((system) => { + const sysCoords = system.coordinates as unknown as [ + { raw: bigint }, + { raw: bigint }, + ] + return ( + sysCoords[0].raw === coordinates.xBigInt && + sysCoords[1].raw === coordinates.yBigInt + ) + }) ?? null + ) +} + +/** + * Find a starbase by coordinates (returns StarSystem that has a starbase) + */ +export const starbaseByCoordinates = async ( + coordinates: Coordinates, +): Promise => { + const system = await systemByCoordinates(coordinates) + if (!system) return null + return getStarbaseFromSystem(system) } diff --git a/src/main/basedbot/lib/sage/state/starbase-by-key.ts b/src/main/basedbot/lib/sage/state/starbase-by-key.ts index b392cee9..385af987 100644 --- a/src/main/basedbot/lib/sage/state/starbase-by-key.ts +++ b/src/main/basedbot/lib/sage/state/starbase-by-key.ts @@ -1,26 +1,41 @@ -import { PublicKey } from '@solana/web3.js' -import { readFromRPC } from '@staratlas/data-source' -import { Starbase } from '@staratlas/sage' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const starbaseByKey = async (key: PublicKey): Promise => { - const starbase = await readFromRPC( - connection, - programs.sage, - key, - Starbase, - 'processed', - ) +/** + * Find star system with starbase by key (C4) + */ - if (!starbase) { - throw new Error('no starbase found') +import type { Address } from '@solana/kit' +import type { StarSystem } from '@staratlas/dev-sage' + +import { fetchAccount } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' + +import { type StarbaseData, getStarbaseFromSystem } from './starbases.js' + +/** + * Find a star system by address + */ +export const systemByKey = async ( + key: Address, +): Promise> => { + const system = await fetchAccount('StarSystem', key) + + if (!system) { + throw new Error(`StarSystem not found: ${key}`) } - if (starbase.type === 'error') { - throw new Error('Error reading starbase account') + return system +} + +/** + * Get starbase data from a star system by address + * Throws if system doesn't have a starbase + */ +export const starbaseByKey = async (key: Address): Promise => { + const system = await systemByKey(key) + const starbase = getStarbaseFromSystem(system) + + if (!starbase) { + throw new Error(`No starbase at system: ${key}`) } - return starbase.data + return starbase } diff --git a/src/main/basedbot/lib/sage/state/starbase-player.ts b/src/main/basedbot/lib/sage/state/starbase-player.ts index 026e48ea..cc06e130 100644 --- a/src/main/basedbot/lib/sage/state/starbase-player.ts +++ b/src/main/basedbot/lib/sage/state/starbase-player.ts @@ -1,195 +1,76 @@ -import { Keypair, PublicKey } from '@solana/web3.js' -import { CargoPod } from '@staratlas/cargo' -import { ixReturnsToIxs, readAllFromRPC } from '@staratlas/data-source' -import { - Game, - SagePlayerProfile, - Ship, - Starbase, - StarbasePlayer, -} from '@staratlas/sage' +/** + * StarbasePlayer utilities for C4 + */ -import { logger } from '../../../../../logger.js' -import { connection } from '../../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx.js' -import { keyPair } from '../../../../../service/wallet' -import { StarAtlasPrograms } from '../../programs.js' +import type { Address } from '@solana/kit' +import type { StarbasePlayer, StarSystem } from '@staratlas/dev-sage' +import { findStarbasePlayerPda } from '@staratlas/dev-sage' -import { sageGame } from './game.js' -import { Player } from './user-account.js' +import { fetchAllAccounts, fetchAccount } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' -export const getCargoPodsForStarbasePlayer = async ( - starbasePlayer: StarbasePlayer, - programs: StarAtlasPrograms, -): Promise => { - const cargoPods = await readAllFromRPC( - connection, - programs.cargo, - CargoPod, - 'processed', - [ - { - memcmp: { - offset: 8 + 1 + 32, - bytes: starbasePlayer.key.toBase58(), - }, - }, - ], - ) - - if (cargoPods.length > 1) { - logger.warn( - `${starbasePlayer.data.starbase.toBase58()} has ${cargoPods.length} cargo pods`, - ) - } - - const cargoPod = cargoPods[0] - - if (!cargoPod) { - throw new Error('Error reading cargo pods') - } - - if (cargoPod.type === 'error') { - throw new Error('Error reading cargoPods account') - } - - return cargoPod.data -} +import type { Player } from './user-account.js' -export const getShipByMint = async ( - mint: PublicKey, - game: Game, - programs: StarAtlasPrograms, -): Promise => { - const ships = await readAllFromRPC( - connection, - programs.sage, - Ship, - 'processed', - [ - { - memcmp: { - offset: 8 + 1, - bytes: game.key.toString(), - }, - }, - { - memcmp: { - offset: 8 + 1 + 32, - bytes: mint.toString(), - }, - }, - ], - ) - - const ship = ships.find( - (s) => - s.type === 'ok' && - (s.data.data as any).next.key.toString() === - '11111111111111111111111111111111', +/** + * Get StarbasePlayer for a player at a specific system + */ +export const getStarbasePlayer = async ( + player: Player, + system: AccountWithKey, +): Promise | null> => { + const allStarbasePlayers = await fetchAllAccounts('StarbasePlayer') + + return ( + allStarbasePlayers.find( + (sp) => + sp.playerProfile === player.profile.key && + sp.system === system.key, + ) ?? null ) - - if (!ship) { - throw new Error('Error reading ship with mint ' + mint.toBase58()) - } - - if (ship.type === 'error') { - throw new Error('Error reading ship account') - } - - return ship.data } -export const getStarbasePlayer = async ( +/** + * Get all StarbasePlayers for a player + */ +export const getAllStarbasePlayers = async ( player: Player, - starbase: Starbase, - programs: StarAtlasPrograms, -): Promise => { - const starbasePlayers = await readAllFromRPC( - connection, - programs.sage, - StarbasePlayer, - 'processed', - [ - { - memcmp: { - offset: 9, - bytes: player.profile.key.toBase58(), - }, - }, - { - memcmp: { - offset: 73, - bytes: starbase.key.toBase58(), - }, - }, - ], +): Promise[]> => { + const allStarbasePlayers = await fetchAllAccounts('StarbasePlayer') + return allStarbasePlayers.filter( + (sp) => sp.playerProfile === player.profile.key, ) +} - const game = await sageGame() - - if (starbasePlayers.length > 1) { - throw new Error('Multiple starbase players found') - } - const [starbasePlayer] = starbasePlayers - - if (!starbasePlayer) { - const [sageProfileAddress] = SagePlayerProfile.findAddress( - programs.sage, - player.profile.key, - game.key, - ) - const [starbasePlayerAddress] = StarbasePlayer.findAddress( - programs.sage, - starbase.key, - sageProfileAddress, - starbase.data.seqId, - ) - - const instructionReturns = [ - StarbasePlayer.registerStarbasePlayer( - programs.sage, - player.profileFaction.key, - sageProfileAddress, - starbase.key, - game.key, - game.data.gameState, - starbase.data.seqId, - ), - StarbasePlayer.createCargoPod( - programs.sage, - programs.cargo, - starbasePlayerAddress, - player.signer, - player.profile.key, - player.profileFaction.key, - starbase.key, - game.data.cargo.statsDefinition, - game.key, - game.data.gameState, - { - keyIndex: 0, - podSeeds: Array.from( - Keypair.generate().publicKey.toBuffer(), - ), - }, - ), - ] - - logger.warn('Starbase player not found, creating', { - player: player.profile.key.toBase58(), - starbase: starbase.key.toBase58(), - }) - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs(instructionReturns, player.signer), - ) - - return getStarbasePlayer(player, starbase, programs) - } +/** + * Get StarbasePlayer by address + */ +export const getStarbasePlayerByKey = async ( + key: Address, +): Promise | null> => { + return fetchAccount('StarbasePlayer', key) +} - if (starbasePlayer.type === 'error') { - throw new Error('Error reading starbasePlayer account') - } +/** + * Derive StarbasePlayer PDA address + */ +export const deriveStarbasePlayerAddress = async ( + system: AccountWithKey, + player: Player, +): Promise
=> { + const [pda] = await findStarbasePlayerPda({ + system: system.key, + character: player.character.key, + }) + return pda +} - return starbasePlayer.data +/** + * Check if player has a StarbasePlayer at a system + */ +export const hasStarbasePlayerAt = async ( + player: Player, + system: AccountWithKey, +): Promise => { + const sp = await getStarbasePlayer(player, system) + return sp !== null } diff --git a/src/main/basedbot/lib/sage/state/starbases.ts b/src/main/basedbot/lib/sage/state/starbases.ts index 5034afba..fb2538f0 100644 --- a/src/main/basedbot/lib/sage/state/starbases.ts +++ b/src/main/basedbot/lib/sage/state/starbases.ts @@ -1,26 +1,91 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { Game, Starbase } from '@staratlas/sage' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' - -export const getStarbases = async (game: Game): Promise> => { - const starbases = await readAllFromRPC( - connection, - programs.sage, - Starbase, - 'processed', - [ - { - memcmp: { - offset: 8 + 1, - bytes: game.key.toBase58(), - }, - }, - ], - ) - - return starbases - .filter((p) => p.type === 'ok' && 'data' in p) - .map((p) => (p as any).data) +/** + * Starbase utilities for C4 + * In C4, Starbase is embedded in StarSystem as an optional field + */ + +import type { StarSystem } from '@staratlas/dev-sage' + +import { fetchAllAccounts } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' + +/** + * Starbase data extracted from StarSystem + */ +export type StarbaseData = { + system: AccountWithKey + level: number + owner: unknown + hp: number + pendingHp: number +} + +/** + * Get all star systems that have a starbase + */ +export const getStarbases = async (): Promise => { + const systems = await fetchAllAccounts('StarSystem') + + return systems + .filter((system) => { + const starbase = system.starbase as { __option: string } | undefined + return starbase?.__option === 'Some' + }) + .map((system) => { + const starbase = system.starbase as { + __option: 'Some' + value: { + level: number + owner: unknown + hp: number + pendingHp: number + } + } + return { + system, + level: starbase.value.level, + owner: starbase.value.owner, + hp: starbase.value.hp, + pendingHp: starbase.value.pendingHp, + } + }) +} + +/** + * Check if a star system has a starbase + */ +export const hasStarbase = (system: AccountWithKey): boolean => { + const starbase = system.starbase as { __option: string } | undefined + return starbase?.__option === 'Some' +} + +/** + * Get starbase data from a star system (if present) + */ +export const getStarbaseFromSystem = ( + system: AccountWithKey, +): StarbaseData | null => { + const starbase = system.starbase as + | { + __option: 'Some' + value: { + level: number + owner: unknown + hp: number + pendingHp: number + } + } + | { __option: 'None' } + | undefined + + if (!starbase || starbase.__option !== 'Some') { + return null + } + + return { + system, + level: starbase.value.level, + owner: starbase.value.owner, + hp: starbase.value.hp, + pendingHp: starbase.value.pendingHp, + } } diff --git a/src/main/basedbot/lib/sage/state/user-account.ts b/src/main/basedbot/lib/sage/state/user-account.ts index 64660638..fcbab768 100644 --- a/src/main/basedbot/lib/sage/state/user-account.ts +++ b/src/main/basedbot/lib/sage/state/user-account.ts @@ -1,194 +1,183 @@ -import { Keypair, PublicKey } from '@solana/web3.js' -import { CargoType } from '@staratlas/cargo' -import { - AsyncSigner, - keypairToAsyncSigner, - readAllFromRPC, -} from '@staratlas/data-source' -import { PlayerProfile } from '@staratlas/player-profile' -import { UserPoints } from '@staratlas/points' -import { ProfileFactionAccount } from '@staratlas/profile-faction' -import { SagePointsCategory, Starbase } from '@staratlas/sage' +import type { Address, KeyPairSigner } from '@solana/kit' +import type { Profile } from '@staratlas/dev-player-profile' +import type { ProfileFactionAccount } from '@staratlas/dev-profile-faction' +import type { Character, StarSystem } from '@staratlas/dev-sage' + import { config } from '../../../../../config/index.js' import { logger } from '../../../../../logger.js' - -import { connection } from '../../../../../service/sol/index.js' -import { programs } from '../../programs.js' +import { fetchAllAccounts, fetchGame } from '../../account-fetcher.js' +import type { AccountWithKey } from '../../types.js' import { Coordinates } from '../../util/coordinates.js' import { Faction, galaxySectorsData } from '../../util/galaxy-sectors-data.js' -import { createAndInitializeCharacter } from '../../util/profile.js' -import { ExtShipData, getShipData } from '../ships.js' - -import { getCargoType, getCargoTypes } from './cargo-types.js' -import { sageGame } from './game.js' -import { starbaseByCoordinates } from './starbase-by-coordinates.js' - -export type XpAccounts = { - councilRank: XpAccount - dataRunning: XpAccount - piloting: XpAccount - mining: XpAccount - crafting: XpAccount -} - -export type XpAccount = { - userPointsAccount: PublicKey - pointsCategory: PublicKey - pointsModifierAccount: PublicKey -} +/** + * Player context for C4 SAGE operations + */ export type Player = { - publicKey: PublicKey + /** Player wallet address */ + address: Address + /** Profile key index */ keyIndex: number - profile: PlayerProfile - profileFaction: ProfileFactionAccount + /** Player profile account */ + profile: AccountWithKey + /** Profile faction account */ + profileFaction: AccountWithKey + /** Character account (required for C4 actions) */ + character: AccountWithKey + /** Faction enum value */ faction: Faction - xpAccounts: XpAccounts - signer: AsyncSigner - homeStarbase: Starbase + /** Signer for transactions */ + signer: KeyPairSigner + /** Home star system */ + homeSystem: AccountWithKey + /** Home coordinates */ homeCoordinates: Coordinates - cargoTypes: Array - fuelCargoType: CargoType - foodCargoType: CargoType - ammoCargoType: CargoType - shipData: Array } -const getXpAccount = ( - playerProfile: PublicKey, - pointsCategory: SagePointsCategory, -): XpAccount => { - const pointsCategoryKey = pointsCategory.category - const pointsModifierAccount = pointsCategory.modifier - const [userPointsAccount] = UserPoints.findAddress( - programs.points, - pointsCategoryKey, - playerProfile, - ) +/** + * Find profiles for a wallet address + */ +async function findProfiles( + walletAddress: Address, +): Promise[]> { + const allProfiles = await fetchAllAccounts('Profile') + return allProfiles.filter((p) => p.profileKeys[0]?.key === walletAddress) +} - return { - userPointsAccount, - pointsModifierAccount, - pointsCategory: pointsCategoryKey, +/** + * Find faction account for a profile + */ +async function findProfileFaction( + profileKey: Address, +): Promise | null> { + const allFactions = await fetchAllAccounts('ProfileFactionAccount') + return allFactions.find((f) => f.profile === profileKey) ?? null +} + +/** + * Find character account for a profile + */ +async function findCharacter( + profileKey: Address, +): Promise | null> { + const allCharacters = await fetchAllAccounts('Character') + return allCharacters.find((c) => c.playerProfile === profileKey) ?? null +} + +/** + * Find star systems + */ +async function findStarSystems(): Promise[]> { + return fetchAllAccounts('StarSystem') +} + +/** + * Find home star system based on faction + */ +function findHomeSystem( + faction: Faction, + systems: AccountWithKey[], +): AccountWithKey | null { + // Find CSS (Central Space Station) for faction + const sectorInfo = galaxySectorsData() + .filter((sector) => sector.closestFaction === faction) + .find((sector) => sector.name.includes('CSS')) + + if (!sectorInfo) { + return null } + + // Find matching star system by coordinates + return ( + systems.find((system) => { + const sysCoords = Coordinates.fromTuple([ + system.coordinates[0].raw, + system.coordinates[1].raw, + ]) + return sysCoords.equals(sectorInfo.coordinates) + }) ?? null + ) } -const getKeyIndex = (_: PlayerProfile): number => 0 +const getKeyIndex = (_: Profile): number => 0 +/** + * Get player context for C4 SAGE operations + */ export const getPlayerContext = async ( - user: PublicKey, - signer: Keypair, + walletAddress: Address, + signer: KeyPairSigner, ): Promise => { - const myProfiles = await readAllFromRPC( - connection, - programs.playerProfile, - PlayerProfile, - 'processed', - [ - { - memcmp: { - offset: PlayerProfile.MIN_DATA_SIZE + 2, - bytes: user.toBase58(), - }, - }, - ], - ) - const game = await sageGame() - - const [profile] = - myProfiles.length > 0 - ? myProfiles - : config.app.autoCreateProfile - ? await createAndInitializeCharacter( - game, - 'fleetbot', - Faction.ONI, - signer, - ) - : [] - - if (!profile) { - throw new Error('no player profile found') - } - - if (profile.type === 'error') { - throw new Error('Error reading account') + // Fetch game to ensure we're connected + await fetchGame() + + // Find player profiles + const myProfiles = await findProfiles(walletAddress) + + if (myProfiles.length === 0) { + if (config.app.autoCreateProfile) { + // TODO: Implement profile creation for C4 + throw new Error( + 'Auto profile creation not yet implemented for C4. Create profile manually.', + ) + } + throw new Error('No player profile found') } + const profile = myProfiles[0] logger.info(`Player profile: ${profile.key}`) - const keyIndex = getKeyIndex(profile.data) - - const [profileFaction] = await readAllFromRPC( - connection, - programs.profileFaction as any, - ProfileFactionAccount, - 'processed', - [ - { - memcmp: { - offset: 9, - bytes: profile.key.toBase58(), - }, - }, - ], - ) + const keyIndex = getKeyIndex(profile) - if (profileFaction.type === 'error') { - throw new Error('Error reading faction account') + // Find faction + const profileFaction = await findProfileFaction(profile.key) + if (!profileFaction) { + throw new Error('No faction account found for profile') } - const xpAccounts = { - councilRank: getXpAccount( - profile.key, - game.data.points.councilRankXpCategory, - ), - dataRunning: getXpAccount( - profile.key, - game.data.points.dataRunningXpCategory, - ), - piloting: getXpAccount(profile.key, game.data.points.pilotXpCategory), - mining: getXpAccount(profile.key, game.data.points.miningXpCategory), - crafting: getXpAccount( - profile.key, - game.data.points.craftingXpCategory, - ), - } - - const cargoTypes = await getCargoTypes() - - const homeCoordinates = galaxySectorsData() - .filter( - (sector) => - sector.closestFaction === profileFaction.data.data.faction, + // Find character (required for C4) + const character = await findCharacter(profile.key) + if (!character) { + throw new Error( + 'No character account found. Create character via Star Atlas app first.', ) - .find((sector) => sector.name.includes('CSS'))?.coordinates - - if (!homeCoordinates) { - throw new Error('No home coordinates found') } - const homeStarbase = await starbaseByCoordinates(homeCoordinates) + // Determine faction enum + // C4 ProfileFactionAccount.faction is a number: 1=MUD, 2=ONI, 3=UST + const factionValue = profileFaction.faction as unknown as number + let faction: Faction + if (factionValue === 1) { + faction = Faction.MUD + } else if (factionValue === 2) { + faction = Faction.ONI + } else if (factionValue === 3) { + faction = Faction.UST + } else { + throw new Error(`Unknown faction value: ${factionValue}`) + } - if (!homeStarbase) { - throw new Error('No home starbase found') + // Find home system + const systems = await findStarSystems() + const homeSystem = findHomeSystem(faction, systems) + if (!homeSystem) { + throw new Error('No home star system found') } - const shipData = await getShipData(game) + + const homeCoordinates = Coordinates.fromTuple([ + homeSystem.coordinates[0].raw, + homeSystem.coordinates[1].raw, + ]) return { - publicKey: user, - profile: profile.data, - profileFaction: profileFaction.data, - faction: profileFaction.data.data.faction, + address: walletAddress, + profile, + profileFaction, + character, + faction, keyIndex, - xpAccounts, - signer: keypairToAsyncSigner(signer), + signer, + homeSystem, homeCoordinates, - cargoTypes, - homeStarbase, - fuelCargoType: getCargoType(cargoTypes, game, game.data.mints.fuel), - foodCargoType: getCargoType(cargoTypes, game, game.data.mints.food), - ammoCargoType: getCargoType(cargoTypes, game, game.data.mints.ammo), - shipData, } } diff --git a/src/main/basedbot/lib/sage/state/user-fleets.ts b/src/main/basedbot/lib/sage/state/user-fleets.ts index f78236f0..c8865a6c 100644 --- a/src/main/basedbot/lib/sage/state/user-fleets.ts +++ b/src/main/basedbot/lib/sage/state/user-fleets.ts @@ -1,26 +1,26 @@ -import { readAllFromRPC } from '@staratlas/data-source' -import { DisbandedFleet, Fleet } from '@staratlas/sage' -import BN from 'bn.js' +/** + * User fleet utilities for C4 + */ -import { connection } from '../../../../../service/sol/index.js' +import type { Fleet } from '@staratlas/dev-sage' + +import { fetchAllAccounts } from '../../account-fetcher.js' import { getFleetState } from '../../fleet-state/fleet-state.js' -import { FleetState } from '../../fleet-state/types.js' -import { programs } from '../../programs.js' -import { Coordinates } from '../../util/coordinates.js' +import type { FleetState } from '../../fleet-state/types.js' +import type { AccountWithKey } from '../../types.js' +import type { Coordinates } from '../../util/coordinates.js' import { getName } from '../util.js' -import { FleetCargo, getFleetCargoBalance } from './fleet-cargo.js' -import { Player } from './user-account.js' -import { WorldMap } from './world-map.js' +import { type FleetCargo, getFleetCargoBalance } from './fleet-cargo.js' +import type { Player } from './user-account.js' type ShipCounts = { total: number - updated: number - xxs: number - xs: number - s: number - m: number - l: number + xxSmall: number + xSmall: number + small: number + medium: number + large: number capital: number commander: number titan: number @@ -48,18 +48,19 @@ type CargoStats = { type MiscStats = { crew: number - respawnTime: number + respawnTimeWithoutFee: number + respawnTimeWithFee: number scanCooldown: number scanRepairKitAmount: number } export type FleetInfo = { - fleet: Fleet + fleet: AccountWithKey location: Coordinates fleetName: string shipCounts: ShipCounts - warpCooldownExpiresAt: BN - scanCooldownExpiresAt: BN + warpCooldownExpiresAt: bigint + scanCooldownExpiresAt: bigint movementStats: MovementStats cargoStats: CargoStats miscStats: MiscStats @@ -67,62 +68,41 @@ export type FleetInfo = { cargoLevels: FleetCargo } -export const getUserDisbandedFleets = async ( +/** + * Get all fleets for a player profile + */ +export const getUserFleets = async ( player: Player, -): Promise> => { - const fleets = await readAllFromRPC( - connection, - programs.sage, - DisbandedFleet, - 'processed', - [ - { - memcmp: { - offset: 8 + 1 + 32, // 8 (discriminator) + 1 (version) + 32 (gameId) - bytes: player.profile.key.toBase58(), - }, - }, - ], - ) - - return fleets - .filter((f) => f.type === 'ok' && 'data' in f) - .map((f) => (f as any).data) +): Promise[]> => { + const allFleets = await fetchAllAccounts('Fleet') + return allFleets.filter((f) => f.ownerProfile === player.profile.key) } -export const getUserFleets = async (player: Player): Promise> => { - const fleets = await readAllFromRPC( - connection, - programs.sage, - Fleet, - 'processed', - [ - { - memcmp: { - offset: 8 + 1 + 32, // 8 (discriminator) + 1 (version) + 32 (gameId) - bytes: player.profile.key.toBase58(), - }, - }, - ], - ) - - return fleets - .filter((f) => f.type === 'ok' && 'data' in f) - .map((f) => (f as any).data) +/** + * Get disbanded fleets for a player + * TODO: Implement for C4 if DisbandedFleet account type exists + */ +export const getUserDisbandedFleets = async ( + _player: Player, +): Promise => { + // In C4, disbanded fleet tracking may work differently + // Return empty array for now + return [] } +/** + * Get fleet info with state and cargo + */ export const getFleetInfo = async ( - fleet: Fleet, - player: Player, - map: WorldMap, + fleet: AccountWithKey, ): Promise => { - const cargoLevels = await getFleetCargoBalance(fleet, player) - const fleetState = await getFleetState(fleet, map, cargoLevels) - const shipCounts = fleet.data.shipCounts as unknown as ShipCounts - const movementStats = fleet.data.stats - .movementStats as unknown as MovementStats - const cargoStats = fleet.data.stats.cargoStats as unknown as CargoStats - const miscStats = fleet.data.stats.miscStats as unknown as MiscStats + const cargoLevels = getFleetCargoBalance(fleet) + const fleetState = await getFleetState(fleet) + + const shipCounts = fleet.shipCounts as unknown as ShipCounts + const movementStats = fleet.stats?.movementStats as unknown as MovementStats + const cargoStats = fleet.stats?.cargoStats as unknown as CargoStats + const miscStats = fleet.stats?.miscStats as unknown as MiscStats const location = fleetState.data.sector const fleetName = getName(fleet) @@ -135,8 +115,8 @@ export const getFleetInfo = async ( fleetName, shipCounts, fleetState, - warpCooldownExpiresAt: fleet.data.warpCooldownExpiresAt, - scanCooldownExpiresAt: fleet.data.scanCooldownExpiresAt, + warpCooldownExpiresAt: fleet.warpCooldownExpiresAt as bigint, + scanCooldownExpiresAt: fleet.scanCooldownExpiresAt as bigint, cargoLevels, } } diff --git a/src/main/basedbot/lib/sage/state/world-map.ts b/src/main/basedbot/lib/sage/state/world-map.ts index 87dc435d..987044b6 100644 --- a/src/main/basedbot/lib/sage/state/world-map.ts +++ b/src/main/basedbot/lib/sage/state/world-map.ts @@ -1,136 +1,160 @@ -import { Game, MineItem, Planet, Resource, Starbase } from '@staratlas/sage' +/** + * World map utilities for C4 + * Provides a unified view of the galaxy map + */ + +import type { CelestialBody, StarSystem } from '@staratlas/dev-sage' + +import { fetchAllAccounts, fetchGame } from '../../account-fetcher.js' import { transformSector } from '../../fleet-state/transform/transform-sector.js' -import { Coordinates } from '../../util/coordinates.js' -import { getName } from '../util.js' +import type { AccountWithKey } from '../../types.js' +import type { Coordinates } from '../../util/coordinates.js' -import { getMineItems } from './mine-items.js' -import { getPlanets } from './planets.js' -import { getResources } from './resources.js' -import { getStarbases } from './starbases.js' +import { type ResourceInfo, getResourceFromCelestialBody } from './resources.js' +import { type StarbaseData, getStarbaseFromSystem } from './starbases.js' -export type PlanetId = string -export type ResourceId = string -export type StarbaseId = string +export type SystemId = string +export type CelestialBodyId = string +/** + * World map containing all galaxy data + */ export type WorldMap = { - starbases: Array - planets: Map> - mineItems: Map - resources: Map> + systems: AccountWithKey[] + starbases: StarbaseData[] + celestialBodies: Map[]> + resources: Map } +/** + * Mineable location info + */ export type Mineable = { - starbase: Starbase - planet: Planet - resource: Resource - mineItem: MineItem + system: AccountWithKey + starbase: StarbaseData | null + celestialBody: AccountWithKey + resource: ResourceInfo } -export const planetsByStarbase = ( - planets: Map>, - starbase: Starbase, -): Set => planets.get(starbase.key.toBase58()) ?? new Set() - -export const resourcesByPlanet = ( - resources: Map>, - planet: Planet, -): Set => resources.get(planet.key.toBase58()) ?? new Set() +/** + * Get celestial bodies for a system + */ +export const celestialBodiesBySystem = ( + map: WorldMap, + system: AccountWithKey, +): AccountWithKey[] => { + return map.celestialBodies.get(system.key) ?? [] +} -export const mineItemByResource = ( - mineItems: Map, - resource: Resource, -): MineItem | undefined => mineItems.get(resource.key.toBase58()) +/** + * Get resource info for a celestial body + */ +export const resourceByCelestialBody = ( + map: WorldMap, + celestialBody: AccountWithKey, +): ResourceInfo | undefined => { + return map.resources.get(celestialBody.key) +} +/** + * Get all mineables at a system by coordinates + */ export const mineablesByCoordinates = ( map: WorldMap, coordinates: Coordinates, -): Set => { - const starbase = map.starbases.find((s) => - transformSector(s.data.sector).equals(coordinates), - ) +): Mineable[] => { + const system = map.systems.find((s) => { + const sysCoords = transformSector([s.coordinates[0], s.coordinates[1]]) + return sysCoords.equals(coordinates) + }) - if (!starbase) { - throw new Error(`No starbase found at ${coordinates}`) + if (!system) { + return [] + } + + const starbase = getStarbaseFromSystem(system) + const celestialBodies = celestialBodiesBySystem(map, system) + const mineables: Mineable[] = [] + + for (const cb of celestialBodies) { + const resource = resourceByCelestialBody(map, cb) + if (resource) { + mineables.push({ + system, + starbase, + celestialBody: cb, + resource, + }) + } } - const planets = planetsByStarbase(map.planets, starbase) - const mineables = new Set() - - planets.forEach((planet) => { - const resources = resourcesByPlanet(map.resources, planet) - - resources.forEach((resource) => { - const mineItem = mineItemByResource(map.mineItems, resource) - - if (mineItem) { - mineables.add({ - starbase, - planet, - resource, - mineItem, - }) - } - }) - }) return mineables } -export const mineableByCoordinates = ( +/** + * Get a specific mineable by coordinates and cargo ID + */ +export const mineableByCoordinatesAndCargoId = ( map: WorldMap, coordinates: Coordinates, - name: string, -): Mineable => { + cargoId: number, +): Mineable | null => { const mineables = mineablesByCoordinates(map, coordinates) + return mineables.find((m) => m.resource.cargoId === cargoId) ?? null +} - const res = Array.from(mineables).find((m) => getName(m.mineItem) === name) - if (!res) { - throw new Error(`No ${name} found at ${coordinates}`) - } - return res +/** + * Get a specific mineable by coordinates and name (legacy compatibility) + * In C4, resources are identified by cargoId, so name matching is approximate + */ +export const mineableByCoordinates = ( + map: WorldMap, + coordinates: Coordinates, + _name: string, +): Mineable | null => { + const mineables = mineablesByCoordinates(map, coordinates) + // In C4, return first mineable at location + // TODO: Implement proper name matching with cargoType lookup + return mineables[0] ?? null } -export const getMapContext = async (game: Game): Promise => { - const [starbases, pl, mI, res] = await Promise.all([ - getStarbases(game), - getPlanets(game), - getMineItems(game), - getResources(game), +/** + * Build the world map from on-chain data + */ +export const getMapContext = async (): Promise => { + const [_game, systems, allCelestialBodies] = await Promise.all([ + fetchGame(), + fetchAllAccounts('StarSystem'), + fetchAllAccounts('CelestialBody'), ]) - const planets = new Map>() - const resources = new Map>() - const mineItems = new Map() - - starbases.forEach((s) => { - const location = transformSector(s.data.sector) - const planetSet = planets.get(s.key.toBase58()) ?? new Set() - - pl.filter((p) => - transformSector(p.data.sector).equals(location), - ).forEach((p) => { - planetSet.add(p) - }) - - planets.set(s.key.toBase58(), planetSet) - }) - - res.forEach((r) => { - const resourceSet = - resources.get(r.data.location.toBase58()) ?? new Set() - - mineItems.set( - r.key.toBase58(), - mI.find((m) => m.key.equals(r.data.mineItem))!, - ) + // Build starbases from systems + const starbases: StarbaseData[] = systems + .map((s) => getStarbaseFromSystem(s)) + .filter((sb): sb is StarbaseData => sb !== null) + + // Group celestial bodies by system + const celestialBodies = new Map[]>() + for (const cb of allCelestialBodies) { + const systemKey = cb.system as string + const existing = celestialBodies.get(systemKey) ?? [] + existing.push(cb) + celestialBodies.set(systemKey, existing) + } - resourceSet.add(r) - resources.set(r.data.location.toBase58(), resourceSet) - }) + // Extract resource info from celestial bodies + const resources = new Map() + for (const cb of allCelestialBodies) { + const resourceInfo = getResourceFromCelestialBody(cb) + if (resourceInfo) { + resources.set(cb.key, resourceInfo) + } + } return { + systems, starbases, - planets, - mineItems, + celestialBodies, resources, } } diff --git a/src/main/basedbot/lib/sage/util.ts b/src/main/basedbot/lib/sage/util.ts index 7ba34323..3a84bb0e 100644 --- a/src/main/basedbot/lib/sage/util.ts +++ b/src/main/basedbot/lib/sage/util.ts @@ -1,16 +1,70 @@ -import { byteArrayToString } from '@staratlas/data-source' -import { Fleet, MineItem, Planet, Starbase } from '@staratlas/sage' +/** + * SAGE utility functions for C4 + */ -export const getName = (item: Fleet | Starbase | Planet | MineItem): string => { - if (!item?.data) { +import type { CelestialBody, Fleet, StarSystem } from '@staratlas/dev-sage' + +import type { AccountWithKey } from '../types.js' + +/** + * Convert byte array to string + */ +function byteArrayToString(bytes: Uint8Array | number[] | string): string { + if (typeof bytes === 'string') { + return bytes + } + + const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes) + + // Find null terminator + let end = arr.length + for (let i = 0; i < arr.length; i++) { + if (arr[i] === 0) { + end = i + break + } + } + + return new TextDecoder().decode(arr.slice(0, end)) +} + +type NamedAccount = + | AccountWithKey + | AccountWithKey + | AccountWithKey + +/** + * Get name from a named account + */ +export const getName = (item: NamedAccount | unknown): string => { + if (!item || typeof item !== 'object') { return 'N/A' } - const name = 'name' in item.data ? item.data.name : undefined - const fleetLabel = - 'fleetLabel' in item.data ? item.data.fleetLabel : undefined + const obj = item as Record + + // Try fleetLabel for Fleet + if ('fleetLabel' in obj && obj.fleetLabel) { + const label = obj.fleetLabel + if (typeof label === 'string') return label + if (label instanceof Uint8Array || Array.isArray(label)) { + return byteArrayToString(label as Uint8Array) + } + } - const dataToConvert = name ?? fleetLabel + // Try name for StarSystem + if ('name' in obj && obj.name) { + const name = obj.name + if (typeof name === 'string') return name + if (name instanceof Uint8Array || Array.isArray(name)) { + return byteArrayToString(name as Uint8Array) + } + } + + // Try key as fallback + if ('key' in obj && typeof obj.key === 'string') { + return obj.key.slice(0, 8) + '...' + } - return dataToConvert ? byteArrayToString(dataToConvert) : 'N/A' + return 'N/A' } diff --git a/src/main/basedbot/lib/types.ts b/src/main/basedbot/lib/types.ts new file mode 100644 index 00000000..c688869a --- /dev/null +++ b/src/main/basedbot/lib/types.ts @@ -0,0 +1,31 @@ +import type { Address, Lamports } from '@solana/kit' + +/** + * Account with its on-chain address attached + */ +export type AccountWithKey = T & { key: Address } + +/** + * Account with address and lamport balance info + */ +export type AccountWithKeyAndLamports = T & { + key: Address + lamports: Lamports + minLamports: Lamports +} + +/** + * Re-export common types from dev-sage for convenience + */ +export type { + CelestialBody, + Character, + Fleet, + Game, + RegionTracker, + StarbasePlayer, + StarSystem, +} from '@staratlas/dev-sage' + +export type { Profile, PlayerName } from '@staratlas/dev-player-profile' +export type { ProfileFactionAccount } from '@staratlas/dev-profile-faction' diff --git a/src/main/basedbot/lib/util/coordinates.ts b/src/main/basedbot/lib/util/coordinates.ts index edaee26c..14daadc1 100644 --- a/src/main/basedbot/lib/util/coordinates.ts +++ b/src/main/basedbot/lib/util/coordinates.ts @@ -1,35 +1,47 @@ -import BN from 'bn.js' import bs58 from 'bs58' +/** + * Coordinate helper class using bigint (C4/Kit standard) + */ export class Coordinates { - private readonly _x: BN - private readonly _y: BN + private readonly _x: bigint + private readonly _y: bigint public static fromString = (str: string): Coordinates => { const [x, y] = str.split(',') - - return new Coordinates(new BN(x, 10), new BN(y, 10)) + return new Coordinates(BigInt(x), BigInt(y)) } - public static fromBN = (x: BN, y: BN): Coordinates => new Coordinates(x, y) + public static fromBigInt = (x: bigint, y: bigint): Coordinates => + new Coordinates(x, y) public static fromNumber = (x: number, y: number): Coordinates => - new Coordinates(x, y) + new Coordinates(BigInt(x), BigInt(y)) - private constructor(x: BN | number, y: BN | number) { - this._x = typeof x === 'number' ? new BN(x, 10) : x - this._y = typeof y === 'number' ? new BN(y, 10) : y - // logger.debug('Coordinates', { x: this._x.toNumber(), y: this._y.toNumber() }) + /** + * Create from C4 coordinate tuple [bigint, bigint] + */ + public static fromTuple = (coords: [bigint, bigint]): Coordinates => + new Coordinates(coords[0], coords[1]) + + private constructor(x: bigint, y: bigint) { + this._x = x + this._y = y } - private static toB58 = (bn: BN): string => - bs58.encode(bn.toTwos(64).toArrayLike(Buffer, 'le', 8)) + private static toB58 = (n: bigint): string => { + // Convert to 8-byte little-endian buffer (twos complement for negative) + const buffer = new ArrayBuffer(8) + const view = new DataView(buffer) + view.setBigInt64(0, n, true) // true = little-endian + return bs58.encode(new Uint8Array(buffer)) + } - get xBN(): BN { + get xBigInt(): bigint { return this._x } - get yBN(): BN { + get yBigInt(): bigint { return this._y } @@ -42,25 +54,26 @@ export class Coordinates { } get x(): number { - return this._x.toNumber() + return Number(this._x) } get y(): number { - return this._y.toNumber() + return Number(this._y) } public distanceFrom = (other: Coordinates): number => { - const x = this._x.sub(other._x) - const y = this._y.sub(other._y) - - return Math.sqrt(x.mul(x).add(y.mul(y)).toNumber()) + const dx = Number(this._x - other._x) + const dy = Number(this._y - other._y) + return Math.sqrt(dx * dx + dy * dy) } public equals = (other: Coordinates): boolean => - this._x.eq(other._x) && this._y.eq(other._y) + this._x === other._x && this._y === other._y - public toString = (): string => - `${this._x.toNumber()},${this._y.toNumber()}` + public toString = (): string => `${this._x},${this._y}` - public toArray = (): [BN, BN] => [this._x, this._y] + /** + * Convert to C4 tuple format for instructions + */ + public toTuple = (): [bigint, bigint] => [this._x, this._y] } diff --git a/src/main/basedbot/lib/util/get-random-fleet.ts b/src/main/basedbot/lib/util/get-random-fleet.ts index 4fa57fdd..e6715d0b 100644 --- a/src/main/basedbot/lib/util/get-random-fleet.ts +++ b/src/main/basedbot/lib/util/get-random-fleet.ts @@ -1,87 +1,23 @@ +/** + * Random fleet generation utilities + * TODO: Implement for C4 using new fleet creation patterns + */ + import { logger } from '../../../../logger.js' -import { FleetShips } from '../sage/act/create-fleet.js' -import { ShipMake } from '../sage/ships.js' -import { Player } from '../sage/state/user-account.js' -import { Faction } from './galaxy-sectors-data.js' +import type { FleetShip } from '../sage/act/create-fleet.js' +import type { Player } from '../sage/state/user-account.js' +/** + * Get a random fleet configuration for a player + * TODO: Implement for C4 - needs ship data from StarbasePlayer.shipsInEscrow + */ export const getRandomFleet = ( - player: Player, + _player: Player, mode: 'mine' | 'transport', -): FleetShips => { - const { faction, shipData } = player - - const factionMakes: Record = { - [Faction.MUD]: [ - 'Fimbul', - 'Fimbul BYOS', - 'Fimbul ECOS', - 'Rainbow', - 'Armstrong Industries', - 'Pearce', - 'Calico', - 'Opal', - ], - [Faction.ONI]: [ - 'Fimbul', - 'Fimbul BYOS', - 'Fimbul ECOS', - 'Rainbow', - 'Armstrong Industries', - 'Busan', - 'Calico', - 'Ogrika', - ], - [Faction.UST]: [ - 'Fimbul', - 'Fimbul BYOS', - 'Fimbul ECOS', - 'Rainbow', - 'Armstrong Industries', - 'VZUS', - 'Ogrika', - 'Opal', - ], - } - - const factionShips = shipData.filter((ship) => - factionMakes[faction]?.includes(ship.make), +): FleetShip[] => { + logger.warn( + `getRandomFleet not yet implemented for C4. Requested mode: ${mode}`, ) - - // Filter by mode (mining or transport) - const roleShips = factionShips.filter((value) => { - if (mode === 'mine') - return value.role === 'miner' || value.role === 'multi-role' - if (mode === 'transport') - return ( - value.role === 'freighter' || - value.role === 'transport' || - value.role === 'multi-role' - ) - return false - }) - logger.info(`Selecting ${mode} fleet out of ${roleShips.length} options`) - - let fleetSize = 0 - const fleet: FleetShips = [] - - while (fleetSize < 145 && fleet.length <= 5) { - let shipAdded = false - - const randomIndex = Math.floor(Math.random() * roleShips.length) - const shipData = roleShips[randomIndex] - - if (fleetSize + shipData.size <= 145) { - fleet.push({ - shipMint: shipData.mint, - count: 1, - }) - - fleetSize += shipData.size - shipAdded = true - } - - if (!shipAdded) break - } - - return fleet + // Return empty fleet for now + return [] } diff --git a/src/main/basedbot/lib/util/pod-cleanup.ts b/src/main/basedbot/lib/util/pod-cleanup.ts index 40f9bec8..a3b4c1fd 100644 --- a/src/main/basedbot/lib/util/pod-cleanup.ts +++ b/src/main/basedbot/lib/util/pod-cleanup.ts @@ -1,190 +1,34 @@ -import { Account, TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { Connection, PublicKey } from '@solana/web3.js' -import { CargoIDLProgram, CargoType } from '@staratlas/cargo' -import { - AsyncSigner, - createAssociatedTokenAccountIdempotent, - getParsedTokenAccountsByOwner, - InstructionReturn, -} from '@staratlas/data-source' -import { - getCargoPodsByAuthority, - SageIDLProgram, - StarbasePlayer, -} from '@staratlas/sage' -import BN from 'bn.js' +/** + * Cargo pod cleanup utilities for C4 + * TODO: Verify if cargo pod cleanup is still needed in C4 + * The C4 cargo model may handle this differently + */ -interface PodCleanup { - mainPod: PublicKey - podsAndTokensToClean: [PublicKey, Account[]][] +import type { Address } from '@solana/kit' + +export interface PodCleanup { + mainPod: Address + podsAndTokensToClean: Array<[Address, unknown[]]> cargoSeqId: number } +/** + * Get cargo pods that need cleanup for a starbase player + * TODO: Implement for C4 if still needed + */ export const getCleanPodsByStarbasePlayerAccounts = async ( - connection: Connection, - cargoProgram: CargoIDLProgram, - starbasePlayer: PublicKey, + _starbasePlayer: Address, ): Promise => { - const cargoPods = await getCargoPodsByAuthority( - connection, - cargoProgram, - starbasePlayer, - ) - if (cargoPods.length > 1) { - let podsToClean = cargoPods.map((cargoPod) => { - if (cargoPod.type === 'error') - throw new Error('Error reading CargoPod account') - return cargoPod.data - }) - const mainPod = podsToClean.reduce((prev, current) => { - return prev.data.openTokenAccounts > current.data.openTokenAccounts - ? prev - : current - }) - podsToClean = podsToClean.filter((it) => !it.key.equals(mainPod.key)) - const podsAndTokensToClean: Array<[PublicKey, Account[]]> = - await Promise.all( - podsToClean.map(async (thisPod) => { - const podTokenAccounts = - await getParsedTokenAccountsByOwner( - connection, - thisPod.key, - ) - return [thisPod.key, podTokenAccounts] - }), - ) - - return { - mainPod: mainPod.key, - podsAndTokensToClean, - cargoSeqId: mainPod.data.seqId, - } - } + // In C4, cargo pod handling may be different + // Return undefined to indicate no cleanup needed for now return undefined } -export const getPodCleanupInstructions = ( - podCleanup: PodCleanup, - sageProgram: SageIDLProgram, - cargoProgram: CargoIDLProgram, - starbasePlayer: PublicKey, - starbase: PublicKey, - playerProfile: PublicKey, - profileFaction: PublicKey, - cargoStatsDefinition: PublicKey, - gameId: PublicKey, - gameState: PublicKey, - key: AsyncSigner, - keyIndex: number, -) => { - const cleanInstructions: InstructionReturn[] = [] - const newTokenAccounts: string[] = [] - if (podCleanup.podsAndTokensToClean.length > 1) { - for ( - let index2 = 0; - index2 < podCleanup.podsAndTokensToClean.length; - index2++ - ) { - const element = podCleanup.podsAndTokensToClean[index2] - const thisPodKey = element[0] - const podTokenAccounts = element[1] - if (podTokenAccounts.length > 0) { - for ( - let index3 = 0; - index3 < podTokenAccounts.length; - index3++ - ) { - const tokenData = podTokenAccounts[index3] - const cargoType = CargoType.findAddress( - cargoProgram, - cargoStatsDefinition, - tokenData.mint, - podCleanup.cargoSeqId, - )[0] - if (Number(tokenData.delegatedAmount) > 0) { - const tokenTo = createAssociatedTokenAccountIdempotent( - tokenData.mint, - podCleanup.mainPod, - true, - TOKEN_PROGRAM_ID, - ) - const tokenToBase58 = tokenTo.address.toBase58() - if (!newTokenAccounts.includes(tokenToBase58)) { - newTokenAccounts.push(tokenToBase58) - cleanInstructions.push(tokenTo.instructions) - } - const transferIx = - StarbasePlayer.transferCargoAtStarbase( - sageProgram, - cargoProgram, - starbasePlayer, - key, - 'funder', - playerProfile, - profileFaction, - starbase, - thisPodKey, - podCleanup.mainPod, - cargoType, - cargoStatsDefinition, - tokenData.address, - tokenTo.address, - tokenData.mint, - gameId, - gameState, - { - amount: new BN( - tokenData.delegatedAmount.toString(), - ), - keyIndex, - }, - ) - cleanInstructions.push(transferIx) - } else { - const closeIx = - StarbasePlayer.closeStarbaseCargoTokenAccount( - sageProgram, - cargoProgram, - starbasePlayer, - key, - 'funder', - playerProfile, - profileFaction, - starbase, - thisPodKey, - cargoType, - cargoStatsDefinition, - tokenData.address, - tokenData.mint, - gameId, - gameState, - { - keyIndex, - }, - ) - cleanInstructions.push(closeIx) - } - } - } - cleanInstructions.push( - StarbasePlayer.removeCargoPod( - sageProgram, - cargoProgram, - starbasePlayer, - key, - playerProfile, - profileFaction, - 'funder', - starbase, - thisPodKey, - gameId, - gameState, - { - keyIndex, - }, - ), - ) - } - } - return cleanInstructions +/** + * Get instructions to clean up cargo pods + * TODO: Implement for C4 if still needed + */ +export const getPodCleanupInstructions = (_podCleanup: PodCleanup): never[] => { + // Return empty array - no cleanup instructions for now + return [] } diff --git a/src/main/basedbot/lib/util/profile.ts b/src/main/basedbot/lib/util/profile.ts index 74ffdb00..94d9ec25 100644 --- a/src/main/basedbot/lib/util/profile.ts +++ b/src/main/basedbot/lib/util/profile.ts @@ -1,368 +1,27 @@ -import { Keypair, PublicKey } from '@solana/web3.js' -import { - AsyncSigner, - InstructionReturn, - ixReturnsToIxs, - keypairToAsyncSigner, - readAllFromRPC, -} from '@staratlas/data-source' -import { - PlayerProfile, - ProfileKeyInput, - ProfilePermissions, -} from '@staratlas/player-profile' -import { UserPoints } from '@staratlas/points' -import { ProfileFactionAccount } from '@staratlas/profile-faction' -import { - Game, - SagePlayerProfile, - Starbase, - StarbasePlayer, -} from '@staratlas/sage' -import { airdrop, airdropCrew, airdropSol } from '../../../../lib/airdrop.js' -import { logger } from '../../../../logger.js' -import { connection } from '../../../../service/sol/index.js' -import { sendAndConfirmInstructions } from '../../../../service/sol/send-and-confirm-tx.js' -import { programs, StarAtlasPrograms } from '../programs.js' -import { starbaseByCoordinates } from '../sage/state/starbase-by-coordinates.js' -import { Faction, galaxySectorsData } from './galaxy-sectors-data.js' -import { config } from '../../../../config/index.js' - -export const getStarbasePlayer = async ( - keyPair: Keypair, - game: Game, - playerProfile: PublicKey, - profileFaction: PublicKey, - starbase: Starbase, - programs: StarAtlasPrograms, -): Promise => { - const starbasePlayers = await readAllFromRPC( - connection, - programs.sage, - StarbasePlayer, - 'processed', - [ - { - memcmp: { - offset: 9, - bytes: playerProfile.toBase58(), - }, - }, - { - memcmp: { - offset: 73, - bytes: starbase.key.toBase58(), - }, - }, - ], - ) - - if (starbasePlayers.length > 1) { - throw new Error('Multiple starbase players found') - } - const [starbasePlayer] = starbasePlayers - - if (!starbasePlayer) { - const [sageProfileAddress] = SagePlayerProfile.findAddress( - programs.sage, - playerProfile, - game.key, - ) - const [starbasePlayerAddress] = StarbasePlayer.findAddress( - programs.sage, - starbase.key, - sageProfileAddress, - starbase.data.seqId, - ) - - const instructionReturns = [ - StarbasePlayer.registerStarbasePlayer( - programs.sage, - profileFaction, - sageProfileAddress, - starbase.key, - game.key, - game.data.gameState, - starbase.data.seqId, - ), - StarbasePlayer.createCargoPod( - programs.sage, - programs.cargo, - starbasePlayerAddress, - keypairToAsyncSigner(keyPair), - playerProfile, - profileFaction, - starbase.key, - game.data.cargo.statsDefinition, - game.key, - game.data.gameState, - { - keyIndex: 0, - podSeeds: Array.from( - Keypair.generate().publicKey.toBuffer(), - ), - }, - ), - ] - - logger.warn('Starbase player not found, creating', { - player: playerProfile.toBase58(), - starbase: starbase.key.toBase58(), - }) - await sendAndConfirmInstructions([keyPair])( - await ixReturnsToIxs( - instructionReturns, - keypairToAsyncSigner(keyPair), - ), - ) - - return getStarbasePlayer( - keyPair, - game, - playerProfile, - profileFaction, - starbase, - programs, - ) - } - - if (starbasePlayer.type === 'error') { - throw new Error('Error reading starbasePlayer account') - } - - return starbasePlayer.data -} - -const getCategory = (category: any): PublicKey => { - if ('category' in category) { - return new PublicKey(category.category) - } - - throw new Error('Category not found') -} - -const getCreatePlayerPointsAccountsIxs = ( - game: Game, - character: PublicKey, -): InstructionReturn[] => { - const pilotXPAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.pilotXpCategory), - ) - - const dataRunningXPAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.dataRunningXpCategory), - ) - - const councilRankXpAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.councilRankXpCategory), - ) - - const miningXpAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.miningXpCategory), - ) - - const craftingXpAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.craftingXpCategory), - ) - - const lpAccountIx = createCharacterUserPointAccount( - character, - getCategory(game.data.points.lpCategory), - ) - - return [ - pilotXPAccountIx.instructions, - dataRunningXPAccountIx.instructions, - councilRankXpAccountIx.instructions, - miningXpAccountIx.instructions, - craftingXpAccountIx.instructions, - lpAccountIx.instructions, - ] -} - -export const createNewCharacter = ( - characterProfileKeypair: AsyncSigner, - signer: AsyncSigner, -): { - character: PublicKey - instruction: InstructionReturn -} => { - logger.info(characterProfileKeypair.publicKey().toBase58()) - logger.info(signer.publicKey().toBase58()) - const instructions = PlayerProfile.createProfile( - programs.playerProfile, - characterProfileKeypair, - [ - { - key: signer, - expireTime: null, - scope: programs.playerProfile.programId, - permissions: ProfilePermissions.all(), - }, - ], - 1, - ) - - return { - character: characterProfileKeypair.publicKey(), - instruction: instructions, - } -} - -export const setCharacterName = ( - signer: AsyncSigner, - character: { - key: PublicKey - index: number - }, - name: string, -) => { - const keyInput: ProfileKeyInput = { - playerProfileProgram: programs.playerProfile, - profileKey: character.key, - key: signer, - keyIndex: character.index, - } - - return PlayerProfile.setName(programs.playerProfile, keyInput, name) -} - -export const setCharacterFaction = ( - signer: AsyncSigner, - character: { - key: PublicKey - index: number - }, - faction: Faction, -) => { - const keyInput: ProfileKeyInput = { - playerProfileProgram: programs.playerProfile, - profileKey: character.key, - key: signer, - keyIndex: character.index, - } - - return ProfileFactionAccount.chooseFaction( - programs.profileFaction, - keyInput, - faction.valueOf(), - ) -} - -export const createCharacterUserPointAccount = ( - player: PublicKey, - category: PublicKey, -) => { - return UserPoints.createUserPointAccount(programs.points, player, category) -} - +/** + * Profile utilities for C4 + * TODO: Reimplement profile creation for C4 + * The C4 SDK uses different patterns for profile/character creation + */ + +import type { KeyPairSigner } from '@solana/kit' +import type { Game } from '@staratlas/dev-sage' + +import type { AccountWithKey } from '../types.js' +import type { Faction } from './galaxy-sectors-data.js' + +/** + * Create and initialize a new character profile + * TODO: Implement for C4 using dev-player-profile and dev-sage + */ export const createAndInitializeCharacter = async ( - game: Game, - name: string, - faction: Faction, - keypair: Keypair, -) => { - await airdropSol( - config.app.airdropUrl, - config.app.airdropToken, - keypair.publicKey, - 10, - ) - const signer = keypairToAsyncSigner(keypair) - const characterKeyPair = Keypair.generate() - const characterProfileSigner = keypairToAsyncSigner(characterKeyPair) - const characterInstruction = createNewCharacter( - characterProfileSigner, - signer, - ) - const character = characterInstruction.character - const nameIx = setCharacterName(signer, { key: character, index: 0 }, name) - const factionIx = setCharacterFaction( - signer, - { key: character, index: 0 }, - faction, - ) - if (!nameIx || !factionIx || !nameIx.instructions.length) { - throw new Error('Failed to create name or faction instruction') - } - const userPointsIxs = getCreatePlayerPointsAccountsIxs(game, character) - const profileIx = SagePlayerProfile.registerSagePlayerProfile( - programs.sage, - character, - game.key, - game.data.gameState, - ) - - await ixReturnsToIxs([characterInstruction.instruction], signer).then( - sendAndConfirmInstructions([keypair, characterKeyPair]), - ) - - await ixReturnsToIxs( - [ - nameIx.instructions, - factionIx.instructions, - profileIx, - ...userPointsIxs, - ], - signer, - ).then(sendAndConfirmInstructions([keypair])) - - const homeCoordinates = galaxySectorsData() - .filter((sector) => sector.closestFaction === faction) - .find((sector) => sector.name.includes('CSS'))?.coordinates - - if (!homeCoordinates) { - throw new Error('No home coordinates found') - } - - const homeStarbase = await starbaseByCoordinates(homeCoordinates) - - if (!homeStarbase) { - throw new Error('No home starbase found') - } - const profiles = await readAllFromRPC( - connection, - programs.playerProfile, - PlayerProfile, - 'processed', - [ - { - memcmp: { - offset: PlayerProfile.MIN_DATA_SIZE + 2, - bytes: keypair.publicKey.toBase58(), - }, - }, - ], + _game: AccountWithKey, + _name: string, + _faction: Faction, + _signer: KeyPairSigner, +): Promise => { + throw new Error( + 'createAndInitializeCharacter not yet implemented for C4. ' + + 'Create profile via Star Atlas app or implement using dev-player-profile SDK.', ) - - const [profile] = profiles - - await getStarbasePlayer( - keypair, - game, - profile.key, - factionIx.faction[0], - homeStarbase, - programs, - ) - - await airdrop( - config.app.airdropUrl, - config.app.airdropToken, - keypair.publicKey, - ) - - await airdropCrew( - config.app.airdropUrl, - config.app.airdropToken, - keypair.publicKey, - 10000, - ) - - return profiles } diff --git a/src/main/fleetbot/fleetbot.ts b/src/main/fleetbot/fleetbot.ts deleted file mode 100644 index 8c6a212b..00000000 --- a/src/main/fleetbot/fleetbot.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { CronJob } from 'cron' - -import { config } from '../../config/index.js' -import * as db from '../../db/index.js' -import { - checkTransactions, - refill, - stockResources, - telegramBot, -} from '../../lib/index.js' -import { logger } from '../../logger.js' -import { initOrderBook } from '../../service/gm/index.js' - -let refillCronJob: CronJob | undefined, - resourcesCronJob: CronJob | undefined, - transactionCronJob: CronJob | undefined - -export const create = async (): Promise => { - logger.info('Starting fleetbot...') - await db.connect() -} - -export const stop = async (): Promise => { - try { - logger.info('Stopping fleetbot...') - if (resourcesCronJob) { - resourcesCronJob.stop() - } - if (transactionCronJob) { - transactionCronJob.stop() - } - if (refillCronJob) { - refillCronJob.stop() - } - await db.close() - } catch (e) { - logger.error(e) - } -} - -export const start = async (): Promise => { - await initOrderBook() - // https://github.com/telegraf/telegraf/issues/1749 - telegramBot.launch().catch((e) => logger.error(e)) - - resourcesCronJob = CronJob.from({ - cronTime: config.cron.resourceInterval, - onTick: stockResources, - runOnInit: config.app.quickstart, - start: true, - }) - refillCronJob = CronJob.from({ - cronTime: config.cron.refillInterval, - onTick: refill, - runOnInit: config.app.quickstart, - start: true, - }) - transactionCronJob = CronJob.from({ - cronTime: config.cron.bookkeeperInterval, - onTick: checkTransactions, - runOnInit: config.app.quickstart, - start: true, - }) -} diff --git a/src/main/fleetbot/index.ts b/src/main/fleetbot/index.ts deleted file mode 100644 index 083cc804..00000000 --- a/src/main/fleetbot/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -import 'reflect-metadata' -import { Sentry } from '../../sentry.js' // import this as early as possible to catch early startup errors - -import { logger } from '../../logger.js' - -import * as app from './fleetbot.js' - -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() - } -} - -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/service/fleet/const/amounts.ts b/src/service/fleet/const/amounts.ts deleted file mode 100644 index 490d75bd..00000000 --- a/src/service/fleet/const/amounts.ts +++ /dev/null @@ -1,8 +0,0 @@ -import Big from 'big.js' - -export type Amounts = { - food: Big - fuel: Big - ammo: Big - tool: Big -} diff --git a/src/service/fleet/const/index.ts b/src/service/fleet/const/index.ts deleted file mode 100644 index 932fb1d5..00000000 --- a/src/service/fleet/const/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './amounts.js' diff --git a/src/service/fleet/get-remaining-details.ts b/src/service/fleet/get-remaining-details.ts deleted file mode 100644 index 7cbe1958..00000000 --- a/src/service/fleet/get-remaining-details.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { ScoreVarsShipInfo, ShipStakingInfo } from '@staratlas/factory' - -export interface ResourceStats { - food: Stats - tool: Stats - fuel: Stats - ammo: Stats -} - -export interface Stats { - unitsBurnt: number - unitsLeftPct: number - unitsLeft: number - secondsLeft: number - totalSeconds: number - maxSeconds: number - maxUnits: number - burnRatePerShip: number - burnRatePerFleet: number -} - -export const getTimePass = (fleet: ShipStakingInfo): number => { - const now = Date.now() / 1000 - const tripStart = fleet.currentCapacityTimestamp.toNumber() - - return now - tripStart -} -export const getRemainFoodSec = ( - fleet: ShipStakingInfo, - tp: number | undefined = undefined, -): number => { - const timePass = tp === undefined ? getTimePass(fleet) : tp - - return fleet.foodCurrentCapacity.toNumber() - timePass -} - -export const getRemainArmsSec = ( - fleet: ShipStakingInfo, - tp: number | undefined = undefined, -): number => { - const timePass = tp === undefined ? getTimePass(fleet) : tp - - return fleet.armsCurrentCapacity.toNumber() - timePass -} - -export const getRemainFuelSec = ( - fleet: ShipStakingInfo, - tp: number | undefined = undefined, -): number => { - const timePass = tp === undefined ? getTimePass(fleet) : tp - - return fleet.fuelCurrentCapacity.toNumber() - timePass -} - -export const getRemainHealthSec = ( - fleet: ShipStakingInfo, - tp: number | undefined = undefined, -): number => { - const timePass = tp === undefined ? getTimePass(fleet) : tp - - return fleet.healthCurrentCapacity.toNumber() - timePass -} - -export const timePassSinceLastAction = (fleet: ShipStakingInfo): number => { - let timePassSinceStart = getTimePass(fleet) - - const [foodRemainSec, armsRemainSec, fuelRemainSec, healthRemainSec] = [ - getRemainFoodSec(fleet), - getRemainArmsSec(fleet), - getRemainFuelSec(fleet), - getRemainHealthSec(fleet), - ] - - const depletionTime = Math.min( - foodRemainSec, - armsRemainSec, - fuelRemainSec, - healthRemainSec, - ) - - if (depletionTime < 0) { - timePassSinceStart = depletionTime + timePassSinceStart - } - - return timePassSinceStart -} - -export const getRemainFoodDetails = ( - shipInfo: ScoreVarsShipInfo, - fleet: ShipStakingInfo, - timePassSinceStart: number, -): Stats => { - const secondsLeft = getRemainFoodSec(fleet, timePassSinceStart) - const unitsBurnRate = 1 / (shipInfo.millisecondsToBurnOneFood / 1000) // Per Second - const burnRatePerFleet = - 1 / - (shipInfo.millisecondsToBurnOneFood / - fleet.shipQuantityInEscrow.toNumber() / - 1000) - const unitsBurnt = - unitsBurnRate * - timePassSinceStart * - fleet.shipQuantityInEscrow.toNumber() - const unitsLeft = - unitsBurnRate * secondsLeft * fleet.shipQuantityInEscrow.toNumber() - const unitsLeftPct = - unitsLeft / - (shipInfo.foodMaxReserve * fleet.shipQuantityInEscrow.toNumber()) - const totalSeconds = fleet.foodCurrentCapacity.toNumber() - const maxSeconds = - shipInfo.foodMaxReserve * - fleet.shipQuantityInEscrow.toNumber() * - (shipInfo.millisecondsToBurnOneFood / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const maxUnits = - shipInfo.foodMaxReserve * fleet.shipQuantityInEscrow.toNumber() - - return { - unitsBurnt, - unitsLeftPct, - unitsLeft, - secondsLeft: Math.max(0, secondsLeft), - totalSeconds, - maxSeconds, - maxUnits, - burnRatePerShip: unitsBurnRate, - burnRatePerFleet, - } -} - -export const getRemainArmsDetails = ( - shipInfo: ScoreVarsShipInfo, - fleet: ShipStakingInfo, - timePassSinceStart: number, -): Stats => { - const secondsLeft = getRemainArmsSec(fleet, timePassSinceStart) - const unitsBurnRate = 1 / (shipInfo.millisecondsToBurnOneArms / 1000) // Per Second - const unitsBurnt = - unitsBurnRate * - timePassSinceStart * - fleet.shipQuantityInEscrow.toNumber() - const burnRatePerFleet = - 1 / - (shipInfo.millisecondsToBurnOneArms / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const unitsLeft = - unitsBurnRate * secondsLeft * fleet.shipQuantityInEscrow.toNumber() - const unitsLeftPct = - unitsLeft / - (shipInfo.armsMaxReserve * fleet.shipQuantityInEscrow.toNumber()) - const maxSeconds = - shipInfo.armsMaxReserve * - fleet.shipQuantityInEscrow.toNumber() * - (shipInfo.millisecondsToBurnOneArms / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const totalSeconds = fleet.armsCurrentCapacity.toNumber() - const maxUnits = - shipInfo.armsMaxReserve * fleet.shipQuantityInEscrow.toNumber() - - return { - unitsBurnt, - unitsLeftPct, - unitsLeft, - secondsLeft: Math.max(0, secondsLeft), - totalSeconds, - maxSeconds, - maxUnits, - burnRatePerShip: unitsBurnRate, - burnRatePerFleet, - } -} - -export const getRemainFuelDetails = ( - shipInfo: ScoreVarsShipInfo, - fleet: ShipStakingInfo, - timePassSinceStart: number, -): Stats => { - const secondsLeft = getRemainFuelSec(fleet, timePassSinceStart) - const unitsBurnRate = 1 / (shipInfo.millisecondsToBurnOneFuel / 1000) // Per Second - const unitsBurnt = - unitsBurnRate * - timePassSinceStart * - fleet.shipQuantityInEscrow.toNumber() - const burnRatePerFleet = - 1 / - (shipInfo.millisecondsToBurnOneFuel / - fleet.shipQuantityInEscrow.toNumber() / - 1000) - const unitsLeft = - unitsBurnRate * secondsLeft * fleet.shipQuantityInEscrow.toNumber() - const unitsLeftPct = - unitsLeft / - (shipInfo.fuelMaxReserve * fleet.shipQuantityInEscrow.toNumber()) - const totalSeconds = fleet.fuelCurrentCapacity.toNumber() - const maxSeconds = - shipInfo.fuelMaxReserve * - fleet.shipQuantityInEscrow.toNumber() * - (shipInfo.millisecondsToBurnOneFuel / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const maxUnits = - shipInfo.fuelMaxReserve * fleet.shipQuantityInEscrow.toNumber() - - return { - unitsBurnt, - unitsLeftPct, - unitsLeft, - secondsLeft: Math.max(0, secondsLeft), - totalSeconds, - maxSeconds, - maxUnits, - burnRatePerShip: unitsBurnRate, - burnRatePerFleet, - } -} - -export const getRemainHealthDetails = ( - shipInfo: ScoreVarsShipInfo, - fleet: ShipStakingInfo, - timePassSinceStart: number, -): Stats => { - const unitsLeftPct = - (fleet.healthCurrentCapacity.toNumber() - timePassSinceStart) / - fleet.healthCurrentCapacity.toNumber() - const secondsLeft = getRemainHealthSec(fleet, timePassSinceStart) - const unitsBurnRate = 1 / (shipInfo.millisecondsToBurnOneToolkit / 1000) - const burnRatePerFleet = - 1 / - (shipInfo.millisecondsToBurnOneToolkit / - fleet.shipQuantityInEscrow.toNumber() / - 1000) - const unitsLeft = - secondsLeft / - (shipInfo.millisecondsToBurnOneToolkit / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const totalSeconds = fleet.healthCurrentCapacity.toNumber() - const maxSeconds = - shipInfo.toolkitMaxReserve * - fleet.shipQuantityInEscrow.toNumber() * - (shipInfo.millisecondsToBurnOneToolkit / - 1000 / - fleet.shipQuantityInEscrow.toNumber()) - const maxUnits = - shipInfo.toolkitMaxReserve * fleet.shipQuantityInEscrow.toNumber() - - return { - unitsBurnt: 0, - unitsLeftPct, - secondsLeft: Math.max(0, secondsLeft), - totalSeconds, - maxSeconds, - maxUnits, - unitsLeft, - burnRatePerShip: unitsBurnRate, - burnRatePerFleet, - } -} - -export const getFleetRemainingResources = ( - shipInfo: ScoreVarsShipInfo, - fleet: ShipStakingInfo, -): ResourceStats => { - const timePassSinceStart = timePassSinceLastAction(fleet) - - return { - food: getRemainFoodDetails(shipInfo, fleet, timePassSinceStart), - ammo: getRemainArmsDetails(shipInfo, fleet, timePassSinceStart), - fuel: getRemainFuelDetails(shipInfo, fleet, timePassSinceStart), - tool: getRemainHealthDetails(shipInfo, fleet, timePassSinceStart), - } -} diff --git a/src/service/fleet/index.ts b/src/service/fleet/index.ts deleted file mode 100644 index 734473a8..00000000 --- a/src/service/fleet/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './get-remaining-details.js' -export * from './refill-fleet.js' diff --git a/src/service/fleet/refill-fleet.ts b/src/service/fleet/refill-fleet.ts deleted file mode 100644 index c6e08df5..00000000 --- a/src/service/fleet/refill-fleet.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { PublicKey, TransactionInstruction } from '@solana/web3.js' -import { - ShipStakingInfo, - createRearmInstruction, - createRefeedInstruction, - createRefuelInstruction, - createRepairInstruction, -} from '@staratlas/factory' - -import { connection, fleetProgram, getAccount } from '../sol/index.js' -import { sendAndConfirmInstructions } from '../sol/send-and-confirm-tx.js' -import { keyPair, resource } from '../wallet/index.js' - -import { Amounts } from './const/index.js' - -export const refillFleet = async ( - player: PublicKey, - fleetUnit: ShipStakingInfo, - amounts: Amounts, -): Promise => { - const [foodAccount, fuelAccount, ammoAccount, toolAccount] = - await Promise.all([ - getAccount(keyPair.publicKey, resource.food), - getAccount(keyPair.publicKey, resource.fuel), - getAccount(keyPair.publicKey, resource.ammo), - getAccount(keyPair.publicKey, resource.tool), - ]) - - const instructions: TransactionInstruction[] = [] - - if (amounts.food.gt(0)) { - instructions.push( - new TransactionInstruction( - await createRefeedInstruction( - connection, - keyPair.publicKey, - player, - amounts.food.toNumber(), - fleetUnit.shipMint, - resource.food, - foodAccount, - fleetProgram, - ), - ), - ) - } - if (amounts.fuel.gt(0)) { - instructions.push( - new TransactionInstruction( - await createRefuelInstruction( - connection, - keyPair.publicKey, - player, - amounts.fuel.toNumber(), - fleetUnit.shipMint, - resource.fuel, - fuelAccount, - fleetProgram, - ), - ), - ) - } - if (amounts.ammo.gt(0)) { - instructions.push( - new TransactionInstruction( - await createRearmInstruction( - connection, - keyPair.publicKey, - player, - amounts.ammo.toNumber(), - fleetUnit.shipMint, - resource.ammo, - ammoAccount, - fleetProgram, - ), - ), - ) - } - if (amounts.tool.gt(0)) { - instructions.push( - new TransactionInstruction( - await createRepairInstruction( - connection, - keyPair.publicKey, - player, - amounts.tool.toNumber(), - fleetUnit.shipMint, - resource.tool, - toolAccount, - fleetProgram, - ), - ), - ) - } - - return sendAndConfirmInstructions([keyPair])(instructions) -} diff --git a/src/service/gm/index.ts b/src/service/gm/index.ts deleted file mode 100644 index f21d6252..00000000 --- a/src/service/gm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './market.js' diff --git a/src/service/gm/market.ts b/src/service/gm/market.ts deleted file mode 100644 index 61ccf3d1..00000000 --- a/src/service/gm/market.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { - createTransferCheckedInstruction, - getAssociatedTokenAddressSync, - getOrCreateAssociatedTokenAccount, -} from '@solana/spl-token' -import { Keypair, PublicKey } from '@solana/web3.js' -import { GmClientService, GmOrderbookService, Order } from '@staratlas/factory' -import Big from 'big.js' - -import { Sentry } from '../../sentry.js' - -import { logger } from '../../logger.js' -import { Amounts } from '../fleet/const/index.js' -import { connection, marketProgram } from '../sol/index.js' -import { sendAndConfirmInstructions } from '../sol/send-and-confirm-tx.js' -import { keyPair, resource } from '../wallet/index.js' - -const gmClientService = new GmClientService() -const gmOrderbookService = new GmOrderbookService(connection, marketProgram) - -const orderSorter = (a: Order, b: Order) => a.price.sub(b.price).toNumber() - -export const getMarketPrice = (res: PublicKey): Big => { - const orders = gmOrderbookService - .getSellOrdersByCurrencyAndItem( - resource.atlas.toString(), - res.toString(), - ) - .sort(orderSorter) - - const [order] = orders - - return Big(order.uiPrice) -} - -export const getResourcePrices = (): Amounts => ({ - food: getMarketPrice(resource.food), - tool: getMarketPrice(resource.tool), - ammo: getMarketPrice(resource.ammo), - fuel: getMarketPrice(resource.fuel), -}) - -export const getBalanceAtlas = async (pubKey: PublicKey): Promise => { - try { - const balance = await getOrCreateAssociatedTokenAccount( - connection, - new Keypair(), - resource.atlas, - pubKey, - ) - - return Big(Number(balance.amount)).div(100000000) - } catch (e) { - Sentry.captureException(e) - logger.error(e) - - return Big(0) - } -} - -export const sendAtlas = ( - receiver: PublicKey, - amount: number, -): Promise => { - const instructions = [ - createTransferCheckedInstruction( - getAssociatedTokenAddressSync( - resource.atlas, - keyPair.publicKey, - true, - ), - resource.atlas, - getAssociatedTokenAddressSync(resource.atlas, receiver, true), - keyPair.publicKey, - Big(amount).mul(100000000).toNumber(), - 8, - [], - ), - ] - - return sendAndConfirmInstructions([keyPair])(instructions) -} - -export const getBalanceMarket = async ( - pubKey: PublicKey, - res: PublicKey, -): Promise => { - const balance = await getOrCreateAssociatedTokenAccount( - connection, - new Keypair(), - res, - pubKey, - ) - - return Big(Number(balance.amount)) -} - -export const getResourceBalances = async ( - player: PublicKey, -): Promise => { - const [tool, food, ammo, fuel] = await Promise.all([ - getBalanceMarket(player, resource.tool), - getBalanceMarket(player, resource.food), - getBalanceMarket(player, resource.ammo), - getBalanceMarket(player, resource.fuel), - ]) - - return { food, tool, ammo, fuel } -} - -export const initOrderBook = async (): Promise => { - await gmOrderbookService.initialize() -} - -export const buyResource = async ( - res: PublicKey, - amount: Big, -): Promise => { - const orders = gmOrderbookService - .getSellOrdersByCurrencyAndItem( - resource.atlas.toString(), - res.toString(), - ) - .sort(orderSorter) - - const [order] = orders - - const exchangeTx = await gmClientService.getCreateExchangeTransaction( - connection, - order, - keyPair.publicKey, - amount.round().toNumber(), - marketProgram, - ) - - logger.info(`Buying ${amount.toFixed(0)} ${res} for ${order.uiPrice} each`) - - return sendAndConfirmInstructions([keyPair])( - exchangeTx.transaction.instructions, - ) -} -export const buyResources = async (amount: Amounts): Promise => { - const res = ( - await Promise.all([ - amount.food.gt(0) - ? buyResource(resource.food, amount.food) - : Promise.resolve(''), - amount.ammo.gt(0) - ? buyResource(resource.ammo, amount.ammo) - : Promise.resolve(''), - amount.fuel.gt(0) - ? buyResource(resource.fuel, amount.fuel) - : Promise.resolve(''), - amount.tool.gt(0) - ? buyResource(resource.tool, amount.tool) - : Promise.resolve(''), - ]) - ).flat() - - return res.filter((r) => r !== '') -} diff --git a/src/service/sol/anchor.ts b/src/service/sol/anchor.ts deleted file mode 100644 index 2dc12bb2..00000000 --- a/src/service/sol/anchor.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AnchorProvider, Wallet } from '@coral-xyz/anchor' - -import { keyPair } from '../wallet/index.js' - -import { connection } from './const/index.js' - -export const anchorProvider = new AnchorProvider( - connection, - new Wallet(keyPair), - {}, -) diff --git a/src/service/sol/const/connection.ts b/src/service/sol/const/connection.ts index ac5dbfb2..f2156212 100644 --- a/src/service/sol/const/connection.ts +++ b/src/service/sol/const/connection.ts @@ -1,14 +1,4 @@ -import { Connection } from '@solana/web3.js' +import { connect } from 'solana-kite' import { config } from '../../../config/index.js' -import { fetchWithRetries } from '../undici-retry.js' -export const connection = new Connection(config.sol.rpcEndpoint, { - wsEndpoint: config.sol.wsEndpoint, - commitment: 'confirmed', - fetch: ( - input: RequestInfo | URL, - init?: RequestInit, - ): Promise => { - return fetchWithRetries(input, init, 5) - }, -}) +export const connection = connect(config.sol.rpcEndpoint, config.sol.wsEndpoint) diff --git a/src/service/sol/const/fleet-program.ts b/src/service/sol/const/fleet-program.ts index 78ec5f10..95c9fc9e 100644 --- a/src/service/sol/const/fleet-program.ts +++ b/src/service/sol/const/fleet-program.ts @@ -1,5 +1,4 @@ -import { PublicKey } from '@solana/web3.js' - +import { address } from '@solana/kit' import { config } from '../../../config/index.js' -export const fleetProgram = new PublicKey(config.sol.fleetAddress) +export const fleetProgram = address(config.sol.fleetAddress) diff --git a/src/service/sol/const/market-program.ts b/src/service/sol/const/market-program.ts index e95b47df..0152bba3 100644 --- a/src/service/sol/const/market-program.ts +++ b/src/service/sol/const/market-program.ts @@ -1,5 +1,4 @@ -import { PublicKey } from '@solana/web3.js' - +import { address } from '@solana/kit' import { config } from '../../../config/index.js' -export const marketProgram = new PublicKey(config.sol.marketAddress) +export const marketProgram = address(config.sol.marketAddress) diff --git a/src/service/sol/get-account.ts b/src/service/sol/get-account.ts index 8270d002..06f41f71 100644 --- a/src/service/sol/get-account.ts +++ b/src/service/sol/get-account.ts @@ -1,26 +1,27 @@ -import { TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' +import { + findAssociatedTokenPda, + TOKEN_PROGRAM_ADDRESS, +} from '@solana-program/token' +import { type Address } from '@solana/kit' -import { Resource } from '../wallet/index.js' +import { type Resource } from '../wallet/index.js' -const resourceAccounts: Map = new Map() +const resourceAccounts: Map = new Map() export const getAccount = async ( - player: PublicKey, + player: Address, resource: Resource, -): Promise => { - if (!resourceAccounts.get(resource.toString())) { - const ret = await PublicKey.findProgramAddress( - [ - player.toBuffer(), - TOKEN_PROGRAM_ID.toBuffer(), - resource.toBuffer(), - ], - new PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'), - ) +): Promise
=> { + const key = `${player}:${resource}` - resourceAccounts.set(resource.toString(), ret[0]) + if (!resourceAccounts.has(key)) { + const [ata] = await findAssociatedTokenPda({ + owner: player, + mint: resource, + tokenProgram: TOKEN_PROGRAM_ADDRESS, + }) + resourceAccounts.set(key, ata) } - return resourceAccounts.get(resource.toString()) as Resource + return resourceAccounts.get(key) as Address } diff --git a/src/service/sol/priority-fee/bloxroute-tip-instruction.ts b/src/service/sol/priority-fee/bloxroute-tip-instruction.ts index 88f44b3b..ce79187d 100644 --- a/src/service/sol/priority-fee/bloxroute-tip-instruction.ts +++ b/src/service/sol/priority-fee/bloxroute-tip-instruction.ts @@ -1,19 +1,17 @@ -import { - PublicKey, - SystemProgram, - TransactionInstruction, -} from '@solana/web3.js' +import { address, type Instruction, type TransactionSigner } from '@solana/kit' +import { getTransferSolInstruction } from '@solana-program/system' -const TRADER_API_TIP_WALLET = 'HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY' -export const createBloxrouteTipInstruction = ( - senderAddress: PublicKey, - tipAmount: number, -): TransactionInstruction => { - const tipAddress = new PublicKey(TRADER_API_TIP_WALLET) +const TRADER_API_TIP_WALLET = address( + 'HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY', +) - return SystemProgram.transfer({ - fromPubkey: senderAddress, - toPubkey: tipAddress, - lamports: tipAmount, +export const createBloxrouteTipInstruction = ( + source: TransactionSigner, + tipAmount: bigint, +): Instruction => { + return getTransferSolInstruction({ + source, + destination: TRADER_API_TIP_WALLET, + amount: tipAmount, }) } diff --git a/src/service/sol/priority-fee/compute-unit-instruction.ts b/src/service/sol/priority-fee/compute-unit-instruction.ts index bba43ab7..46dacfba 100644 --- a/src/service/sol/priority-fee/compute-unit-instruction.ts +++ b/src/service/sol/priority-fee/compute-unit-instruction.ts @@ -1,54 +1,17 @@ -import { - AddressLookupTableAccount, - ComputeBudgetProgram, - PublicKey, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, -} from '@solana/web3.js' +import { getSetComputeUnitLimitInstruction } from '@solana-program/compute-budget' +import type { Instruction } from '@solana/kit' import { logger } from '../../../logger.js' -import { keyPair } from '../../wallet/index.js' -import { connection } from '../const/index.js' - -const getSimulationUnits = async ( - instructions: TransactionInstruction[], - payer: PublicKey, - lookupTables: AddressLookupTableAccount[], -): Promise => { - const testInstructions = [ - ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), - ...instructions, - ] - - const testVersionedTxn = new VersionedTransaction( - new TransactionMessage({ - instructions: testInstructions, - payerKey: payer, - recentBlockhash: PublicKey.default.toString(), - }).compileToV0Message(lookupTables), - ) - - const simulation = await connection.simulateTransaction(testVersionedTxn, { - replaceRecentBlockhash: true, - sigVerify: false, - }) - - if (simulation.value.err) { - return undefined - } - - return simulation.value.unitsConsumed -} +// TODO: Add simulation-based compute unit estimation in M2 +// For now, use a conservative default export const createComputeUnitInstruction = async ( - instructions: TransactionInstruction[], -): Promise => { - const units = - ((await getSimulationUnits(instructions, keyPair.publicKey, [])) ?? - 200_000) * 1.5 + _instructions: Instruction[], +): Promise => { + // Default to 400k units with headroom + const units = 400_000 - logger.debug(`Esitmated Compute Units: ${units}`) + logger.debug(`Compute Units (default): ${units}`) - return ComputeBudgetProgram.setComputeUnitLimit({ units }) + return getSetComputeUnitLimitInstruction({ units }) } diff --git a/src/service/sol/priority-fee/priority-fee-instruction.ts b/src/service/sol/priority-fee/priority-fee-instruction.ts index 09aeaf8f..7f7ea967 100644 --- a/src/service/sol/priority-fee/priority-fee-instruction.ts +++ b/src/service/sol/priority-fee/priority-fee-instruction.ts @@ -1,97 +1,49 @@ -import { - AddressLookupTableAccount, - ComputeBudgetProgram, - PublicKey, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, -} from '@solana/web3.js' -import base58 from 'bs58' -import { config } from '../../../config/index.js' +import { getSetComputeUnitPriceInstruction } from '@solana-program/compute-budget' +import type { Instruction } from '@solana/kit' +import { config } from '../../../config/index.js' import { logger } from '../../../logger.js' -import { programs } from '../../../main/basedbot/lib/programs.js' -import { keyPair } from '../../wallet/index.js' import { rpcFetch } from '../rpc-fetch.js' -const getDummyTransaction = ( - instructions: TransactionInstruction[], - payer: PublicKey, - lookupTables: AddressLookupTableAccount[], -): VersionedTransaction => { - const testInstructions = [ - ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }), - ...instructions, - ] - - return new VersionedTransaction( - new TransactionMessage({ - instructions: testInstructions, - payerKey: payer, - recentBlockhash: PublicKey.default.toString(), - }).compileToV0Message(lookupTables), - ) -} - export const createPriorityFeeInstruction = async ( - instructions: TransactionInstruction[], -): Promise => { - const transaction = getDummyTransaction(instructions, keyPair.publicKey, []) - let encodedTx: string | undefined - try { - encodedTx = base58.encode(transaction.serialize()) - } catch (e) { - logger.error((e as any).message) - } - + _instructions: Instruction[], +): Promise => { try { - const result = - config.sol.rpcEndpoint.includes('devnet') || - config.sol.rpcEndpoint.includes('validator') || - config.sol.rpcEndpoint.includes('localhost') - ? await rpcFetch({ - jsonrpc: '2.0', - id: 1, - method: 'getRecentPrioritizationFees', - params: [[programs.sage.programId.toBase58()]], - }) - : await rpcFetch({ - jsonrpc: '2.0', - id: 1, - method: 'getRecentPrioritizationFees', - params: { - transaction: encodedTx, - percentiles: [50, 75, 95, 100], - lookbackSlots: 10, - }, - }) + // Fetch priority fee from RPC + const result = await rpcFetch({ + jsonrpc: '2.0', + id: 1, + method: 'getRecentPrioritizationFees', + params: [[]], + }) - const feeData = (result as any).result.result as Array<{ + const feeData = (result as any).result as Array<{ slot: number prioritizationFee: number }> const microLamports = - feeData.find((f: any) => f.slot == -1)?.prioritizationFee ?? - Math.max(...feeData.map((f: any) => f.prioritizationFee)) + feeData.length > 0 + ? Math.max(...feeData.map((f) => f.prioritizationFee)) + : 5000 - // const microLamports = 5000 logger.debug(`Priority fee estimates: ${microLamports}`) const feeLimit = config.sol.feeLimit + const finalFee = + feeLimit > 0 ? Math.min(feeLimit, microLamports) : microLamports + if (feeLimit > 0 && microLamports > feeLimit) { logger.debug(`Capping fee at ${feeLimit}`) } - return ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: - feeLimit > 0 - ? Math.min(feeLimit, microLamports) - : microLamports, + + return getSetComputeUnitPriceInstruction({ + microLamports: BigInt(finalFee), }) } catch (e) { - logger.error((e as any).message) - return ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: 5000, + logger.error((e as Error).message) + return getSetComputeUnitPriceInstruction({ + microLamports: 5000n, }) } } diff --git a/src/service/sol/send-and-confirm-tx.ts b/src/service/sol/send-and-confirm-tx.ts index 4458f5ff..ec0dc54d 100644 --- a/src/service/sol/send-and-confirm-tx.ts +++ b/src/service/sol/send-and-confirm-tx.ts @@ -1,249 +1,153 @@ import { - Keypair, - LAMPORTS_PER_SOL, - PublicKey, - TransactionInstruction, - TransactionMessage, - VersionedTransaction, -} from '@solana/web3.js' -import { config } from '../../config/index.js' + appendTransactionMessageInstructions, + createTransactionMessage, + getBase64EncodedWireTransaction, + pipe, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, + signTransactionMessageWithSigners, + type Instruction, + type KeyPairSigner, + type Signature, +} from '@solana/kit' +import { lamports } from '@solana/kit' +import { config } from '../../config/index.js' import { logger } from '../../logger.js' - import { connection } from './const/index.js' import { createBloxrouteTipInstruction } from './priority-fee/bloxroute-tip-instruction.js' import { createComputeUnitInstruction } from './priority-fee/compute-unit-instruction.js' import { createPriorityFeeInstruction } from './priority-fee/priority-fee-instruction.js' -// Constants for Solana transaction size limits -const MAX_TRANSACTION_SIZE = 1232 // Maximum size of a transaction in bytes -const TRANSACTION_HEADER_SIZE = 100 // Approximate size of transaction header, adjust if needed -const SIGNATURE_SIZE = 64 // Size of a signature in bytes - const sleep = (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms) }) -type Blockhash = string -type BlockhashWithExpiryBlockHeight = Readonly<{ - blockhash: Blockhash - lastValidBlockHeight: number -}> +const confirmTx = async (signature: Signature): Promise => { + const res = await connection.rpc.getSignatureStatuses([signature]).send() -const confirmTx = async (txId: string): Promise => { - const res = await connection.getSignatureStatus(txId) + const status = res.value[0] - // logger.debug(`Signature: ${txId} with status: ${JSON.stringify(res)}`) - - if (res?.value && 'confirmationStatus' in res.value) { + if (status && status.confirmationStatus) { if ( - res.value.confirmationStatus === 'finalized' || - res.value.confirmationStatus === 'confirmed' || - res.value.confirmationStatus === 'processed' + status.confirmationStatus === 'finalized' || + status.confirmationStatus === 'confirmed' || + status.confirmationStatus === 'processed' ) { - const log = res.value.err ? logger.warn : logger.debug - - // log(`Transaction ${res.value.confirmationStatus}: ${txId} with status: ${res.value.confirmationStatus}`) - log(`Signature: ${txId} with status: ${JSON.stringify(res)}`) - - // logger.info(`https://solscan.io/tx/${txId}`) - - return txId - } - } - throw new Error('Transaction confirmation failed') -} - -export const sendAndConfirmTx = async ( - transaction: VersionedTransaction, - latestBlockHash?: BlockhashWithExpiryBlockHeight, -): Promise => { - const blockHash = latestBlockHash ?? (await connection.getLatestBlockhash()) - let blockheight = await connection.getBlockHeight() - - let txId: string | undefined - - while (blockheight <= blockHash.lastValidBlockHeight) { - blockheight = await connection.getBlockHeight() - // logger.info( - // `${blockHash.lastValidBlockHeight} - ${blockheight} = ${blockHash.lastValidBlockHeight - blockheight}`, - // ) - try { - txId = await connection.sendRawTransaction( - transaction.serialize(), - { skipPreflight: true }, + const log = status.err ? logger.warn : logger.debug + log( + `Signature: ${signature} with status: ${JSON.stringify(status)}`, ) - } catch (e) { - const message = (e as any).message as string - - const logs = (e as any).logs as string[] - - if (logs) { - logs.filter((log) => log.includes('AnchorError')).forEach( - (log) => { - logger.error(log) - }, - ) - } - - if (message.includes('has already been processed') && txId) { - await confirmTx(txId) - - return txId - } - throw e - } - - try { - await confirmTx(txId) - - return txId - } catch (_e) { - await sleep(500) + return signature } } - - throw new Error(`Transaction ${txId} failed to confirm`) -} - -const createAndSignTransaction = ( - instructions: TransactionInstruction[], - blockhash: Blockhash, - signers: Array, -): VersionedTransaction => { - const messageV0 = new TransactionMessage({ - payerKey: signers[0].publicKey, - recentBlockhash: blockhash, - instructions, - }).compileToV0Message() - const transaction = new VersionedTransaction(messageV0) - - transaction.sign(signers) - - return transaction -} - -const getInstructionSize = ( - instructions: TransactionInstruction[], - signer: Keypair, -): number => { - const messageV0 = new TransactionMessage({ - payerKey: signer.publicKey, - recentBlockhash: PublicKey.default.toBase58(), - instructions, - }).compileToV0Message() - - // Serialize the message and return its length - return new VersionedTransaction(messageV0).serialize().byteLength - // return messageV0.serialize().length -} -const getOptimalInstructionChunk = ( - instructions: TransactionInstruction[], - maxSize: number, - signer: Keypair, -): TransactionInstruction[] => { - for (let i = 0; i < instructions.length; ++i) { - const instructionSize = getInstructionSize( - instructions.slice(0, i + 1), - signer, - ) - - logger.debug( - `Transaction with ${i + 1} instructions has size ${instructionSize}`, - ) - - if (instructionSize > maxSize) { - return instructions.slice(0, i) - } - } - - return instructions + throw new Error('Transaction confirmation failed') } export const sendAndConfirmInstructions = - (signers: Array) => - async (instructionArray: TransactionInstruction[]): Promise => { + (signer: KeyPairSigner) => + async (instructions: Instruction[]): Promise => { const maxRetries = 10 - let instructions = instructionArray - const results: string[] = [] - - while (instructions.length > 0) { - const availableSize = - MAX_TRANSACTION_SIZE - TRANSACTION_HEADER_SIZE - SIGNATURE_SIZE - - const chunk = getOptimalInstructionChunk( - instructions, - availableSize, - signers[0], - ) - - for (let i = 0; i < maxRetries; ++i) { - const [ - latestBlockHash, - priorityFeeInstruction, - computeUnitsInstruction, - ] = await Promise.all([ - connection.getLatestBlockhash(), - createPriorityFeeInstruction(chunk), - createComputeUnitInstruction(chunk), - ]) - - const txInstructions = [ - computeUnitsInstruction, - priorityFeeInstruction, + const results: Signature[] = [] + + // For now, send all instructions in one transaction + // TODO: Add chunking logic for large instruction sets + for (let i = 0; i < maxRetries; ++i) { + try { + const [latestBlockhash, priorityFeeIx, computeUnitsIx] = + await Promise.all([ + connection.rpc.getLatestBlockhash().send(), + createPriorityFeeInstruction(instructions), + createComputeUnitInstruction(instructions), + ]) + + const allInstructions: Instruction[] = [ + computeUnitsIx, + priorityFeeIx, ...(config.sol.bloxroute ? [ createBloxrouteTipInstruction( - signers[0].publicKey, - 0.0001 * LAMPORTS_PER_SOL, + signer, + lamports(100_000n), // 0.0001 SOL ), ] : []), - ...chunk, + ...instructions, ] - const transaction = createAndSignTransaction( - txInstructions, - latestBlockHash.blockhash, - signers, + const transactionMessage = pipe( + createTransactionMessage({ version: 0 }), + (tx) => setTransactionMessageFeePayerSigner(signer, tx), + (tx) => + setTransactionMessageLifetimeUsingBlockhash( + latestBlockhash.value, + tx, + ), + (tx) => + appendTransactionMessageInstructions( + allInstructions, + tx, + ), ) - const rawTransaction = transaction.serialize() + const signedTransaction = + await signTransactionMessageWithSigners(transactionMessage) - if (rawTransaction.length > MAX_TRANSACTION_SIZE) { - throw new Error( - `Transaction too large: ${rawTransaction.length} bytes`, - ) - } + const serialized = + getBase64EncodedWireTransaction(signedTransaction) - try { - logger.debug( - Buffer.from(transaction.serialize()).toString('base64'), - ) - const result = await sendAndConfirmTx( - transaction, - latestBlockHash, - ) + logger.debug(`Transaction: ${serialized}`) - results.push(result) - instructions = instructions.slice(chunk.length) - break // Exit retry loop if successful - } catch (e) { - const message = (e as any).message as string + const signature = await connection.rpc + .sendTransaction(serialized, { + encoding: 'base64', + skipPreflight: true, + }) + .send() - logger.error( - `Transaction failed: ${message}, retrying... (${i + 1}/${maxRetries})`, - ) + // Poll for confirmation + let confirmed = false + const startSlot = latestBlockhash.value.lastValidBlockHeight + + while (!confirmed) { + const currentSlot = await connection.rpc + .getBlockHeight() + .send() + + if (currentSlot > startSlot + 150n) { + throw new Error('Transaction expired') + } - if (i === maxRetries - 1) { - throw new Error( - `Transaction failed after ${maxRetries} attempts`, - ) + try { + await confirmTx(signature) + confirmed = true + results.push(signature) + } catch { + await sleep(500) } } + + return results + } catch (e) { + const message = (e as Error).message + + logger.error( + `Transaction failed: ${message}, retrying... (${i + 1}/${maxRetries})`, + ) + + if (i === maxRetries - 1) { + throw new Error( + `Transaction failed after ${maxRetries} attempts: ${message}`, + ) + } + + await sleep(1000) } } return results } + +// Legacy export for compatibility +export const sendAndConfirmTx = sendAndConfirmInstructions diff --git a/src/service/wallet/init-keypair.ts b/src/service/wallet/init-keypair.ts index 75cf458e..419b54f8 100644 --- a/src/service/wallet/init-keypair.ts +++ b/src/service/wallet/init-keypair.ts @@ -1,4 +1,10 @@ -import { Keypair, PublicKey } from '@solana/web3.js' +import { + address, + createKeyPairFromBytes, + createSignerFromKeyPair, + type Address, + type KeyPairSigner, +} from '@solana/kit' import { mnemonicToSeedSync } from 'bip39' import { derivePath } from 'ed25519-hd-key' import { readFileSync } from 'node:fs' @@ -6,56 +12,73 @@ import { readFileSync } from 'node:fs' import { config } from '../../config/index.js' import { logger } from '../../logger.js' -export const loadKeypairFromFile = (filePath: string): Keypair => { +export const loadKeypairFromFile = async ( + filePath: string, +): Promise => { const loadedKeyBytes = Uint8Array.from( JSON.parse(readFileSync(filePath, 'utf8')), ) - return Keypair.fromSecretKey(loadedKeyBytes) + const keyPair = await createKeyPairFromBytes(loadedKeyBytes) + return createSignerFromKeyPair(keyPair) } -const initKeypairBySecretKey = (key: number[], pubKey: PublicKey): Keypair => { - const keypair = Keypair.fromSecretKey(new Uint8Array(key)) +const initKeypairBySecretKey = async ( + key: number[], + pubKey: Address, +): Promise => { + const keyPair = await createKeyPairFromBytes(new Uint8Array(key)) + const signer = await createSignerFromKeyPair(keyPair) - if (keypair.publicKey.equals(pubKey)) { - logger.info(`Found keypair for ${pubKey.toBase58()}`) - - return keypair + if (signer.address === pubKey) { + logger.info(`Found keypair for ${pubKey}`) + return signer } throw new Error('PubKey does not match Private key') } -const initKeypairByMnemonic = ( +const initKeypairByMnemonic = async ( mnemonic: string, - pubKey: PublicKey, -): Keypair => { + pubKey: Address, +): Promise => { const seed = mnemonicToSeedSync(mnemonic, '') for (let i = 0; i < 1000; ++i) { const path = `m/44'/501'/${i}'/0'` - const keypair = Keypair.fromSeed( - derivePath(path, seed.toString('hex')).key, - ) + const derivedKey = derivePath(path, seed.toString('hex')).key + // Create 64-byte secret key (32 private + 32 public placeholder) + const secretKey = new Uint8Array(64) + secretKey.set(derivedKey) - logger.debug(`${path} => ${keypair.publicKey.toBase58()}`) + const keyPair = await createKeyPairFromBytes(secretKey) + const signer = await createSignerFromKeyPair(keyPair) - if (keypair.publicKey.equals(pubKey)) { - logger.info(`Found keypair for ${pubKey.toBase58()} at ${path}`) + logger.debug(`${path} => ${signer.address}`) - return keypair + if (signer.address === pubKey) { + logger.info(`Found keypair for ${pubKey} at ${path}`) + return signer } } throw new Error('PubKey not found in derivation Path') } -export const keyPair = - config.user.keyMode === 'mnemonic' - ? initKeypairByMnemonic( - config.user.mnemonic, - new PublicKey(config.user.pubKey), - ) - : initKeypairBySecretKey( - config.user.secretKey, - new PublicKey(config.user.pubKey), - ) +// Initialize keypair - this returns a Promise now +const initKeyPair = async (): Promise => { + const pubKey = address(config.user.pubKey) + + if (config.user.keyMode === 'mnemonic') { + return initKeypairByMnemonic(config.user.mnemonic, pubKey) + } + return initKeypairBySecretKey(config.user.secretKey, pubKey) +} + +// Export as a promise that will be awaited at startup +export const keyPairPromise = initKeyPair() + +// For backwards compatibility during migration - will be set after init +export let keyPair: KeyPairSigner +keyPairPromise.then((kp) => { + keyPair = kp +}) diff --git a/src/service/wallet/resource.ts b/src/service/wallet/resource.ts index 8883674e..d15511a2 100644 --- a/src/service/wallet/resource.ts +++ b/src/service/wallet/resource.ts @@ -1,13 +1,12 @@ -import { PublicKey } from '@solana/web3.js' - +import { address, type Address } from '@solana/kit' import { config } from '../../config/index.js' -export type Resource = PublicKey +export type Resource = Address export const resource = { - atlas: new PublicKey(config.sol.atlasMint), - food: new PublicKey(config.sol.foodMint), - fuel: new PublicKey(config.sol.fuelMint), - ammo: new PublicKey(config.sol.ammoMint), - tool: new PublicKey(config.sol.toolMint), + atlas: address(config.sol.atlasMint), + food: address(config.sol.foodMint), + fuel: address(config.sol.fuelMint), + ammo: address(config.sol.ammoMint), + tool: address(config.sol.toolMint), } diff --git a/tsup.config.ts b/tsup.config.ts index 2ddfb79b..49394ba0 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -2,7 +2,6 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: [ - 'src/main/fleetbot/index.ts', 'src/main/basedbot/index.ts', 'src/main/airdrop/index.ts', 'src/main/migrate/index.ts',