diff --git a/src/main/basedbot/basedbot.ts b/src/main/basedbot/basedbot.ts index 4bfab292..ce501fc2 100644 --- a/src/main/basedbot/basedbot.ts +++ b/src/main/basedbot/basedbot.ts @@ -1,8 +1,16 @@ +import { + getAssociatedTokenAddressSync, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import { getParsedTokenAccountsByOwner } from '@staratlas/data-source' +import BN from 'bn.js' + import { Sentry } from '../../sentry' import { config } from '../../config' import { logger } from '../../logger' import { sleep } from '../../service/sleep' +import { connection } from '../../service/sol' import { keyPair } from '../../service/wallet' import { mineBiomass } from './fsm/configs/mine-biomass' @@ -18,6 +26,7 @@ import { mineSilicia } from './fsm/configs/mine-silicia' import { mineTitaniumOre } from './fsm/configs/mine-titanium-ore' import { createMiningStrategy } from './fsm/mine' import { Strategy } from './fsm/strategy' +import { depositCargo } from './lib/sage/act/deposit-cargo' import { settleFleet } from './lib/sage/state/settle-fleet' import { getPlayerContext, Player } from './lib/sage/state/user-account' import { @@ -30,6 +39,7 @@ import { mineableByCoordinates, WorldMap, } from './lib/sage/state/world-map' +// eslint-disable-next-line import/max-dependencies import { Coordinates } from './lib/util/coordinates' // eslint-disable-next-line require-await @@ -69,6 +79,48 @@ const applyStrategy = ( return strategy.send(fleetInfo) } +const importR4 = async (player: Player): Promise => { + await Promise.all( + [ + player.game.data.mints.food, + player.game.data.mints.ammo, + player.game.data.mints.fuel, + player.game.data.mints.repairKit, + ].map(async (mint) => { + //TODO: Make it easier to get the amount of a token + // This is being used in multiple places + const allTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + player.signer.publicKey(), + TOKEN_PROGRAM_ID, + ) + + const sourceTokenAccount = getAssociatedTokenAddressSync( + mint, + player.signer.publicKey(), + true, + ) + const [mintTokenAccount] = allTokenAccounts.filter((it) => + it.address.equals(sourceTokenAccount), + ) + const amountAtOrigin = new BN(mintTokenAccount.amount.toString()) + + if (amountAtOrigin.gtn(0)) { + logger.info( + `Importing R4 for ${mint.toBase58()}: ${amountAtOrigin}`, + ) + + await depositCargo( + player, + player.homeStarbase, + mint, + amountAtOrigin, + ) + } + }), + ) +} + const basedbot = async (botConfig: BotConfig) => { const { player, map } = botConfig const fleets = await getUserFleets(player) @@ -76,6 +128,8 @@ const basedbot = async (botConfig: BotConfig) => { fleets.map((f) => getFleetInfo(f, player, map)), ) + await importR4(player) + await Promise.all( fleetInfos.map((fleetInfo) => settleFleet(fleetInfo, player, map)), ) diff --git a/src/main/basedbot/lib/sage/act/deposit-cargo.ts b/src/main/basedbot/lib/sage/act/deposit-cargo.ts new file mode 100644 index 00000000..40da5edf --- /dev/null +++ b/src/main/basedbot/lib/sage/act/deposit-cargo.ts @@ -0,0 +1,98 @@ +import { + getAssociatedTokenAddressSync, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import { + createAssociatedTokenAccountIdempotent, + getParsedTokenAccountsByOwner, + InstructionReturn, + ixReturnsToIxs, +} from '@staratlas/data-source' +import { Starbase } from '@staratlas/sage' +import BN from 'bn.js' + +import { connection } from '../../../../../service/sol' +import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' +import { programs } from '../../programs' +import { depositCargoIx } from '../ix/import-cargo' +import { getCargoType } from '../state/cargo-types' +import { + getCargoPodsForStarbasePlayer, + getStarbasePlayer, +} from '../state/starbase-player' +import { Player } from '../state/user-account' + +export const depositCargo = async ( + player: Player, + starbase: Starbase, + mint: PublicKey, + amount: BN, + // eslint-disable-next-line max-params +): Promise => { + const instructions: InstructionReturn[] = [] + + const starbasePlayer = await getStarbasePlayer(player, starbase, programs) + + const sourceTokenAccount = getAssociatedTokenAddressSync( + mint, + player.signer.publicKey(), + ) + + const cargoPodTo = await getCargoPodsForStarbasePlayer( + starbasePlayer, + programs, + ) + const destinationTokenAccount = getAssociatedTokenAddressSync( + mint, + cargoPodTo.key, + true, + ) + + instructions.push( + createAssociatedTokenAccountIdempotent(mint, cargoPodTo.key, true) + .instructions, + ) + + const cargoType = getCargoType(player.cargoTypes, player.game, mint) + + const allTokenAccounts = await getParsedTokenAccountsByOwner( + connection, + player.signer.publicKey(), + TOKEN_PROGRAM_ID, + ) + + const [cargoTokenAccount] = allTokenAccounts.filter((it) => + it.address.equals(sourceTokenAccount), + ) + + if (!cargoTokenAccount) { + throw new Error('Cargo token account not found') + } + const amountAtOrigin = new BN(cargoTokenAccount.amount.toString()) + + if (!cargoTokenAccount) { + throw new Error('Cargo not found at origin') + } + if (amountAtOrigin.lt(new BN(amount))) { + throw new Error('Not enough cargo available at origin') + } + + instructions.push( + depositCargoIx( + player, + starbase, + starbasePlayer, + cargoPodTo.key, + sourceTokenAccount, + destinationTokenAccount, + cargoType.key, + programs, + amount, + ), + ) + + await sendAndConfirmInstructions( + await ixReturnsToIxs(instructions, player.signer), + ) +} diff --git a/src/main/basedbot/lib/sage/act/exit-respawn.ts b/src/main/basedbot/lib/sage/act/exit-respawn.ts index e1f05ca9..be59d003 100644 --- a/src/main/basedbot/lib/sage/act/exit-respawn.ts +++ b/src/main/basedbot/lib/sage/act/exit-respawn.ts @@ -1,18 +1,17 @@ import { ixReturnsToIxs } from '@staratlas/data-source' +import { Starbase } from '@staratlas/sage' import { logger } from '../../../../../logger' import { sendAndConfirmInstructions } from '../../../../../service/sol/send-and-confirm-tx' import { programs } from '../../programs' -import { Coordinates } from '../../util/coordinates' import { exitRespawnIx } from '../ix/exit-respawn' -import { starbaseByCoordinates } from '../state/starbase-by-coordinates' import { getStarbasePlayer } from '../state/starbase-player' import { Player } from '../state/user-account' import { FleetInfo } from '../state/user-fleets' export const exitRespawn = async ( fleetInfo: FleetInfo, - location: Coordinates, + starbase: Starbase, player: Player, ): Promise => { const { fleet } = fleetInfo @@ -23,12 +22,6 @@ export const exitRespawn = async ( return } - const starbase = await starbaseByCoordinates(location) - - if (!starbase) { - throw new Error(`No starbase found at ${location}`) - } - const starbasePlayer = await getStarbasePlayer(player, starbase, programs) const ix = exitRespawnIx( diff --git a/src/main/basedbot/lib/sage/ix/import-cargo.ts b/src/main/basedbot/lib/sage/ix/import-cargo.ts new file mode 100644 index 00000000..52cd9323 --- /dev/null +++ b/src/main/basedbot/lib/sage/ix/import-cargo.ts @@ -0,0 +1,40 @@ +import { PublicKey } from '@solana/web3.js' +import { InstructionReturn } from '@staratlas/data-source' +import { Starbase, StarbasePlayer } from '@staratlas/sage' +import BN from 'bn.js' + +import { StarAtlasPrograms } from '../../programs' +import { Player } from '../state/user-account' + +export const depositCargoIx = ( + player: Player, + starbase: Starbase, + starbasePlayer: StarbasePlayer, + cargoPodTo: PublicKey, + tokenFrom: PublicKey, + tokenTo: PublicKey, + cargoType: PublicKey, + programs: StarAtlasPrograms, + amount: BN, + // eslint-disable-next-line max-params +): InstructionReturn => + StarbasePlayer.depositCargoToGame( + programs.sage, + programs.cargo, + starbasePlayer.key, + player.signer, + player.profile.key, + player.profileFaction.key, + starbase.key, + cargoPodTo, + cargoType, + player.game.data.cargo.statsDefinition, + tokenFrom, + tokenTo, + player.game.key, + player.game.data.gameState, + { + amount, + keyIndex: 0, + }, + ) diff --git a/src/main/basedbot/lib/sage/state/settle-fleet.ts b/src/main/basedbot/lib/sage/state/settle-fleet.ts index f48670dc..d0a744b5 100644 --- a/src/main/basedbot/lib/sage/state/settle-fleet.ts +++ b/src/main/basedbot/lib/sage/state/settle-fleet.ts @@ -1,6 +1,5 @@ import { now } from '../../../../../dayjs' import { logger } from '../../../../../logger' -import { Coordinates } from '../../util/coordinates' import { endMine } from '../act/end-mine' import { endMove } from '../act/end-move' import { exitRespawn } from '../act/exit-respawn' @@ -47,12 +46,7 @@ export const settleFleet = async ( const { ETA } = fleetInfo.fleetState.data if (ETA.isBefore(now())) { - // TODO: Respawn at Home Base based on Faction - await exitRespawn( - fleetInfo, - Coordinates.fromNumber(-40, 30), - player, - ) + await exitRespawn(fleetInfo, player.homeStarbase, player) } break } diff --git a/src/main/basedbot/lib/sage/state/user-account.ts b/src/main/basedbot/lib/sage/state/user-account.ts index 56d2e4a3..2b0f390c 100644 --- a/src/main/basedbot/lib/sage/state/user-account.ts +++ b/src/main/basedbot/lib/sage/state/user-account.ts @@ -8,13 +8,15 @@ import { import { PlayerProfile } from '@staratlas/player-profile' import { PointsModifier } from '@staratlas/points' import { ProfileFactionAccount } from '@staratlas/profile-faction' -import { Game } from '@staratlas/sage' +import { Game, Starbase } from '@staratlas/sage' import { connection } from '../../../../../service/sol' import { programs, xpCategoryIds } from '../../programs' +import { Coordinates } from '../../util/coordinates' import { getCargoType, getCargoTypes } from './cargo-types' import { sageGame } from './game' +import { starbaseByCoordinates } from './starbase-by-coordinates' export type XpAccounts = { councilRank: XpAccount @@ -38,6 +40,7 @@ export type Player = { xpAccounts: XpAccounts signer: AsyncSigner game: Game + homeStarbase: Starbase cargoTypes: Array fuelCargoType: CargoType foodCargoType: CargoType @@ -164,6 +167,29 @@ export const getPlayerContext = async ( const game = await sageGame() const cargoTypes = await getCargoTypes() + let homeCoords + + //TODO: add correct Coordinates for each faction + switch (profileFaction.data.data.faction) { + case 0: + homeCoords = Coordinates.fromNumber(0, 0) + break + case 1: + homeCoords = Coordinates.fromNumber(0, 0) + break + case 2: + homeCoords = Coordinates.fromNumber(-40, 30) + break + default: + throw new Error('Unknown faction') + } + + const homeStarbase = await starbaseByCoordinates(homeCoords) + + if (!homeStarbase) { + throw new Error('No home starbase found') + } + return { profile: profile.data, profileFaction: profileFaction.data, @@ -172,14 +198,9 @@ export const getPlayerContext = async ( signer: keypairToAsyncSigner(signer), game, 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), } } - -// export const getAccountName = (profileProgram: StarAtlasProgram, account: PlayerProfile): string => { -// const playerNameAddress = PlayerName.findAddress(profileProgram.program as any, account.key) -// const decodedPlayerName = PlayerName.decodeData({ -// accountInfo: account.accountInfo, accountId: playerNameAddress }, profileProgram.program as any) -// }