From 35a6e1a19ae92a156d6db44d578b746fdb386a0c Mon Sep 17 00:00:00 2001 From: Al366io Date: Sat, 20 Jan 2024 01:49:32 +0100 Subject: [PATCH 1/6] Update wallet configuration and dependencies --- .env.copy | 2 ++ README.md | 2 +- buy.ts | 11 ++++++++-- package-lock.json | 56 +++++++++++++++++++++++++++++++++++++++++------ package.json | 2 ++ wallet.json | 1 - 6 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 .env.copy delete mode 100644 wallet.json diff --git a/.env.copy b/.env.copy new file mode 100644 index 0000000..bc2bf03 --- /dev/null +++ b/.env.copy @@ -0,0 +1,2 @@ +PRIVATE_KEY= +``` \ No newline at end of file diff --git a/README.md b/README.md index 6afebc4..66e4dff 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ In order to run the script you need to: - Transfer some SOL to it. - Convert some SOL to USDC. - We need USDC because the script is buying USDC pairs. -- Export wallet private key and paste it into: `wallet.json` +- Set your Private Key in the .env file (remove the .copy from the file name when done) - Modify the buy.ts file and enter your RPC endpoint - Find line where it says: `ENTER RPC ENDPOINT HERE` and `ENTER RPC WEBSOCKET ENDPOINT HERE` and replace it with your endpoint diff --git a/buy.ts b/buy.ts index 501efa5..1507f2a 100644 --- a/buy.ts +++ b/buy.ts @@ -14,7 +14,6 @@ import { TransactionMessage, VersionedTransaction, } from '@solana/web3.js'; -import secret from './wallet.json'; import { getAllAccountsV4, getTokenAccounts, @@ -26,6 +25,9 @@ import { retry } from './utils'; import { USDC_AMOUNT, USDC_TOKEN_ID } from './common'; import { getAllMarketsV3 } from './market'; import pino from 'pino'; +import dotenv from 'dotenv'; +import bs58 from "bs58"; +dotenv.config(); const transport = pino.transport({ targets: [ @@ -80,9 +82,14 @@ let existingTokenAccounts: Map = new Map< let wallet: Keypair; let usdcTokenKey: PublicKey; +const PRIVATE_KEY: string = process.env.PRIVATE_KEY || ''; async function init(): Promise { - wallet = Keypair.fromSecretKey(new Uint8Array(secret)); + if (!PRIVATE_KEY) { + console.error('PRIVATE_KEY is not set'); + process.exit(1); + } + wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); logger.info(`Wallet Address: ${wallet.publicKey.toString()}`); const allLiquidityPools = await getAllAccountsV4(solanaConnection); existingLiquidityPools = new Set( diff --git a/package-lock.json b/package-lock.json index 3a4acfc..91baa92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "@solana/spl-token": "^0.3.11", "bigint-buffer": "^1.1.5", "bn.js": "^5.2.1", + "bs58": "^5.0.0", + "dotenv": "^16.3.2", "pino": "^8.17.2", "pino-pretty": "^10.3.1", "pino-std-serializers": "^6.2.2" @@ -248,6 +250,22 @@ "superstruct": "^0.14.2" } }, + "node_modules/@solana/web3.js/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@solana/web3.js/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -375,12 +393,9 @@ } }, "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -456,7 +471,15 @@ "text-encoding-utf-8": "^1.0.2" } }, - "node_modules/bs58": { + "node_modules/borsh/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/borsh/node_modules/bs58": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", @@ -464,6 +487,14 @@ "base-x": "^3.0.2" } }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dependencies": { + "base-x": "^4.0.0" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -573,6 +604,17 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", + "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", diff --git a/package.json b/package.json index f0279cb..c634a68 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "@solana/spl-token": "^0.3.11", "bigint-buffer": "^1.1.5", "bn.js": "^5.2.1", + "bs58": "^5.0.0", + "dotenv": "^16.3.2", "pino": "^8.17.2", "pino-pretty": "^10.3.1", "pino-std-serializers": "^6.2.2" diff --git a/wallet.json b/wallet.json deleted file mode 100644 index 0637a08..0000000 --- a/wallet.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file From 4d204bf251390d3817aa37602a7565ba4543100a Mon Sep 17 00:00:00 2001 From: Al366io Date: Sat, 20 Jan 2024 01:59:58 +0100 Subject: [PATCH 2/6] Add logic to use env variables --- .env.copy | 2 ++ buy.ts | 22 ++++++++++++---------- utils/utils.ts | 13 +++++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/.env.copy b/.env.copy index bc2bf03..4020a4d 100644 --- a/.env.copy +++ b/.env.copy @@ -1,2 +1,4 @@ PRIVATE_KEY= +RPC_ENDPOINT= +RPC_WEBSOCKET_ENDPOINT= ``` \ No newline at end of file diff --git a/buy.ts b/buy.ts index 1507f2a..3e25751 100644 --- a/buy.ts +++ b/buy.ts @@ -21,13 +21,11 @@ import { RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, OPENBOOK_PROGRAM_ID, } from './liquidity'; -import { retry } from './utils'; +import { retry, retrieveEnvVariable } from './utils'; import { USDC_AMOUNT, USDC_TOKEN_ID } from './common'; import { getAllMarketsV3 } from './market'; import pino from 'pino'; -import dotenv from 'dotenv'; import bs58 from "bs58"; -dotenv.config(); const transport = pino.transport({ targets: [ @@ -60,12 +58,16 @@ export const logger = pino( ); const network = 'mainnet-beta'; +const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); +const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger); + +if (!RPC_ENDPOINT || !RPC_WEBSOCKET_ENDPOINT) { + logger.error('RPC_ENDPOINT / RPC_WEBSOCKET_ENDPOINT is not set'); + process.exit(1); +} const solanaConnection = new Connection( - 'ENTER RPC ENDPOINT HERE', - { - wsEndpoint: - 'ENTER RPC WEBSOCKET ENDPOINT HERE', - }, + RPC_ENDPOINT, + {wsEndpoint: RPC_WEBSOCKET_ENDPOINT}, ); export type MinimalTokenAccountData = { @@ -82,11 +84,11 @@ let existingTokenAccounts: Map = new Map< let wallet: Keypair; let usdcTokenKey: PublicKey; -const PRIVATE_KEY: string = process.env.PRIVATE_KEY || ''; +const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); async function init(): Promise { if (!PRIVATE_KEY) { - console.error('PRIVATE_KEY is not set'); + logger.error('PRIVATE_KEY is not set'); process.exit(1); } wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); diff --git a/utils/utils.ts b/utils/utils.ts index 6d241c1..68b2aeb 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -1,3 +1,7 @@ +import { Logger } from "pino"; +import dotenv from 'dotenv'; +dotenv.config(); + /** * Runs the function `fn` * and retries automatically if it fails. @@ -23,3 +27,12 @@ export const retry = async ( }; export const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const retrieveEnvVariable = (variableName: string, logger: Logger) => { + const variable = process.env[variableName] || ''; + if (!variable) { + logger.error(`${variableName} is not set`); + process.exit(1); + } + return variable; +} From 7e6c68309b9d5eb452ff4a88527f5115f7d50096 Mon Sep 17 00:00:00 2001 From: Al366io Date: Sat, 20 Jan 2024 02:01:05 +0100 Subject: [PATCH 3/6] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 66e4dff..c254f75 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,6 @@ In order to run the script you need to: - Transfer some SOL to it. - Convert some SOL to USDC. - We need USDC because the script is buying USDC pairs. -- Set your Private Key in the .env file (remove the .copy from the file name when done) -- Modify the buy.ts file and enter your RPC endpoint - - Find line where it says: `ENTER RPC ENDPOINT HERE` and `ENTER RPC WEBSOCKET ENDPOINT HERE` - and replace it with your endpoint +- Set your PRIVATE_KEY, RPC_ENDPOINT and RPC_WEBSOCKET_ENDPOINT in the .env file (remove the .copy from the file name when done) - Install dependencies by typing: `npm install` - Run the script by typing: `npm run buy` in terminal From 0720e868ca1c90fba6709e8054c0c5cc416901a5 Mon Sep 17 00:00:00 2001 From: Al366io Date: Sat, 20 Jan 2024 02:01:57 +0100 Subject: [PATCH 4/6] remove unused code --- buy.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/buy.ts b/buy.ts index 3e25751..d11cc99 100644 --- a/buy.ts +++ b/buy.ts @@ -61,10 +61,6 @@ const network = 'mainnet-beta'; const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger); -if (!RPC_ENDPOINT || !RPC_WEBSOCKET_ENDPOINT) { - logger.error('RPC_ENDPOINT / RPC_WEBSOCKET_ENDPOINT is not set'); - process.exit(1); -} const solanaConnection = new Connection( RPC_ENDPOINT, {wsEndpoint: RPC_WEBSOCKET_ENDPOINT}, From 1ee44996c461d780dcc4557eacc73a3feedd7de3 Mon Sep 17 00:00:00 2001 From: Al366io Date: Sat, 20 Jan 2024 02:02:37 +0100 Subject: [PATCH 5/6] remove unused code --- buy.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/buy.ts b/buy.ts index d11cc99..e927963 100644 --- a/buy.ts +++ b/buy.ts @@ -83,10 +83,6 @@ let usdcTokenKey: PublicKey; const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); async function init(): Promise { - if (!PRIVATE_KEY) { - logger.error('PRIVATE_KEY is not set'); - process.exit(1); - } wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); logger.info(`Wallet Address: ${wallet.publicKey.toString()}`); const allLiquidityPools = await getAllAccountsV4(solanaConnection); From 9ee545d59b4d6afd0f0160384674567bb3321459 Mon Sep 17 00:00:00 2001 From: Al366io Date: Mon, 22 Jan 2024 12:51:17 +0100 Subject: [PATCH 6/6] Refactors / Cleaning --- buy.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/buy.ts b/buy.ts index e927963..be53e73 100644 --- a/buy.ts +++ b/buy.ts @@ -25,7 +25,7 @@ import { retry, retrieveEnvVariable } from './utils'; import { USDC_AMOUNT, USDC_TOKEN_ID } from './common'; import { getAllMarketsV3 } from './market'; import pino from 'pino'; -import bs58 from "bs58"; +import bs58 from 'bs58'; const transport = pino.transport({ targets: [ @@ -50,7 +50,7 @@ export const logger = pino( { redact: ['poolKeys'], serializers: { - error: pino.stdSerializers.err + error: pino.stdSerializers.err, }, base: undefined, }, @@ -59,13 +59,15 @@ export const logger = pino( const network = 'mainnet-beta'; const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); -const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger); - -const solanaConnection = new Connection( - RPC_ENDPOINT, - {wsEndpoint: RPC_WEBSOCKET_ENDPOINT}, +const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable( + 'RPC_WEBSOCKET_ENDPOINT', + logger, ); +const solanaConnection = new Connection(RPC_ENDPOINT, { + wsEndpoint: RPC_WEBSOCKET_ENDPOINT, +}); + export type MinimalTokenAccountData = { mint: PublicKey; address: PublicKey; @@ -168,14 +170,20 @@ async function buy(accountId: PublicKey, accountData: any): Promise { solanaConnection.getLatestBlockhash({ commitment: 'processed' }), ]); + const baseMint = poolKeys.baseMint.toString(); + const tokenAccountOut = + existingTokenAccounts && existingTokenAccounts.get(baseMint)?.address; + + if (!tokenAccountOut) { + logger.info(`No token account for ${baseMint}`); + return; + } const { innerTransaction, address } = Liquidity.makeSwapFixedInInstruction( { poolKeys, userKeys: { tokenAccountIn: usdcTokenKey, - tokenAccountOut: existingTokenAccounts.get( - poolKeys.baseMint.toString(), - )!.address, + tokenAccountOut: tokenAccountOut, owner: wallet.publicKey, }, amountIn: USDC_AMOUNT * 1000000,