From 42b7267c2c70e7bc46e9e051e07f2b796a00de31 Mon Sep 17 00:00:00 2001 From: William Date: Thu, 23 Jan 2025 17:48:10 +0800 Subject: [PATCH] add yellowstone grpc --- Cargo.toml | 47 +- README.md | 14 +- crates/pumpfun-cpi/Cargo.toml | 27 - crates/pumpfun-cpi/LICENSE | 201 ---- crates/pumpfun-cpi/README.md | 14 - crates/pumpfun-cpi/idl.classic.json | 1011 ----------------- crates/pumpfun-cpi/idl.json | 805 ------------- crates/pumpfun-cpi/src/lib.rs | 3 - crates/pumpfun/Cargo.toml | 34 - crates/pumpfun/LICENSE-APACHE | 201 ---- crates/pumpfun/LICENSE-MIT | 21 - crates/pumpfun/README.md | 133 --- crates/pumpfun/RUSTDOC.md | 111 -- crates/pumpfun/src/sdk.rs | 730 ------------ .../src => src}/accounts/bonding_curve.rs | 0 .../pumpfun/src => src}/accounts/global.rs | 2 +- {crates/pumpfun/src => src}/accounts/mod.rs | 0 {crates/pumpfun/src => src}/constants/mod.rs | 0 {crates/pumpfun/src => src}/error/mod.rs | 8 +- src/grpc/event.rs | 123 ++ src/grpc/mod.rs | 7 + src/grpc/myerror.rs | 13 + src/grpc/subscribe_logs.rs | 136 +++ src/grpc/subscribe_tx.rs | 73 ++ src/grpc/yellowstone_grpc.rs | 284 +++++ src/instruction/dex_events.rs | 123 ++ .../src => src}/instruction/logs_data.rs | 0 .../src => src}/instruction/logs_events.rs | 0 .../src => src}/instruction/logs_filters.rs | 2 +- .../src => src}/instruction/logs_parser.rs | 2 +- .../src => src}/instruction/logs_subscribe.rs | 4 +- .../pumpfun/src => src}/instruction/mod.rs | 59 +- {crates/pumpfun/src => src}/jito/mod.rs | 2 +- {crates/pumpfun/src => src}/lib.rs | 61 +- {crates/pumpfun/src => src}/main.rs | 2 +- {crates/pumpfun/src => src}/utils/mod.rs | 0 36 files changed, 897 insertions(+), 3356 deletions(-) delete mode 100755 crates/pumpfun-cpi/Cargo.toml delete mode 100755 crates/pumpfun-cpi/LICENSE delete mode 100755 crates/pumpfun-cpi/README.md delete mode 100755 crates/pumpfun-cpi/idl.classic.json delete mode 100755 crates/pumpfun-cpi/idl.json delete mode 100755 crates/pumpfun-cpi/src/lib.rs delete mode 100755 crates/pumpfun/Cargo.toml delete mode 100755 crates/pumpfun/LICENSE-APACHE delete mode 100755 crates/pumpfun/LICENSE-MIT delete mode 100755 crates/pumpfun/README.md delete mode 100755 crates/pumpfun/RUSTDOC.md delete mode 100644 crates/pumpfun/src/sdk.rs rename {crates/pumpfun/src => src}/accounts/bonding_curve.rs (100%) rename {crates/pumpfun/src => src}/accounts/global.rs (99%) rename {crates/pumpfun/src => src}/accounts/mod.rs (100%) rename {crates/pumpfun/src => src}/constants/mod.rs (100%) rename {crates/pumpfun/src => src}/error/mod.rs (94%) create mode 100755 src/grpc/event.rs create mode 100755 src/grpc/mod.rs create mode 100755 src/grpc/myerror.rs create mode 100755 src/grpc/subscribe_logs.rs create mode 100755 src/grpc/subscribe_tx.rs create mode 100755 src/grpc/yellowstone_grpc.rs create mode 100755 src/instruction/dex_events.rs rename {crates/pumpfun/src => src}/instruction/logs_data.rs (100%) rename {crates/pumpfun/src => src}/instruction/logs_events.rs (100%) rename {crates/pumpfun/src => src}/instruction/logs_filters.rs (98%) rename {crates/pumpfun/src => src}/instruction/logs_parser.rs (99%) rename {crates/pumpfun/src => src}/instruction/logs_subscribe.rs (96%) rename {crates/pumpfun/src => src}/instruction/mod.rs (81%) rename {crates/pumpfun/src => src}/jito/mod.rs (99%) rename {crates/pumpfun/src => src}/lib.rs (94%) rename {crates/pumpfun/src => src}/main.rs (95%) rename {crates/pumpfun/src => src}/utils/mod.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index d767148..f809cba 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,44 @@ -[workspace] -members = ["crates/*"] -resolver = "2" +[package] +name = "mai3-pumpfun-sdk" +version = "2.4.5" +edition = "2021" +authors = ["William "] +repository = "https://github.com/MiracleAI-Labs/pumpfun-sdk" +description = "Rust SDK to interact with the Pump.fun Solana program." +license = "MIT" +keywords = ["solana", "memecoins", "pumpfun", "pumpfun-sdk"] +readme = "README.md" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +solana-sdk = "2.1.7" +solana-client = "2.1.7" +solana-transaction-status = "2.1.7" +spl-token = "7.0.0" +spl-associated-token-account = "6.0.0" +borsh = { version = "1.5.3", features = ["derive"] } +isahc = "1.7.2" +mpl-token-metadata = "5.1.0" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.134" +futures = "0.3.31" +futures-util = "0.3.31" +base64 = "0.22.1" +bs58 = "0.5.1" +rand = "0.8.5" +bincode = "1.3.3" +anyhow = "1.0.90" +reqwest = { version = "0.11.27", features = ["json"] } +tonic = { version = "0.12.3", features = ["tls", "tls-webpki-roots", "tls-roots"] } +tokio = { version = "1.42.0" , features = ["full", "rt-multi-thread"]} +yellowstone-grpc-client = { version = "4.1.0" } +yellowstone-grpc-proto = { version = "4.1.1" } +rustls = { version = "0.23.20", features = ["ring"] } +dotenvy = "0.15.7" +pretty_env_logger = "0.5.0" +log = "0.4.22" +chrono = "0.4.39" +regex = "1" + diff --git a/README.md b/README.md index 49e1e55..60c2047 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ use mai3_pumpfun_sdk::instruction::{ logs_events::DexEvent, logs_subscribe::{tokens_subscription, stop_subscription} }; -use anchor_client::solana_sdk::commitment_config::CommitmentConfig; +use solana_sdk::commitment_config::CommitmentConfig; println!("Starting token subscription..."); @@ -74,14 +74,12 @@ println!("Ended token subscription."); ### pumpfun Create, Buy, Sell ```rust -use anchor_client::{ - solana_sdk::{ - native_token::LAMPORTS_PER_SOL, - signature::{Keypair, Signature}, - signer::Signer, - }, - Cluster, +use solana_sdk::{ + native_token::LAMPORTS_PER_SOL, + signature::{Keypair, Signature}, + signer::Signer, }; + use mai3_pumpfun_sdk::{accounts::BondingCurveAccount, utils::CreateTokenMetadata, PriorityFee, PumpFun}; // Create a new PumpFun client diff --git a/crates/pumpfun-cpi/Cargo.toml b/crates/pumpfun-cpi/Cargo.toml deleted file mode 100755 index c50f2df..0000000 --- a/crates/pumpfun-cpi/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "pumpfun-cpi" -version = "1.1.0" -edition = "2021" -authors = ["Rapheal Bamidele "] -repository = "https://github.com/nhuxhr/pumpfun-rs" -description = "Autogenerated CPI client for the Pump.fun Solana program." -license = "Apache-2.0" -keywords = ["solana", "memecoins", "pumpfun", "cpi"] -readme = "README.md" - -[lib] -crate-type = ["cdylib", "rlib"] - -[features] -default = ["cpi"] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-gen = "0.3.1" -anchor-lang = "0.30.1" - -[package.metadata.workspaces] -independent = true diff --git a/crates/pumpfun-cpi/LICENSE b/crates/pumpfun-cpi/LICENSE deleted file mode 100755 index c9c4c83..0000000 --- a/crates/pumpfun-cpi/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 Rapheal Bamidele - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/crates/pumpfun-cpi/README.md b/crates/pumpfun-cpi/README.md deleted file mode 100755 index cffc940..0000000 --- a/crates/pumpfun-cpi/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Pump.fun Solana Program CPI - -## Overview - -This crate provides CPI (Cross-Program Invocation) bindings for interacting with the Pump.fun Solana program. Pump.fun is a Solana-based marketplace enabling users to create and distribute their own tokens, primarily memecoins. - -The crate was automatically generated by [anchor-gen](https://github.com/saber-hq/anchor-gen) from the program's IDL (Interface Description Language) file, ensuring type-safe and reliable cross-program interactions. - -## Features - -- Type-safe CPI bindings for all Pump.fun program instructions -- Account validation and constraint checking -- Automatically generated account structs and instruction builders -- Full integration with Anchor's programming model diff --git a/crates/pumpfun-cpi/idl.classic.json b/crates/pumpfun-cpi/idl.classic.json deleted file mode 100755 index 8716a90..0000000 --- a/crates/pumpfun-cpi/idl.classic.json +++ /dev/null @@ -1,1011 +0,0 @@ -{ - "version": "0.1.0", - "name": "pump", - "instructions": [ - { - "name": "initialize", - "accounts": [ - { - "name": "global", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - } - ], - "args": [], - "discriminant": { - "type": "u8", - "value": 175, - "bytes": [175, 175, 109, 31, 13, 152, 155, 237] - } - }, - { - "name": "setParams", - "accounts": [ - { - "name": "global", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - }, - { - "name": "eventAuthority", - "isMut": false, - "isSigner": false, - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "isMut": false, - "isSigner": false, - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "feeRecipient", - "type": "publicKey", - "attrs": [] - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "feeBasisPoints", - "type": "u64", - "attrs": [] - } - ], - "discriminant": { - "type": "u8", - "value": 165, - "bytes": [165, 31, 134, 53, 189, 180, 130, 255] - } - }, - { - "name": "create", - "accounts": [ - { - "name": "mint", - "isMut": true, - "isSigner": true - }, - { - "name": "mintAuthority", - "isMut": false, - "isSigner": false - }, - { - "name": "bondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedBondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "global", - "isMut": false, - "isSigner": false - }, - { - "name": "mplTokenMetadata", - "isMut": false, - "isSigner": false, - "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" - }, - { - "name": "metadata", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false, - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "associatedTokenProgram", - "isMut": false, - "isSigner": false, - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "rent", - "isMut": false, - "isSigner": false, - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "eventAuthority", - "isMut": false, - "isSigner": false, - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "isMut": false, - "isSigner": false, - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "name", - "type": "string", - "attrs": [] - }, - { - "name": "symbol", - "type": "string", - "attrs": [] - }, - { - "name": "uri", - "type": "string", - "attrs": [] - } - ], - "discriminant": { - "type": "u8", - "value": 24, - "bytes": [24, 30, 200, 40, 5, 28, 7, 119] - } - }, - { - "name": "buy", - "accounts": [ - { - "name": "global", - "isMut": false, - "isSigner": false - }, - { - "name": "feeRecipient", - "isMut": true, - "isSigner": false - }, - { - "name": "mint", - "isMut": false, - "isSigner": false - }, - { - "name": "bondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedBondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedUser", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false, - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "rent", - "isMut": false, - "isSigner": false, - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "eventAuthority", - "isMut": false, - "isSigner": false, - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "isMut": false, - "isSigner": false, - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "amount", - "type": "u64", - "attrs": [] - }, - { - "name": "maxSolCost", - "type": "u64", - "attrs": [] - } - ], - "discriminant": { - "type": "u8", - "value": 102, - "bytes": [102, 6, 61, 18, 1, 218, 235, 234] - } - }, - { - "name": "sell", - "accounts": [ - { - "name": "global", - "isMut": false, - "isSigner": false - }, - { - "name": "feeRecipient", - "isMut": true, - "isSigner": false - }, - { - "name": "mint", - "isMut": false, - "isSigner": false - }, - { - "name": "bondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedBondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedUser", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - }, - { - "name": "associatedTokenProgram", - "isMut": false, - "isSigner": false, - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false, - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "eventAuthority", - "isMut": false, - "isSigner": false, - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "isMut": false, - "isSigner": false, - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "amount", - "type": "u64", - "attrs": [] - }, - { - "name": "minSolOutput", - "type": "u64", - "attrs": [] - } - ], - "discriminant": { - "type": "u8", - "value": 51, - "bytes": [51, 230, 133, 164, 1, 127, 131, 173] - } - }, - { - "name": "withdraw", - "accounts": [ - { - "name": "global", - "isMut": false, - "isSigner": false - }, - { - "name": "lastWithdraw", - "isMut": true, - "isSigner": false - }, - { - "name": "mint", - "isMut": false, - "isSigner": false - }, - { - "name": "bondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedBondingCurve", - "isMut": true, - "isSigner": false - }, - { - "name": "associatedUser", - "isMut": true, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false, - "address": "11111111111111111111111111111111" - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false, - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "rent", - "isMut": false, - "isSigner": false, - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "eventAuthority", - "isMut": false, - "isSigner": false, - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "isMut": false, - "isSigner": false, - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [], - "discriminant": { - "type": "u8", - "value": 183, - "bytes": [183, 18, 70, 156, 148, 109, 161, 34] - } - } - ], - "accounts": [ - { - "name": "BondingCurve", - "type": { - "kind": "struct", - "fields": [ - { - "name": "virtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "virtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "complete", - "type": "bool", - "attrs": [] - } - ] - } - }, - { - "name": "Global", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initialized", - "type": "bool", - "attrs": [] - }, - { - "name": "authority", - "type": "publicKey", - "attrs": [] - }, - { - "name": "feeRecipient", - "type": "publicKey", - "attrs": [] - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "feeBasisPoints", - "type": "u64", - "attrs": [] - } - ] - } - } - ], - "types": [ - { - "name": "Global", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initialized", - "type": "bool", - "attrs": [] - }, - { - "name": "authority", - "type": "publicKey", - "attrs": [] - }, - { - "name": "feeRecipient", - "type": "publicKey", - "attrs": [] - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "feeBasisPoints", - "type": "u64", - "attrs": [] - } - ] - } - }, - { - "name": "LastWithdraw", - "type": { - "kind": "struct", - "fields": [ - { - "name": "lastWithdrawTimestamp", - "type": "i64", - "attrs": [] - } - ] - } - }, - { - "name": "BondingCurve", - "type": { - "kind": "struct", - "fields": [ - { - "name": "virtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "virtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "complete", - "type": "bool", - "attrs": [] - } - ] - } - }, - { - "name": "CreateEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": "string", - "attrs": [] - }, - { - "name": "symbol", - "type": "string", - "attrs": [] - }, - { - "name": "uri", - "type": "string", - "attrs": [] - }, - { - "name": "mint", - "type": "publicKey", - "attrs": [] - }, - { - "name": "bondingCurve", - "type": "publicKey", - "attrs": [] - }, - { - "name": "user", - "type": "publicKey", - "attrs": [] - } - ] - } - }, - { - "name": "TradeEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "mint", - "type": "publicKey", - "attrs": [] - }, - { - "name": "solAmount", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenAmount", - "type": "u64", - "attrs": [] - }, - { - "name": "isBuy", - "type": "bool", - "attrs": [] - }, - { - "name": "user", - "type": "publicKey", - "attrs": [] - }, - { - "name": "timestamp", - "type": "i64", - "attrs": [] - }, - { - "name": "virtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "virtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "realTokenReserves", - "type": "u64", - "attrs": [] - } - ] - } - }, - { - "name": "CompleteEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "publicKey", - "attrs": [] - }, - { - "name": "mint", - "type": "publicKey", - "attrs": [] - }, - { - "name": "bondingCurve", - "type": "publicKey", - "attrs": [] - }, - { - "name": "timestamp", - "type": "i64", - "attrs": [] - } - ] - } - }, - { - "name": "SetParamsEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "feeRecipient", - "type": "publicKey", - "attrs": [] - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "attrs": [] - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "attrs": [] - }, - { - "name": "feeBasisPoints", - "type": "u64", - "attrs": [] - } - ] - } - } - ], - "events": [ - { - "name": "CreateEvent", - "fields": [ - { - "name": "name", - "type": "string", - "index": false - }, - { - "name": "symbol", - "type": "string", - "index": false - }, - { - "name": "uri", - "type": "string", - "index": false - }, - { - "name": "mint", - "type": "publicKey", - "index": false - }, - { - "name": "bondingCurve", - "type": "publicKey", - "index": false - }, - { - "name": "user", - "type": "publicKey", - "index": false - } - ] - }, - { - "name": "TradeEvent", - "fields": [ - { - "name": "mint", - "type": "publicKey", - "index": false - }, - { - "name": "solAmount", - "type": "u64", - "index": false - }, - { - "name": "tokenAmount", - "type": "u64", - "index": false - }, - { - "name": "isBuy", - "type": "bool", - "index": false - }, - { - "name": "user", - "type": "publicKey", - "index": false - }, - { - "name": "timestamp", - "type": "i64", - "index": false - }, - { - "name": "virtualSolReserves", - "type": "u64", - "index": false - }, - { - "name": "virtualTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "realSolReserves", - "type": "u64", - "index": false - }, - { - "name": "realTokenReserves", - "type": "u64", - "index": false - } - ] - }, - { - "name": "CompleteEvent", - "fields": [ - { - "name": "user", - "type": "publicKey", - "index": false - }, - { - "name": "mint", - "type": "publicKey", - "index": false - }, - { - "name": "bondingCurve", - "type": "publicKey", - "index": false - }, - { - "name": "timestamp", - "type": "i64", - "index": false - } - ] - }, - { - "name": "SetParamsEvent", - "fields": [ - { - "name": "feeRecipient", - "type": "publicKey", - "index": false - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "index": false - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "index": false - }, - { - "name": "feeBasisPoints", - "type": "u64", - "index": false - } - ] - } - ], - "errors": [ - { - "code": 6000, - "name": "NotAuthorized", - "msg": "The given account is not authorized to execute this instruction." - }, - { - "code": 6001, - "name": "AlreadyInitialized", - "msg": "The program is already initialized." - }, - { - "code": 6002, - "name": "TooMuchSolRequired", - "msg": "slippage: Too much SOL required to buy the given amount of tokens." - }, - { - "code": 6003, - "name": "TooLittleSolReceived", - "msg": "slippage: Too little SOL received to sell the given amount of tokens." - }, - { - "code": 6004, - "name": "MintDoesNotMatchBondingCurve", - "msg": "The mint does not match the bonding curve." - }, - { - "code": 6005, - "name": "BondingCurveComplete", - "msg": "The bonding curve has completed and liquidity migrated to raydium." - }, - { - "code": 6006, - "name": "BondingCurveNotComplete", - "msg": "The bonding curve has not completed." - }, - { - "code": 6007, - "name": "NotInitialized", - "msg": "The program is not initialized." - }, - { - "code": 6008, - "name": "WithdrawTooFrequent", - "msg": "Withdraw too frequent" - } - ], - "metadata": { - "origin": "anchor", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } -} diff --git a/crates/pumpfun-cpi/idl.json b/crates/pumpfun-cpi/idl.json deleted file mode 100755 index 5be7fda..0000000 --- a/crates/pumpfun-cpi/idl.json +++ /dev/null @@ -1,805 +0,0 @@ -{ - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", - "metadata": { - "name": "pump", - "version": "0.1.0", - "spec": "0.1.0" - }, - "instructions": [ - { - "name": "initialize", - "discriminator": [175, 175, 109, 31, 13, 152, 155, 237], - "docs": ["Creates the global state."], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - } - ], - "args": [] - }, - { - "name": "setParams", - "discriminator": [165, 31, 134, 53, 189, 180, 130, 255], - "docs": ["Sets the global state parameters."], - "accounts": [ - { - "name": "global", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "event_authority", - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "feeRecipient", - "type": "pubkey" - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64" - }, - { - "name": "initialVirtualSolReserves", - "type": "u64" - }, - { - "name": "initialRealTokenReserves", - "type": "u64" - }, - { - "name": "tokenTotalSupply", - "type": "u64" - }, - { - "name": "feeBasisPoints", - "type": "u64" - } - ] - }, - { - "name": "create", - "discriminator": [24, 30, 200, 40, 5, 28, 7, 119], - "docs": ["Creates a new coin and bonding curve."], - "accounts": [ - { - "name": "mint", - "writable": true, - "signer": true - }, - { - "name": "mint_authority", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 109, 105, 110, 116, 45, 97, 117, 116, 104, 111, 114, 105, 116, - 121 - ] - } - ] - } - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "signer": false - }, - { - "name": "global", - "writable": false, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "mpl_token_metadata", - "address": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" - }, - { - "name": "metadata", - "writable": true, - "signer": false - }, - { - "name": "user", - "isMut": true, - "isSigner": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "rent", - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "event_authority", - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, - { - "name": "uri", - "type": "string" - } - ] - }, - { - "name": "buy", - "discriminator": [102, 6, 61, 18, 1, 218, 235, 234], - "docs": ["Buys tokens from a bonding curve."], - "accounts": [ - { - "name": "global", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "fee_recipient", - "writable": true, - "signer": false - }, - { - "name": "mint", - "writable": false, - "signer": false - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associated_bonding_curve", - "writable": true, - "signer": false - }, - { - "name": "associated_user", - "writable": true, - "signer": false - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "rent", - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "event_authority", - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "maxSolCost", - "type": "u64" - } - ] - }, - { - "name": "sell", - "discriminator": [51, 230, 133, 164, 1, 127, 131, 173], - "docs": ["Sells tokens into a bonding curve."], - "accounts": [ - { - "name": "global", - "writable": false, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "feeRecipient", - "writable": true, - "signer": false - }, - { - "name": "mint", - "writable": false, - "signer": false - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associatedBondingCurve", - "writable": true, - "signer": false - }, - { - "name": "associatedUser", - "writable": true, - "signer": false - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "associated_token_program", - "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "event_authority", - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "minSolOutput", - "type": "u64" - } - ] - }, - { - "name": "withdraw", - "discriminator": [183, 18, 70, 156, 148, 109, 161, 34], - "docs": [ - "Allows the admin to withdraw liquidity for a migration once the bonding curve completes" - ], - "accounts": [ - { - "name": "global", - "writable": false, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [103, 108, 111, 98, 97, 108] - } - ] - } - }, - { - "name": "lastWithdraw", - "writable": true, - "signer": false - }, - { - "name": "mint", - "writable": false, - "signer": false - }, - { - "name": "bonding_curve", - "writable": true, - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 98, 111, 110, 100, 105, 110, 103, 45, 99, 117, 114, 118, 101 - ] - }, - { - "kind": "account", - "path": "mint" - } - ] - } - }, - { - "name": "associatedBondingCurve", - "writable": true, - "signer": false - }, - { - "name": "associatedUser", - "writable": true, - "signer": false - }, - { - "name": "user", - "writable": true, - "signer": true - }, - { - "name": "system_program", - "address": "11111111111111111111111111111111" - }, - { - "name": "token_program", - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "name": "rent", - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "name": "event_authority", - "address": "Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1" - }, - { - "name": "program", - "address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" - } - ], - "args": [] - } - ], - "accounts": [ - { - "name": "BondingCurve", - "discriminator": [23, 183, 248, 55, 96, 216, 172, 96] - }, - { - "name": "Global", - "discriminator": [167, 232, 232, 177, 200, 108, 114, 127] - } - ], - "events": [ - { - "name": "CreateEvent", - "discriminator": [27, 114, 169, 77, 222, 235, 99, 118] - }, - { - "name": "TradeEvent", - "discriminator": [189, 219, 127, 211, 78, 230, 97, 238] - }, - { - "name": "CompleteEvent", - "discriminator": [95, 114, 97, 156, 212, 46, 152, 8] - }, - { - "name": "SetParamsEvent", - "discriminator": [223, 195, 159, 246, 62, 48, 143, 131] - } - ], - "types": [ - { - "name": "Global", - "type": { - "kind": "struct", - "fields": [ - { - "name": "initialized", - "type": "bool" - }, - { - "name": "authority", - "type": "pubkey" - }, - { - "name": "feeRecipient", - "type": "pubkey" - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64" - }, - { - "name": "initialVirtualSolReserves", - "type": "u64" - }, - { - "name": "initialRealTokenReserves", - "type": "u64" - }, - { - "name": "tokenTotalSupply", - "type": "u64" - }, - { - "name": "feeBasisPoints", - "type": "u64" - } - ] - } - }, - { - "name": "LastWithdraw", - "type": { - "kind": "struct", - "fields": [ - { - "name": "lastWithdrawTimestamp", - "type": "i64" - } - ] - } - }, - { - "name": "BondingCurve", - "type": { - "kind": "struct", - "fields": [ - { - "name": "virtualTokenReserves", - "type": "u64" - }, - { - "name": "virtualSolReserves", - "type": "u64" - }, - { - "name": "realTokenReserves", - "type": "u64" - }, - { - "name": "realSolReserves", - "type": "u64" - }, - { - "name": "tokenTotalSupply", - "type": "u64" - }, - { - "name": "complete", - "type": "bool" - } - ] - } - }, - { - "name": "CreateEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": "string", - "index": false - }, - { - "name": "symbol", - "type": "string", - "index": false - }, - { - "name": "uri", - "type": "string", - "index": false - }, - { - "name": "mint", - "type": "pubkey", - "index": false - }, - { - "name": "bondingCurve", - "type": "pubkey", - "index": false - }, - { - "name": "user", - "type": "pubkey", - "index": false - } - ] - } - }, - { - "name": "TradeEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "mint", - "type": "pubkey", - "index": false - }, - { - "name": "solAmount", - "type": "u64", - "index": false - }, - { - "name": "tokenAmount", - "type": "u64", - "index": false - }, - { - "name": "isBuy", - "type": "bool", - "index": false - }, - { - "name": "user", - "type": "pubkey", - "index": false - }, - { - "name": "timestamp", - "type": "i64", - "index": false - }, - { - "name": "virtualSolReserves", - "type": "u64", - "index": false - }, - { - "name": "virtualTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "realSolReserves", - "type": "u64", - "index": false - }, - { - "name": "realTokenReserves", - "type": "u64", - "index": false - } - ] - } - }, - { - "name": "CompleteEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "user", - "type": "pubkey", - "index": false - }, - { - "name": "mint", - "type": "pubkey", - "index": false - }, - { - "name": "bondingCurve", - "type": "pubkey", - "index": false - }, - { - "name": "timestamp", - "type": "i64", - "index": false - } - ] - } - }, - { - "name": "SetParamsEvent", - "type": { - "kind": "struct", - "fields": [ - { - "name": "feeRecipient", - "type": "pubkey", - "index": false - }, - { - "name": "initialVirtualTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "initialVirtualSolReserves", - "type": "u64", - "index": false - }, - { - "name": "initialRealTokenReserves", - "type": "u64", - "index": false - }, - { - "name": "tokenTotalSupply", - "type": "u64", - "index": false - }, - { - "name": "feeBasisPoints", - "type": "u64", - "index": false - } - ] - } - } - ], - "errors": [ - { - "code": 6000, - "name": "NotAuthorized", - "msg": "The given account is not authorized to execute this instruction." - }, - { - "code": 6001, - "name": "AlreadyInitialized", - "msg": "The program is already initialized." - }, - { - "code": 6002, - "name": "TooMuchSolRequired", - "msg": "slippage: Too much SOL required to buy the given amount of tokens." - }, - { - "code": 6003, - "name": "TooLittleSolReceived", - "msg": "slippage: Too little SOL received to sell the given amount of tokens." - }, - { - "code": 6004, - "name": "MintDoesNotMatchBondingCurve", - "msg": "The mint does not match the bonding curve." - }, - { - "code": 6005, - "name": "BondingCurveComplete", - "msg": "The bonding curve has completed and liquidity migrated to raydium." - }, - { - "code": 6006, - "name": "BondingCurveNotComplete", - "msg": "The bonding curve has not completed." - }, - { - "code": 6007, - "name": "NotInitialized", - "msg": "The program is not initialized." - }, - { - "code": 6008, - "name": "WithdrawTooFrequent", - "msg": "Withdraw too frequent" - } - ] -} diff --git a/crates/pumpfun-cpi/src/lib.rs b/crates/pumpfun-cpi/src/lib.rs deleted file mode 100755 index 6f3fc8c..0000000 --- a/crates/pumpfun-cpi/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -anchor_gen::generate_cpi_crate!("idl.classic.json"); - -declare_id!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); diff --git a/crates/pumpfun/Cargo.toml b/crates/pumpfun/Cargo.toml deleted file mode 100755 index e6d08e7..0000000 --- a/crates/pumpfun/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "mai3-pumpfun-sdk" -version = "2.4.4" -edition = "2021" -authors = ["William "] -repository = "https://github.com/MiracleAI-Labs/pumpfun-sdk" -description = "Rust SDK to interact with the Pump.fun Solana program." -license = "MIT OR Apache-2.0" -keywords = ["solana", "memecoins", "pumpfun", "pumpfun-sdk", "cpi"] -readme = "README.md" - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -anchor-client = { version = "0.30.1", features = ["async"] } -anchor-spl = "0.30.1" -borsh = { version = "1.5.3", features = ["derive"] } -isahc = "1.7.2" -mpl-token-metadata = "5.1.0" -pumpfun-cpi = { path = "../pumpfun-cpi", version = "1.1.0" } -serde = { version = "1.0.215", features = ["derive"] } -serde_json = "1.0.132" -solana-sdk = "1.18.26" -tokio = "1.42.0" -futures = "0.3.31" -futures-util = "0.3.31" -base64 = "0.22.1" -bs58 = "0.5.1" -rand = "0.8.5" -bincode = "1.3.3" -anyhow = "1.0.90" -reqwest = { version = "0.11.27", features = ["json"] } - diff --git a/crates/pumpfun/LICENSE-APACHE b/crates/pumpfun/LICENSE-APACHE deleted file mode 100755 index c9c4c83..0000000 --- a/crates/pumpfun/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 Rapheal Bamidele - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/crates/pumpfun/LICENSE-MIT b/crates/pumpfun/LICENSE-MIT deleted file mode 100755 index 1372510..0000000 --- a/crates/pumpfun/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Rapheal Bamidele - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/crates/pumpfun/README.md b/crates/pumpfun/README.md deleted file mode 100755 index 93949c7..0000000 --- a/crates/pumpfun/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# Pump.fun Solana Program SDK - -## Overview - -The `Pump.fun Solana Program SDK` is a Rust library that provides an interface for interacting with the Pump.fun Solana program. Pump.fun is a Solana-based marketplace enabling users to create and distribute their own tokens, primarily memecoins. - -## Installation - -Add this crate to your project using cargo: - -```sh -cargo add mai3-pumpfun-sdk -``` - -## Usage - -The main entry point is the `PumpFun` struct which provides methods for interacting with the program: - -> **Note:** The SDK automatically creates Associated Token Accounts (ATAs) when needed during buy transactions. No manual ATA creation is required. - -```rust -use anchor_client::{ - solana_sdk::{ - native_token::LAMPORTS_PER_SOL, - signature::{Keypair, Signature}, - signer::Signer, - }, - Cluster, -}; -use mai3_pumpfun_sdk::{accounts::BondingCurveAccount, utils::CreateTokenMetadata, PriorityFee, PumpFun}; - -// Create a new PumpFun client -let payer: Keypair = Keypair::new(); -let client: PumpFun<'_> = PumpFun::new(Cluster::Mainnet, &payer, None, None); - -// Mint keypair -let mint: Keypair = Keypair::new(); - -// Token metadata -let metadata: CreateTokenMetadata = CreateTokenMetadata { - name: "Lorem ipsum".to_string(), - symbol: "LIP".to_string(), - description: "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quam, nisi.".to_string(), - file: "/path/to/image.png".to_string(), - twitter: None, - telegram: None, - website: Some("https://example.com".to_string()), -}; - -// Optional priority fee to expedite transaction processing (e.g., 100 LAMPORTS per compute unit, equivalent to a 0.01 SOL priority fee) -let fee: Option = Some(PriorityFee { - limit: Some(100_000), - price: Some(100_000_000), -}); - -// Create token with metadata -let signature: Signature = client.create(&mint, metadata.clone(), fee).await?; -println!("Created token: {}", signature); - -// Print amount of SOL and LAMPORTS -let amount_sol: u64 = 1; -let amount_lamports: u64 = LAMPORTS_PER_SOL * amount_sol; -println!("Amount in SOL: {}", amount_sol); -println!("Amount in LAMPORTS: {}", amount_lamports); - -// Create and buy tokens with metadata -let signature: Signature = client.create_and_buy(&mint, metadata.clone(), amount_lamports, None, fee).await?; -println!("Created and bought tokens: {}", signature); - -// Print the curve -let curve: BondingCurveAccount = client.get_bonding_curve_account(&mint.pubkey())?; -println!("{:?}", curve); - -// Buy tokens (ATA will be created automatically if needed) -let signature: Signature = client.buy(&mint.pubkey(), amount_lamports, None, fee).await?; -println!("Bought tokens: {}", signature); - -// Sell tokens (sell all tokens) -let signature: Signature = client.sell(&mint.pubkey(), None, None, fee).await?; -println!("Sold tokens: {}", signature); -``` - -## Features - -- Create new tokens with metadata and custom image -- Buy tokens using SOL with automatic ATA creation -- Sell tokens for SOL with slippage protection -- Query global and bonding curve state -- Calculate prices, fees and slippage -- Priority fee support for faster transactions -- IPFS metadata storage - -## Architecture - -The SDK is organized into several modules: - -- `cpi`: Cross-program invocation interfaces -- `accounts`: Account structs for deserializing on-chain state -- `constants`: Program constants like seeds and public keys -- `error`: Custom error types for error handling -- `instruction`: Transaction instruction builders -- `utils`: Helper functions and utilities - -The main `PumpFun` struct provides high-level methods that abstract away the complexity of: - -- Managing Program Derived Addresses (PDAs) -- Constructing and signing transactions -- Handling account lookups and deserialization -- Calculating prices, fees and slippage -- IPFS metadata uploads -- Priority fee configuration -- Associated Token Account management - -## Contributing - -We welcome contributions! Please submit a pull request or open an issue to discuss any changes. - -## License - -This project is licensed under either of the following licenses, at your option: - -- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) -- MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) - -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. - -## Disclaimer - -This software is provided "as is," without warranty of any kind, express or implied. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. - -**Use at your own risk.** The authors take no responsibility for any harm or damage caused by the use of this software. Users are responsible for ensuring the suitability and safety of this software for their specific use cases. - -By using this software, you acknowledge that you have read, understood, and agree to this disclaimer. diff --git a/crates/pumpfun/RUSTDOC.md b/crates/pumpfun/RUSTDOC.md deleted file mode 100755 index 5ec0730..0000000 --- a/crates/pumpfun/RUSTDOC.md +++ /dev/null @@ -1,111 +0,0 @@ -# Pump.fun Solana Program SDK - -This library provides a Rust interface for interacting with the Pump.fun Solana program. -Pump.fun is a Solana-based marketplace that enables users to create and distribute their own tokens, primarily memecoins. - -## Getting Started - -Add this crate to your project using cargo: - -```sh -cargo add pumpfun -``` - -## Usage - -The main entry point is the `PumpFun` struct which provides methods for interacting with the program: - -> **Note:** The SDK automatically creates Associated Token Accounts (ATAs) when needed during buy transactions. No manual ATA creation is required. - -```rust,no_run -use anchor_client::{ - solana_sdk::{ - native_token::LAMPORTS_PER_SOL, - signature::{Keypair, Signature}, - signer::Signer, - }, - Cluster, -}; -use mai3_pumpfun_sdk::{accounts::BondingCurveAccount, utils::CreateTokenMetadata, PriorityFee, PumpFun}; - -// Create a new PumpFun client -let payer: Keypair = Keypair::new(); -let client: PumpFun = PumpFun::new(Cluster::Mainnet, None, &payer, None, None); - -// Mint keypair -let mint: Keypair = Keypair::new(); - -// Token metadata -let metadata: CreateTokenMetadata = CreateTokenMetadata { - name: "Lorem ipsum".to_string(), - symbol: "LIP".to_string(), - description: "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quam, nisi.".to_string(), - file: "/path/to/image.png".to_string(), - twitter: None, - telegram: None, - website: Some("https://example.com".to_string()), -}; - -// Optional priority fee to expedite transaction processing (e.g., 100 LAMPORTS per compute unit, equivalent to a 0.01 SOL priority fee) -let fee: Option = Some(PriorityFee { - limit: Some(100_000), - price: Some(100_000_000), -}); - -// Create token with metadata -let signature: Signature = client.create(&mint, metadata.clone(), fee).await?; -println!("Created token: {}", signature); - -// Print amount of SOL and LAMPORTS -let amount_sol: u64 = 1; -let amount_lamports: u64 = LAMPORTS_PER_SOL * amount_sol; -println!("Amount in SOL: {}", amount_sol); -println!("Amount in LAMPORTS: {}", amount_lamports); - -// Create and buy tokens with metadata -let signature: Signature = client.create_and_buy(&mint, metadata.clone(), amount_lamports, None, fee).await?; -println!("Created and bought tokens: {}", signature); - -// Print the curve -let curve: BondingCurveAccount = client.get_bonding_curve_account(&mint.pubkey())?; -println!("{:?}", curve); - -// Buy tokens (ATA will be created automatically if needed) -let signature: Signature = client.buy(&mint.pubkey(), amount_lamports, None, fee).await?; -println!("Bought tokens: {}", signature); - -// Sell tokens (sell all tokens) -let signature: Signature = client.sell(&mint.pubkey(), None, None, fee).await?; -println!("Sold tokens: {}", signature); -``` - -## Features - -- Create new tokens with metadata and custom image -- Buy tokens using SOL with automatic ATA creation -- Sell tokens for SOL with slippage protection -- Query global and bonding curve state -- Calculate prices, fees and slippage -- Priority fee support for faster transactions -- IPFS metadata storage - -## Architecture - -The SDK is organized into several modules: - -- `cpi`: Cross-program invocation interfaces -- `accounts`: Account structs for deserializing on-chain state -- `constants`: Program constants like seeds and public keys -- `error`: Custom error types for error handling -- `instruction`: Transaction instruction builders -- `utils`: Helper functions and utilities - -The main `PumpFun` struct provides high-level methods that abstract away the complexity of: - -- Managing Program Derived Addresses (PDAs) -- Constructing and signing transactions -- Handling account lookups and deserialization -- Calculating prices, fees and slippage -- IPFS metadata uploads -- Priority fee configuration -- Associated Token Account management diff --git a/crates/pumpfun/src/sdk.rs b/crates/pumpfun/src/sdk.rs deleted file mode 100644 index d9c9ba2..0000000 --- a/crates/pumpfun/src/sdk.rs +++ /dev/null @@ -1,730 +0,0 @@ -use { - super::jito::JitoClient, crate::{InfrastructureError, InfrastructureResult}, borsh::{BorshDeserialize, BorshSerialize}, solana_client::rpc_client::RpcClient, solana_sdk::{ - commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, instruction::{AccountMeta, Instruction}, pubkey::Pubkey, signature::{Keypair, Signature, Signer}, system_instruction, system_program, sysvar, transaction::Transaction - }, spl_associated_token_account::{get_associated_token_address, instruction::create_associated_token_account}, spl_token, std::str::FromStr -}; - -const PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"; -const DEFAULT_SLIPPAGE: u64 = 500; // 10% -const DEFAULT_COMPUTE_UNIT_LIMIT: u32 = 68_000; -const DEFAULT_COMPUTE_UNIT_PRICE: u64 = 400_000; -pub struct PumpFunSDK { - client: RpcClient, - jito_client: Option, -} - -impl Clone for PumpFunSDK { - fn clone(&self) -> Self { - Self { - client: RpcClient::new_with_commitment( - self.client.url().to_string(), - self.client.commitment() - ), - jito_client: self.jito_client.clone(), - } - } -} - -#[derive(BorshDeserialize, BorshSerialize)] -pub struct GlobalAccount { - pub discriminator: u64, - pub initialized: bool, - pub authority: Pubkey, - pub fee_recipient: Pubkey, - pub initial_virtual_token_reserves: u64, - pub initial_virtual_sol_reserves: u64, - pub initial_real_token_reserves: u64, - pub token_total_supply: u64, - pub fee_basis_points: u64, -} - -impl GlobalAccount { - pub fn get_initial_buy_price(&self, amount: u64) -> u64 { - if amount == 0 { - return 0; - } - - let n = self.initial_virtual_sol_reserves - .checked_mul(self.initial_virtual_token_reserves) - .unwrap_or(0); - - let i = self.initial_virtual_sol_reserves - .checked_add(amount) - .unwrap_or(u64::MAX); - - let r = n.checked_div(i) - .map(|x| x.checked_add(1).unwrap_or(u64::MAX)) - .unwrap_or(0); - - let s = self.initial_virtual_token_reserves - .checked_sub(r) - .unwrap_or(0); - - if s < self.initial_real_token_reserves { - s - } else { - self.initial_real_token_reserves - } - } - - pub fn from_buffer(buffer: &[u8]) -> InfrastructureResult { - Self::try_from_slice(buffer) - .map_err(|e| InfrastructureError::Other(e.to_string())) - } -} - -#[derive(BorshDeserialize, BorshSerialize)] -pub struct BondingCurveAccount { - pub discriminator: u64, - pub virtual_token_reserves: u64, - pub virtual_sol_reserves: u64, - pub real_token_reserves: u64, - pub real_sol_reserves: u64, - pub token_total_supply: u64, - pub complete: bool, -} - -impl BondingCurveAccount { - pub fn get_buy_price(&self, amount: u64) -> InfrastructureResult { - if self.complete { - return Err(InfrastructureError::Other("Curve is complete".to_string())); - } - - if amount == 0 { - return Ok(0); - } - - // 使用 u128 进行计算,模仿 TypeScript 中的 bigint 计算 - let virtual_sol = self.virtual_sol_reserves as u128; - let virtual_token = self.virtual_token_reserves as u128; - let amount = amount as u128; - - // n = virtualSolReserves * virtualTokenReserves - let n = virtual_sol - .checked_mul(virtual_token) - .ok_or_else(|| InfrastructureError::Other("Overflow in virtual reserves multiplication".to_string()))?; - - // i = virtualSolReserves + amount - let i = virtual_sol - .checked_add(amount) - .ok_or_else(|| InfrastructureError::Other("Overflow in sol reserves addition".to_string()))?; - - // r = n / i + 1 - let r = n.checked_div(i) - .ok_or_else(|| InfrastructureError::Other("Division by zero".to_string()))? - .checked_add(1) - .ok_or_else(|| InfrastructureError::Other("Overflow in token calculation".to_string()))?; - - // s = virtualTokenReserves - r - let s = virtual_token - .checked_sub(r) - .ok_or_else(|| InfrastructureError::Other("Underflow in token calculation".to_string()))?; - - // 确保结果不超过 u64 - if s > u64::MAX as u128 { - return Err(InfrastructureError::Other("Result exceeds u64 max value".to_string())); - } - - let result = s as u64; - - // Return the minimum of s and realTokenReserves - Ok(if result < self.real_token_reserves { - result - } else { - self.real_token_reserves - }) - } - - pub fn get_sell_price(&self, amount: u64, fee_basis_points: u64) -> InfrastructureResult { - if self.complete { - return Err(InfrastructureError::Other("Curve is complete".to_string())); - } - - if amount == 0 { - return Ok(0); - } - - let amount = amount as u128; - let virtual_sol = self.virtual_sol_reserves as u128; - let virtual_token = self.virtual_token_reserves as u128; - let fee_basis_points = fee_basis_points as u128; - - // n = (amount * virtualSolReserves) / (virtualTokenReserves + amount) - let n = amount - .checked_mul(virtual_sol) - .ok_or_else(|| InfrastructureError::Other("Overflow in sol calculation".to_string()))? - .checked_div( - virtual_token - .checked_add(amount) - .ok_or_else(|| InfrastructureError::Other("Overflow in token addition".to_string()))? - ) - .ok_or_else(|| InfrastructureError::Other("Division by zero".to_string()))?; - - // a = (n * feeBasisPoints) / 10000 - let a = n - .checked_mul(fee_basis_points) - .ok_or_else(|| InfrastructureError::Other("Overflow in fee calculation".to_string()))? - .checked_div(10000) - .ok_or_else(|| InfrastructureError::Other("Division by zero in fee calculation".to_string()))?; - - // result = n - a - let result = n - .checked_sub(a) - .ok_or_else(|| InfrastructureError::Other("Underflow in final calculation".to_string()))?; - - if result > u64::MAX as u128 { - return Err(InfrastructureError::Other("Result exceeds u64 max value".to_string())); - } - - Ok(result as u64) - } - - pub fn get_market_cap_sol(&self) -> u64 { - if self.virtual_token_reserves == 0 { - return 0; - } - - self.token_total_supply - .checked_mul(self.virtual_sol_reserves) - .and_then(|n| n.checked_div(self.virtual_token_reserves)) - .unwrap_or(0) - } - - pub fn get_final_market_cap_sol(&self, fee_basis_points: u64) -> Result> { - let total_sell_value = self.get_buy_out_price(self.real_token_reserves, fee_basis_points)?; - let total_virtual_value = self.virtual_sol_reserves - .checked_add(total_sell_value) - .ok_or("Overflow in virtual value calculation")?; - let total_virtual_tokens = self.virtual_token_reserves - .checked_sub(self.real_token_reserves) - .ok_or("Underflow in virtual tokens calculation")?; - - if total_virtual_tokens == 0 { - return Ok(0); - } - - self.token_total_supply - .checked_mul(total_virtual_value) - .ok_or("Overflow in market cap calculation")? - .checked_div(total_virtual_tokens) - .ok_or("Division by zero in market cap calculation".into()) - } - - pub fn get_buy_out_price(&self, amount: u64, fee_basis_points: u64) -> Result> { - let sol_tokens = if amount < self.real_sol_reserves { - self.real_sol_reserves - } else { - amount - }; - - let total_sell_value = sol_tokens - .checked_mul(self.virtual_sol_reserves) - .ok_or("Overflow in sell value calculation")? - .checked_div( - self.virtual_token_reserves - .checked_sub(sol_tokens) - .ok_or("Underflow in token reserves calculation")? - ) - .ok_or("Division by zero")? - .checked_add(1) - .ok_or("Overflow in sell value calculation")?; - - let fee = total_sell_value - .checked_mul(fee_basis_points) - .ok_or("Overflow in fee calculation")? - .checked_div(10000) - .ok_or("Division by zero in fee calculation")?; - - total_sell_value - .checked_add(fee) - .ok_or("Overflow in final price calculation".into()) - } - - pub fn from_buffer(buffer: &[u8]) -> Result> { - Self::try_from_slice(buffer).map_err(|e| e.into()) - } -} - -impl PumpFunSDK { - pub fn new(rpc_url: &str) -> Self { - Self { - client: RpcClient::new_with_commitment( - rpc_url.to_string(), - CommitmentConfig::processed(), - ), - jito_client: None, - } - } - - pub fn with_jito(mut self, jito_endpoint: &str) -> Self { - self.jito_client = Some(JitoClient::new(jito_endpoint)); - self - } - - // 添加公共方法来获取代币余额 - pub async fn get_token_balance(&self, wallet_address: &Pubkey, mint: &Pubkey) -> InfrastructureResult { - // 获取用户代币账户地址 - let user_token_account = get_associated_token_address(&wallet_address, &mint); - // 首先检查账户是否存在 - match self.client.get_token_account_balance(&user_token_account) { - Ok(balance) => { - balance.amount - .parse::() - .map_err(|e| InfrastructureError::Other(e.to_string())) - }, - Err(_) => Ok(0) // 如果账户不存在,返回 None - } - } - - // 添加公共方法来获取 SOL 余额 - pub async fn get_sol_balance(&self, address: &Pubkey) -> Result> { - Ok(self.client.get_balance(address)?) - } - - // Buy token with SOL - pub async fn buy( - &self, - buyer: &Keypair, - mint: Pubkey, - buy_amount_sol: u64, - slippage_basis_points: Option, - ) -> InfrastructureResult { - let start_time = std::time::Instant::now(); - println!("Starting buy operation..."); - - // 1. 获取 bonding curve 账户 - let bonding_curve_start = std::time::Instant::now(); - let bonding_curve_address = self.get_bonding_curve_pda(&mint)?; - let bonding_curve = self.get_bonding_curve_account(&bonding_curve_address).await?; - println!("Bonding curve virtual sol reserves: {}", bonding_curve.virtual_sol_reserves); - println!("Bonding curve real sol reserves: {}", bonding_curve.real_sol_reserves); - println!("Got bonding curve account: {:?}ms", bonding_curve_start.elapsed().as_millis()); - - // 2. 计算买入数量和滑点 - let buy_amount = bonding_curve.get_buy_price(buy_amount_sol)?; - let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); - let max_sol_cost = calculate_with_slippage_buy(buy_amount_sol, slippage); - - // 3. 获取全局账户 - let global_start = std::time::Instant::now(); - let global_account = self.get_global_account().await?; - println!("Got global account: {:?}ms", global_start.elapsed().as_millis()); - - // 4. 准备所有指令 - let mut instructions = vec![]; - - instructions.push( - ComputeBudgetInstruction::set_compute_unit_limit(DEFAULT_COMPUTE_UNIT_LIMIT) - ); - - // 5. 添加 ATA 创建指令 - instructions.push( - create_associated_token_account( - &buyer.pubkey(), // payer - &buyer.pubkey(), // wallet - &mint, // mint - &spl_token::id(), // token program - ), - ); - - // 6. 添加买入指令 - instructions.push( - self.create_buy_instruction( - &buyer.pubkey(), - mint, - global_account.fee_recipient, - buy_amount, - max_sol_cost, - bonding_curve_address, - )?, - ); - - // 7. 创建并发送交易 - let tx_start = std::time::Instant::now(); - let recent_blockhash = self.client.get_latest_blockhash()?; - let transaction = Transaction::new_signed_with_payer( - &instructions, - Some(&buyer.pubkey()), - &[buyer], - recent_blockhash, - ); - - let signature = self.client - .send_transaction(&transaction) - .map_err(|e| InfrastructureError::Other(format!("Transaction failed: {}", e)))?; - println!("Transaction sent and confirmed: {:?}ms", tx_start.elapsed().as_millis()); - - println!("Total buy operation time: {:?}ms", start_time.elapsed().as_millis()); - Ok(signature) - } - - // Sell token for SOL - pub async fn sell( - &self, - seller: &Keypair, - mint: Pubkey, - sell_amount: u64, - slippage_basis_points: Option, - ) -> Result> { - let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); - - // 1. 获取账户信息 - let bonding_curve_address = self.get_bonding_curve_pda(&mint)?; - let bonding_curve = self.get_bonding_curve_account(&bonding_curve_address).await?; - let global_account = self.get_global_account().await?; - - // 2. 计算卖出价格和滑点 - let sell_price = bonding_curve.get_sell_price(sell_amount, global_account.fee_basis_points)?; - let min_out_amount = calculate_with_slippage_sell(sell_price, slippage); - - // 3. 构建交易指令 - let instructions = vec![ - // // 设置计算预算 - // ComputeBudgetInstruction::set_compute_unit_limit(DEFAULT_COMPUTE_UNIT_LIMIT), - // ComputeBudgetInstruction::set_compute_unit_price(DEFAULT_COMPUTE_UNIT_PRICE), - - - // 卖出指令 - self.create_sell_instruction( - &seller.pubkey(), - mint, - global_account.fee_recipient, - sell_amount, - min_out_amount, - bonding_curve_address, - )?, - ]; - - // 5. 发送交易 - let recent_blockhash = self.client.get_latest_blockhash()?; - let transaction = Transaction::new_signed_with_payer( - &instructions, - Some(&seller.pubkey()), - &[seller], - recent_blockhash, - ); - - let signature = self.client.send_transaction(&transaction)?; - println!("Transaction sent: {}", signature); - - Ok(signature) - } - - // 辅助函数:获取 bonding curve PDA - fn get_bonding_curve_pda(&self, mint: &Pubkey) -> InfrastructureResult { - let seeds: &[&[u8]] = &[b"bonding-curve", mint.as_ref()]; - let (pda, _) = Pubkey::find_program_address( - seeds, - &Pubkey::from_str(PROGRAM_ID).unwrap(), - ); - Ok(pda) - } - - // Helper functions - fn create_buy_instruction( - &self, - buyer: &Pubkey, - mint: Pubkey, - fee_recipient: Pubkey, - amount: u64, - max_sol_cost: u64, - bonding_curve_address: Pubkey, - ) -> InfrastructureResult { - let associated_bonding_curve = get_associated_token_address(&bonding_curve_address, &mint); - let associated_user = get_associated_token_address(buyer, &mint); - let global_seeds: &[&[u8]] = &[b"global"]; - let (global_pda, _) = Pubkey::find_program_address( - global_seeds, - &Pubkey::from_str(PROGRAM_ID).unwrap(), - ); - - let accounts = vec![ - AccountMeta::new_readonly(global_pda, false), - AccountMeta::new(fee_recipient, false), - AccountMeta::new_readonly(mint, false), - AccountMeta::new(bonding_curve_address, false), - AccountMeta::new(associated_bonding_curve, false), - AccountMeta::new(associated_user, false), - AccountMeta::new(*buyer, true), - AccountMeta::new_readonly(system_program::id(), false), - AccountMeta::new_readonly(spl_token::id(), false), - AccountMeta::new_readonly(sysvar::rent::id(), false), - AccountMeta::new_readonly(Pubkey::from_str("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1").unwrap(), false), - AccountMeta::new_readonly(Pubkey::from_str(PROGRAM_ID).unwrap(), false), - ]; - - let mut data = Vec::with_capacity(8 + 8 + 8); - data.extend_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // discriminator - data.extend_from_slice(&amount.to_le_bytes()); - data.extend_from_slice(&max_sol_cost.to_le_bytes()); - - Ok(Instruction { - program_id: Pubkey::from_str(PROGRAM_ID).unwrap(), - accounts, - data, - }) - } - - fn create_sell_instruction( - &self, - seller: &Pubkey, - mint: Pubkey, - fee_recipient: Pubkey, - amount: u64, - min_sol_output: u64, - bonding_curve_address: Pubkey, - ) -> InfrastructureResult { - let associated_bonding_curve = get_associated_token_address(&bonding_curve_address, &mint); - let associated_user = get_associated_token_address(seller, &mint); - let global_seeds: &[&[u8]] = &[b"global"]; - let (global_pda, _) = Pubkey::find_program_address( - global_seeds, - &Pubkey::from_str(PROGRAM_ID).unwrap(), - ); - - let accounts = vec![ - AccountMeta::new_readonly(global_pda, false), - AccountMeta::new(fee_recipient, false), - AccountMeta::new_readonly(mint, false), - AccountMeta::new(bonding_curve_address, false), - AccountMeta::new(associated_bonding_curve, false), - AccountMeta::new(associated_user, false), - AccountMeta::new(*seller, true), - AccountMeta::new_readonly(system_program::id(), false), - AccountMeta::new_readonly(spl_associated_token_account::id(), false), - AccountMeta::new_readonly(spl_token::id(), false), - AccountMeta::new_readonly(Pubkey::from_str("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1").unwrap(), false), - AccountMeta::new_readonly(Pubkey::from_str(PROGRAM_ID).unwrap(), false), - ]; - - let mut data = Vec::with_capacity(8 + 8 + 8); - data.extend_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // discriminator - data.extend_from_slice(&amount.to_le_bytes()); - data.extend_from_slice(&min_sol_output.to_le_bytes()); - - Ok(Instruction { - program_id: Pubkey::from_str(PROGRAM_ID).unwrap(), - accounts, - data, - }) - } - - pub async fn get_global_account(&self) -> InfrastructureResult { - let seeds: &[&[u8]] = &[b"global"]; - let (global_account_pda, _) = Pubkey::find_program_address( - seeds, - &Pubkey::from_str(PROGRAM_ID).unwrap(), - ); - - let account = self.client.get_account(&global_account_pda) - .map_err(|e| InfrastructureError::Other(e.to_string()))?; - GlobalAccount::from_buffer(&account.data) - .map_err(|e| InfrastructureError::Other(e.to_string())) - } - - pub async fn get_bonding_curve_account(&self, bonding_curve_address: &Pubkey) -> InfrastructureResult { - let account = self.client.get_account(&bonding_curve_address) - .map_err(|e| InfrastructureError::Other(e.to_string()))?; - BondingCurveAccount::from_buffer(&account.data) - .map_err(|e| InfrastructureError::Other(e.to_string())) - } - - /// 使用 Jito MEV 发送买入交易 - pub async fn buy_via_jito( - &self, - buyer: &Keypair, - mint: Pubkey, - buy_amount_sol: u64, - slippage_basis_points: Option, - ) -> InfrastructureResult { - let jito_client = self.jito_client.as_ref() - .ok_or_else(|| InfrastructureError::Other("Jito client not configured".to_string()))?; - - let start_time = std::time::Instant::now(); - println!("Starting Jito buy operation..."); - - // 1. 获取 bonding curve 账户 - let bonding_curve_address = self.get_bonding_curve_pda(&mint)?; - let bonding_curve = self.get_bonding_curve_account(&bonding_curve_address).await?; - - // 2. 计算买入数量和滑点 - let buy_amount = bonding_curve.get_buy_price(buy_amount_sol)?; - let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); - let max_sol_cost = calculate_with_slippage_buy(buy_amount_sol, slippage); - - // 3. 获取全局账户 - let global_account = self.get_global_account().await?; - - // 4. 获取优先费用估算 - let priority_fees = jito_client.estimate_priority_fees(&bonding_curve_address).await?; - - // 计算每计算单元的优先费用(使用 Extreme 级别) - let priority_fee_per_cu = priority_fees.per_compute_unit.extreme; - - // 完整的单位转换过程 - let total_priority_fee_microlamports = priority_fee_per_cu as u128 * DEFAULT_COMPUTE_UNIT_LIMIT as u128; - let total_priority_fee_lamports = total_priority_fee_microlamports / 1_000_000; - let total_priority_fee_sol = total_priority_fee_lamports as f64 / 1_000_000_000.0; - - println!("Priority fee details:"); - println!(" Per CU (microlamports): {}", priority_fee_per_cu); - println!(" Total (lamports): {}", total_priority_fee_lamports); - println!(" Total (SOL): {:.9}", total_priority_fee_sol); - - // 5. 获取 tip account - let tip_account = jito_client.get_tip_account().await?; - - // 6. 准备所有指令 - let mut instructions = vec![]; - - // 添加计算预算指令,包括优先费用 - instructions.push( - ComputeBudgetInstruction::set_compute_unit_limit(DEFAULT_COMPUTE_UNIT_LIMIT) - ); - - // 添加 ATA 创建指令 - instructions.push( - create_associated_token_account( - &buyer.pubkey(), - &buyer.pubkey(), - &mint, - &spl_token::id(), - ), - ); - - // 添加买入指令 - instructions.push( - self.create_buy_instruction( - &buyer.pubkey(), - mint, - global_account.fee_recipient, - buy_amount, - max_sol_cost, - bonding_curve_address, - )?, - ); - - // 添加 tip 指令 - instructions.push( - system_instruction::transfer( - &buyer.pubkey(), - &tip_account, - total_priority_fee_lamports as u64, - ), - ); - - // 7. 创建并发送交易 - let recent_blockhash = self.client.get_latest_blockhash()?; - let transaction = Transaction::new_signed_with_payer( - &instructions, - Some(&buyer.pubkey()), - &[buyer], - recent_blockhash, - ); - - // 8. 通过 Jito 发送交易 - let signature = jito_client.send_transaction(&transaction).await?; - println!("Total Jito buy operation time: {:?}ms", start_time.elapsed().as_millis()); - - Ok(signature) - } - - pub fn get_jito_client(&self) -> Option<&JitoClient> { - self.jito_client.as_ref() - } - - // 添加基于 Jito 的卖出方法 - pub async fn sell_via_jito( - &self, - seller: &Keypair, - mint: Pubkey, - sell_amount: u64, - slippage_basis_points: Option, - ) -> InfrastructureResult { - let jito_client = self.jito_client.as_ref() - .ok_or_else(|| InfrastructureError::Other("Jito client not configured".to_string()))?; - - let start_time = std::time::Instant::now(); - println!("Starting Jito sell operation..."); - - // 1. 获取账户信息 - let bonding_curve_address = self.get_bonding_curve_pda(&mint)?; - let bonding_curve = self.get_bonding_curve_account(&bonding_curve_address).await?; - let global_account = self.get_global_account().await?; - - // 2. 计算卖出价格和滑点 - let sell_price = bonding_curve.get_sell_price(sell_amount, global_account.fee_basis_points)?; - let slippage = slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE); - let min_out_amount = calculate_with_slippage_sell(sell_price, slippage); - - // 3. 获取优先费用估算 - let priority_fees = jito_client.estimate_priority_fees(&bonding_curve_address).await?; - let priority_fee_per_cu = priority_fees.per_compute_unit.extreme; - - // 完整的单位转换过程 - let total_priority_fee_microlamports = priority_fee_per_cu as u128 * DEFAULT_COMPUTE_UNIT_LIMIT as u128; - let total_priority_fee_lamports = total_priority_fee_microlamports / 1_000_000; - let total_priority_fee_sol = total_priority_fee_lamports as f64 / 1_000_000_000.0; - - println!("Priority fee details:"); - println!(" Per CU (microlamports): {}", priority_fee_per_cu); - println!(" Total (lamports): {}", total_priority_fee_lamports); - println!(" Total (SOL): {:.9}", total_priority_fee_sol); - - // 4. 获取 tip account - let tip_account = jito_client.get_tip_account().await?; - - // 5. 准备所有指令 - let mut instructions = vec![ - // 添加计算预算指令 - ComputeBudgetInstruction::set_compute_unit_limit(DEFAULT_COMPUTE_UNIT_LIMIT), - - // 卖出指令 - self.create_sell_instruction( - &seller.pubkey(), - mint, - global_account.fee_recipient, - sell_amount, - min_out_amount, - bonding_curve_address, - )?, - - // 添加 tip 指令 - system_instruction::transfer( - &seller.pubkey(), - &tip_account, - total_priority_fee_lamports as u64, - ), - ]; - - // 6. 创建并发送交易 - let recent_blockhash = self.client.get_latest_blockhash()?; - let transaction = Transaction::new_signed_with_payer( - &instructions, - Some(&seller.pubkey()), - &[seller], - recent_blockhash, - ); - - // 7. 通过 Jito 发送交易 - let signature = jito_client.send_transaction(&transaction).await?; - println!("Total Jito sell operation time: {:?}ms", start_time.elapsed().as_millis()); - - Ok(signature) - } -} - -fn calculate_with_slippage_sell(amount: u64, basis_points: u64) -> u64 { - amount.saturating_sub( - amount.saturating_mul(basis_points) - .saturating_div(10000) - ) -} - -// 添加滑点计算函数 -fn calculate_with_slippage_buy(amount: u64, basis_points: u64) -> u64 { - amount.saturating_add( - amount.saturating_mul(basis_points) - .saturating_div(10000) - ) -} \ No newline at end of file diff --git a/crates/pumpfun/src/accounts/bonding_curve.rs b/src/accounts/bonding_curve.rs similarity index 100% rename from crates/pumpfun/src/accounts/bonding_curve.rs rename to src/accounts/bonding_curve.rs diff --git a/crates/pumpfun/src/accounts/global.rs b/src/accounts/global.rs similarity index 99% rename from crates/pumpfun/src/accounts/global.rs rename to src/accounts/global.rs index 1357f0f..3da2250 100755 --- a/crates/pumpfun/src/accounts/global.rs +++ b/src/accounts/global.rs @@ -23,7 +23,7 @@ //! - `new`: Creates a new global account instance //! - `get_initial_buy_price`: Calculates the initial amount of tokens received for a given SOL amount -use anchor_client::solana_sdk::pubkey::Pubkey; +use solana_sdk::pubkey::Pubkey; use borsh::{BorshDeserialize, BorshSerialize}; /// Represents the global configuration account for token pricing and fees diff --git a/crates/pumpfun/src/accounts/mod.rs b/src/accounts/mod.rs similarity index 100% rename from crates/pumpfun/src/accounts/mod.rs rename to src/accounts/mod.rs diff --git a/crates/pumpfun/src/constants/mod.rs b/src/constants/mod.rs similarity index 100% rename from crates/pumpfun/src/constants/mod.rs rename to src/constants/mod.rs diff --git a/crates/pumpfun/src/error/mod.rs b/src/error/mod.rs similarity index 94% rename from crates/pumpfun/src/error/mod.rs rename to src/error/mod.rs index 656a62e..a531694 100755 --- a/crates/pumpfun/src/error/mod.rs +++ b/src/error/mod.rs @@ -12,15 +12,13 @@ //! - `BorshError`: An error occurred while serializing or deserializing data using Borsh. //! - `SolanaClientError`: An error occurred while interacting with the Solana RPC client. //! - `UploadMetadataError`: An error occurred while uploading metadata to IPFS. -//! - `AnchorClientError`: An error occurred while interacting with the Anchor client. //! - `InvalidInput`: Invalid input parameters were provided. //! - `InsufficientFunds`: Insufficient funds for a transaction. //! - `SimulationError`: Transaction simulation failed. //! - `RateLimitExceeded`: Rate limit exceeded. -use anchor_client::solana_client; use serde_json::Error; -use anchor_client::solana_client::{ +use solana_client::{ client_error::ClientError as SolanaClientError, pubsub_client::PubsubClientError }; @@ -38,8 +36,6 @@ pub enum ClientError { SolanaClientError(solana_client::client_error::ClientError), /// Error uploading metadata UploadMetadataError(Box), - /// Error from Anchor client - AnchorClientError(anchor_client::ClientError), /// Invalid input parameters InvalidInput(&'static str), /// Insufficient funds for transaction @@ -94,7 +90,6 @@ impl std::fmt::Display for ClientError { Self::BorshError(err) => write!(f, "Borsh serialization error: {}", err), Self::SolanaClientError(err) => write!(f, "Solana client error: {}", err), Self::UploadMetadataError(err) => write!(f, "Metadata upload error: {}", err), - Self::AnchorClientError(err) => write!(f, "Anchor client error: {}", err), Self::InvalidInput(msg) => write!(f, "Invalid input: {}", msg), Self::InsufficientFunds => write!(f, "Insufficient funds for transaction"), Self::SimulationError(msg) => write!(f, "Transaction simulation failed: {}", msg), @@ -126,7 +121,6 @@ impl std::error::Error for ClientError { Self::BorshError(err) => Some(err), Self::SolanaClientError(err) => Some(err), Self::UploadMetadataError(err) => Some(err.as_ref()), - Self::AnchorClientError(err) => Some(err), Self::ExternalService(_) => None, Self::Redis(_, _) => None, Self::Solana(_, _) => None, diff --git a/src/grpc/event.rs b/src/grpc/event.rs new file mode 100755 index 0000000..b69d096 --- /dev/null +++ b/src/grpc/event.rs @@ -0,0 +1,123 @@ +use anyhow::anyhow; +use base64::engine::general_purpose; +use base64::Engine; +use borsh::{BorshDeserialize, BorshSerialize}; +use regex::Regex; +use solana_sdk::pubkey::Pubkey; + +use super::myerror::AppError; + +pub const PROGRAM_DATA: &str = "Program data: "; + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct TradeEvent { + pub mint: Pubkey, + pub sol_amount: u64, + pub token_amount: u64, + pub is_buy: bool, + pub user: Pubkey, + pub timestamp: u64, + pub virtual_sol_reserves: u64, + pub virtual_token_reserves: u64, + pub real_sol_reserves: u64, + pub real_token_reserves: u64, +} + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct CompleteEvent { + pub user: Pubkey, + pub mint: Pubkey, + pub bonding_curve: Pubkey, + pub timestamp: u64, +} + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct SwapBaseInLog { + pub log_type: u8, + // input + pub amount_in: u64, + pub minimum_out: u64, + pub direction: u64, + // user info + pub user_source: u64, + // pool info + pub pool_coin: u64, + pub pool_pc: u64, + // calc result + pub out_amount: u64, +} + +pub trait EventTrait: Sized + std::fmt::Debug { + fn from_bytes(bytes: &[u8]) -> Result; +} + +impl EventTrait for TradeEvent { + fn from_bytes(bytes: &[u8]) -> Result { + TradeEvent::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +impl EventTrait for CompleteEvent { + fn from_bytes(bytes: &[u8]) -> Result { + CompleteEvent::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +impl EventTrait for SwapBaseInLog { + fn from_bytes(bytes: &[u8]) -> Result { + SwapBaseInLog::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PumpEvent {} + +impl PumpEvent { + pub fn parse_logs(logs: &Vec) -> Option { + let mut event: Option = None; + if !logs.is_empty() { + let logs_iter = logs.iter().peekable(); + + for l in logs_iter.rev() { + if let Some(log) = l.strip_prefix(PROGRAM_DATA) { + let borsh_bytes = general_purpose::STANDARD.decode(log).unwrap(); + let slice: &[u8] = &borsh_bytes[8..]; + + if let Ok(e) = T::from_bytes(slice) { + event = Some(e); + } + } + } + } + event + } +} + +#[derive(Debug, Clone, Copy)] +pub struct RaydiumEvent {} + +impl RaydiumEvent { + pub fn parse_logs(logs: &Vec) -> Option { + let mut event: Option = None; + + if !logs.is_empty() { + let logs_iter = logs.iter().peekable(); + + for l in logs_iter.rev() { + let re = Regex::new(r"ray_log: (?P[A-Za-z0-9+/=]+)").unwrap(); + + if let Some(caps) = re.captures(l) { + if let Some(base64) = caps.name("base64") { + let bytes = general_purpose::STANDARD.decode(base64.as_str()).unwrap(); + + if let Ok(e) = T::from_bytes(&bytes) { + event = Some(e); + } + } + } + } + } + + event + } +} \ No newline at end of file diff --git a/src/grpc/mod.rs b/src/grpc/mod.rs new file mode 100755 index 0000000..45e31c0 --- /dev/null +++ b/src/grpc/mod.rs @@ -0,0 +1,7 @@ + +pub mod event; +pub mod myerror; +// pub mod subscribe_logs; +// pub mod subscribe_tx; +pub mod yellowstone_grpc; + diff --git a/src/grpc/myerror.rs b/src/grpc/myerror.rs new file mode 100755 index 0000000..e1beb9d --- /dev/null +++ b/src/grpc/myerror.rs @@ -0,0 +1,13 @@ +use anyhow::Error; +#[derive(Debug)] +#[allow(dead_code)] +pub struct AppError(Error); + +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} \ No newline at end of file diff --git a/src/grpc/subscribe_logs.rs b/src/grpc/subscribe_logs.rs new file mode 100755 index 0000000..5a0fef1 --- /dev/null +++ b/src/grpc/subscribe_logs.rs @@ -0,0 +1,136 @@ +#[cfg(test)] +mod subscribe_tx_tests { + use crate::common::{ + event::{PumpEvent, RaydiumEvent, SwapBaseInLog, TradeEvent}, + myerror::AppError, + yellowstone_grpc::{TransactionPretty, YellowstoneGrpc}, + }; + use anyhow::anyhow; + use chrono::Local; + use dotenvy::dotenv; + use futures::{channel::mpsc, sink::SinkExt, stream::StreamExt}; + use log::{error, info}; + use solana_sdk::pubkey; + use solana_sdk::pubkey::Pubkey; + use solana_transaction_status::{ + option_serializer::OptionSerializer, EncodedTransactionWithStatusMeta, + }; + use std::env; + use tokio::test; + use yellowstone_grpc_proto::geyser::{ + subscribe_update::UpdateOneof, SubscribeRequest, SubscribeRequestPing, + }; + const AMM_V4: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); + const PUMP_PROGRAM_ID: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); + pub enum SwapType { + Pump, + Raydium, + } + + #[test] + async fn test_subscribe_tx() -> Result<(), AppError> { + dotenv().ok(); + pretty_env_logger::init_custom_env("RUST_LOG"); + let yellowstone_url = env::var("YELLOWSTONE_URL")?; + + info!("::: {:?}", yellowstone_url); + let yellowstone_grpc = YellowstoneGrpc::new(yellowstone_url); + + let addrs = vec![ + "Aa4QWNkS3RLUv7DA9BM1a2Hzm4HDQo5PyRefqDJnpump".to_string(), + "BnDssYyGDF9aj5j2N5BwsJFk9YMneQ8P7LQkoYkrpump".to_string(), + ]; + let transactions = yellowstone_grpc.subscribe_transaction(addrs, vec![], vec![]); + + let (mut subscribe_tx, mut stream) = yellowstone_grpc.connect(transactions).await??; + + let (mut tx, mut rx) = mpsc::channel::(1000); + + tokio::spawn(async move { + while let Some(message) = stream.next().await { + match message { + Ok(msg) => { + match msg.update_oneof { + Some(UpdateOneof::Transaction(sut)) => { + let transaction_pretty: TransactionPretty = sut.into(); + let _ = tx.try_send(transaction_pretty); + } + Some(UpdateOneof::Ping(_)) => { + // This is necessary to keep load balancers that expect client pings alive. If your load balancer doesn't + // require periodic client pings then this is unnecessary + let _ = subscribe_tx + .send(SubscribeRequest { + ping: Some(SubscribeRequestPing { id: 1 }), + ..Default::default() + }) + .await; + info!("service is ping: {}", Local::now()); + } + Some(UpdateOneof::Pong(_)) => { + info!("service is pong: {}", Local::now()); + } + _ => {} + } + continue; + } + Err(error) => { + error!("error: {error:?}"); + break; + } + } + } + }); + + while let Some(transaction_pretty) = rx.next().await { + let trade_raw = transaction_pretty.tx.clone(); + let meta = &trade_raw.meta.clone().unwrap(); + if meta.err.is_some() { + continue; + } + + let logs = if let OptionSerializer::Some(logs) = &meta.log_messages { + logs + } else { + &vec![] + }; + + if let Ok(swap_type) = get_swap_type(&trade_raw) { + match swap_type { + SwapType::Raydium => { + let event = RaydiumEvent::parse_logs::(logs); + info!("RaydiumEvent {:#?}", event); + } + SwapType::Pump => { + let event = PumpEvent::parse_logs::(logs); + info!("PumpEvent {:#?}", event); + } + } + }; + } + Ok(()) + } + + #[allow(dead_code)] + pub fn get_swap_type( + trade_raw: &EncodedTransactionWithStatusMeta, + ) -> Result { + let transaction = &trade_raw.transaction.decode(); + if let Some(transaction) = transaction { + let account_keys = transaction.message.static_account_keys(); + + let program_index = account_keys + .iter() + .position(|item| item == &AMM_V4 || item == &PUMP_PROGRAM_ID) + .ok_or(anyhow!("swap type program_id not found"))?; + + let program_id = account_keys[program_index]; + let _type = match program_id { + AMM_V4 => Ok(SwapType::Raydium), + PUMP_PROGRAM_ID => Ok(SwapType::Pump), + _ => Err(AppError::from(anyhow!("program_id ix not found"))), + }; + return _type; + } + Err(AppError::from(anyhow!("program_id ix not found"))) + } +} \ No newline at end of file diff --git a/src/grpc/subscribe_tx.rs b/src/grpc/subscribe_tx.rs new file mode 100755 index 0000000..7fb0aa0 --- /dev/null +++ b/src/grpc/subscribe_tx.rs @@ -0,0 +1,73 @@ +#[cfg(test)] +mod subscribe_tx_tests { + use crate::common::{ + myerror::AppError, + yellowstone_grpc::{TransactionPretty, YellowstoneGrpc}, + }; + use chrono::Local; + use dotenvy::dotenv; + use futures::{channel::mpsc, sink::SinkExt, stream::StreamExt}; + use log::{error, info}; + use std::env; + use tokio::test; + use yellowstone_grpc_proto::geyser::{ + subscribe_update::UpdateOneof, SubscribeRequest, SubscribeRequestPing, + }; + + #[test] + async fn test_subscribe_tx() -> Result<(), AppError> { + dotenv().ok(); + pretty_env_logger::init_custom_env("RUST_LOG"); + let yellowstone_url = env::var("YELLOWSTONE_URL")?; + + info!("::: {:?}", yellowstone_url); + let yellowstone_grpc = YellowstoneGrpc::new(yellowstone_url); + + let addrs = vec!["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P".to_string()]; + let transactions = yellowstone_grpc.subscribe_transaction(addrs, vec![], vec![]); + + let (mut subscribe_tx, mut stream) = yellowstone_grpc.connect(transactions).await??; + + let (mut tx, mut rx) = mpsc::channel::(1000); + + tokio::spawn(async move { + while let Some(message) = stream.next().await { + match message { + Ok(msg) => { + match msg.update_oneof { + Some(UpdateOneof::Transaction(sut)) => { + let transaction_pretty: TransactionPretty = sut.into(); + let _ = tx.try_send(transaction_pretty); + } + Some(UpdateOneof::Ping(_)) => { + // This is necessary to keep load balancers that expect client pings alive. If your load balancer doesn't + // require periodic client pings then this is unnecessary + let _ = subscribe_tx + .send(SubscribeRequest { + ping: Some(SubscribeRequestPing { id: 1 }), + ..Default::default() + }) + .await; + info!("service is ping: {}", Local::now()); + } + Some(UpdateOneof::Pong(_)) => { + info!("service is pong: {}", Local::now()); + } + _ => {} + } + continue; + } + Err(error) => { + error!("error: {error:?}"); + break; + } + } + } + }); + + while let Some(event) = rx.next().await { + info!("TransactionPretty {:#?}", event); + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/grpc/yellowstone_grpc.rs b/src/grpc/yellowstone_grpc.rs new file mode 100755 index 0000000..ab1d32b --- /dev/null +++ b/src/grpc/yellowstone_grpc.rs @@ -0,0 +1,284 @@ +use std::{collections::HashMap, fmt, time::Duration}; + +use futures::{channel::mpsc, sink::Sink, Stream, StreamExt, SinkExt}; +use rustls::crypto::{ring::default_provider, CryptoProvider}; +use tonic::{transport::channel::ClientTlsConfig, Status}; +use yellowstone_grpc_client::{GeyserGrpcClient, GeyserGrpcClientResult}; +use yellowstone_grpc_proto::geyser::{ + CommitmentLevel, SubscribeRequest, SubscribeRequestFilterTransactions, SubscribeUpdate, + SubscribeUpdateTransaction, subscribe_update::UpdateOneof, SubscribeRequestPing, +}; +use log::{error, info}; +use chrono::Local; +use solana_sdk::{pubkey, pubkey::Pubkey, signature::Signature}; +use solana_transaction_status::{ + option_serializer::OptionSerializer, EncodedTransactionWithStatusMeta, UiTransactionEncoding, +}; +use anyhow::anyhow; + +use crate::grpc::event::{PumpEvent, RaydiumEvent, SwapBaseInLog, TradeEvent}; +use crate::grpc::myerror::AppError; + +// 类型别名定义 +type TransactionsFilterMap = HashMap; +type GrpcStreamResult = GeyserGrpcClientResult<( + Box + Unpin + Send>, + Box> + Unpin + Send>, +)>; + +// 常量定义 +const AMM_V4: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"); +const PUMP_PROGRAM_ID: Pubkey = pubkey!("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"); +const CONNECT_TIMEOUT: u64 = 10; +const REQUEST_TIMEOUT: u64 = 60; +const CHANNEL_SIZE: usize = 1000; + +// 枚举定义 +#[derive(Debug)] +pub enum SwapType { + Pump, + Raydium, +} + +// 结构体定义 +#[allow(dead_code)] +pub struct TransactionPretty { + pub slot: u64, + pub signature: Signature, + pub is_vote: bool, + pub tx: EncodedTransactionWithStatusMeta, +} + +impl fmt::Debug for TransactionPretty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct TxWrap<'a>(&'a EncodedTransactionWithStatusMeta); + impl<'a> fmt::Debug for TxWrap<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let serialized = serde_json::to_string(self.0).expect("failed to serialize"); + fmt::Display::fmt(&serialized, f) + } + } + + f.debug_struct("TransactionPretty") + .field("slot", &self.slot) + .field("signature", &self.signature) + .field("is_vote", &self.is_vote) + .field("tx", &TxWrap(&self.tx)) + .finish() + } +} + +impl From for TransactionPretty { + fn from(SubscribeUpdateTransaction { transaction, slot }: SubscribeUpdateTransaction) -> Self { + let tx = transaction.expect("should be defined"); + Self { + slot, + signature: Signature::try_from(tx.signature.as_slice()).expect("valid signature"), + is_vote: tx.is_vote, + tx: yellowstone_grpc_proto::convert_from::create_tx_with_meta(tx) + .expect("valid tx with meta") + .encode(UiTransactionEncoding::Base64, Some(u8::MAX), true) + .expect("failed to encode"), + } + } +} + +pub struct YellowstoneGrpc { + endpoint: String, +} + +impl YellowstoneGrpc { + pub fn new(endpoint: String) -> Self { + Self { endpoint } + } + + pub async fn connect( + &self, + transactions: TransactionsFilterMap, + ) -> Result< + GeyserGrpcClientResult<( + impl Sink, + impl Stream>, + )>, + AppError, + > { + if CryptoProvider::get_default().is_none() { + default_provider() + .install_default() + .map_err(|e| anyhow::anyhow!("Failed to install crypto provider: {:?}", e))?; + } + + let mut client = GeyserGrpcClient::build_from_shared(self.endpoint.clone())? + .tls_config(ClientTlsConfig::new().with_native_roots())? + .connect_timeout(Duration::from_secs(10)) + .timeout(Duration::from_secs(60)) + .connect() + .await?; + + let subscribe_request = SubscribeRequest { + transactions, + commitment: Some(CommitmentLevel::Processed.into()), + ..Default::default() + }; + + Ok(client.subscribe_with_request(Some(subscribe_request)).await) + } + + pub async fn subscribe_accounts(&self, accounts: Vec) -> Result<(), AppError> { + let transactions = self.get_subscribe_request_filter(accounts, vec![], vec![]); + let (mut subscribe_tx, mut stream) = self.connect(transactions).await??; + let (mut tx, mut rx) = mpsc::channel::(CHANNEL_SIZE); + + tokio::spawn(async move { + while let Some(message) = stream.next().await { + match message { + Ok(msg) => { + if let Err(e) = Self::handle_stream_message(msg, &mut tx, &mut subscribe_tx).await { + error!("Error handling message: {:?}", e); + break; + } + } + Err(error) => { + error!("Stream error: {error:?}"); + break; + } + } + } + }); + + while let Some(transaction_pretty) = rx.next().await { + if let Err(e) = Self::process_transaction(transaction_pretty).await { + error!("Error processing transaction: {:?}", e); + } + } + Ok(()) + } + + pub async fn subscribe_pumpfun(&self) -> Result<(), AppError> { + let addrs = vec![PUMP_PROGRAM_ID.to_string()]; + let transactions = self.get_subscribe_request_filter(addrs, vec![], vec![]); + let (mut subscribe_tx, mut stream) = self.connect(transactions).await??; + let (mut tx, mut rx) = mpsc::channel::(CHANNEL_SIZE); + + tokio::spawn(async move { + while let Some(message) = stream.next().await { + match message { + Ok(msg) => { + if let Err(e) = Self::handle_stream_message(msg, &mut tx, &mut subscribe_tx).await { + error!("Error handling message: {:?}", e); + break; + } + } + Err(error) => { + error!("Stream error: {error:?}"); + break; + } + } + } + }); + + while let Some(transaction_pretty) = rx.next().await { + if let Err(e) = Self::process_transaction(transaction_pretty).await { + error!("Error processing transaction: {:?}", e); + } + } + Ok(()) + } + + pub fn get_subscribe_request_filter( + &self, + account_include: Vec, + account_exclude: Vec, + account_required: Vec, + ) -> TransactionsFilterMap { + let mut transactions = HashMap::new(); + transactions.insert( + "client".to_string(), + SubscribeRequestFilterTransactions { + vote: Some(false), + failed: Some(false), + signature: None, + account_include, + account_exclude, + account_required, + }, + ); + transactions + } + + + async fn handle_stream_message( + msg: SubscribeUpdate, + tx: &mut mpsc::Sender, + subscribe_tx: &mut (impl Sink + Unpin), + ) -> Result<(), AppError> { + match msg.update_oneof { + Some(UpdateOneof::Transaction(sut)) => { + let transaction_pretty = TransactionPretty::from(sut); + tx.try_send(transaction_pretty).map_err(|e| AppError::from(anyhow!("Send error: {:?}", e)))?; + } + Some(UpdateOneof::Ping(_)) => { + subscribe_tx + .send(SubscribeRequest { + ping: Some(SubscribeRequestPing { id: 1 }), + ..Default::default() + }) + .await + .map_err(|e| AppError::from(anyhow!("Ping error: {:?}", e)))?; + info!("service is ping: {}", Local::now()); + } + Some(UpdateOneof::Pong(_)) => { + info!("service is pong: {}", Local::now()); + } + _ => {} + } + Ok(()) + } + + async fn process_transaction(transaction_pretty: TransactionPretty) -> Result<(), AppError> { + let trade_raw = transaction_pretty.tx; + let meta = trade_raw.meta.as_ref() + .ok_or_else(|| AppError::from(anyhow!("Missing transaction metadata")))?; + + if meta.err.is_some() { + return Ok(()); + } + + let logs = if let OptionSerializer::Some(logs) = &meta.log_messages { + logs + } else { + &vec![] + }; + + if let Ok(swap_type) = Self::get_swap_type(&trade_raw) { + match swap_type { + SwapType::Raydium => { + let event = RaydiumEvent::parse_logs::(logs); + info!("RaydiumEvent {:#?}", event); + } + SwapType::Pump => { + let event = PumpEvent::parse_logs::(logs); + info!("PumpEvent {:#?}", event); + } + } + } + Ok(()) + } + + pub fn get_swap_type(trade_raw: &EncodedTransactionWithStatusMeta) -> Result { + let transaction = trade_raw.transaction.decode() + .ok_or_else(|| AppError::from(anyhow!("Failed to decode transaction")))?; + + let account_keys = transaction.message.static_account_keys(); + let program_index = account_keys + .iter() + .position(|item| item == &AMM_V4 || item == &PUMP_PROGRAM_ID) + .ok_or_else(|| AppError::from(anyhow!("swap type program_id not found")))?; + + match account_keys[program_index] { + AMM_V4 => Ok(SwapType::Raydium), + PUMP_PROGRAM_ID => Ok(SwapType::Pump), + _ => Err(AppError::from(anyhow!("Invalid program_id"))) + } + } +} \ No newline at end of file diff --git a/src/instruction/dex_events.rs b/src/instruction/dex_events.rs new file mode 100755 index 0000000..b69d096 --- /dev/null +++ b/src/instruction/dex_events.rs @@ -0,0 +1,123 @@ +use anyhow::anyhow; +use base64::engine::general_purpose; +use base64::Engine; +use borsh::{BorshDeserialize, BorshSerialize}; +use regex::Regex; +use solana_sdk::pubkey::Pubkey; + +use super::myerror::AppError; + +pub const PROGRAM_DATA: &str = "Program data: "; + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct TradeEvent { + pub mint: Pubkey, + pub sol_amount: u64, + pub token_amount: u64, + pub is_buy: bool, + pub user: Pubkey, + pub timestamp: u64, + pub virtual_sol_reserves: u64, + pub virtual_token_reserves: u64, + pub real_sol_reserves: u64, + pub real_token_reserves: u64, +} + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct CompleteEvent { + pub user: Pubkey, + pub mint: Pubkey, + pub bonding_curve: Pubkey, + pub timestamp: u64, +} + +#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize)] +pub struct SwapBaseInLog { + pub log_type: u8, + // input + pub amount_in: u64, + pub minimum_out: u64, + pub direction: u64, + // user info + pub user_source: u64, + // pool info + pub pool_coin: u64, + pub pool_pc: u64, + // calc result + pub out_amount: u64, +} + +pub trait EventTrait: Sized + std::fmt::Debug { + fn from_bytes(bytes: &[u8]) -> Result; +} + +impl EventTrait for TradeEvent { + fn from_bytes(bytes: &[u8]) -> Result { + TradeEvent::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +impl EventTrait for CompleteEvent { + fn from_bytes(bytes: &[u8]) -> Result { + CompleteEvent::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +impl EventTrait for SwapBaseInLog { + fn from_bytes(bytes: &[u8]) -> Result { + SwapBaseInLog::try_from_slice(bytes).map_err(|e| AppError::from(anyhow!(e.to_string()))) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct PumpEvent {} + +impl PumpEvent { + pub fn parse_logs(logs: &Vec) -> Option { + let mut event: Option = None; + if !logs.is_empty() { + let logs_iter = logs.iter().peekable(); + + for l in logs_iter.rev() { + if let Some(log) = l.strip_prefix(PROGRAM_DATA) { + let borsh_bytes = general_purpose::STANDARD.decode(log).unwrap(); + let slice: &[u8] = &borsh_bytes[8..]; + + if let Ok(e) = T::from_bytes(slice) { + event = Some(e); + } + } + } + } + event + } +} + +#[derive(Debug, Clone, Copy)] +pub struct RaydiumEvent {} + +impl RaydiumEvent { + pub fn parse_logs(logs: &Vec) -> Option { + let mut event: Option = None; + + if !logs.is_empty() { + let logs_iter = logs.iter().peekable(); + + for l in logs_iter.rev() { + let re = Regex::new(r"ray_log: (?P[A-Za-z0-9+/=]+)").unwrap(); + + if let Some(caps) = re.captures(l) { + if let Some(base64) = caps.name("base64") { + let bytes = general_purpose::STANDARD.decode(base64.as_str()).unwrap(); + + if let Ok(e) = T::from_bytes(&bytes) { + event = Some(e); + } + } + } + } + } + + event + } +} \ No newline at end of file diff --git a/crates/pumpfun/src/instruction/logs_data.rs b/src/instruction/logs_data.rs similarity index 100% rename from crates/pumpfun/src/instruction/logs_data.rs rename to src/instruction/logs_data.rs diff --git a/crates/pumpfun/src/instruction/logs_events.rs b/src/instruction/logs_events.rs similarity index 100% rename from crates/pumpfun/src/instruction/logs_events.rs rename to src/instruction/logs_events.rs diff --git a/crates/pumpfun/src/instruction/logs_filters.rs b/src/instruction/logs_filters.rs similarity index 98% rename from crates/pumpfun/src/instruction/logs_filters.rs rename to src/instruction/logs_filters.rs index 43bb4cd..5ffbd09 100755 --- a/crates/pumpfun/src/instruction/logs_filters.rs +++ b/src/instruction/logs_filters.rs @@ -1,7 +1,7 @@ use crate::instruction::logs_data::DexInstruction; use crate::instruction::logs_parser::{parse_create_token_data, parse_trade_data}; use crate::error::ClientResult; -use anchor_client::solana_sdk::pubkey::Pubkey; +use solana_sdk::pubkey::Pubkey; pub struct LogFilter; impl LogFilter { diff --git a/crates/pumpfun/src/instruction/logs_parser.rs b/src/instruction/logs_parser.rs similarity index 99% rename from crates/pumpfun/src/instruction/logs_parser.rs rename to src/instruction/logs_parser.rs index be33028..fe7e9c0 100755 --- a/crates/pumpfun/src/instruction/logs_parser.rs +++ b/src/instruction/logs_parser.rs @@ -6,7 +6,7 @@ use crate::instruction::{ logs_filters::LogFilter }; -use anchor_client::solana_sdk::pubkey::Pubkey; +use solana_sdk::pubkey::Pubkey; pub async fn process_logs( signature: &str, diff --git a/crates/pumpfun/src/instruction/logs_subscribe.rs b/src/instruction/logs_subscribe.rs similarity index 96% rename from crates/pumpfun/src/instruction/logs_subscribe.rs rename to src/instruction/logs_subscribe.rs index 90e0f32..3a4c25f 100755 --- a/crates/pumpfun/src/instruction/logs_subscribe.rs +++ b/src/instruction/logs_subscribe.rs @@ -1,9 +1,9 @@ -use anchor_client::solana_client::{ +use solana_client::{ nonblocking::pubsub_client::PubsubClient, rpc_config::{RpcTransactionLogsConfig, RpcTransactionLogsFilter} }; -use anchor_client::solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; use std::sync::Arc; use tokio::sync::mpsc; use tokio::task::JoinHandle; diff --git a/crates/pumpfun/src/instruction/mod.rs b/src/instruction/mod.rs similarity index 81% rename from crates/pumpfun/src/instruction/mod.rs rename to src/instruction/mod.rs index 8c2a1ca..f7e97b3 100755 --- a/crates/pumpfun/src/instruction/mod.rs +++ b/src/instruction/mod.rs @@ -23,9 +23,8 @@ pub use logs_events::*; pub use logs_subscribe::*; use crate::{constants, PumpFun}; -use anchor_client::anchor_lang::InstructionData; -use anchor_spl::associated_token::get_associated_token_address; -use pumpfun_cpi as cpi; +use spl_associated_token_account::get_associated_token_address; + use solana_sdk::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, @@ -33,6 +32,54 @@ use solana_sdk::{ signer::Signer, }; +pub struct Create { + pub _name: String, + pub _symbol: String, + pub _uri: String, +} + +impl Create { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 8 + 8); + data.extend_from_slice(&[24, 30, 200, 40, 5, 28, 7, 119]); // discriminator + data.extend_from_slice(&self._name.as_bytes()); + data.extend_from_slice(&self._symbol.as_bytes()); + data.extend_from_slice(&self._uri.as_bytes()); + data + } +} + +pub struct Buy { + pub _amount: u64, + pub _max_sol_cost: u64, +} + +impl Buy { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 8 + 8); + data.extend_from_slice(&[102, 6, 61, 18, 1, 218, 235, 234]); // discriminator + data.extend_from_slice(&self._amount.to_le_bytes()); + data.extend_from_slice(&self._max_sol_cost.to_le_bytes()); + data + } +} + +pub struct Sell { + pub _amount: u64, + pub _min_sol_output: u64, +} + +impl Sell { + pub fn data(&self) -> Vec { + let mut data = Vec::with_capacity(8 + 8 + 8); + data.extend_from_slice(&[51, 230, 133, 164, 1, 127, 131, 173]); // discriminator + data.extend_from_slice(&self._amount.to_le_bytes()); + data.extend_from_slice(&self._min_sol_output.to_le_bytes()); + data + } +} + + /// Creates an instruction to create a new token with bonding curve /// /// Creates a new SPL token with an associated bonding curve that determines its price. @@ -46,7 +93,7 @@ use solana_sdk::{ /// # Returns /// /// Returns a Solana instruction that when executed will create the token and its accounts -pub fn create(payer: &Keypair, mint: &Keypair, args: cpi::instruction::Create) -> Instruction { +pub fn create(payer: &Keypair, mint: &Keypair, args: Create) -> Instruction { let bonding_curve: Pubkey = PumpFun::get_bonding_curve_pda(&mint.pubkey()).unwrap(); Instruction::new_with_bytes( constants::accounts::PUMPFUN, @@ -93,7 +140,7 @@ pub fn buy( payer: &Keypair, mint: &Pubkey, fee_recipient: &Pubkey, - args: cpi::instruction::Buy, + args: Buy, ) -> Instruction { let bonding_curve: Pubkey = PumpFun::get_bonding_curve_pda(mint).unwrap(); Instruction::new_with_bytes( @@ -136,7 +183,7 @@ pub fn sell( payer: &Keypair, mint: &Pubkey, fee_recipient: &Pubkey, - args: cpi::instruction::Sell, + args: Sell, ) -> Instruction { let bonding_curve: Pubkey = PumpFun::get_bonding_curve_pda(mint).unwrap(); Instruction::new_with_bytes( diff --git a/crates/pumpfun/src/jito/mod.rs b/src/jito/mod.rs similarity index 99% rename from crates/pumpfun/src/jito/mod.rs rename to src/jito/mod.rs index e4d8e27..ce03eed 100755 --- a/crates/pumpfun/src/jito/mod.rs +++ b/src/jito/mod.rs @@ -4,7 +4,7 @@ use rand::seq::SliceRandom; use reqwest::Client; use serde_json::{json, Value}; -use anchor_client::solana_sdk::{ +use solana_sdk::{ pubkey::Pubkey, transaction::Transaction, }; diff --git a/crates/pumpfun/src/lib.rs b/src/lib.rs similarity index 94% rename from crates/pumpfun/src/lib.rs rename to src/lib.rs index f13b043..71e8ad7 100755 --- a/crates/pumpfun/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,25 @@ -#![doc = include_str!("../RUSTDOC.md")] - pub mod accounts; pub mod constants; pub mod error; pub mod instruction; pub mod utils; pub mod jito; +pub mod grpc; -use anchor_client::{ - solana_client::rpc_client::RpcClient, - solana_sdk::{ - commitment_config::CommitmentConfig, - pubkey::Pubkey, - signature::{Keypair, Signature}, - signer::Signer, - instruction::Instruction, - system_instruction, - compute_budget::ComputeBudgetInstruction, - transaction::Transaction, - }, +use solana_client::rpc_client::RpcClient; +use solana_sdk::{ + commitment_config::CommitmentConfig, + pubkey::Pubkey, + signature::{Keypair, Signature}, + signer::Signer, + instruction::Instruction, + system_instruction, + compute_budget::ComputeBudgetInstruction, + transaction::Transaction, }; -use anchor_client::Cluster; -use anchor_spl::associated_token::{ +use spl_associated_token_account::{ get_associated_token_address, - spl_associated_token_account::instruction::create_associated_token_account, + create_associated_token_account, }; use instruction::logs_subscribe; @@ -31,13 +27,13 @@ use instruction::logs_subscribe::SubscriptionHandle; use instruction::logs_events::DexEvent; use std::sync::Arc; -use borsh::BorshDeserialize; use std::time::Instant; -pub use pumpfun_cpi as cpi; use crate::jito::JitoClient; use crate::error::ClientError; +use borsh::BorshDeserialize; + // Constants const DEFAULT_SLIPPAGE: u64 = 1000; // 10% const DEFAULT_COMPUTE_UNIT_LIMIT: u32 = 10_000_000; @@ -79,13 +75,13 @@ impl Clone for PumpFun { impl PumpFun { /// Create a new PumpFun client instance pub fn new( - cluster: Cluster, + rpc_url: String, commitment: Option, payer: Arc, jito_url: Option, ) -> Self { let rpc = RpcClient::new_with_commitment( - cluster.url(), + rpc_url, commitment.unwrap_or(CommitmentConfig::confirmed()) ); @@ -114,7 +110,7 @@ impl PumpFun { instructions.push(instruction::create( &self.payer.clone(), mint, - cpi::instruction::Create { + instruction::Create { _name: ipfs.metadata.name, _symbol: ipfs.metadata.symbol, _uri: ipfs.metadata.image, @@ -157,7 +153,7 @@ impl PumpFun { instructions.push(instruction::create( &self.payer.clone(), mint, - cpi::instruction::Create { + instruction::Create { _name: ipfs.metadata.name, _symbol: ipfs.metadata.symbol, _uri: ipfs.metadata.image, @@ -170,7 +166,6 @@ impl PumpFun { &self.payer.pubkey(), &self.payer.pubkey(), &mint.pubkey(), - &constants::accounts::TOKEN_PROGRAM, )); } @@ -178,7 +173,7 @@ impl PumpFun { &self.payer.clone(), &mint.pubkey(), &global_account.fee_recipient, - cpi::instruction::Buy { + instruction::Buy { _amount: buy_amount, _max_sol_cost: buy_amount_with_slippage, }, @@ -221,7 +216,6 @@ impl PumpFun { &self.payer.pubkey(), &self.payer.pubkey(), mint, - &constants::accounts::TOKEN_PROGRAM, )); } @@ -229,7 +223,7 @@ impl PumpFun { &self.payer.clone(), mint, &global_account.fee_recipient, - cpi::instruction::Buy { + instruction::Buy { _amount: buy_amount, _max_sol_cost: buy_amount_with_slippage, }, @@ -276,7 +270,6 @@ impl PumpFun { &self.payer.pubkey(), &self.payer.pubkey(), mint, - &constants::accounts::TOKEN_PROGRAM, )); } @@ -284,7 +277,7 @@ impl PumpFun { self.payer.as_ref(), mint, &global_account.fee_recipient, - cpi::instruction::Buy { + instruction::Buy { _amount: buy_amount, _max_sol_cost: buy_amount_with_slippage, }, @@ -346,7 +339,7 @@ impl PumpFun { &self.payer.clone(), mint, &global_account.fee_recipient, - cpi::instruction::Sell { + instruction::Sell { _amount: amount, _min_sol_output: min_sol_output_with_slippage, }, @@ -451,7 +444,7 @@ impl PumpFun { &self.payer.clone(), mint, &global_account.fee_recipient, - cpi::instruction::Sell { + instruction::Sell { _amount: amount, _min_sol_output: min_sol_output_with_slippage, }, @@ -523,17 +516,17 @@ impl PumpFun { // PDA related methods pub fn get_global_pda() -> Pubkey { - Pubkey::find_program_address(&[constants::seeds::GLOBAL_SEED], &cpi::ID).0 + Pubkey::find_program_address(&[constants::seeds::GLOBAL_SEED], &constants::accounts::PUMPFUN).0 } pub fn get_mint_authority_pda() -> Pubkey { - Pubkey::find_program_address(&[constants::seeds::MINT_AUTHORITY_SEED], &cpi::ID).0 + Pubkey::find_program_address(&[constants::seeds::MINT_AUTHORITY_SEED], &constants::accounts::PUMPFUN).0 } pub fn get_bonding_curve_pda(mint: &Pubkey) -> Option { Pubkey::try_find_program_address( &[constants::seeds::BONDING_CURVE_SEED, mint.as_ref()], - &cpi::ID + &constants::accounts::PUMPFUN ).map(|(pubkey, _)| pubkey) } diff --git a/crates/pumpfun/src/main.rs b/src/main.rs similarity index 95% rename from crates/pumpfun/src/main.rs rename to src/main.rs index ef392db..b9779b2 100755 --- a/crates/pumpfun/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use mai3_pumpfun_sdk::instruction::{ logs_events::DexEvent, logs_subscribe::{tokens_subscription, stop_subscription} }; -use anchor_client::solana_sdk::commitment_config::CommitmentConfig; +use solana_sdk::commitment_config::CommitmentConfig; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/crates/pumpfun/src/utils/mod.rs b/src/utils/mod.rs similarity index 100% rename from crates/pumpfun/src/utils/mod.rs rename to src/utils/mod.rs