Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Solana]: Add a function for generating token addresses for the Token-2022 Program #4010

Merged
Merged
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
8 changes: 8 additions & 0 deletions include/TrustWalletCore/TWSolanaAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ void TWSolanaAddressDelete(struct TWSolanaAddress* _Nonnull address);
TW_EXPORT_METHOD
TWString* _Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress);

/// Derive token 2022 address for token
///
/// \param address Non-null pointer to a Solana Address
/// \param tokenMintAddress Non-null pointer to a token mint address as a string
/// \return Null pointer if the token 2022 address for a token is not found, valid pointer otherwise
TW_EXPORT_METHOD
TWString* _Nullable TWSolanaAddressToken2022Address(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress);

/// Returns the address string representation.
///
/// \param address Non-null pointer to a Solana Address
Expand Down
3 changes: 2 additions & 1 deletion rust/chains/tw_solana/src/program/stake_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ impl StakeProgram {
/// https://github.com/solana-labs/solana-program-library/blob/master/associated-token-account/program/src/lib.rs#L35
pub fn get_associated_token_address(
main_address: SolanaAddress,
token_program_id: SolanaAddress,
token_mint_address: SolanaAddress,
) -> AddressResult<SolanaAddress> {
SolanaAddress::find_program_address(
&[
main_address.bytes().as_slice(),
TOKEN_PROGRAM_ID_ADDRESS.bytes().as_slice(),
token_program_id.bytes().as_slice(),
token_mint_address.bytes().as_slice(),
],
*ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS,
Expand Down
9 changes: 7 additions & 2 deletions rust/chains/tw_solana/tests/get_default_token_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use std::str::FromStr;
use tw_solana::address::SolanaAddress;
use tw_solana::blockhash::Blockhash;
use tw_solana::defined_addresses::TOKEN_PROGRAM_ID_ADDRESS;
use tw_solana::program::stake_program::StakeProgram;

fn test_get_default_token_address_impl(
Expand All @@ -16,8 +17,12 @@ fn test_get_default_token_address_impl(
let token_mint_address = SolanaAddress::from_str(token_mint_address).unwrap();
let expected = SolanaAddress::from_str(expected).unwrap();

let actual = StakeProgram::get_associated_token_address(main_address, token_mint_address)
.expect("!get_associated_token_address");
let actual = StakeProgram::get_associated_token_address(
main_address,
*TOKEN_PROGRAM_ID_ADDRESS,
token_mint_address,
)
.expect("!get_associated_token_address");
assert_eq!(actual, expected);
}

Expand Down
35 changes: 30 additions & 5 deletions rust/wallet_core_rs/src/ffi/solana/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use tw_memory::ffi::tw_string::TWString;
use tw_memory::ffi::RawPtrTrait;
use tw_misc::try_or_else;
use tw_solana::address::SolanaAddress;
use tw_solana::defined_addresses::{TOKEN_2022_PROGRAM_ID_ADDRESS, TOKEN_PROGRAM_ID_ADDRESS};
use tw_solana::program::stake_program::StakeProgram;

/// Derive default token address for token
Expand All @@ -20,26 +21,50 @@ use tw_solana::program::stake_program::StakeProgram;
pub unsafe extern "C" fn tw_solana_address_default_token_address(
address: *const TWString,
token_mint_address: *const TWString,
) -> *mut TWString {
tw_solana_address_token_address_impl(address, token_mint_address, *TOKEN_PROGRAM_ID_ADDRESS)
}

/// Derive token 2022 address for token
///
/// \param address Non-null pointer to a Solana Address
/// \param token_mint_address Non-null pointer to a token mint address as a string
/// \return Null pointer if the token 2022 address for a token is not found, valid pointer otherwise
#[no_mangle]
pub unsafe extern "C" fn tw_solana_address_token_2022_address(
satoshiotomakan marked this conversation as resolved.
Show resolved Hide resolved
address: *const TWString,
token_mint_address: *const TWString,
) -> *mut TWString {
tw_solana_address_token_address_impl(
address,
token_mint_address,
*TOKEN_2022_PROGRAM_ID_ADDRESS,
)
}

unsafe fn tw_solana_address_token_address_impl(
address: *const TWString,
token_mint_address: *const TWString,
token_address: SolanaAddress,
) -> *mut TWString {
let main_address = try_or_else!(TWString::from_ptr_as_ref(address), std::ptr::null_mut);
let main_address = try_or_else!(main_address.as_str(), std::ptr::null_mut);
let main_address = try_or_else!(SolanaAddress::from_str(main_address), std::ptr::null_mut);

let token_mint_address = try_or_else!(
TWString::from_ptr_as_ref(token_mint_address),
std::ptr::null_mut
);
let token_mint_address = try_or_else!(token_mint_address.as_str(), std::ptr::null_mut);

let main_address = try_or_else!(SolanaAddress::from_str(main_address), std::ptr::null_mut);
let token_mint_address = try_or_else!(
SolanaAddress::from_str(token_mint_address),
std::ptr::null_mut
);

let token_address = try_or_else!(
StakeProgram::get_associated_token_address(main_address, token_mint_address),
let associated_token_address = try_or_else!(
StakeProgram::get_associated_token_address(main_address, token_address, token_mint_address),
std::ptr::null_mut
);

TWString::from(token_address.to_string()).into_ptr()
TWString::from(associated_token_address.to_string()).into_ptr()
}
24 changes: 23 additions & 1 deletion rust/wallet_core_rs/tests/solana/solana_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// Copyright © 2017 Trust Wallet.

use tw_memory::test_utils::tw_string_helper::TWStringHelper;
use wallet_core_rs::ffi::solana::address::tw_solana_address_default_token_address;
use wallet_core_rs::ffi::solana::address::{
tw_solana_address_default_token_address, tw_solana_address_token_2022_address,
};

#[test]
fn test_solana_address_default_token_address() {
Expand All @@ -24,3 +26,23 @@ fn test_solana_address_default_token_address() {

assert_eq!(actual, "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP");
}

#[test]
fn test_solana_address_token_2022_address() {
let main_address = "68dzdXkni9BrAwU1asAwurMEdQhXUJq6MNY8niDAny8t";
let main_address = TWStringHelper::create(main_address);

let token_mint_address = "7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1";
let token_mint_address = TWStringHelper::create(token_mint_address);

let actual = unsafe {
TWStringHelper::wrap(tw_solana_address_token_2022_address(
main_address.ptr(),
token_mint_address.ptr(),
))
}
.to_string()
.expect("!tw_solana_address_associated_token_address returned a nullptr");

assert_eq!(actual, "3PaFQnebQMHBgthRScup2B932cMxA1GBP7m9roCkomHq");
}
19 changes: 19 additions & 0 deletions src/interface/TWSolanaAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ TWString* _Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _
}
}

TWString* _Nullable TWSolanaAddressToken2022Address(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress) {
try {
if (address == nullptr || tokenMintAddress == nullptr) {
return nullptr;
}
Rust::TWStringWrapper tokenMintAddressWrapper = TWStringUTF8Bytes(tokenMintAddress);
Rust::TWStringWrapper mainAddress = address->impl.string();

Rust::TWStringWrapper newTokenAddress = Rust::tw_solana_address_token_2022_address(mainAddress.get(), tokenMintAddressWrapper.get());

if (!newTokenAddress) {
return nullptr;
}
return TWStringCreateWithUTF8Bytes(newTokenAddress.c_str());
} catch (...) {
return nullptr;
}
}

TWString* _Nonnull TWSolanaAddressDescription(struct TWSolanaAddress* _Nonnull address) {
return TWStringCreateWithUTF8Bytes(address->impl.string().c_str());
}
23 changes: 23 additions & 0 deletions tests/chains/Solana/TWSolanaAddressTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,26 @@ TEST(TWSolanaProgram, defaultTokenAddressError) {

EXPECT_EQ(TWSolanaAddressDefaultTokenAddress(solanaAddress.get(), serumToken.get()), nullptr);
}

TEST(TWSolanaProgram, token2022Address) {
const auto solAddress = STRING("68dzdXkni9BrAwU1asAwurMEdQhXUJq6MNY8niDAny8t");
const auto catwifhatToken = STRING("7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1");

auto solanaAddress = WRAP(TWSolanaAddress, TWSolanaAddressCreateWithString(solAddress.get()));
auto description = WRAPS(TWSolanaAddressDescription(solanaAddress.get()));
auto tokenAddress = WRAPS(TWSolanaAddressToken2022Address(solanaAddress.get(), catwifhatToken.get()));

assertStringsEqual(tokenAddress, "3PaFQnebQMHBgthRScup2B932cMxA1GBP7m9roCkomHq");
assertStringsEqual(description, "68dzdXkni9BrAwU1asAwurMEdQhXUJq6MNY8niDAny8t");
}

TEST(TWSolanaProgram, token2022AddressError) {
const auto solAddress = STRING("68dzdXkni9BrAwU1asAwurMEdQhXUJq6MNY8niDAny8t");
// Invalid token mint address.
const auto catwifhatToken = STRING("7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icF");

auto solanaAddress = WRAP(TWSolanaAddress, TWSolanaAddressCreateWithString(solAddress.get()));
auto description = WRAPS(TWSolanaAddressDescription(solanaAddress.get()));

EXPECT_EQ(TWSolanaAddressToken2022Address(solanaAddress.get(), catwifhatToken.get()), nullptr);
}
Loading