@clonegod/ttd-bsc-send-tx 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +40 -0
  2. package/dist/_48club/http.d.ts +9 -0
  3. package/dist/_48club/http.js +69 -0
  4. package/dist/_48club/index.d.ts +4 -0
  5. package/dist/_48club/index.js +11 -0
  6. package/dist/_48club/member.d.ts +1 -0
  7. package/dist/_48club/member.js +21 -0
  8. package/dist/_48club/sp_signature.d.ts +4 -0
  9. package/dist/_48club/sp_signature.js +27 -0
  10. package/dist/_48club/ws.d.ts +9 -0
  11. package/dist/_48club/ws.js +63 -0
  12. package/dist/blockrazor/index.d.ts +7 -0
  13. package/dist/blockrazor/index.js +67 -0
  14. package/dist/bloxroute/index.d.ts +1 -0
  15. package/dist/bloxroute/index.js +5 -0
  16. package/dist/bloxroute/ws.d.ts +10 -0
  17. package/dist/bloxroute/ws.js +65 -0
  18. package/dist/constants.d.ts +5 -0
  19. package/dist/constants.js +9 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +7 -0
  22. package/dist/rpc/index.d.ts +6 -0
  23. package/dist/rpc/index.js +47 -0
  24. package/dist/transaction_sender.d.ts +9 -0
  25. package/dist/transaction_sender.js +116 -0
  26. package/dist/types/index.d.ts +8 -0
  27. package/dist/types/index.js +2 -0
  28. package/dist/ws_client.d.ts +6 -0
  29. package/dist/ws_client.js +18 -0
  30. package/dist/ws_server.d.ts +1 -0
  31. package/dist/ws_server.js +93 -0
  32. package/package.json +29 -0
  33. package/scripts/deploy.sh +29 -0
  34. package/scripts/start-ttd-bsc-send-tx-ws.sh +24 -0
  35. package/src/_48club/http.ts +60 -0
  36. package/src/_48club/index.ts +4 -0
  37. package/src/_48club/member.ts +19 -0
  38. package/src/_48club/sp_signature.ts +28 -0
  39. package/src/_48club/ws.ts +47 -0
  40. package/src/blockrazor/index.ts +57 -0
  41. package/src/bloxroute/index.ts +1 -0
  42. package/src/bloxroute/ws.ts +53 -0
  43. package/src/constants.ts +5 -0
  44. package/src/index.ts +2 -0
  45. package/src/rpc/index.ts +42 -0
  46. package/src/transaction_sender.ts +123 -0
  47. package/src/types/index.ts +8 -0
  48. package/src/ws_client.ts +22 -0
  49. package/src/ws_server.ts +104 -0
  50. package/tsconfig.json +25 -0
@@ -0,0 +1,8 @@
1
+ export interface BscSendTxRequestType {
2
+ builder: '48club' | 'bloxroute';
3
+ pair: string;
4
+ mainTx: string;
5
+ tipTx: string;
6
+ _48spSign: string;
7
+ blockNumber: number;
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ export declare class BscSendTxProxy {
2
+ private wsUrl;
3
+ private ws;
4
+ constructor();
5
+ sendTransaction(builder: '48club' | 'bloxroute', pair: string, mainTx: string, tipTx: string, _48spSign?: string): void;
6
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BscSendTxProxy = void 0;
4
+ const ttd_core_1 = require("@clonegod/ttd-core");
5
+ class BscSendTxProxy {
6
+ constructor() {
7
+ const host = process.env.SEND_TX_WS_HOST || '127.0.0.1';
8
+ this.wsUrl = `ws://${host}:${ttd_core_1.SERVICE_PORT.SEND_TX_WS}/bsc/send_tx`;
9
+ this.ws = new ttd_core_1.WebSocketClient(this.wsUrl);
10
+ this.ws.onOpen(() => console.log('BscSendTxProxy connected:', this.wsUrl));
11
+ this.ws.onMessage((message) => console.log('BscSendTxProxy response:', message));
12
+ this.ws.connect();
13
+ }
14
+ sendTransaction(builder, pair, mainTx, tipTx, _48spSign = null) {
15
+ this.ws.send(JSON.stringify({ builder, pair, mainTx, tipTx, _48spSign }));
16
+ }
17
+ }
18
+ exports.BscSendTxProxy = BscSendTxProxy;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require('dotenv').config();
13
+ const ttd_core_1 = require("@clonegod/ttd-core");
14
+ const ws_1 = require("ws");
15
+ const _48club_1 = require("./_48club");
16
+ const bloxroute_1 = require("./bloxroute");
17
+ class BscSendTxWebSocketServer {
18
+ constructor(appConfig, serverPort = ttd_core_1.SERVICE_PORT.SEND_TX_WS) {
19
+ this.wss = null;
20
+ this.latestBlockNumber = 0;
21
+ this._48clubWsSender = null;
22
+ this.bloxrouteWsSender = null;
23
+ this.appConfig = appConfig;
24
+ this.serverPort = serverPort;
25
+ this.appConfig.arb_event_subscriber.subscribe_new_block(ttd_core_1.CHAIN_ID.BSC, (data) => {
26
+ try {
27
+ this.latestBlockNumber = JSON.parse(data).blockNumber;
28
+ }
29
+ catch (error) {
30
+ (0, ttd_core_1.log_error)('parse block update event failed', error);
31
+ }
32
+ });
33
+ this.initSenders();
34
+ }
35
+ initSenders() {
36
+ if (process.env.SEND_TX_48CLUB_WS === 'true') {
37
+ this._48clubWsSender = new _48club_1._48ClubWsSender();
38
+ this._48clubWsSender.connect();
39
+ }
40
+ if (process.env.SEND_TX_BLOXROUTE_WS === 'true') {
41
+ this.bloxrouteWsSender = new bloxroute_1.BloXRouteWsSender();
42
+ this.bloxrouteWsSender.connect();
43
+ }
44
+ }
45
+ handleMessage(ws, message) {
46
+ var _a, _b;
47
+ try {
48
+ const request = JSON.parse(message);
49
+ if (!request.mainTx || !request.builder || !request.tipTx) {
50
+ ws.send(JSON.stringify({ error: 'Missing required fields: mainTx, builder, tipTx' }));
51
+ return;
52
+ }
53
+ request.blockNumber = this.latestBlockNumber;
54
+ (0, ttd_core_1.log_info)(`[WS_SERVER] recv bundle request`, { builder: request.builder, pair: request.pair });
55
+ switch (request.builder) {
56
+ case '48club':
57
+ (_a = this._48clubWsSender) === null || _a === void 0 ? void 0 : _a.sendTransaction(request);
58
+ break;
59
+ case 'bloxroute':
60
+ (_b = this.bloxrouteWsSender) === null || _b === void 0 ? void 0 : _b.sendTransaction(request);
61
+ break;
62
+ }
63
+ }
64
+ catch (error) {
65
+ (0, ttd_core_1.log_error)(`[WS_SERVER] handle request failed`, error);
66
+ }
67
+ }
68
+ start() {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ if (this.wss)
71
+ return;
72
+ this.wss = new ws_1.WebSocketServer({ port: this.serverPort });
73
+ this.wss.on('connection', (ws) => {
74
+ (0, ttd_core_1.log_info)(`[WS_SERVER] client connected`);
75
+ ws.on('message', (msg) => this.handleMessage(ws, msg.toString()));
76
+ ws.on('close', () => (0, ttd_core_1.log_info)(`[WS_SERVER] client disconnected`));
77
+ ws.on('error', (err) => (0, ttd_core_1.log_error)(`[WS_SERVER] ws error`, err));
78
+ });
79
+ (0, ttd_core_1.log_info)(`[WS_SERVER] started on port ${this.serverPort}`);
80
+ });
81
+ }
82
+ }
83
+ process.on('uncaughtException', (err) => { (0, ttd_core_1.log_error)(`[BSC_SEND_TX] uncaught exception`, err); process.exit(1); });
84
+ process.on('unhandledRejection', (reason) => { (0, ttd_core_1.log_error)(`[BSC_SEND_TX] unhandled rejection`, reason instanceof Error ? reason : new Error(String(reason))); process.exit(1); });
85
+ const main = () => __awaiter(void 0, void 0, void 0, function* () {
86
+ const appConfig = new ttd_core_1.AppConfig();
87
+ yield appConfig.init();
88
+ yield appConfig.subscribe_config_change();
89
+ const wsPort = ttd_core_1.SERVICE_PORT.SEND_TX_WS;
90
+ const server = new BscSendTxWebSocketServer(appConfig, wsPort);
91
+ yield server.start();
92
+ });
93
+ main().catch((err) => { (0, ttd_core_1.log_error)(`[BSC_SEND_TX] startup failed`, err); process.exit(1); });
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@clonegod/ttd-bsc-send-tx",
3
+ "version": "1.0.0",
4
+ "description": "BSC 交易发送模块(HTTP直发 + WS bundle 转发)",
5
+ "license": "UNLICENSED",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "clean": "rm -rf dist node_modules",
10
+ "build": "tsc --outDir ./dist",
11
+ "start": "node dist/ws_server.js",
12
+ "push": "npm run build && npm publish"
13
+ },
14
+ "dependencies": {
15
+ "@clonegod/ttd-core": "3.0.8",
16
+ "axios": "^1.12.0",
17
+ "dotenv": "^16.4.7",
18
+ "ethers": "^5.8.0",
19
+ "ws": "^8.18.3"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.14.0",
23
+ "@types/ws": "^8.5.12",
24
+ "typescript": "^5.8.2"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ echo $PWD
2
+
3
+ module_name="ttd-bsc-send-tx"
4
+
5
+ # Delete Old File
6
+ rm -rf dist
7
+ rm -rf deploy
8
+
9
+ # Create deploy folder
10
+ date_time=`date +%Y%m%d-%H%M%S`
11
+ target_dir=deploy/${date_time}/${module_name}
12
+ mkdir -p ${target_dir}
13
+
14
+ # Build
15
+ npm run build
16
+
17
+ # Copy dist and package.json
18
+ cp -r dist ${target_dir}
19
+ cp package.json ${target_dir}
20
+
21
+ # Delete .d.ts file
22
+ find ${target_dir} -maxdepth 3 -name "*.d.ts" -type f -delete
23
+
24
+ # Show files
25
+ tree ${target_dir}
26
+
27
+ cat package.json | grep "ttd-core"
28
+ cat package.json | grep "ttd-bsc-common" || true
29
+
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ echo "args count: $#"
4
+ echo "$@"
5
+ echo
6
+
7
+ # args
8
+ app_id="ttd-bsc-send-tx-ws"
9
+ app_ns="BSC"
10
+
11
+ # sync code
12
+ git pull
13
+ yarn install
14
+ npm run build
15
+
16
+ # start app
17
+ echo "$app_id ($app_ns)"
18
+
19
+ pm2 del $app_id 2> /dev/null || true && pm2 start "dist/ws_server.js" --name $app_id --namespace $app_ns
20
+
21
+ echo
22
+
23
+ pm2 log $app_id
24
+
@@ -0,0 +1,60 @@
1
+ import axios from 'axios';
2
+ import { SoulPointSignature } from './sp_signature';
3
+ import { get_48club_sp_private_key } from './member';
4
+
5
+ /**
6
+ * 48Club HTTP API
7
+ * https://docs.48.club/puissant-builder
8
+ */
9
+ export class _48ClubTrade {
10
+ private rpcUrl: string;
11
+
12
+ constructor() {
13
+ this.rpcUrl = process.env._48CLUB_RPC_URL || 'https://puissant-bsc.48.club';
14
+ }
15
+
16
+ async sendPrivateTransactionWith48SP(signedTx: string): Promise<string> {
17
+ const spSign = await this.get48SPSignature([signedTx]);
18
+
19
+ const response = await axios.post(this.rpcUrl, {
20
+ jsonrpc: "2.0", id: 1,
21
+ method: "eth_sendPrivateTransaction",
22
+ params: [{ tx: signedTx, preferences: { '48spSign': spSign } }]
23
+ }, {
24
+ headers: { 'Content-Type': 'application/json' }
25
+ });
26
+
27
+ if (response.data.error) {
28
+ throw new Error(`48club private tx: ${response.data.error.message}`);
29
+ }
30
+ return response.data.result;
31
+ }
32
+
33
+ async sendBundle(params: { txs: string[] }): Promise<string> {
34
+ const currentTimestamp = Math.floor(Date.now() / 1000);
35
+ const spSign = await this.get48SPSignature(params.txs);
36
+
37
+ const response = await axios.post(this.rpcUrl, {
38
+ jsonrpc: "2.0", id: 1,
39
+ method: "eth_sendBundle",
40
+ params: [{
41
+ txs: params.txs,
42
+ maxTimestamp: currentTimestamp + 2,
43
+ '48spSign': spSign
44
+ }]
45
+ }, {
46
+ headers: { 'Content-Type': 'application/json' }
47
+ });
48
+
49
+ if (response.data.error) {
50
+ throw new Error(`48club bundle: ${response.data.error.message}`);
51
+ }
52
+ return response.data.result;
53
+ }
54
+
55
+ async get48SPSignature(txs: string[]): Promise<string> {
56
+ const privateKey = get_48club_sp_private_key();
57
+ if (!privateKey) return '';
58
+ return SoulPointSignature.generate48SPSignature(privateKey, txs);
59
+ }
60
+ }
@@ -0,0 +1,4 @@
1
+ export { _48ClubTrade } from './http';
2
+ export { _48ClubWsSender } from './ws';
3
+ export { SoulPointSignature } from './sp_signature';
4
+ export { get_48club_sp_private_key } from './member';
@@ -0,0 +1,19 @@
1
+ import { load_wallet } from '@clonegod/ttd-core';
2
+
3
+ var _48club_sp_wallet_private_key: string | null = null;
4
+
5
+ export const get_48club_sp_private_key = (): string | null => {
6
+ if (_48club_sp_wallet_private_key) {
7
+ return _48club_sp_wallet_private_key;
8
+ }
9
+
10
+ try {
11
+ const wallet = load_wallet(process.env._48CLUB_SP_WALLET_ID || 'TTD-PAYMENT');
12
+ console.log('Load 48club SP wallet success, wallet address:', wallet.public_key);
13
+ _48club_sp_wallet_private_key = wallet.private_key;
14
+ return _48club_sp_wallet_private_key;
15
+ } catch (error: any) {
16
+ console.error('Load 48club SP wallet failed:' + error.message);
17
+ return null;
18
+ }
19
+ };
@@ -0,0 +1,28 @@
1
+ import { ethers } from 'ethers';
2
+
3
+ /**
4
+ * 48 SoulPoint 签名
5
+ * https://docs.48.club/puissant-builder/48-soulpoint-benefits
6
+ */
7
+ export class SoulPointSignature {
8
+ static generate48SPSignature(privateKey: string, txs: string[]): string {
9
+ const wallet = new ethers.Wallet(privateKey);
10
+ const txHashes = txs.map(tx => tx.startsWith('0x') ? ethers.utils.keccak256(tx) : tx);
11
+ const concatenatedHashes = ethers.utils.concat(txHashes);
12
+ const messageHash = ethers.utils.keccak256(concatenatedHashes);
13
+ const signature = wallet._signingKey().signDigest(messageHash);
14
+ return signature.compact;
15
+ }
16
+
17
+ static verify48SPSignature(signature: string, txs: string[], expectedSignerAddress: string): boolean {
18
+ try {
19
+ const txHashes = txs.map(tx => tx.startsWith('0x') ? ethers.utils.keccak256(tx) : tx);
20
+ const concatenatedHashes = ethers.utils.concat(txHashes);
21
+ const messageHash = ethers.utils.keccak256(concatenatedHashes);
22
+ const recoveredAddress = ethers.utils.recoverAddress(messageHash, signature);
23
+ return recoveredAddress.toLowerCase() === expectedSignerAddress.toLowerCase();
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,47 @@
1
+ import { log_info, log_warn, WebSocketClient } from '@clonegod/ttd-core';
2
+ import { BscSendTxRequestType } from '../types';
3
+
4
+ /**
5
+ * 48Club WebSocket 发送器(长连接发送 bundle 到 builder)
6
+ */
7
+ export class _48ClubWsSender {
8
+ private wsClient: WebSocketClient | null = null;
9
+ private wsUrl: string;
10
+
11
+ constructor() {
12
+ this.wsUrl = process.env._48CLUB_WS_URL || 'wss://puissant-builder.48.club/';
13
+ }
14
+
15
+ connect(): void {
16
+ if (this.wsClient?.isConnected()) return;
17
+ if (this.wsClient) { this.wsClient.disconnect(); this.wsClient = null; }
18
+
19
+ this.wsClient = new WebSocketClient(this.wsUrl, { reconnectInterval: 1000 });
20
+ this.wsClient.onOpen(() => log_info(`[48club-ws] connected`));
21
+ this.wsClient.onMessage((msg: any) => log_info(`[48club-ws] response:`, msg));
22
+ this.wsClient.connect();
23
+ }
24
+
25
+ isConnected(): boolean {
26
+ return this.wsClient?.isConnected() || false;
27
+ }
28
+
29
+ async sendTransaction(request: BscSendTxRequestType): Promise<void> {
30
+ if (!this.isConnected()) { log_warn('[48club-ws] not connected'); return; }
31
+ if (!request.tipTx) { log_warn('[48club-ws] tipTx required for bundle'); return; }
32
+
33
+ const currentTimestamp = Math.floor(Date.now() / 1000);
34
+ const bundleParams: any = {
35
+ txs: [request.mainTx, request.tipTx],
36
+ maxTimestamp: currentTimestamp + 2,
37
+ };
38
+ if (request.blockNumber > 0) bundleParams.maxBlockNumber = request.blockNumber + 2;
39
+ if (request._48spSign) bundleParams['48spSign'] = request._48spSign;
40
+
41
+ this.wsClient!.send(JSON.stringify({
42
+ jsonrpc: '2.0', id: '1',
43
+ method: 'eth_sendBundle',
44
+ params: [bundleParams]
45
+ }));
46
+ }
47
+ }
@@ -0,0 +1,57 @@
1
+ import { log_error } from '@clonegod/ttd-core';
2
+ import axios from 'axios';
3
+
4
+ /**
5
+ * BlockRazor HTTP API
6
+ * https://blockrazor.gitbook.io/blockrazor/bsc/block-builder
7
+ */
8
+ export class BlockRazorTrade {
9
+ private rpcUrl: string;
10
+ private authToken: string;
11
+
12
+ constructor() {
13
+ this.rpcUrl = process.env.BLOCKRAZOR_RPC_URL || 'https://rpc.blockrazor.builders';
14
+ this.authToken = process.env.BLOCKRAZOR_AUTH_TOKEN || '';
15
+ }
16
+
17
+ async sendPrivateTransaction(signedTx: string): Promise<string> {
18
+ try {
19
+ const response = await axios.post(this.rpcUrl, {
20
+ jsonrpc: "2.0", id: "1",
21
+ method: "eth_sendPrivateTransaction",
22
+ params: [signedTx]
23
+ }, {
24
+ headers: { 'Content-Type': 'application/json', 'Authorization': this.authToken }
25
+ });
26
+
27
+ if (response.data.error) {
28
+ throw new Error(`${response.data.error.code} - ${response.data.error.message}`);
29
+ }
30
+ return response.data.result;
31
+ } catch (error: any) {
32
+ log_error('blockrazor sendPrivateTransaction failed', error);
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ async sendBundle(transactions: string[]): Promise<string> {
38
+ try {
39
+ const currentTimestamp = Math.floor(Date.now() / 1000);
40
+ const response = await axios.post(this.rpcUrl, {
41
+ jsonrpc: "2.0", id: "1",
42
+ method: "eth_sendBundle",
43
+ params: [{ txs: transactions, maxTimestamp: currentTimestamp + 5 }]
44
+ }, {
45
+ headers: { 'Content-Type': 'application/json', 'Authorization': this.authToken }
46
+ });
47
+
48
+ if (response.data.error) {
49
+ throw new Error(`Bundle: ${response.data.error.message}`);
50
+ }
51
+ return response.data.result;
52
+ } catch (error: any) {
53
+ log_error('blockrazor sendBundle failed', error);
54
+ throw error;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1 @@
1
+ export { BloXRouteWsSender } from './ws';
@@ -0,0 +1,53 @@
1
+ import { log_info, log_warn, WebSocketClient } from '@clonegod/ttd-core';
2
+ import { BscSendTxRequestType } from '../types';
3
+
4
+ /**
5
+ * BloXRoute WebSocket 发送器(长连接发送 bundle 到 builder)
6
+ * https://docs.bloxroute.com/bsc-and-eth/apis/transaction-bundles
7
+ */
8
+ export class BloXRouteWsSender {
9
+ private wsClient: WebSocketClient | null = null;
10
+ private wsUrl: string;
11
+ private authToken: string;
12
+
13
+ constructor() {
14
+ this.wsUrl = process.env.BLOXROUTE_WS_URL || 'wss://api.blxrbdn.com/ws';
15
+ this.authToken = process.env.BLOX_AUTH_KEY || '';
16
+ }
17
+
18
+ connect(): void {
19
+ if (this.wsClient?.isConnected()) return;
20
+ if (this.wsClient) { this.wsClient.disconnect(); this.wsClient = null; }
21
+
22
+ const headers: { [key: string]: string } = {};
23
+ if (this.authToken) headers['Authorization'] = this.authToken;
24
+
25
+ this.wsClient = new WebSocketClient(this.wsUrl, { headers, reconnectInterval: 1000 });
26
+ this.wsClient.onOpen(() => log_info(`[bloxroute-ws] connected`));
27
+ this.wsClient.onMessage((msg: any) => log_info(`[bloxroute-ws] response:`, msg));
28
+ this.wsClient.connect();
29
+ }
30
+
31
+ isConnected(): boolean {
32
+ return this.wsClient?.isConnected() || false;
33
+ }
34
+
35
+ async sendTransaction(request: BscSendTxRequestType): Promise<void> {
36
+ if (!this.isConnected()) { log_warn('[bloxroute-ws] not connected'); return; }
37
+ if (!request.tipTx) { log_warn('[bloxroute-ws] tipTx required for bundle'); return; }
38
+
39
+ const targetBlockNumber = request.blockNumber + 4;
40
+
41
+ this.wsClient!.send(JSON.stringify({
42
+ id: "1",
43
+ method: "blxr_submit_bundle",
44
+ params: {
45
+ transaction: [request.mainTx, request.tipTx],
46
+ blockchain_network: "BSC-Mainnet",
47
+ block_number: `0x${targetBlockNumber.toString(16)}`,
48
+ max_timestamp: Math.floor(Date.now() / 1000) + 2,
49
+ mev_builders: { "all": "" }
50
+ }
51
+ }));
52
+ }
53
+ }
@@ -0,0 +1,5 @@
1
+ export enum BSC_EOA_ADDRESS {
2
+ BLOCKRAZOR = '0x1266C6bE60392A8Ff346E8d5ECCd3E69dD9c5F20',
3
+ _48CLUB = '0x4848489f0b2BEdd788c696e2D79b6b69D7484848',
4
+ BLXR = '0x74c5F8C6ffe41AD4789602BDB9a48E6Cad623520',
5
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { TransactionSender } from './transaction_sender';
2
+ export { BSC_EOA_ADDRESS } from './constants';
@@ -0,0 +1,42 @@
1
+ import { log_warn } from '@clonegod/ttd-core';
2
+
3
+ export class BscMainnetRpc {
4
+ private url: string;
5
+ private headers: Headers;
6
+
7
+ constructor(rpc_endpoint: string) {
8
+ this.url = rpc_endpoint || process.env.BSC_RPC_ENDPOINT || '';
9
+ this.headers = new Headers();
10
+ this.headers.append("Content-Type", "application/json");
11
+ }
12
+
13
+ async eth_sendRawTransaction(signedTx: string): Promise<string> {
14
+ setTimeout(async () => {
15
+ console.log(`Sending transaction to ${this.url}`);
16
+
17
+ const raw = JSON.stringify({
18
+ jsonrpc: "2.0",
19
+ method: "eth_sendRawTransaction",
20
+ params: [signedTx],
21
+ id: 1
22
+ });
23
+
24
+ const requestOptions = {
25
+ method: 'POST',
26
+ headers: this.headers,
27
+ body: raw,
28
+ redirect: 'follow' as const
29
+ };
30
+
31
+ const response = await fetch(this.url, requestOptions);
32
+ const result = await response.json();
33
+
34
+ if (result.error) {
35
+ log_warn(result.error.message);
36
+ }
37
+ return result.result;
38
+ }, 0);
39
+
40
+ return '';
41
+ }
42
+ }