Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ston-fi/tonlib-rs

Rust SDK for The Open Network
https://github.com/ston-fi/tonlib-rs

sdk-rust ton

Last synced: about 2 months ago
JSON representation

Rust SDK for The Open Network

Awesome Lists containing this project

README

        

# Rust SDK for The Open Network

Rust SDK for [The Open Network](https://ton.org/)

## Features

* Rust SDK for The Open Network
* Using `tonlibjson` as data provider
* Support parsing and generation of Cells methods for more convenient interaction with data structures
* Support of Wallet versions (3, 3 revision 2, 4 revision 2)
* Derive wallet address
* Support of TON Mnemonics
* NaCL-compatible Ed25519 signing of transactions
* Support jetton functions: getting of jetton data and wallet address for jetton
* Support internal and external jetton metadata loading
* Connection pooling & retries support for better server-level interaction
* Support of IPFS jetton metadata

## Dependencies

`tonlib-sys` - https://github.com/ston-fi/tonlib-sys

## Prerequisites

For Linux:
```shell
sudo apt install build-essential cmake libsodium-dev libsecp256k1-dev lz4 liblz4-dev
```

For macOS:
```shell
brew install --cask mactex
brew install readline secp256k1 ccache pkgconfig cmake libsodium
```

### Build library

You can build the library using the following command:

```bash
cargo build
```

## Usage

To use this library in your Rust application, add the following to your Cargo.toml file:

```toml
[dependencies]
tonlib = "0.15"
```

Then, in your Rust code, you can import the library with:

```rust
use tonlib;
```

### Cell

Creating a `Cell` and writing data to it:

``` rust
use anyhow::anyhow;
use tonlib::address::TonAddress;
use tonlib::cell::CellBuilder;

fn write_cell() -> anyhow::Result<()> {
let mut writer = CellBuilder::new();
let addr = TonAddress::from_base64_url("EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR")?;
let cell = writer
.store_u32(32, 0xFAD45AADu32)?
.store_bit(true)?
.store_u8(8, 234u8)?
.store_slice(&[0xFA, 0xD4, 0x5A, 0xAD, 0xAA, 0x12, 0xFF, 0x45])?
.store_address(&addr)?
.store_string("Hello, TON")?
.build()?;
# Ok(())
}
```

Reading data from a `Cell`:

```rust
use tonlib::cell::Cell;
fn read_cell(cell: Cell) -> anyhow::Result<()> {
let mut reader = cell.parser();
let u32_value = reader.load_u32(32)?;
let bit_value = reader.load_bit()?;
let u8_value = reader.load_u8(8)?;
let bytes_value = reader.load_bytes(8)?;
let address_value = reader.load_address()?;
let str_value = reader.ensure_empty()?;
Ok(())
}
```

### TON blockchain client

To call methods, create a client:

```rust

use tonlib::client::TonClient;
use tonlib::client::TonClientBuilder;
async fn create_client()-> anyhow::Result<()>{
TonClient::set_log_verbosity_level(2); //setup of logging level
let client = TonClientBuilder::new()
.with_pool_size(10)
.with_keystore_dir(String::from("/tmp"))
.build()
.await?;
Ok(())
}
```

`TonClient::set_log_verbosity_level(2);` sets the logging level.

By default, the connection is made to mainnet. But you can also specify a test network when creating the client:

```rust
use tonlib::config::TESTNET_CONFIG;
use tonlib::client::TonConnectionParams;
use tonlib::client::TonClientBuilder;
async fn create_client_with_conn_params()-> anyhow::Result<()>{
let client = TonClientBuilder::new()
.with_connection_params(&TonConnectionParams {
config: TESTNET_CONFIG.to_string(),
blockchain_name: None,
use_callbacks_for_network: false,
ignore_cache: false,
keystore_dir: None,
})
.with_pool_size(10)
.build()
.await?;
Ok(())
}
```

After creating the client, you can call methods on the TON blockchain:

```rust
use tonlib::address::TonAddress;
use tonlib::tl::InternalTransactionId;
use tonlib::tl::NULL_BLOCKS_ACCOUNT_TRANSACTION_ID;
use tonlib::tl::BlocksTransactions;
use tonlib::tl::BlocksShards;
use tonlib::tl::BlockId;
use tonlib::tl::BlocksMasterchainInfo;
use tonlib::client::TonClient;
use tonlib::client::TonClientInterface;

async fn call_blockchain_methods()-> anyhow::Result<()>{
let client = TonClient::builder().build().await?;
let (_, info) = client.get_masterchain_info().await?;
println!("MasterchainInfo: {:?}", &info);
let block_id = BlockId {
workchain: info.last.workchain,
shard: info.last.shard,
seqno: info.last.seqno,
};
let block_id_ext = client.lookup_block(1, &block_id, 0, 0).await?;
println!("BlockIdExt: {:?}", &block_id_ext);
let block_shards: BlocksShards = client.get_block_shards(&info.last).await?;
let mut shards = block_shards.shards.clone();
println!("Shards: {:?}", &block_shards);
shards.insert(0, info.last.clone());
for shard in &shards {
println!("Processing shard: {:?}", shard);
let workchain = shard.workchain;
let txs: BlocksTransactions = client
.get_block_transactions(&shard, 7, 1024, &NULL_BLOCKS_ACCOUNT_TRANSACTION_ID)
.await?;
println!(
"Number of transactions: {}, incomplete: {}",
txs.transactions.len(),
txs.incomplete
);
for tx_id in txs.transactions {
let mut t: [u8; 32] = [0; 32];
t.clone_from_slice(tx_id.account.as_slice());
let addr = TonAddress::new(workchain, &t);
let id = InternalTransactionId {
hash: tx_id.hash.clone(),
lt: tx_id.lt,
};
let tx = client
.get_raw_transactions_v2(&addr, &id, 1, false)
.await?;
println!("Tx: {:?}", tx.transactions[0])
}
}
Ok(())
}
```

You can get the account state for any contract:

```rust
use tonlib::address::TonAddress;
use tonlib::client::TonClient;
use crate::tonlib::client::TonClientInterface;

async fn get_state()-> anyhow::Result<()>{
let client = TonClient::builder().build().await?;
let address = TonAddress::from_base64_url(
"EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt-",
)?;
let r = client
.get_account_state(&address)
.await;
Ok(())
}
```

### Working with contracts and jettons

Methods for working with tokens and wallets:

``` rust
use tonlib::client::TonClient;
use tonlib::contract::TonContractFactory;
use crate::tonlib::contract::JettonMasterContract;
use crate::tonlib::contract::JettonWalletContract;

async fn method_call() -> anyhow::Result<()> {
let client = TonClient::builder().build().await?;
let contract_factory = TonContractFactory::builder(&client).build().await?;
let master_contract = contract_factory.get_contract(
&"EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR".parse()?,
);
let jetton_data = master_contract.get_jetton_data().await?;

let wallet_contract = contract_factory.get_contract(
&"EQCGY3OVLtD9KRcOsP2ldQDtuY0FMzV7wPoxjrFbayBXc23c".parse()?,
);
let wallet_data = wallet_contract.get_wallet_data().await?;
Ok(())
}
```

To load the metadata of the token, one may use generic `MetaLoader` and it type aliases: `JettonMetaLoader, NftItemMetaLoader NftColletionMetaLoader`:

```rust
use tonlib::client::TonClient;
use tonlib::contract::TonContractFactory;
use tonlib::contract::JettonMasterContract;
use tonlib::meta::JettonMetaLoader;
use tonlib::meta::LoadMeta;

async fn load_meta() -> anyhow::Result<()> {
let client = TonClient::builder().build().await?;
let contract_factory = TonContractFactory::builder(&client).build().await?;
let contract =
contract_factory.get_contract(&"EQB-MPwrd1G6WKNkLz_VnV6WqBDd142KMQv-g1O-8QUA3728".parse()?);
let jetton_data = contract.get_jetton_data().await?;
let loader = JettonMetaLoader::default()?;
let content_res = loader.load(&jetton_data.content).await?;

Ok(())
}
```

Get the wallet address for the token:

```rust
use tonlib::address::TonAddress;
use tonlib::client::TonClient;
use tonlib::contract::TonContractFactory;
use tonlib::contract::JettonMasterContract;

async fn get_wallet_address() -> anyhow::Result<()> {

let client = TonClient::default().await?;
let contract_factory = TonContractFactory::builder(&client).build().await?;
let contract =
contract_factory.get_contract(&"EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR".parse()?,
);
let owner_address = TonAddress::from_base64_url(
"EQB2BtXDXaQuIcMYW7JEWhHmwHfPPwa-eoCdefiAxOhU3pQg",
)?;
let wallet_address = contract.get_wallet_address(&owner_address).await?;
Ok(())
}
```

### Send message to TON

Create key pair from secret phrase ( )

```rust
use tonlib::mnemonic::Mnemonic;
use tonlib::mnemonic::KeyPair;
async fn create_key_pair() -> anyhow::Result<()> {
let mnemonic = Mnemonic::new(
vec![
"dose", "ice", "enrich", "trigger", "test", "dove", "century", "still", "betray",
"gas", "diet", "dune",
],
&None,
)?;
let key_pair = mnemonic.to_key_pair();
Ok(())
}

```
And now you are ready to send transfer messages to TON blockchain.

Create a jetton transfer:

```rust

use num_bigint::BigUint;
use std::time::SystemTime;

use tonlib::address::TonAddress;
use tonlib::cell::BagOfCells;
use tonlib::client::TonClient;
use tonlib::client::TonClientInterface;
use tonlib::contract::TonContractFactory;
use tonlib::contract::JettonMasterContract;
use tonlib::message::JettonTransferMessage;

use tonlib::message::TransferMessage;
use tonlib::mnemonic::KeyPair;
use tonlib::mnemonic::Mnemonic;
use tonlib::wallet::TonWallet;
use tonlib::wallet::WalletVersion;

async fn create_jetton_transfer() -> anyhow::Result<()> {

let seqno:i32 = 30000000;

let self_address: TonAddress = "EQB2BtXDXaQuIcMYW7JEWhHmwHfPPwa-eoCdefiAxOhU3pQg "
.parse()
.unwrap();
let mnemonic_str = "mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic mnemonic";
let mnemonic: Mnemonic = Mnemonic::from_str(mnemonic_str, &None).unwrap();
let key_pair: KeyPair = mnemonic.to_key_pair().unwrap();
let jetton_master_address: TonAddress = "EQDCJL0iQHofcBBvFBHdVG233Ri2V4kCNFgfRT-gqAd3Oc86"
.parse()
.unwrap();

let client = TonClient::default().await?;
let contract_factory = TonContractFactory::builder(&client).build().await?;
let jetton_master =
contract_factory.get_contract(&jetton_master_address);
let self_jetton_wallet_addr = jetton_master.get_wallet_address(&self_address).await?;
let wallet = TonWallet::derive_default(WalletVersion::V4R2, &key_pair)?;
let dest: TonAddress = "".parse()?;
let src: TonAddress = "".parse()?;
let jetton_amount = BigUint::from(1000000u64);
let jetton_transfer = JettonTransferMessage::new(&dest, &jetton_amount)
.with_query_id(100500)
.with_response_destination(&self_address)
.build()?;
let ton_amount = BigUint::from(200000000u64); // 0.2 TON
let transfer = TransferMessage::new(&src, &ton_amount)
.with_data(jetton_transfer)
.build()?;
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs() as u32;
let body = wallet.create_external_body(now + 60, seqno.try_into().unwrap(), vec![transfer])?;
let signed = wallet.sign_external_body(&body)?;
let wrapped = wallet.wrap_signed_body(signed)?;
let boc = BagOfCells::from_root(wrapped);
let tx = boc.serialize(true)?;

let hash = client.send_raw_message_return_hash(tx.as_slice()).await?;

Ok(())
}
```

Create a simple transfer:

```rust

use anyhow::anyhow;
use num_bigint::BigUint;
use std::time::SystemTime;

use tonlib::address::TonAddress;
use tonlib::cell::BagOfCells;
use tonlib::message::TransferMessage;
use tonlib::wallet::TonWallet;
use tonlib::client::TonClient;
use tonlib::client::TonClientInterface;
use tonlib::mnemonic::KeyPair;
use tonlib::mnemonic::Mnemonic;
use tonlib::wallet::WalletVersion;

async fn create_simple_transfer() -> anyhow::Result<()> {
let mnemonic = Mnemonic::new(
vec![
"dose", "ice", "enrich", "trigger", "test", "dove", "century", "still", "betray",
"gas", "diet", "dune",
],
&None,
)?;
let key_pair = mnemonic.to_key_pair()?;
let seqno = 30000000;

let client = TonClient::default().await?;
let wallet = TonWallet::derive_default(WalletVersion::V4R2, &key_pair)?;
let dest: TonAddress = "".parse()?;
let value = BigUint::from(10000000u64); // 0.01 TON
let transfer = TransferMessage::new(&dest, &value).build()?;
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs() as u32;
let body = wallet.create_external_body(now + 60, seqno, vec![transfer])?;
let signed = wallet.sign_external_body(&body)?;
let wrapped = wallet.wrap_signed_body(signed)?;
let boc = BagOfCells::from_root(wrapped);
let tx = boc.serialize(true)?;
let hash = client.send_raw_message_return_hash(tx.as_slice()).await?;

Ok(())
}
```

## Cross-compilation
In order to cross-compile for specific cpu microachitecture set environment variable `TARGET_CPU_MARCH` to the required. Supported values are listen in https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html

## Contributing

If you want to contribute to this library, please feel free to open a pull request on GitHub.

## License
This library is licensed under the MIT license. See the LICENSE file for details. -->