Skip to content

Commit

Permalink
Merge branch 'feature/rpc-lib-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Feb 3, 2025
2 parents 4ce6567 + f707341 commit f46db6a
Show file tree
Hide file tree
Showing 38 changed files with 2,573 additions and 64 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ members = [
"nekoton-abi",
"nekoton-contracts",
"nekoton-derive",
"nekoton-jetton",
"nekoton-proto",
"nekoton-transport",
"nekoton-utils",
Expand All @@ -40,7 +41,7 @@ once_cell = "1.12.0"
parking_lot = "0.12.0"
pbkdf2 = { version = "0.12.2", optional = true }
quick_cache = "0.4.1"
rand = { version = "0.8", features = ["getrandom"] , optional = true }
rand = { version = "0.8", features = ["getrandom"], optional = true }
secstr = { version = "0.5.0", features = ["serde"], optional = true }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion nekoton-abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ton_abi = { git = "https://github.com/broxus/ton-labs-abi" }
ton_block = { git = "https://github.com/broxus/ton-labs-block.git" }
ton_executor = { git = "https://github.com/broxus/ton-labs-executor.git" }
ton_types = { git = "https://github.com/broxus/ton-labs-types.git" }
ton_vm = { git = "https://github.com/broxus/ton-labs-vm.git" }
ton_vm = { git = "https://github.com/broxus/ton-labs-vm.git", branch = "feature/jetton" }

nekoton-derive = { path = "../nekoton-derive", optional = true }
nekoton-utils = { path = "../nekoton-utils" }
Expand Down
4 changes: 4 additions & 0 deletions nekoton-contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ ton_types = { git = "https://github.com/broxus/ton-labs-types.git" }
ton_abi = { git = "https://github.com/broxus/ton-labs-abi" }

nekoton-abi = { path = "../nekoton-abi", features = ["derive"] }
nekoton-jetton = { path = "../nekoton-jetton" }
nekoton-utils = { path = "../nekoton-utils" }

[dev-dependencies]
base64 = "0.13"

[features]
web = ["ton_abi/web"]
106 changes: 106 additions & 0 deletions nekoton-contracts/src/jetton/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use nekoton_abi::num_bigint::BigUint;
use nekoton_abi::{ExecutionContext, StackItem};
use ton_block::Serializable;
use ton_types::SliceData;

pub use root_token_contract::{JettonRootData, JettonRootMeta};
pub use token_wallet_contract::JettonWalletData;

mod root_token_contract;
mod token_wallet_contract;

#[derive(Copy, Clone)]
pub struct RootTokenContract<'a>(pub ExecutionContext<'a>);

pub const GET_JETTON_DATA: &str = "get_jetton_data";
pub const GET_JETTON_META: &str = "get_jetton_meta";
pub const GET_WALLET_DATA: &str = "get_wallet_data";
pub const GET_WALLET_ADDRESS: &str = "get_wallet_address";

impl RootTokenContract<'_> {
pub fn name(&self) -> anyhow::Result<Option<String>> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data.content.name)
}

pub fn symbol(&self) -> anyhow::Result<Option<String>> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data.content.symbol)
}

pub fn decimals(&self) -> anyhow::Result<Option<u8>> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data.content.decimals)
}

pub fn total_supply(&self) -> anyhow::Result<BigUint> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data.total_supply)
}

pub fn wallet_code(&self) -> anyhow::Result<ton_types::Cell> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data.wallet_code)
}

pub fn get_wallet_address(
&self,
owner: &ton_block::MsgAddressInt,
) -> anyhow::Result<ton_block::MsgAddressInt> {
let arg = StackItem::Slice(SliceData::load_cell(owner.serialize()?)?);
let result = self.0.run_getter(GET_WALLET_ADDRESS, &[arg])?;

let address = root_token_contract::get_wallet_address(result)?;
Ok(address)
}

pub fn get_details(&self) -> anyhow::Result<JettonRootData> {
let result = self.0.run_getter(GET_JETTON_DATA, &[])?;

let data = root_token_contract::get_jetton_data(result)?;
Ok(data)
}

pub fn get_meta(&self) -> anyhow::Result<JettonRootMeta> {
let result = self.0.run_getter(GET_JETTON_META, &[])?;

let meta = root_token_contract::get_jetton_meta(result)?;
Ok(meta)
}
}

#[derive(Copy, Clone)]
pub struct TokenWalletContract<'a>(pub ExecutionContext<'a>);

impl<'a> TokenWalletContract<'a> {
pub fn root(&self) -> anyhow::Result<ton_block::MsgAddressInt> {
let result = self.0.run_getter(GET_WALLET_DATA, &[])?;

let data = token_wallet_contract::get_wallet_data(result)?;
Ok(data.root_address)
}

pub fn balance(&self) -> anyhow::Result<BigUint> {
let result = self.0.run_getter(GET_WALLET_DATA, &[])?;

let data = token_wallet_contract::get_wallet_data(result)?;
Ok(data.balance)
}

pub fn get_details(&self) -> anyhow::Result<JettonWalletData> {
let result = self.0.run_getter(GET_WALLET_DATA, &[])?;

let data = token_wallet_contract::get_wallet_data(result)?;
Ok(data)
}
}
147 changes: 147 additions & 0 deletions nekoton-contracts/src/jetton/root_token_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use anyhow::Result;
use nekoton_abi::num_bigint::BigUint;
use nekoton_abi::VmGetterOutput;
use nekoton_jetton::{JettonMetaData, MetaDataContent};
use thiserror::Error;
use ton_block::{Deserializable, MsgAddressInt};
use ton_types::{Cell, SliceData};

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct JettonRootData {
pub total_supply: BigUint,
pub mintable: bool,
pub admin_address: MsgAddressInt,
pub content: JettonMetaData,
pub wallet_code: Cell,
}

#[derive(PartialEq, Eq, Debug, Clone)]
pub struct JettonRootMeta {
pub name: String,
pub symbol: String,
pub decimals: u8,
pub base_chain_id: String,
pub base_token: String,
}

pub fn get_jetton_data(res: VmGetterOutput) -> Result<JettonRootData> {
if !res.is_ok {
return Err(RootContractError::ExecutionFailed {
exit_code: res.exit_code,
}
.into());
}

const JETTON_DATA_STACK_ELEMENTS: usize = 5;

let stack = res.stack;
if stack.len() != JETTON_DATA_STACK_ELEMENTS {
return Err(RootContractError::InvalidMethodResultStackSize {
actual: stack.len(),
expected: JETTON_DATA_STACK_ELEMENTS,
}
.into());
}

let total_supply = stack[0].as_integer()?.into(0..=u128::MAX)?;

let mintable = stack[1].as_bool()?;

let mut address_data = stack[2].as_slice()?.clone();

let admin_address = MsgAddressInt::construct_from(&mut address_data).unwrap_or_default();

let content = stack[3].as_cell()?;
let content = MetaDataContent::parse(content)?;

let wallet_code = stack[4].as_cell()?.clone();

let dict = match content {
MetaDataContent::Internal { dict } => dict,
MetaDataContent::Unsupported => {
return Err(RootContractError::UnsupportedContentType.into())
}
};

Ok(JettonRootData {
total_supply: BigUint::from(total_supply),
mintable,
admin_address,
wallet_code,
content: (&dict).into(),
})
}

pub fn get_jetton_meta(res: VmGetterOutput) -> Result<JettonRootMeta> {
if !res.is_ok {
return Err(RootContractError::ExecutionFailed {
exit_code: res.exit_code,
}
.into());
}

const JETTON_META_STACK_ELEMENTS: usize = 5;

let stack = res.stack;
if stack.len() != JETTON_META_STACK_ELEMENTS {
return Err(RootContractError::InvalidMethodResultStackSize {
actual: stack.len(),
expected: JETTON_META_STACK_ELEMENTS,
}
.into());
}

let slice = SliceData::load_cell_ref(stack[0].as_cell()?)?;
let name = String::from_utf8_lossy(slice.remaining_data().data()).to_string();

let slice = SliceData::load_cell_ref(stack[1].as_cell()?)?;
let symbol = String::from_utf8_lossy(slice.remaining_data().data()).to_string();

let decimals = stack[2].as_integer()?.into(0..=u8::MAX)?;

let base_chain_id = stack[3].as_integer()?.to_string();
let base_token = stack[4].as_integer()?.to_string();

Ok(JettonRootMeta {
name,
symbol,
decimals,
base_chain_id,
base_token,
})
}

pub fn get_wallet_address(res: VmGetterOutput) -> Result<MsgAddressInt> {
if !res.is_ok {
return Err(RootContractError::ExecutionFailed {
exit_code: res.exit_code,
}
.into());
}

const WALLET_ADDRESS_STACK_ELEMENTS: usize = 1;

let stack = res.stack;
if stack.len() != WALLET_ADDRESS_STACK_ELEMENTS {
return Err(RootContractError::InvalidMethodResultStackSize {
actual: stack.len(),
expected: WALLET_ADDRESS_STACK_ELEMENTS,
}
.into());
}

let mut address_data = stack[0].as_slice()?.clone();
let address = MsgAddressInt::construct_from(&mut address_data)?;

Ok(address)
}

#[derive(Error, Debug)]
pub enum RootContractError {
#[error("ExecutionFailed (exit_code: {exit_code})")]
ExecutionFailed { exit_code: i32 },
#[error("Invalid method result stack size (actual: {actual}, expected {expected})")]
InvalidMethodResultStackSize { actual: usize, expected: usize },
#[error("Unsupported metadata content type")]
UnsupportedContentType,
}
58 changes: 58 additions & 0 deletions nekoton-contracts/src/jetton/token_wallet_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use nekoton_abi::num_bigint::BigUint;
use nekoton_abi::VmGetterOutput;
use thiserror::Error;
use ton_block::{Deserializable, MsgAddressInt};
use ton_types::Cell;

#[derive(Debug, Clone)]
pub struct JettonWalletData {
pub balance: BigUint,
pub owner_address: MsgAddressInt,
pub root_address: MsgAddressInt,
pub wallet_code: Cell,
}

pub fn get_wallet_data(res: VmGetterOutput) -> anyhow::Result<JettonWalletData> {
if !res.is_ok {
return Err(WalletContractError::ExecutionFailed {
exit_code: res.exit_code,
}
.into());
}

const WALLET_DATA_STACK_ELEMENTS: usize = 4;

let stack = res.stack;
if stack.len() == WALLET_DATA_STACK_ELEMENTS {
let balance = stack[0].as_integer()?.into(0..=u128::MAX)?;

let mut address_data = stack[1].as_slice()?.clone();
let owner_address = MsgAddressInt::construct_from(&mut address_data)?;

let mut data = stack[2].as_slice()?.clone();
let root_address = MsgAddressInt::construct_from(&mut data)?;

let wallet_code = stack[3].as_cell()?.clone();

Ok(JettonWalletData {
balance: BigUint::from(balance),
owner_address,
root_address,
wallet_code,
})
} else {
Err(WalletContractError::InvalidMethodResultStackSize {
actual: stack.len(),
expected: WALLET_DATA_STACK_ELEMENTS,
}
.into())
}
}

#[derive(Error, Debug)]
pub enum WalletContractError {
#[error("ExecutionFailed (exit_code: {exit_code})")]
ExecutionFailed { exit_code: i32 },
#[error("Invalid method result stack size (actual: {actual}, expected {expected})")]
InvalidMethodResultStackSize { actual: usize, expected: usize },
}
1 change: 1 addition & 0 deletions nekoton-contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use anyhow::Result;
use nekoton_abi::{ExecutionContext, ExecutionOutput};

pub mod dens;
pub mod jetton;
pub mod old_tip3;
pub mod tip1155;
pub mod tip3;
Expand Down
3 changes: 3 additions & 0 deletions nekoton-contracts/src/wallets/code/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ declare_tvc! {
multisig2_1 => "./Multisig2_1.tvc" (MULTISIG2_1_CODE),
surf_wallet => "./Surf.tvc" (SURF_WALLET_CODE),
wallet_v3 => "./wallet_v3_code.boc" (WALLET_V3_CODE),
wallet_v4r1 => "./wallet_v4r1_code.boc" (WALLET_V4R1_CODE),
wallet_v4r2 => "./wallet_v4r2_code.boc" (WALLET_V4R2_CODE),
wallet_v5r1 => "./wallet_v5r1_code.boc" (WALLET_V5R1_CODE),
highload_wallet_v2 => "./highload_wallet_v2_code.boc" (HIGHLOAD_WALLET_V2_CODE),
ever_wallet => "./ever_wallet_code.boc" (EVER_WALLET_CODE),
}
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
27 changes: 27 additions & 0 deletions nekoton-jetton/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "nekoton-jetton"
version = "0.13.0"
authors = [
"Alexey Pashinov <[email protected]>",
"Vladimir Petrzhikovskiy <[email protected]>",
"Ivan Kalinin <[email protected]>"
]
rust-version = "1.62.0"
edition = "2021"

[dependencies]
anyhow = "1.0"
lazy_static = "1.4"
serde = { version = "1.0.183", features = ["derive"] }
sha2 = "0.10.8"

ton_block = { git = "https://github.com/broxus/ton-labs-block.git" }
ton_types = { git = "https://github.com/broxus/ton-labs-types.git" }
ton_abi = { git = "https://github.com/broxus/ton-labs-abi" }

nekoton-utils = { path = "../nekoton-utils" }
num-bigint = "0.4"
num-traits = "0.2"

[features]
web = ["ton_abi/web"]
Loading

0 comments on commit f46db6a

Please sign in to comment.