Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/gem_tron/src/provider/preload_mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ mod tests {
use primitives::delegation::DelegationValidator;

fn chain_parameter(key: &str, value: i64) -> ChainParameter {
ChainParameter { key: key.to_string(), value: Some(value) }
ChainParameter {
key: key.to_string(),
value: Some(value),
}
}

fn account_usage(free_bandwidth: u64, staked_bandwidth: u64, available_energy: u64) -> TronAccountUsage {
Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub use self::transaction_metadata_types::{TransactionNFTTransferMetadata, Trans
pub mod wallet_connect_namespace;
pub use self::wallet_connect_namespace::WalletConnectCAIP2;
pub mod wallet_connect;
pub use self::wallet_connect::{WCEthereumTransaction, WalletConnectRequest};
pub use self::wallet_connect::{WCEthereumTransaction, WCTonMessage, WalletConnectRequest};
pub mod account;
pub use self::account::Account;
pub mod wallet;
Expand Down
10 changes: 10 additions & 0 deletions crates/primitives/src/wallet_connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ pub struct WCEthereumTransaction {
pub data: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better usability and consistency with other data structures in the crate, consider deriving Clone and PartialEq for WCTonMessage. This will make it easier to use in tests and other logic where copying or comparison is needed.

Suggested change
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

#[typeshare(swift = "Equatable, Hashable, Sendable")]
#[serde(rename_all = "camelCase")]
pub struct WCTonMessage {
pub address: String,
pub amount: String,
pub payload: Option<String>,
pub state_init: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WalletConnectRequest {
Expand Down
21 changes: 19 additions & 2 deletions crates/primitives/src/wallet_connect_namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub enum WalletConnectCAIP2 {
Algorand,
#[serde(rename = "sui")]
Sui,
#[serde(rename = "ton")]
Ton,
}

impl WalletConnectCAIP2 {
Expand All @@ -27,8 +29,8 @@ impl WalletConnectCAIP2 {
ChainType::Cosmos => Some(format!("{}:{}", WalletConnectCAIP2::Cosmos.as_ref(), chain.network_id())),
ChainType::Algorand => Some(WalletConnectCAIP2::Algorand.as_ref().to_string()),
ChainType::Sui => Some(WalletConnectCAIP2::Sui.as_ref().to_string()),
ChainType::Ton => Some(WalletConnectCAIP2::Ton.as_ref().to_string()),
ChainType::Bitcoin
| ChainType::Ton
| ChainType::Tron
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the definition of -239 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| ChainType::Aptos
| ChainType::Xrp
Expand All @@ -47,6 +49,7 @@ impl WalletConnectCAIP2 {
WalletConnectCAIP2::Cosmos => Some(ChainType::Cosmos),
WalletConnectCAIP2::Algorand => Some(ChainType::Algorand),
WalletConnectCAIP2::Sui => Some(ChainType::Sui),
WalletConnectCAIP2::Ton => Some(ChainType::Ton),
}
}

Expand All @@ -65,6 +68,7 @@ impl WalletConnectCAIP2 {
WalletConnectCAIP2::Solana => Some(Chain::Solana),
WalletConnectCAIP2::Algorand => Some(Chain::Algorand),
WalletConnectCAIP2::Sui => Some(Chain::Sui),
WalletConnectCAIP2::Ton => Some(Chain::Ton),
}
}

Expand All @@ -75,8 +79,8 @@ impl WalletConnectCAIP2 {
ChainType::Cosmos => Self::get_namespace(chain).map(|namespace| format!("{}:{}", namespace, chain.network_id())),
ChainType::Algorand => Some("wGHE2Pwdvd7S12BL5FaOP20EGYesN73k".to_string()),
ChainType::Sui => Some("mainnet".to_string()),
ChainType::Ton => Some("-239".to_string()),
ChainType::Bitcoin
| ChainType::Ton
| ChainType::Tron
| ChainType::Aptos
| ChainType::Xrp
Expand Down Expand Up @@ -114,6 +118,7 @@ mod tests {
assert_eq!(WalletConnectCAIP2::get_chain_type("cosmos".to_string()), Some(ChainType::Cosmos));
assert_eq!(WalletConnectCAIP2::get_chain_type("algorand".to_string()), Some(ChainType::Algorand));
assert_eq!(WalletConnectCAIP2::get_chain_type("sui".to_string()), Some(ChainType::Sui));
assert_eq!(WalletConnectCAIP2::get_chain_type("ton".to_string()), Some(ChainType::Ton));
assert_eq!(WalletConnectCAIP2::get_chain_type("unknown".to_string()), None);
}

Expand All @@ -123,6 +128,7 @@ mod tests {
assert_eq!(WalletConnectCAIP2::get_chain("eip155".to_string(), "56".to_string()), Some(Chain::SmartChain));
assert_eq!(WalletConnectCAIP2::get_chain("solana".to_string(), "ignored".to_string()), Some(Chain::Solana));
assert_eq!(WalletConnectCAIP2::get_chain("sui".to_string(), "mainnet".to_string()), Some(Chain::Sui));
assert_eq!(WalletConnectCAIP2::get_chain("ton".to_string(), "-239".to_string()), Some(Chain::Ton));
}

#[test]
Expand All @@ -133,9 +139,20 @@ mod tests {
Ok(Chain::Solana)
);
assert_eq!(WalletConnectCAIP2::resolve_chain(Some("sui:mainnet".to_string())), Ok(Chain::Sui));
assert_eq!(WalletConnectCAIP2::resolve_chain(Some("ton:-239".to_string())), Ok(Chain::Ton));
assert!(WalletConnectCAIP2::resolve_chain(Some("invalid".to_string())).is_err());
assert!(WalletConnectCAIP2::resolve_chain(Some("eip155:1:extra".to_string())).is_err());
assert!(WalletConnectCAIP2::resolve_chain(None).is_err());
assert!(WalletConnectCAIP2::resolve_chain(Some("unknown:chain".to_string())).is_err());
}

#[test]
fn test_get_namespace_ton() {
assert_eq!(WalletConnectCAIP2::get_namespace(Chain::Ton), Some("ton".to_string()));
}

#[test]
fn test_get_reference_ton() {
assert_eq!(WalletConnectCAIP2::get_reference(Chain::Ton), Some("-239".to_string()));
}
}
4 changes: 4 additions & 0 deletions crates/primitives/src/wallet_connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ pub enum WalletConnectionMethods {
SuiSignTransaction,
#[serde(rename = "sui_signAndExecuteTransaction")]
SuiSignAndExecuteTransaction,
#[serde(rename = "ton_sendMessage")]
TonSendMessage,
#[serde(rename = "ton_signData")]
TonSignData,
}

#[derive(Debug, Serialize)]
Expand Down
1 change: 1 addition & 0 deletions crates/signer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
gem_hash = { path = "../gem_hash" }
zeroize = { version = "1.8.2" }
strum = { workspace = true }

[dev-dependencies]
18 changes: 11 additions & 7 deletions crates/signer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ mod eip712;
mod error;
mod secp256k1;
mod sui;
mod ton;

pub use eip712::hash_typed_data as hash_eip712;
pub use error::SignerError;
pub use sui::SUI_PERSONAL_MESSAGE_SIGNATURE_LEN;
use std::borrow::Cow;

use crate::ed25519::{sign_digest as sign_ed25519_digest, signing_key_from_bytes};
use crate::secp256k1::sign_digest as sign_secp256k1_digest;
use crate::sui::assemble_signature;
use ed25519_dalek::Signer as DalekSigner;
use std::borrow::Cow;
use sui_types::PersonalMessage;
use zeroize::Zeroizing;

use crate::ed25519::{sign_digest as sign_ed25519_digest, signing_key_from_bytes};
use crate::secp256k1::sign_digest as sign_secp256k1_digest;
use crate::sui::assemble_signature;

pub use eip712::hash_typed_data as hash_eip712;
pub use error::SignerError;
pub use sui::SUI_PERSONAL_MESSAGE_SIGNATURE_LEN;
pub use ton::{TonSignDataPayload, TonSignDataResponse, TonSignDataType};

#[derive(Debug, Default)]
pub struct Signer;

Expand Down
162 changes: 162 additions & 0 deletions crates/signer/src/ton.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use strum::{AsRefStr, EnumString};

use crate::error::SignerError;

#[derive(Clone, Debug, PartialEq, AsRefStr, EnumString)]
#[strum(serialize_all = "lowercase")]
pub enum TonSignDataType {
Text,
Binary,
Cell,
}

impl TonSignDataType {
pub fn data_field(&self) -> &'static str {
match self {
TonSignDataType::Text => "text",
TonSignDataType::Binary => "bytes",
TonSignDataType::Cell => "cell",
}
}
}

#[derive(Deserialize)]
struct TonSignDataPayloadRaw {
#[serde(rename = "type")]
payload_type: String,
text: Option<String>,
bytes: Option<String>,
cell: Option<String>,
}

pub struct TonSignDataPayload {
pub payload_type: TonSignDataType,
pub data: String,
}

impl TonSignDataPayload {
pub fn parse(json: &str) -> Result<Self, SignerError> {
let raw: TonSignDataPayloadRaw = serde_json::from_str(json)?;

let payload_type = TonSignDataType::from_str(&raw.payload_type).map_err(|_| SignerError::new(format!("Unknown payload type: {}", raw.payload_type)))?;

let data = match payload_type {
TonSignDataType::Text => raw.text.ok_or("Missing text field")?,
TonSignDataType::Binary => raw.bytes.ok_or("Missing bytes field")?,
TonSignDataType::Cell => raw.cell.ok_or("Missing cell field")?,
};

Ok(Self { payload_type, data })
}

pub fn hash(&self) -> Vec<u8> {
self.data.as_bytes().to_vec()
}

pub fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"type": self.payload_type.as_ref(),
self.payload_type.data_field(): self.data,
})
}
}

#[derive(Serialize)]
pub struct TonSignDataResponse {
signature: String,
#[serde(rename = "publicKey")]
public_key: String,
timestamp: u64,
domain: String,
payload: serde_json::Value,
}

impl TonSignDataResponse {
pub fn new(signature: String, public_key: String, timestamp: u64, domain: String, payload: serde_json::Value) -> Self {
Self {
signature,
public_key,
timestamp,
domain,
payload,
}
}

pub fn to_json(&self) -> Result<String, SignerError> {
serde_json::to_string(self).map_err(SignerError::from)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_payload_text() {
let json = r#"{"type":"text","text":"Hello TON"}"#;
let parsed = TonSignDataPayload::parse(json).unwrap();

assert_eq!(parsed.payload_type, TonSignDataType::Text);
assert_eq!(parsed.data, "Hello TON");
assert_eq!(parsed.hash(), b"Hello TON".to_vec());
}

#[test]
fn test_parse_payload_binary() {
let json = r#"{"type":"binary","bytes":"SGVsbG8="}"#;
let parsed = TonSignDataPayload::parse(json).unwrap();

assert_eq!(parsed.payload_type, TonSignDataType::Binary);
assert_eq!(parsed.data, "SGVsbG8=");
}

#[test]
fn test_parse_payload_cell() {
let json = r#"{"type":"cell","cell":"te6c"}"#;
let parsed = TonSignDataPayload::parse(json).unwrap();

assert_eq!(parsed.payload_type, TonSignDataType::Cell);
assert_eq!(parsed.data, "te6c");
}

#[test]
fn test_payload_to_json() {
let payload = TonSignDataPayload {
payload_type: TonSignDataType::Text,
data: "Hello TON".to_string(),
};

let json = payload.to_json();
assert_eq!(json["type"], "text");
assert_eq!(json["text"], "Hello TON");
}

#[test]
fn test_response_to_json() {
let payload = TonSignDataPayload {
payload_type: TonSignDataType::Text,
data: "Hello TON".to_string(),
};

let response = TonSignDataResponse::new(
"c2lnbmF0dXJl".to_string(),
"cHVibGljS2V5".to_string(),
1234567890,
"example.com".to_string(),
payload.to_json(),
);

let json = response.to_json().unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();

assert_eq!(parsed["signature"], "c2lnbmF0dXJl");
assert_eq!(parsed["publicKey"], "cHVibGljS2V5");
assert_eq!(parsed["timestamp"], 1234567890);
assert_eq!(parsed["domain"], "example.com");
assert_eq!(parsed["payload"]["type"], "text");
assert_eq!(parsed["payload"]["text"], "Hello TON");
}
}
6 changes: 5 additions & 1 deletion gemstone/src/config/wallet_connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ pub struct WalletConnectConfig {
}

pub fn get_wallet_connect_config() -> WalletConnectConfig {
let chains: Vec<Chain> = [vec![Chain::Solana, Chain::Sui], EVMChain::all().iter().map(|x| x.to_chain()).collect()].concat();
let chains: Vec<Chain> = [
vec![Chain::Solana, Chain::Sui, Chain::Ton],
EVMChain::all().iter().map(|x| x.to_chain()).collect(),
]
.concat();

WalletConnectConfig {
chains: chains.into_iter().map(|x| x.to_string()).collect(),
Expand Down
Loading
Loading