#import "TON.h" #import "tonlib/Client.h" static td::SecureString makeSecureString(NSData * _Nonnull data) { if (data == nil || data.length == 0) { return td::SecureString(); } else { return td::SecureString((const char *)data.bytes, (size_t)data.length); } } static std::string makeString(NSData * _Nonnull data) { if (data == nil || data.length == 0) { return std::string(); } else { return std::string((const char *)data.bytes, ((const char *)data.bytes) + data.length); } } static NSData * _Nonnull makeData(std::string &string) { if (string.size() == 0) { return [NSData data]; } else { return [[NSData alloc] initWithBytes:string.data() length:string.size()]; } } static NSString * _Nullable readString(std::string &string) { if (string.size() == 0) { return @""; } else { return [[NSString alloc] initWithBytes:string.data() length:string.size() encoding:NSUTF8StringEncoding]; } } static NSData * _Nullable readSecureString(td::SecureString &string) { if (string.size() == 0) { return [NSData data]; } else { return [[NSData alloc] initWithBytes:string.data() length:string.size()]; } } static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::object_ptr &message) { if (message == nullptr) { return nil; } NSString *source = readString(message->source_->account_address_); NSString *destination = readString(message->destination_->account_address_); id contents = nil; if (message->msg_data_->get_id() == tonlib_api::msg_dataRaw::ID) { auto msgData = tonlib_api::move_object_as(message->msg_data_); contents = [[TONTransactionMessageContentsRawData alloc] initWithData:makeData(msgData->body_)]; } else if (message->msg_data_->get_id() == tonlib_api::msg_dataText::ID) { auto msgData = tonlib_api::move_object_as(message->msg_data_); NSString *text = readString(msgData->text_); if (text == nil) { contents = [[TONTransactionMessageContentsPlainText alloc] initWithText:@""]; } else { contents = [[TONTransactionMessageContentsPlainText alloc] initWithText:text]; } } else if (message->msg_data_->get_id() == tonlib_api::msg_dataDecryptedText::ID) { auto msgData = tonlib_api::move_object_as(message->msg_data_); NSString *text = readString(msgData->text_); if (text == nil) { contents = [[TONTransactionMessageContentsPlainText alloc] initWithText:@""]; } else { contents = [[TONTransactionMessageContentsPlainText alloc] initWithText:text]; } } else if (message->msg_data_->get_id() == tonlib_api::msg_dataEncryptedText::ID) { auto msgData = tonlib_api::move_object_as(message->msg_data_); TONEncryptedData *encryptedData = [[TONEncryptedData alloc] initWithSourceAddress:source data:makeData(msgData->text_)]; contents = [[TONTransactionMessageContentsEncryptedText alloc] initWithEncryptedData:encryptedData]; } else { contents = [[TONTransactionMessageContentsRawData alloc] initWithData:[NSData data]]; } if (source == nil || destination == nil) { return nil; } return [[TONTransactionMessage alloc] initWithValue:message->value_ source:source destination:destination contents:contents bodyHash:makeData(message->body_hash_)]; } @implementation TONKey - (instancetype)initWithPublicKey:(NSString *)publicKey secret:(NSData *)secret { self = [super init]; if (self != nil) { _publicKey = publicKey; _secret = secret; } return self; } @end @implementation TONAccountState - (instancetype)initWithIsInitialized:(bool)isInitialized balance:(int64_t)balance seqno:(int32_t)seqno lastTransactionId:(TONTransactionId * _Nullable)lastTransactionId syncUtime:(int64_t)syncUtime { self = [super init]; if (self != nil) { _isInitialized = isInitialized; _balance = balance; _seqno = seqno; _lastTransactionId = lastTransactionId; _syncUtime = syncUtime; } return self; } @end @implementation TONTransactionId - (instancetype)initWithLt:(int64_t)lt transactionHash:(NSData *)transactionHash { self = [super init]; if (self != nil) { _lt = lt; _transactionHash = transactionHash; } return self; } @end @implementation TONTransactionMessageContentsRawData - (instancetype)initWithData:(NSData * _Nonnull)data { self = [super init]; if (self != nil) { _data = data; } return self; } @end @implementation TONTransactionMessageContentsPlainText - (instancetype)initWithText:(NSString * _Nonnull)text { self = [super init]; if (self != nil) { _text = text; } return self; } @end @implementation TONTransactionMessageContentsEncryptedText - (instancetype)initWithEncryptedData:(TONEncryptedData * _Nonnull)encryptedData { self = [super init]; if (self != nil) { _encryptedData = encryptedData; } return self; } @end @implementation TONTransactionMessage - (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination contents:(id _Nonnull)contents bodyHash:(NSData * _Nonnull)bodyHash { self = [super init]; if (self != nil) { _value = value; _source = source; _destination = destination; _contents = contents; _bodyHash = bodyHash; } return self; } @end @implementation TONTransaction - (instancetype)initWithData:(NSData * _Nonnull)data transactionId:(TONTransactionId * _Nonnull)transactionId timestamp:(int64_t)timestamp storageFee:(int64_t)storageFee otherFee:(int64_t)otherFee inMessage:(TONTransactionMessage * _Nullable)inMessage outMessages:(NSArray * _Nonnull)outMessages { self = [super init]; if (self != nil) { _data = data; _transactionId = transactionId; _timestamp = timestamp; _storageFee = storageFee; _otherFee = otherFee; _inMessage = inMessage; _outMessages = outMessages; } return self; } @end @implementation TONExternalRequest - (instancetype)initWithData:(NSData * _Nonnull)data onResult:(void (^)(NSData * _Nullable, NSString * _Nullable))onResult { self = [super init]; if (self != nil) { _data = data; _onResult = [onResult copy]; } return self; } @end @implementation TONFees - (instancetype)initWithInFwdFee:(int64_t)inFwdFee storageFee:(int64_t)storageFee gasFee:(int64_t)gasFee fwdFee:(int64_t)fwdFee { self = [super init]; if (self != nil) { _inFwdFee = inFwdFee; _storageFee = storageFee; _gasFee = gasFee; _fwdFee = fwdFee; } return self; } @end @implementation TONSendGramsQueryFees - (instancetype)initWithSourceFees:(TONFees * _Nonnull)sourceFees destinationFees:(NSArray * _Nonnull)destinationFees { self = [super init]; if (self != nil) { _sourceFees = sourceFees; _destinationFees = destinationFees; } return self; } @end @implementation TONPreparedSendGramsQuery - (instancetype)initWithQueryId:(int64_t)queryId validUntil:(int64_t)validUntil bodyHash:(NSData *)bodyHash { self = [super init]; if (self != nil) { _queryId = queryId; _validUntil = validUntil; _bodyHash = bodyHash; } return self; } @end @implementation TONSendGramsResult - (instancetype)initWithSentUntil:(int64_t)sentUntil bodyHash:(NSData *)bodyHash { self = [super init]; if (self != nil) { _sentUntil = sentUntil; _bodyHash = bodyHash; } return self; } @end @implementation TONValidatedConfig - (instancetype)initWithDefaultWalletId:(int64_t)defaultWalletId { self = [super init]; if (self != nil) { _defaultWalletId = defaultWalletId; } return self; } @end @implementation TONEncryptedData - (instancetype)initWithSourceAddress:(NSString * _Nonnull)sourceAddress data:(NSData * _Nonnull)data { self = [super init]; if (self != nil) { _sourceAddress = sourceAddress; _data = data; } return self; } @end using tonlib_api::make_object; @interface TONReceiveThreadParams : NSObject @property (nonatomic, readonly) std::shared_ptr client; @property (nonatomic, copy, readonly) void (^received)(tonlib::Client::Response &); @end @implementation TONReceiveThreadParams - (instancetype)initWithClient:(std::shared_ptr)client received:(void (^)(tonlib::Client::Response &))received { self = [super init]; if (self != nil) { _client = client; _received = [received copy]; } return self; } @end @interface TONRequestHandler : NSObject @property (nonatomic, copy, readonly) void (^completion)(tonlib_api::object_ptr &); @end @implementation TONRequestHandler - (instancetype)initWithCompletion:(void (^)(tonlib_api::object_ptr &))completion { self = [super init]; if (self != nil) { _completion = [completion copy]; } return self; } @end @implementation TONError - (instancetype)initWithText:(NSString *)text { self = [super init]; if (self != nil) { _text = text; } return self; } @end typedef enum { TONInitializationStatusInitializing, TONInitializationStatusReady, TONInitializationStatusError } TONInitializationStatus; @interface TON () { std::shared_ptr _client; uint64_t _nextRequestId; NSLock *_requestHandlersLock; NSMutableDictionary *_requestHandlers; SPipe *_initializedStatus; NSMutableSet *_sendGramRandomIds; SQueue *_queue; bool _enableExternalRequests; } @end @implementation TON + (void)receiveThread:(TONReceiveThreadParams *)params { while (true) { auto response = params.client->receive(1000); if (response.object) { params.received(response); } } } - (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest enableExternalRequests:(bool)enableExternalRequests syncStateUpdated:(void (^)(float))syncStateUpdated { self = [super init]; if (self != nil) { _queue = [SQueue mainQueue]; _requestHandlersLock = [[NSLock alloc] init]; _requestHandlers = [[NSMutableDictionary alloc] init]; _initializedStatus = [[SPipe alloc] initWithReplay:true]; _initializedStatus.sink(@(TONInitializationStatusInitializing)); _nextRequestId = 1; _sendGramRandomIds = [[NSMutableSet alloc] init]; _enableExternalRequests = enableExternalRequests; _client = std::make_shared(); [self setupLogging]; std::weak_ptr weakClient = _client; NSLock *requestHandlersLock = _requestHandlersLock; NSMutableDictionary *requestHandlers = _requestHandlers; NSThread *thread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(receiveThread:) object:[[TONReceiveThreadParams alloc] initWithClient:_client received:^(tonlib::Client::Response &response) { if (response.object->get_id() == tonlib_api::updateSendLiteServerQuery::ID) { auto result = tonlib_api::move_object_as(response.object); int64_t requestId = result->id_; NSData *data = makeData(result->data_); if (performExternalRequest) { performExternalRequest([[TONExternalRequest alloc] initWithData:data onResult:^(NSData * _Nullable result, NSString * _Nullable error) { auto strongClient = weakClient.lock(); if (strongClient != nullptr) { if (result != nil) { auto query = make_object( requestId, makeString(result) ); strongClient->send({ 1, std::move(query) }); } else if (error != nil) { auto query = make_object( requestId, make_object( 400, error.UTF8String ) ); strongClient->send({ 1, std::move(query) }); } } }]); } return; } else if (response.object->get_id() == tonlib_api::updateSyncState::ID) { auto result = tonlib_api::move_object_as(response.object); if (result->sync_state_->get_id() == tonlib_api::syncStateInProgress::ID) { auto syncStateInProgress = tonlib_api::move_object_as(result->sync_state_); int32_t currentDelta = syncStateInProgress->current_seqno_ - syncStateInProgress->from_seqno_; int32_t fullDelta = syncStateInProgress->to_seqno_ - syncStateInProgress->from_seqno_; if (currentDelta > 0 && fullDelta > 0) { float progress = ((float)currentDelta) / ((float)fullDelta); syncStateUpdated(progress); } else { syncStateUpdated(0.0f); } } else if (result->sync_state_->get_id() == tonlib_api::syncStateDone::ID) { syncStateUpdated(1.0f); } return; } NSNumber *requestId = @(response.id); [requestHandlersLock lock]; TONRequestHandler *handler = requestHandlers[requestId]; [requestHandlers removeObjectForKey:requestId]; [requestHandlersLock unlock]; if (handler != nil) { handler.completion(response.object); } }]]; [thread start]; [[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil]; SPipe *initializedStatus = _initializedStatus; [[self requestInitWithConfigString:config blockchainName:blockchainName keystoreDirectory:keystoreDirectory enableExternalRequests:enableExternalRequests] startWithNext:nil error:^(id error) { NSString *errorText = @"Unknown error"; if ([error isKindOfClass:[TONError class]]) { errorText = ((TONError *)error).text; } initializedStatus.sink(@(TONInitializationStatusError)); } completed:^{ initializedStatus.sink(@(TONInitializationStatusReady)); }]; } return self; } - (void)setupLogging { #if DEBUG auto query = make_object( make_object() ); _client->execute({ INT16_MAX + 1, std::move(query) }); #else auto query = make_object( make_object() ); _client->execute({ INT16_MAX + 1, std::move(query) }); #endif } - (NSData * __nullable)decrypt:(NSData *)encryptedData secret:(NSData *)data { auto query = make_object(makeSecureString(encryptedData), makeSecureString(data)); tonlib_api::object_ptr result = _client->execute({ INT16_MAX + 1, std::move(query) }).object; if (result->get_id() == tonlib_api::error::ID) { return nil; } else { tonlib_api::object_ptr value = tonlib_api::move_object_as(result); return readSecureString(value->bytes_); } } - (NSData *)encrypt:(NSData *)decryptedData secret:(NSData *)data { auto query = make_object(makeSecureString(decryptedData), makeSecureString(data)); tonlib_api::object_ptr result = _client->execute({ INT16_MAX + 1, std::move(query) }).object; tonlib_api::object_ptr value = tonlib_api::move_object_as(result); return readSecureString(value->bytes_); } - (SSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory enableExternalRequests:(bool)enableExternalRequests { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else { [subscriber putCompletion]; } }]; bool ignoreCache = false; #if DEBUG ignoreCache = true; #endif auto query = make_object(make_object( make_object( configString.UTF8String, blockchainName.UTF8String, enableExternalRequests, ignoreCache ), make_object( keystoreDirectory.UTF8String ) )); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)updateConfig:(NSString *)config blockchainName:(NSString *)blockchainName { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else { [subscriber putCompletion]; } }]; auto query = make_object( make_object( config.UTF8String, blockchainName.UTF8String, _enableExternalRequests, false ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)validateConfig:(NSString *)config blockchainName:(NSString *)blockchainName { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::options_configInfo::ID) { auto result = tonlib_api::move_object_as(object); [subscriber putNext:[[TONValidatedConfig alloc] initWithDefaultWalletId:result->default_wallet_id_]]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( make_object( config.UTF8String, blockchainName.UTF8String, _enableExternalRequests, false ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::key::ID) { auto result = tonlib_api::move_object_as(object); NSString *publicKey = [[NSString alloc] initWithData:[[NSData alloc] initWithBytes:result->public_key_.data() length:result->public_key_.length()] encoding:NSUTF8StringEncoding]; if (publicKey == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error decoding UTF8 string in createKeyWithLocalPassword"]]; return; } NSData *secret = [[NSData alloc] initWithBytes:result->secret_.data() length:result->secret_.length()]; [subscriber putNext:[[TONKey alloc] initWithPublicKey:publicKey secret:secret]]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( makeSecureString(localPassword), makeSecureString(mnemonicPassword), td::SecureString() ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey initialWalletId:(int64_t)initialWalletId { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { NSData *publicKeyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getWalletAccountAddressWithPublicKey"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::accountAddress::ID) { auto result = tonlib_api::move_object_as(object); [subscriber putNext:[[NSString alloc] initWithUTF8String:result->account_address_.c_str()]]; [subscriber putCompletion]; } else { assert(false); } }]; auto initialAccountState = make_object( makeString(publicKeyData), initialWalletId ); auto query = make_object( tonlib_api::move_object_as(initialAccountState), 1 ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)getAccountStateWithAddress:(NSString *)accountAddress { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::fullAccountState::ID) { auto fullAccountState = tonlib_api::move_object_as(object); int32_t seqNo = -1; if (fullAccountState->account_state_->get_id() == tonlib_api::uninited_accountState::ID) { seqNo = -1; } else if (fullAccountState->account_state_->get_id() == tonlib_api::wallet_v3_accountState::ID) { auto v3AccountState = tonlib_api::move_object_as(fullAccountState->account_state_); seqNo = v3AccountState->seqno_; } else { [subscriber putError:[[TONError alloc] initWithText:@"Unknown type"]]; return; } TONTransactionId *lastTransactionId = nil; if (fullAccountState->last_transaction_id_ != nullptr) { lastTransactionId = [[TONTransactionId alloc] initWithLt:fullAccountState->last_transaction_id_->lt_ transactionHash:makeData(fullAccountState->last_transaction_id_->hash_)]; } [subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:false balance:fullAccountState->balance_ seqno:-1 lastTransactionId:lastTransactionId syncUtime:fullAccountState->sync_utime_]]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object(make_object(accountAddress.UTF8String)); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)generateSendGramsQueryFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount comment:(NSData *)comment encryptComment:(bool)encryptComment forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { if ([_sendGramRandomIds containsObject:@(randomId)]) { [_sendGramRandomIds addObject:@(randomId)]; return [[SBlockDisposable alloc] initWithBlock:^{ }]; } NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; __weak TON *weakSelf = self; SQueue *queue = _queue; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { [queue dispatch:^{ __strong TON *strongSelf = weakSelf; if (strongSelf != nil) { [_sendGramRandomIds removeObject:@(randomId)]; } }]; auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::query_info::ID) { auto result = tonlib_api::move_object_as(object); TONPreparedSendGramsQuery *preparedQuery = [[TONPreparedSendGramsQuery alloc] initWithQueryId:result->id_ validUntil:result->valid_until_ bodyHash:makeData(result->body_hash_)]; [subscriber putNext:preparedQuery]; [subscriber putCompletion]; } else { [subscriber putCompletion]; } }]; tonlib_api::object_ptr inputMessageData; if (encryptComment && comment.length != 0) { inputMessageData = make_object( makeString(comment) ); } else { inputMessageData = make_object( makeString(comment) ); } std::vector > inputMessages; inputMessages.push_back(make_object( make_object(address.UTF8String), makeString([NSData data]), amount, tonlib_api::move_object_as(inputMessageData) )); auto inputAction = make_object( std::move(inputMessages), forceIfDestinationNotInitialized ); auto query = make_object( make_object( make_object( makeString(publicKeyData), makeSecureString(key.secret) ), makeSecureString(localPassword) ), make_object(fromAddress.UTF8String), timeout, tonlib_api::move_object_as(inputAction) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)generateFakeSendGramsQueryFromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount comment:(NSData *)comment encryptComment:(bool)encryptComment forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::query_info::ID) { auto result = tonlib_api::move_object_as(object); TONPreparedSendGramsQuery *preparedQuery = [[TONPreparedSendGramsQuery alloc] initWithQueryId:result->id_ validUntil:result->valid_until_ bodyHash:makeData(result->body_hash_)]; [subscriber putNext:preparedQuery]; [subscriber putCompletion]; } else { [subscriber putCompletion]; } }]; tonlib_api::object_ptr inputMessageData; if (encryptComment && comment.length != 0) { inputMessageData = make_object( makeString(comment) ); } else { inputMessageData = make_object( makeString(comment) ); } std::vector > inputMessages; inputMessages.push_back(make_object( make_object(address.UTF8String), makeString([NSData data]), amount, tonlib_api::move_object_as(inputMessageData) )); auto inputAction = make_object( std::move(inputMessages), forceIfDestinationNotInitialized ); auto query = make_object( make_object(), make_object(fromAddress.UTF8String), timeout, tonlib_api::move_object_as(inputAction) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)estimateSendGramsQueryFees:(TONPreparedSendGramsQuery *)preparedQuery { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::query_fees::ID) { auto result = tonlib_api::move_object_as(object); TONFees *sourceFees = [[TONFees alloc] initWithInFwdFee:result->source_fees_->in_fwd_fee_ storageFee:result->source_fees_->storage_fee_ gasFee:result->source_fees_->gas_fee_ fwdFee:result->source_fees_->fwd_fee_]; NSMutableArray *destinationFees = [[NSMutableArray alloc] init]; for (auto &fee : result->destination_fees_) { TONFees *destinationFee = [[TONFees alloc] initWithInFwdFee:fee->in_fwd_fee_ storageFee:fee->storage_fee_ gasFee:fee->gas_fee_ fwdFee:fee->fwd_fee_]; [destinationFees addObject:destinationFee]; } [subscriber putNext:[[TONSendGramsQueryFees alloc] initWithSourceFees:sourceFees destinationFees:destinationFees]]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( preparedQuery.queryId, true ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)commitPreparedSendGramsQuery:(TONPreparedSendGramsQuery *)preparedQuery { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::ok::ID) { [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( preparedQuery.queryId ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in exportKey"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::exportedKey::ID) { auto result = tonlib_api::move_object_as(object); NSMutableArray *wordList = [[NSMutableArray alloc] init]; for (auto &it : result->word_list_) { NSString *string = [[NSString alloc] initWithData:[[NSData alloc] initWithBytes:it.data() length:it.size()] encoding:NSUTF8StringEncoding]; if (string == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error decoding UTF8 string in exportedKey::word_list"]]; return; } [wordList addObject:string]; } [subscriber putNext:wordList]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( make_object( make_object( makeString(publicKeyData), makeSecureString(key.secret) ), makeSecureString(localPassword) ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray *)wordList { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::key::ID) { auto result = tonlib_api::move_object_as(object); NSString *publicKey = [[NSString alloc] initWithData:[[NSData alloc] initWithBytes:result->public_key_.data() length:result->public_key_.length()] encoding:NSUTF8StringEncoding]; if (publicKey == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error decoding UTF8 string in importKeyWithLocalPassword"]]; return; } NSData *secret = [[NSData alloc] initWithBytes:result->secret_.data() length:result->secret_.length()]; [subscriber putNext:[[TONKey alloc] initWithPublicKey:publicKey secret:secret]]; [subscriber putCompletion]; } else { assert(false); } }]; std::vector wordVector; for (NSString *word in wordList) { NSData *wordData = [word dataUsingEncoding:NSUTF8StringEncoding]; wordVector.push_back(makeSecureString(wordData)); } auto query = make_object( makeSecureString(localPassword), makeSecureString(mnemonicPassword), make_object(std::move(wordVector))); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)deleteKey:(TONKey *)key { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in deleteKey"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else { [subscriber putCompletion]; } }]; auto query = make_object( make_object( makeString(publicKeyData), makeSecureString(key.secret) ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)deleteAllKeys { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else { [subscriber putCompletion]; } }]; auto query = make_object(); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { NSData *addressData = [address dataUsingEncoding:NSUTF8StringEncoding]; if (addressData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getTransactionListWithAddress"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::raw_transactions::ID) { auto result = tonlib_api::move_object_as(object); NSMutableArray *transactions = [[NSMutableArray alloc] init]; for (auto &it : result->transactions_) { TONTransactionId *transactionId = [[TONTransactionId alloc] initWithLt:it->transaction_id_->lt_ transactionHash:makeData(it->transaction_id_->hash_)]; TONTransactionMessage *inMessage = parseTransactionMessage(it->in_msg_); NSMutableArray * outMessages = [[NSMutableArray alloc] init]; for (auto &messageIt : it->out_msgs_) { TONTransactionMessage *outMessage = parseTransactionMessage(messageIt); if (outMessage != nil) { [outMessages addObject:outMessage]; } } [transactions addObject:[[TONTransaction alloc] initWithData:makeData(it->data_) transactionId:transactionId timestamp:it->utime_ storageFee:it->storage_fee_ otherFee:it->other_fee_ inMessage:inMessage outMessages:outMessages]]; } [subscriber putNext:transactions]; [subscriber putCompletion]; } else { assert(false); } }]; auto query = make_object( make_object(), make_object( makeString(addressData) ), make_object( lt, makeString(hash) ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } - (SSignal *)decryptMessagesWithKey:(TONKey * _Nonnull)key localPassword:(NSData * _Nonnull)localPassword messages:(NSArray * _Nonnull)messages { return [[[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) { NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in decryptMessagesWithKey"]]; return [[SBlockDisposable alloc] initWithBlock:^{}]; } uint64_t requestId = _nextRequestId; _nextRequestId += 1; _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { if (object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(object); [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::msg_dataDecryptedArray::ID) { auto result = tonlib_api::move_object_as(object); if (result->elements_.size() != messages.count) { [subscriber putError:[[TONError alloc] initWithText:@"API interaction error"]]; } else { NSMutableArray > *resultMessages = [[NSMutableArray alloc] init]; int index = 0; for (auto &it : result->elements_) { if (it->data_->get_id() == tonlib_api::msg_dataDecryptedText::ID) { auto dataDecryptedText = tonlib_api::move_object_as(it->data_); NSString *decryptedString = readString(dataDecryptedText->text_); if (decryptedString != nil) { [resultMessages addObject:[[TONTransactionMessageContentsPlainText alloc] initWithText:decryptedString]]; } else { [resultMessages addObject:[[TONTransactionMessageContentsEncryptedText alloc] initWithEncryptedData:messages[index]]]; } } else { [resultMessages addObject:[[TONTransactionMessageContentsEncryptedText alloc] initWithEncryptedData:messages[index]]]; } index++; } [subscriber putNext:resultMessages]; [subscriber putCompletion]; } } else { assert(false); } }]; std::vector> inputData; for (TONEncryptedData *message in messages) { NSData *sourceAddressData = [message.sourceAddress dataUsingEncoding:NSUTF8StringEncoding]; if (sourceAddressData == nil) { continue; } inputData.push_back(make_object( make_object( makeString(sourceAddressData) ), make_object( makeString(message.data) ) )); } auto query = make_object( make_object( make_object( makeString(publicKeyData), makeSecureString(key.secret) ), makeSecureString(localPassword) ), make_object( std::move(inputData) ) ); _client->send({ requestId, std::move(query) }); return [[SBlockDisposable alloc] initWithBlock:^{ }]; }] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]]; } @end