mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
a8dcdc3a74
63 changed files with 5445 additions and 4439 deletions
|
|
@ -4772,3 +4772,111 @@ Any member of this group will be able to see messages in the channel.";
|
|||
"Updated.YesterdayAt" = "yesterday at %@";
|
||||
"Updated.AtDate" = "%@";
|
||||
"Updated.TodayAt" = "today at %@";
|
||||
|
||||
"Wallet.Info.WalletCreated" = "Wallet Created";
|
||||
"Wallet.Info.Address" = "Your wallet address";
|
||||
"Wallet.Info.YourBalance" = "your balance";
|
||||
"Wallet.Info.Receive" = "Receive";
|
||||
"Wallet.Info.Send" = "Send";
|
||||
"Wallet.Info.RefreshErrorTitle" = "An Error Occurred";
|
||||
"Wallet.Info.RefreshErrorText" = "The wallet state can not be retrieved at this time. Please try again later.";
|
||||
"Wallet.Info.UnknownTransaction" = "Empty Transaction";
|
||||
"Wallet.Info.TransactionTo" = "to";
|
||||
"Wallet.Info.TransactionFrom" = "from";
|
||||
"Wallet.Info.Updating" = "updating";
|
||||
"Wallet.Qr.ScanCode" = "Scan QR Code";
|
||||
"Wallet.Qr.Title" = "QR Code";
|
||||
"Wallet.Receive.Title" = "Receive Grams";
|
||||
"Wallet.Receive.AddressHeader" = "YOUR WALLET ADDRESS";
|
||||
"Wallet.Receive.InvoiceUrlHeader" = "INVOICE URL";
|
||||
"Wallet.Receive.CopyAddress" = "Copy Wallet Address";
|
||||
"Wallet.Receive.CopyInvoiceUrl" = "Copy Invoice URL";
|
||||
"Wallet.Receive.ShareAddress" = "Share Wallet Address";
|
||||
"Wallet.Receive.ShareInvoiceUrl" = "Share Invoice URL";
|
||||
"Wallet.Receive.ShareUrlInfo" = "Share this link with other Gram wallet owners to receive Grams from them.";
|
||||
"Wallet.Receive.AmountHeader" = "AMOUNT";
|
||||
"Wallet.Receive.AmountText" = "Grams to receive";
|
||||
"Wallet.Receive.CommentHeader" = "COMMENT (OPTIONAL)";
|
||||
"Wallet.Receive.CommentInfo" = "Description of the payment";
|
||||
"Wallet.Receive.AddressCopied" = "Address copied to clipboard.";
|
||||
"Wallet.Receive.InvoiceUrlCopied" = "Invoice URL copied to clipboard.";
|
||||
"Wallet.Send.Title" = "Send Grams";
|
||||
"Wallet.Send.AddressHeader" = "RECIPIENT WALLET ADDRESS";
|
||||
"Wallet.Send.AddressText" = "Enter wallet address...";
|
||||
"Wallet.Send.AddressInfo" = "Copy the 48-letter address of the recipient here or ask them to send you a ton:// link.";
|
||||
"Wallet.Send.Balance" = "Balance: %@";
|
||||
"Wallet.Send.AmountText" = "Grams to send";
|
||||
"Wallet.Send.Confirmation" = "Confirmation";
|
||||
"Wallet.Send.ConfirmationText" = "Do you want to send **%1$@** Grams to\n%2$@?";
|
||||
"Wallet.Send.ConfirmationConfirm" = "Confirm";
|
||||
"Wallet.Send.Send" = "Send";
|
||||
"Wallet.Settings.Title" = "Wallet Settings";
|
||||
"Wallet.Settings.DeleteWallet" = "Delete Wallet";
|
||||
"Wallet.Intro.NotNow" = "Not Now";
|
||||
"Wallet.Intro.ImportExisting" = "Import existing wallet";
|
||||
"Wallet.Intro.CreateErrorTitle" = "An Error Occurred";
|
||||
"Wallet.Intro.CreateErrorText" = "Sorry. Please try again.";
|
||||
"Wallet.Intro.Title" = "Gram Wallet";
|
||||
"Wallet.Intro.Text" = "Gram wallet allows you to make fast and secure blockchain-based payments without intermediaries.";
|
||||
"Wallet.Intro.CreateWallet" = "Create My Wallet";
|
||||
"Wallet.Intro.Terms" = "By creating the wallet you accept\n[Terms of Conditions]().";
|
||||
"Wallet.Intro.TermsUrl" = "https://telegram.org/tos/wallet";
|
||||
"Wallet.Created.Title" = "Congratulations";
|
||||
"Wallet.Created.Text" = "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode.";
|
||||
"Wallet.Created.Proceed" = "Proceed";
|
||||
"Wallet.Completed.Title" = "Ready to go!";
|
||||
"Wallet.Completed.Text" = "You’re all set. Now you have a wallet that only you control - directly, without middlemen or bankers.";
|
||||
"Wallet.Completed.ViewWallet" = "View My Wallet";
|
||||
"Wallet.RestoreFailed.Title" = "Too Bad";
|
||||
"Wallet.RestoreFailed.Text" = "Without the secret words, you can't'nrestore access to the wallet.";
|
||||
"Wallet.RestoreFailed.CreateWallet" = "Create a New Wallet";
|
||||
"Wallet.RestoreFailed.EnterWords" = "Enter 24 words";
|
||||
"Wallet.Sending.Title" = "Sending Grams";
|
||||
"Wallet.Sending.Text" = "Please wait a few seconds for your transaction to be processed...";
|
||||
"Wallet.Sending.Title" = "Sending Grams";
|
||||
"Wallet.Sent.Title" = "Done!";
|
||||
"Wallet.Sent.Text" = "**%@ Grams** have been sent.";
|
||||
"Wallet.Sent.ViewWallet" = "View My Wallet";
|
||||
"Wallet.SecureStorageNotAvailable.Title" = "Setup Passcode";
|
||||
"Wallet.SecureStorageNotAvailable.Text" = "Please set up Passcode on your device to enable secure payments with your Gram wallet.";
|
||||
"Wallet.SecureStorageReset.Title" = "Security Settings Have Changed";
|
||||
"Wallet.SecureStorageReset.BiometryTouchId" = "Touch ID";
|
||||
"Wallet.SecureStorageReset.BiometryFaceId" = "Face ID";
|
||||
"Wallet.SecureStorageReset.BiometryText" = "Unfortunately, your wallet is no longer available because your system Passcode or %@ has been turned off.";
|
||||
"Wallet.SecureStorageReset.PasscodeText" = "Unfortunately, your wallet is no longer available because your system Passcode has been turned off.";
|
||||
"Wallet.SecureStorageChanged.BiometryText" = "Unfortunately, your wallet is no longer available due to the change in your system security settings (Passcode/%@). To restore your wallet, tap \"Import existing wallet\".";
|
||||
"Wallet.SecureStorageChanged.PasscodeText" = "Unfortunately, your wallet is no longer available due to the change in your system security settings (Passcode). To restore your wallet, tap \"Import existing wallet\".";
|
||||
"Wallet.SecureStorageChanged.ImportWallet" = "Import Existing Wallet";
|
||||
"Wallet.SecureStorageChanged.CreateWallet" = "Create New Wallet";
|
||||
"Wallet.TransactionInfo.Title" = "Transaction";
|
||||
"Wallet.TransactionInfo.NoAddress" = "No Address";
|
||||
"Wallet.TransactionInfo.RecipientHeader" = "RECIPIENT";
|
||||
"Wallet.TransactionInfo.SenderHeader" = "SENDER";
|
||||
"Wallet.TransactionInfo.CopyAddress" = "Copy Wallet Address";
|
||||
"Wallet.TransactionInfo.AddressCopied" = "Address copied to clipboard.";
|
||||
"Wallet.TransactionInfo.SendGrams" = "Send Grams";
|
||||
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
|
||||
"Wallet.WordCheck.Title" = "Test Time!";
|
||||
"Wallet.WordCheck.Text" = "Let’s check that you wrote them down correctly. Please enter words\n**%1$@**, **%2$@** and **%3$@** below:";
|
||||
"Wallet.WordCheck.Continue" = "Continue";
|
||||
"Wallet.WordCheck.IncorrectHeader" = "Incorrect words!";
|
||||
"Wallet.WordCheck.IncorrectText" = "The secret words you have entered do not match the ones in the list.";
|
||||
"Wallet.WordCheck.TryAgain" = "Try Again";
|
||||
"Wallet.WordCheck.ViewWords" = "View Words";
|
||||
"Wallet.WordImport.Title" = "24 Secret Words";
|
||||
"Wallet.WordImport.Text" = "Please restore access to your wallet by\nentering the 24 secret words you wrote down when creating the wallet.";
|
||||
"Wallet.WordImport.Continue" = "Continue";
|
||||
"Wallet.WordImport.CanNotRemember" = "I don't have those";
|
||||
"Wallet.WordImport.IncorrectTitle" = "Incorrect words";
|
||||
"Wallet.WordImport.IncorrectText" = "Sorry, you have entered incorrect secret words. Please double check and try again.";
|
||||
"Wallet.Words.Title" = "24 Secret Words";
|
||||
"Wallet.Words.Text" = "Write down these 24 words in the correct order and store them in a secret place.\n\nUse these secret words to restore access to your wallet if you lose your passcode or Telegram account.";
|
||||
"Wallet.Words.Done" = "Done";
|
||||
"Wallet.Words.NotDoneTitle" = "Sure Done?";
|
||||
"Wallet.Words.NotDoneText" = "You didn't have enough time to write those words down.";
|
||||
"Wallet.Words.NotDoneOk" = "OK, Sorry";
|
||||
"Wallet.Words.NotDoneResponse" = "Apologies Accepted";
|
||||
|
||||
"Wallet.Send.ErrorInvalidAddress" = "Invalid wallet address. Please correct and try again.";
|
||||
"Wallet.Send.ErrorDecryptionFailed" = "Encryption error. Please check device passcode settings and try again.";
|
||||
"Wallet.Send.SendAnyway" = "Send Anyway";
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -67,8 +65,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -67,8 +65,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -67,8 +65,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -67,8 +65,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -67,8 +65,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -2549,8 +2549,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -2572,8 +2570,6 @@
|
|||
ReferencedContainer = "container:Project.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -451,11 +451,44 @@ public protocol SharedAccountContext: class {
|
|||
func beginNewAuth(testingEnvironment: Bool)
|
||||
}
|
||||
|
||||
public struct TonContext {
|
||||
private final class TonInstanceData {
|
||||
var config: String?
|
||||
var instance: TonInstance?
|
||||
}
|
||||
|
||||
public final class StoredTonContext {
|
||||
private let basePath: String
|
||||
private let postbox: Postbox
|
||||
private let network: Network
|
||||
public let keychain: TonKeychain
|
||||
private let currentInstance = Atomic<TonInstanceData>(value: TonInstanceData())
|
||||
|
||||
public init(basePath: String, postbox: Postbox, network: Network, keychain: TonKeychain) {
|
||||
self.basePath = basePath
|
||||
self.postbox = postbox
|
||||
self.network = network
|
||||
self.keychain = keychain
|
||||
}
|
||||
|
||||
public func context(config: String) -> TonContext {
|
||||
return self.currentInstance.with { data -> TonContext in
|
||||
if let instance = data.instance, data.config == config {
|
||||
return TonContext(instance: instance, keychain: self.keychain)
|
||||
} else {
|
||||
data.config = config
|
||||
let instance = TonInstance(basePath: self.basePath, config: config, blockchainName: "testnet", network: self.network)
|
||||
data.instance = instance
|
||||
return TonContext(instance: instance, keychain: self.keychain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class TonContext {
|
||||
public let instance: TonInstance
|
||||
public let keychain: TonKeychain
|
||||
|
||||
public init(instance: TonInstance, keychain: TonKeychain) {
|
||||
fileprivate init(instance: TonInstance, keychain: TonKeychain) {
|
||||
self.instance = instance
|
||||
self.keychain = keychain
|
||||
}
|
||||
|
|
@ -464,7 +497,7 @@ public struct TonContext {
|
|||
public protocol AccountContext: class {
|
||||
var sharedContext: SharedAccountContext { get }
|
||||
var account: Account { get }
|
||||
var tonContext: TonContext? { get }
|
||||
var tonContext: StoredTonContext? { get }
|
||||
|
||||
var liveLocationManager: LiveLocationManager? { get }
|
||||
var fetchManager: FetchManager { get }
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ API_AVAILABLE(ios(10))
|
|||
return cipherText;
|
||||
}
|
||||
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data cancelled:(bool *)cancelled {
|
||||
- (NSData * _Nullable)decrypt:(NSData * _Nonnull)data cancelled:(bool *)cancelled {
|
||||
CFErrorRef error = NULL;
|
||||
NSData *plainText = (NSData *)CFBridgingRelease(SecKeyCreateDecryptedData(_privateKey, kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM, (__bridge CFDataRef)data, &error));
|
||||
|
||||
|
|
|
|||
|
|
@ -783,7 +783,7 @@ public extension ContainedViewLayoutTransition {
|
|||
}
|
||||
|
||||
func updateSublayerTransformOffset(layer: CALayer, offset: CGPoint, completion: ((Bool) -> Void)? = nil) {
|
||||
let t = layer.transform
|
||||
let t = layer.sublayerTransform
|
||||
let currentOffset = CGPoint(x: t.m41, y: t.m42)
|
||||
if currentOffset == offset {
|
||||
if let completion = completion {
|
||||
|
|
|
|||
|
|
@ -156,11 +156,8 @@ final class KeyboardViewManager {
|
|||
guard let keyboardWindow = self.host.keyboardWindow else {
|
||||
return
|
||||
}
|
||||
let previousPosition = keyboardWindow.layer.position
|
||||
let updatedPosition = CGPoint(x: leftEdge + keyboardWindow.layer.bounds.width / 2.0, y: previousPosition.y)
|
||||
keyboardWindow.layer.position = updatedPosition
|
||||
if transition.isAnimated && !previousPosition.x.isEqual(to: updatedPosition.x) {
|
||||
transition.animatePositionAdditive(layer: keyboardWindow.layer, offset: CGPoint(x: previousPosition.x - updatedPosition.x, y: 0.0))
|
||||
}
|
||||
let t = keyboardWindow.layer.sublayerTransform
|
||||
let currentOffset = CGPoint(x: t.m41, y: t.m42)
|
||||
transition.updateSublayerTransformOffset(layer: keyboardWindow.layer, offset: CGPoint(x: leftEdge, y: currentOffset.y))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,16 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
panRecognizer.delaysTouchesBegan = false
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.view.addGestureRecognizer(panRecognizer)
|
||||
|
||||
self.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
if strongSelf.state.transition != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
|
@ -317,7 +327,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
if case .animated = transition, let fromValue = fromValue, let toValue = toValue {
|
||||
//self.keyboardManager?.surfaces = fromValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? []
|
||||
if let currentTransition = self.state.transition {
|
||||
assertionFailure()
|
||||
//assertionFailure()
|
||||
}
|
||||
|
||||
fromValue.value.viewWillDisappear(true)
|
||||
|
|
@ -406,7 +416,9 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||
childFrame.origin.x = child.value.displayNode.frame.origin.x
|
||||
switch transition.type {
|
||||
case .pop:
|
||||
shouldSyncKeyboard = true
|
||||
if transition.previous.value === child.value {
|
||||
shouldSyncKeyboard = true
|
||||
}
|
||||
case .push:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1059,6 +1059,18 @@ public class Window1 {
|
|||
let currentLocation = location
|
||||
let deltaY = keyboardGestureBeginLocation.y - location.y
|
||||
if deltaY * deltaY >= 3.0 * 3.0 || self.windowLayout.upperKeyboardInputPositionBound != nil {
|
||||
if self.windowLayout.upperKeyboardInputPositionBound == nil {
|
||||
var enableGesture = true
|
||||
if let view = self.hostView.containerView.hitTest(location, with: nil) {
|
||||
if doesViewTreeDisableInteractiveTransitionGestureRecognizer(view) {
|
||||
enableGesture = false
|
||||
}
|
||||
}
|
||||
if !enableGesture {
|
||||
self.keyboardGestureBeginLocation = nil
|
||||
return
|
||||
}
|
||||
}
|
||||
self.updateLayout {
|
||||
$0.update(upperKeyboardInputPositionBound: currentLocation.y + (self.keyboardGestureAccessoryHeight ?? 0.0), transition: .immediate, overrideTransition: false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,14 +73,22 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@end
|
||||
|
||||
@interface TONSendGramsResult : NSObject
|
||||
|
||||
@property (nonatomic, readonly) int64_t sentUntil;
|
||||
|
||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil;
|
||||
|
||||
@end
|
||||
|
||||
@interface TON : NSObject
|
||||
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest;
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest;
|
||||
|
||||
- (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
|
||||
- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey;
|
||||
- (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress;
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString *)textMessage;
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString *)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId;
|
||||
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
|
||||
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
|
||||
- (MTSignal *)deleteKey:(TONKey *)key;
|
||||
|
|
|
|||
|
|
@ -140,6 +140,18 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
|
|||
|
||||
@end
|
||||
|
||||
@implementation TONSendGramsResult
|
||||
|
||||
- (instancetype)initWithSentUntil:(int64_t)sentUntil {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_sentUntil = sentUntil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
using tonlib_api::make_object;
|
||||
|
||||
@interface TONReceiveThreadParams : NSObject
|
||||
|
|
@ -204,6 +216,8 @@ typedef enum {
|
|||
NSLock *_requestHandlersLock;
|
||||
NSMutableDictionary<NSNumber *, TONRequestHandler *> *_requestHandlers;
|
||||
MTPipe *_initializedStatus;
|
||||
NSMutableSet *_sendGramRandomIds;
|
||||
MTQueue *_queue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -219,14 +233,16 @@ typedef enum {
|
|||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest {
|
||||
- (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config blockchainName:(NSString *)blockchainName performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_queue = [MTQueue mainQueue];
|
||||
_requestHandlersLock = [[NSLock alloc] init];
|
||||
_requestHandlers = [[NSMutableDictionary alloc] init];
|
||||
_initializedStatus = [[MTPipe alloc] initWithReplay:true];
|
||||
_initializedStatus.sink(@(TONInitializationStatusInitializing));
|
||||
_nextRequestId = 1;
|
||||
_sendGramRandomIds = [[NSMutableSet alloc] init];
|
||||
|
||||
_client = std::make_shared<tonlib::Client>();
|
||||
|
||||
|
|
@ -278,7 +294,7 @@ typedef enum {
|
|||
[[NSFileManager defaultManager] createDirectoryAtPath:keystoreDirectory withIntermediateDirectories:true attributes:nil error:nil];
|
||||
|
||||
MTPipe *initializedStatus = _initializedStatus;
|
||||
[[self requestInitWithConfigString:config keystoreDirectory:keystoreDirectory] startWithNext:nil error:^(id error) {
|
||||
[[self requestInitWithConfigString:config blockchainName:blockchainName keystoreDirectory:keystoreDirectory] startWithNext:nil error:^(id error) {
|
||||
NSString *errorText = @"Unknown error";
|
||||
if ([error isKindOfClass:[TONError class]]) {
|
||||
errorText = ((TONError *)error).text;
|
||||
|
|
@ -291,7 +307,7 @@ typedef enum {
|
|||
return self;
|
||||
}
|
||||
|
||||
- (MTSignal *)requestInitWithConfigString:(NSString *)configString keystoreDirectory:(NSString *)keystoreDirectory {
|
||||
- (MTSignal *)requestInitWithConfigString:(NSString *)configString blockchainName:(NSString *)blockchainName keystoreDirectory:(NSString *)keystoreDirectory {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
|
@ -306,9 +322,13 @@ typedef enum {
|
|||
}];
|
||||
|
||||
auto query = make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
configString.UTF8String,
|
||||
keystoreDirectory.UTF8String,
|
||||
false
|
||||
make_object<tonlib_api::config>(
|
||||
configString.UTF8String,
|
||||
blockchainName.UTF8String,
|
||||
false,
|
||||
false
|
||||
),
|
||||
keystoreDirectory.UTF8String
|
||||
));
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
||||
|
|
@ -427,8 +447,15 @@ typedef enum {
|
|||
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
|
||||
}
|
||||
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString * _Nonnull)textMessage {
|
||||
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString * _Nonnull)textMessage forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId {
|
||||
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
|
||||
if ([_sendGramRandomIds containsObject:@(randomId)]) {
|
||||
[_sendGramRandomIds addObject:@(randomId)];
|
||||
|
||||
return [[MTBlockDisposable alloc] initWithBlock:^{
|
||||
}];
|
||||
}
|
||||
|
||||
NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (publicKeyData == nil) {
|
||||
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]];
|
||||
|
|
@ -443,10 +470,23 @@ typedef enum {
|
|||
uint64_t requestId = _nextRequestId;
|
||||
_nextRequestId += 1;
|
||||
|
||||
__weak TON *weakSelf = self;
|
||||
MTQueue *queue = _queue;
|
||||
_requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr<tonlib_api::Object> &object) {
|
||||
if (object->get_id() == tonlib_api::error::ID) {
|
||||
[queue dispatchOnQueue:^{
|
||||
__strong TON *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[_sendGramRandomIds removeObject:@(randomId)];
|
||||
}
|
||||
}];
|
||||
auto error = tonlib_api::move_object_as<tonlib_api::error>(object);
|
||||
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
|
||||
} else if (object->get_id() == tonlib_api::sendGramsResult::ID) {
|
||||
auto result = tonlib_api::move_object_as<tonlib_api::sendGramsResult>(object);
|
||||
TONSendGramsResult *sendResult = [[TONSendGramsResult alloc] initWithSentUntil:result->sent_until_];
|
||||
[subscriber putNext:sendResult];
|
||||
[subscriber putCompletion];
|
||||
} else {
|
||||
[subscriber putCompletion];
|
||||
}
|
||||
|
|
@ -463,6 +503,8 @@ typedef enum {
|
|||
make_object<tonlib_api::accountAddress>(fromAddress.UTF8String),
|
||||
make_object<tonlib_api::accountAddress>(address.UTF8String),
|
||||
amount,
|
||||
timeout,
|
||||
forceIfDestinationNotInitialized,
|
||||
makeString(textMessageData)
|
||||
);
|
||||
_client->send({ requestId, std::move(query) });
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@
|
|||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
|
@ -42,17 +40,6 @@
|
|||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "E66DC04E89A74F8D00000000"
|
||||
BuildableName = "libSwiftSignalKit.dylib"
|
||||
BlueprintName = "SwiftSignalKit#shared"
|
||||
ReferencedContainer = "container:SwiftSignalKit.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
|
|
|||
|
|
@ -107,12 +107,19 @@ public final class SearchDisplayController {
|
|||
insertSubnode(self.contentNode, false)
|
||||
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: layout.size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: layout.safeInsets, statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), navigationBarHeight: navigationBarHeight, transition: .immediate)
|
||||
|
||||
let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil)
|
||||
|
||||
let contentNodePosition = self.contentNode.layer.position
|
||||
self.contentNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - navigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
|
||||
var contentNavigationBarHeight = navigationBarHeight
|
||||
if layout.statusBarHeight == nil {
|
||||
contentNavigationBarHeight += 28.0
|
||||
}
|
||||
|
||||
self.contentNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
|
||||
self.searchBar.placeholderString = placeholder.placeholderString
|
||||
|
|
@ -158,11 +165,16 @@ public final class SearchDisplayController {
|
|||
|
||||
let contentNode = self.contentNode
|
||||
if animated {
|
||||
if let placeholder = placeholder, let (_, navigationBarHeight) = self.containerLayout {
|
||||
if let placeholder = placeholder, let (layout, navigationBarHeight) = self.containerLayout {
|
||||
let contentNodePosition = self.contentNode.layer.position
|
||||
let targetTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil)
|
||||
|
||||
self.contentNode.layer.animatePosition(from: contentNodePosition, to: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (targetTextBackgroundFrame.maxY + 8.0 - navigationBarHeight)), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
var contentNavigationBarHeight = navigationBarHeight
|
||||
if layout.statusBarHeight == nil {
|
||||
contentNavigationBarHeight += 28.0
|
||||
}
|
||||
|
||||
self.contentNode.layer.animatePosition(from: contentNodePosition, to: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (targetTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
}
|
||||
contentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak contentNode] _ in
|
||||
contentNode?.removeFromSupernode()
|
||||
|
|
|
|||
|
|
@ -205,9 +205,9 @@ private enum SettingsEntry: ItemListNodeEntry {
|
|||
return 1010
|
||||
case .language:
|
||||
return 1011
|
||||
case .passport:
|
||||
return 1012
|
||||
case .wallet:
|
||||
return 1012
|
||||
case .passport:
|
||||
return 1013
|
||||
case .watch:
|
||||
return 1014
|
||||
|
|
@ -574,7 +574,7 @@ private func settingsEntries(account: Account, presentationData: PresentationDat
|
|||
let languageName = presentationData.strings.primaryComponent.localizedName
|
||||
entries.append(.language(presentationData.theme, PresentationResourcesSettings.language, presentationData.strings.Settings_AppLanguage, languageName.isEmpty ? presentationData.strings.Localization_LanguageName : languageName))
|
||||
|
||||
if hasWallet || experimentalUISettings.wallets {
|
||||
if hasWallet {
|
||||
entries.append(.wallet(presentationData.theme, PresentationResourcesSettings.wallet, "Gram Wallet", ""))
|
||||
}
|
||||
if hasPassport {
|
||||
|
|
|
|||
|
|
@ -46,13 +46,15 @@ private final class TonInstanceImpl {
|
|||
private let queue: Queue
|
||||
private let basePath: String
|
||||
private let config: String
|
||||
private let blockchainName: String
|
||||
private let network: Network
|
||||
private var instance: TON?
|
||||
|
||||
init(queue: Queue, basePath: String, config: String, network: Network) {
|
||||
init(queue: Queue, basePath: String, config: String, blockchainName: String, network: Network) {
|
||||
self.queue = queue
|
||||
self.basePath = basePath
|
||||
self.config = config
|
||||
self.blockchainName = blockchainName
|
||||
self.network = network
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +64,7 @@ private final class TonInstanceImpl {
|
|||
instance = current
|
||||
} else {
|
||||
let network = self.network
|
||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, performExternalRequest: { request in
|
||||
instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
|
||||
let _ = (network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))).start(next: { result in
|
||||
switch result {
|
||||
case let .liteResponse(response):
|
||||
|
|
@ -82,11 +84,11 @@ public final class TonInstance {
|
|||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<TonInstanceImpl>
|
||||
|
||||
public init(basePath: String, config: String, network: Network) {
|
||||
public init(basePath: String, config: String, blockchainName: String, network: Network) {
|
||||
self.queue = .mainQueue()
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return TonInstanceImpl(queue: queue, basePath: basePath, config: config, network: network)
|
||||
return TonInstanceImpl(queue: queue, basePath: basePath, config: config, blockchainName: blockchainName, network: network)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -290,7 +292,7 @@ public final class TonInstance {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String) -> Signal<Never, SendGramsFromWalletError> {
|
||||
fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
return keychain.decrypt(walletInfo.encryptedSecret)
|
||||
|> mapError { _ -> SendGramsFromWalletError in
|
||||
return .secretDecryptionFailed
|
||||
|
|
@ -302,10 +304,24 @@ public final class TonInstance {
|
|||
|
||||
self.impl.with { impl in
|
||||
impl.withInstance { ton in
|
||||
let cancel = ton.sendGrams(from: key, localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage).start(next: { _ in
|
||||
preconditionFailure()
|
||||
}, error: { _ in
|
||||
subscriber.putError(.generic)
|
||||
let cancel = ton.sendGrams(from: key, localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId).start(next: { result in
|
||||
guard let result = result as? TONSendGramsResult else {
|
||||
subscriber.putError(.generic)
|
||||
return
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
}, error: { error in
|
||||
if let error = error as? TONError {
|
||||
if error.text == "Failed to parse account address" {
|
||||
subscriber.putError(.invalidAddress)
|
||||
} else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
|
||||
subscriber.putError(.destinationIsNotInitialized)
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
} else {
|
||||
subscriber.putError(.generic)
|
||||
}
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
})
|
||||
|
|
@ -684,9 +700,11 @@ public func getCombinedWalletState(postbox: Postbox, walletInfo: WalletInfo, ton
|
|||
public enum SendGramsFromWalletError {
|
||||
case generic
|
||||
case secretDecryptionFailed
|
||||
case invalidAddress
|
||||
case destinationIsNotInitialized
|
||||
}
|
||||
|
||||
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo, toAddress: String, amount: Int64, textMessage: String) -> Signal<Never, SendGramsFromWalletError> {
|
||||
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
|
||||
return getServerWalletSalt(network: network)
|
||||
|> mapError { _ -> SendGramsFromWalletError in
|
||||
return .generic
|
||||
|
|
@ -695,7 +713,7 @@ public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keyc
|
|||
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
|
||||
|> castError(SendGramsFromWalletError.self)
|
||||
|> mapToSignal { fromAddress in
|
||||
return tonInstance.sendGramsFromWallet(keychain: keychain, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage)
|
||||
return tonInstance.sendGramsFromWallet(keychain: keychain, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -102,7 +102,7 @@ public final class AccountContextImpl: AccountContext {
|
|||
}
|
||||
public let account: Account
|
||||
|
||||
public let tonContext: TonContext?
|
||||
public let tonContext: StoredTonContext?
|
||||
|
||||
public let fetchManager: FetchManager
|
||||
private let prefetchManager: PrefetchManager?
|
||||
|
|
@ -131,7 +131,7 @@ public final class AccountContextImpl: AccountContext {
|
|||
private let deviceSpecificContactImportContexts: QueueLocalObject<DeviceSpecificContactImportContexts>
|
||||
private var managedAppSpecificContactsDisposable: Disposable?
|
||||
|
||||
public init(sharedContext: SharedAccountContextImpl, account: Account, tonContext: TonContext?, limitsConfiguration: LimitsConfiguration) {
|
||||
public init(sharedContext: SharedAccountContextImpl, account: Account, tonContext: StoredTonContext?, limitsConfiguration: LimitsConfiguration) {
|
||||
self.sharedContextImpl = sharedContext
|
||||
self.account = account
|
||||
self.tonContext = tonContext
|
||||
|
|
|
|||
|
|
@ -653,6 +653,7 @@ final class SharedApplicationContext {
|
|||
|
||||
#if targetEnvironment(simulator)
|
||||
tonKeychain = TonKeychain(encryptionPublicKey: {
|
||||
//return .single(nil)
|
||||
return .single(Data())
|
||||
}, encrypt: { data in
|
||||
return Signal { subscriber in
|
||||
|
|
@ -941,10 +942,7 @@ final class SharedApplicationContext {
|
|||
|> deliverOnMainQueue
|
||||
|> map { accountAndSettings -> AuthorizedApplicationContext? in
|
||||
return accountAndSettings.flatMap { account, limitsConfiguration, callListSettings in
|
||||
var tonContext: TonContext?
|
||||
if let path = getAppBundle().path(forResource: "cfg", ofType: "txt"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)), let config = String(data: data, encoding: .utf8) {
|
||||
tonContext = TonContext(instance: TonInstance(basePath: account.basePath, config: config, network: account.network), keychain: tonKeychain)
|
||||
}
|
||||
let tonContext = StoredTonContext(basePath: account.basePath, postbox: account.postbox, network: account.network, keychain: tonKeychain)
|
||||
let context = AccountContextImpl(sharedContext: sharedApplicationContext.sharedContext, account: account, tonContext: tonContext, limitsConfiguration: limitsConfiguration)
|
||||
return AuthorizedApplicationContext(sharedApplicationContext: sharedApplicationContext, mainWindow: self.mainWindow, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedApplicationContext.sharedContext.accountManager, showCallsTab: callListSettings.showTab, reinitializedNotificationSettings: {
|
||||
let _ = (self.context.get()
|
||||
|
|
|
|||
|
|
@ -539,7 +539,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
guard let strongSelf = self, !actions.isEmpty else {
|
||||
return
|
||||
}
|
||||
let reactions: [(String, String, String)] = [
|
||||
/*let reactions: [(String, String, String)] = [
|
||||
("😔", "Sad", "sad"),
|
||||
("😳", "Surprised", "surprised"),
|
||||
("😂", "Fun", "lol"),
|
||||
|
|
@ -562,8 +562,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
if Namespaces.Message.allScheduled.contains(message.id.namespace) {
|
||||
reactionItems = []
|
||||
}
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer)
|
||||
}*/
|
||||
let controller = ContextController(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message)), items: .single(actions), reactionItems: [], recognizer: recognizer)
|
||||
strongSelf.currentContextController = controller
|
||||
controller.reactionSelected = { [weak controller] value in
|
||||
guard let strongSelf = self, let message = updatedMessages.first else {
|
||||
|
|
|
|||
|
|
@ -427,7 +427,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
}
|
||||
}
|
||||
|
||||
let reactions: [(String, String, String)] = [
|
||||
/*let reactions: [(String, String, String)] = [
|
||||
("😔", "Sad", "sad"),
|
||||
("😳", "Surprised", "surprised"),
|
||||
("😂", "Fun", "lol"),
|
||||
|
|
@ -444,7 +444,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
if item.controllerInteraction.canSetupReply(item.message) {
|
||||
//reactionItems.append(.reply)
|
||||
}
|
||||
return reactionItems
|
||||
return reactionItems*/
|
||||
|
||||
return []
|
||||
}
|
||||
reactionRecognizer.getReactionContainer = { [weak self] in
|
||||
return self?.item?.controllerInteraction.reactionContainerNode()
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"liteservers": [
|
||||
{
|
||||
"ip": 1137658550,
|
||||
"port": 4924,
|
||||
"id": {
|
||||
"@type": "pub.ed25519",
|
||||
"key": "peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M="
|
||||
}
|
||||
}
|
||||
],
|
||||
"validator": {
|
||||
"@type": "validator.config.global",
|
||||
"zero_state": {
|
||||
"workchain": -1,
|
||||
"shard": -9223372036854775808,
|
||||
"seqno": 0,
|
||||
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=",
|
||||
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1021,19 +1021,26 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
}
|
||||
|
||||
public func openWallet(context: AccountContext, walletContext: OpenWalletContext, present: @escaping (ViewController) -> Void) {
|
||||
guard let tonContext = context.tonContext else {
|
||||
guard let storedContext = context.tonContext else {
|
||||
return
|
||||
}
|
||||
let _ = (combineLatest(queue: .mainQueue(),
|
||||
availableWallets(postbox: context.account.postbox),
|
||||
tonContext.keychain.encryptionPublicKey()
|
||||
storedContext.keychain.encryptionPublicKey(),
|
||||
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { wallets, currentPublicKey in
|
||||
|> deliverOnMainQueue).start(next: { wallets, currentPublicKey, preferences in
|
||||
let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
|
||||
guard let config = WalletConfiguration.config(appConfiguration: appConfiguration) else {
|
||||
return
|
||||
}
|
||||
let tonContext = storedContext.context(config: config)
|
||||
|
||||
if wallets.wallets.isEmpty {
|
||||
if let _ = currentPublicKey {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro))
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
} else {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageNotAvailable))
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageNotAvailable, walletCreatedPreloadState: nil))
|
||||
}
|
||||
} else {
|
||||
let walletInfo = wallets.wallets[0].info
|
||||
|
|
@ -1045,15 +1052,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
case .generic:
|
||||
present(WalletInfoScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, address: address))
|
||||
case let .send(address, amount, comment):
|
||||
present(walletSendScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, address: address, amount: amount, comment: comment))
|
||||
present(walletSendScreen(context: context, tonContext: tonContext, randomId: arc4random64(), walletInfo: walletInfo, address: address, amount: amount, comment: comment))
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageReset(.changed)))
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageReset(.changed), walletCreatedPreloadState: nil))
|
||||
}
|
||||
} else {
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageReset(.notAvailable)))
|
||||
present(WalletSplashScreen(context: context, tonContext: tonContext, mode: .secureStorageReset(.notAvailable), walletCreatedPreloadState: nil))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ static_library(
|
|||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/GlassButtonNode:GlassButtonNode",
|
||||
"//submodules/UrlHandling:UrlHandling",
|
||||
"//submodules/LocalAuth:LocalAuth",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
|
|
|||
|
|
@ -13,10 +13,18 @@ public struct WalletConfiguration {
|
|||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
|
||||
if let data = appConfiguration.data, let enabled = data["wallet_enabled"] as? Bool {
|
||||
if let data = appConfiguration.data, let enabled = data["wallet_enabled"] as? Bool, let _ = data["wallet_config"] as? String {
|
||||
return WalletConfiguration(enabled: enabled)
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
public static func config(appConfiguration: AppConfiguration) -> String? {
|
||||
if let data = appConfiguration.data, let config = data["wallet_config"] as? String {
|
||||
return config
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,10 +143,10 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
|||
let sideInset: CGFloat = 16.0
|
||||
var iconOffset = CGPoint()
|
||||
|
||||
let title = "Wallet Created"
|
||||
let text = "Your wallet address"
|
||||
let title = item.strings.Wallet_Info_WalletCreated
|
||||
let text = item.strings.Wallet_Info_Address
|
||||
|
||||
let textColor = UIColor.black
|
||||
let textColor = item.theme.list.itemPrimaryTextColor
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: Font.bold(32.0), textColor: textColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - sideInset * 2.0, height: .greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.1, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
|
|
@ -171,6 +171,8 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
|||
}
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.item = item
|
||||
|
||||
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
||||
let _ = titleApply()
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public final class WalletInfoScreen: ViewController {
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.push(walletSendScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo))
|
||||
strongSelf.push(walletSendScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, randomId: arc4random64(), walletInfo: strongSelf.walletInfo))
|
||||
}, receiveAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
|
@ -100,6 +100,8 @@ public final class WalletInfoScreen: ViewController {
|
|||
}
|
||||
|
||||
private final class WalletInfoBalanceNode: ASDisplayNode {
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
|
||||
let balanceIntegralTextNode: ImmediateTextNode
|
||||
let balanceFractionalTextNode: ImmediateTextNode
|
||||
let balanceIconNode: AnimatedStickerNode
|
||||
|
|
@ -108,7 +110,7 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
|
|||
didSet {
|
||||
let integralString = NSMutableAttributedString()
|
||||
let fractionalString = NSMutableAttributedString()
|
||||
if let range = self.balance.range(of: ".") {
|
||||
if let range = self.balance.range(of: self.dateTimeFormat.decimalSeparator) {
|
||||
let integralPart = String(self.balance[..<range.lowerBound])
|
||||
let fractionalPart = String(self.balance[range.lowerBound...])
|
||||
integralString.append(NSAttributedString(string: integralPart, font: Font.medium(48.0), textColor: .white))
|
||||
|
|
@ -123,7 +125,9 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
|
|||
|
||||
var isLoading: Bool = true
|
||||
|
||||
init(account: Account, theme: PresentationTheme) {
|
||||
init(account: Account, theme: PresentationTheme, dateTimeFormat: PresentationDateTimeFormat) {
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
|
||||
self.balanceIntegralTextNode = ImmediateTextNode()
|
||||
self.balanceIntegralTextNode.displaysAsynchronously = false
|
||||
self.balanceIntegralTextNode.attributedText = NSAttributedString(string: " ", font: Font.bold(39.0), textColor: .white)
|
||||
|
|
@ -195,34 +199,40 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
|||
private let balanceSubtitleNode: ImmediateTextNode
|
||||
private let receiveButtonNode: SolidRoundedButtonNode
|
||||
private let sendButtonNode: SolidRoundedButtonNode
|
||||
private let headerBackgroundNode: ASImageNode
|
||||
private let headerBackgroundNode: ASDisplayNode
|
||||
private let headerCornerNode: ASImageNode
|
||||
|
||||
init(account: Account, presentationData: PresentationData, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void) {
|
||||
self.balanceNode = WalletInfoBalanceNode(account: account, theme: presentationData.theme)
|
||||
self.balanceNode = WalletInfoBalanceNode(account: account, theme: presentationData.theme, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
|
||||
self.balanceSubtitleNode = ImmediateTextNode()
|
||||
self.balanceSubtitleNode.displaysAsynchronously = false
|
||||
self.balanceSubtitleNode.attributedText = NSAttributedString(string: "your balance", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
|
||||
self.balanceSubtitleNode.attributedText = NSAttributedString(string: presentationData.strings.Wallet_Info_YourBalance, font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
|
||||
|
||||
self.headerBackgroundNode = ASImageNode()
|
||||
self.headerBackgroundNode.displaysAsynchronously = false
|
||||
self.headerBackgroundNode.displayWithoutProcessing = true
|
||||
self.headerBackgroundNode.image = generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in
|
||||
self.headerBackgroundNode = ASDisplayNode()
|
||||
self.headerBackgroundNode.backgroundColor = .black
|
||||
|
||||
self.headerCornerNode = ASImageNode()
|
||||
self.headerCornerNode.displaysAsynchronously = false
|
||||
self.headerCornerNode.displayWithoutProcessing = true
|
||||
self.headerCornerNode.image = generateImage(CGSize(width: 20.0, height: 10.0), rotatedContext: { size, context in
|
||||
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: size.height / 2.0), size: size))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 20.0, height: 20.0)))
|
||||
})?.stretchableImage(withLeftCapWidth: 10, topCapHeight: 1)
|
||||
|
||||
self.receiveButtonNode = SolidRoundedButtonNode(title: "Receive", icon: UIImage(bundleImageName: "Wallet/ReceiveButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
self.sendButtonNode = SolidRoundedButtonNode(title: "Send", icon: UIImage(bundleImageName: "Wallet/SendButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
self.receiveButtonNode = SolidRoundedButtonNode(title: presentationData.strings.Wallet_Info_Receive, icon: UIImage(bundleImageName: "Wallet/ReceiveButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
self.sendButtonNode = SolidRoundedButtonNode(title: presentationData.strings.Wallet_Info_Send, icon: UIImage(bundleImageName: "Wallet/SendButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false)
|
||||
|
||||
self.refreshNode = WalletRefreshNode(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.headerBackgroundNode)
|
||||
self.addSubnode(self.headerCornerNode)
|
||||
self.addSubnode(self.receiveButtonNode)
|
||||
self.addSubnode(self.sendButtonNode)
|
||||
self.addSubnode(self.balanceNode)
|
||||
|
|
@ -298,7 +308,8 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
|
|||
transition.updateFrameAdditive(node: self.balanceSubtitleNode, frame: balanceSubtitleFrame)
|
||||
|
||||
let headerHeight: CGFloat = 1000.0
|
||||
transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: effectiveOffset + 10.0 - headerHeight), size: CGSize(width: size.width, height: headerHeight)))
|
||||
transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: -1.0, y: effectiveOffset - headerHeight), size: CGSize(width: size.width + 2.0, height: headerHeight)))
|
||||
transition.updateFrame(node: self.headerCornerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: effectiveOffset), size: CGSize(width: size.width, height: 10.0)))
|
||||
|
||||
let buttonOffset = effectiveOffset
|
||||
|
||||
|
|
@ -739,7 +750,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||
strongSelf.contentReady.set(.single(true))
|
||||
}
|
||||
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "The wallet state can not be retrieved at this time. Please try again later.", actions: [
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorTitle, text: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), nil)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ private let transactionIcon = UIImage(bundleImageName: "Wallet/TransactionGem")?
|
|||
|
||||
class WalletInfoTransactionItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let walletTransaction: WalletTransaction
|
||||
let action: () -> Void
|
||||
|
|
@ -20,6 +21,7 @@ class WalletInfoTransactionItem: ListViewItem {
|
|||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, walletTransaction: WalletTransaction, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.walletTransaction = walletTransaction
|
||||
self.action = action
|
||||
|
|
@ -188,9 +190,9 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||
titleColor = item.theme.list.itemPrimaryTextColor
|
||||
if item.walletTransaction.outMessages.isEmpty {
|
||||
directionText = ""
|
||||
text = "Empty Transaction"
|
||||
text = item.strings.Wallet_Info_UnknownTransaction
|
||||
} else {
|
||||
directionText = "to"
|
||||
directionText = item.strings.Wallet_Info_TransactionTo
|
||||
for message in item.walletTransaction.outMessages {
|
||||
if !text.isEmpty {
|
||||
text.append("\n")
|
||||
|
|
@ -206,7 +208,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||
} else {
|
||||
title = "+\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))"
|
||||
titleColor = item.theme.chatList.secretTitleColor
|
||||
directionText = "from"
|
||||
directionText = item.strings.Wallet_Info_TransactionFrom
|
||||
if let inMessage = item.walletTransaction.inMessage {
|
||||
text = formatAddress(inMessage.source)
|
||||
description = inMessage.textMessage
|
||||
|
|
@ -222,7 +224,7 @@ class WalletInfoTransactionItemNode: ListViewItemNode {
|
|||
let (directionLayout, directionApply) = makeDirectionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: directionText, font: directionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let titleString = NSMutableAttributedString()
|
||||
if let range = title.range(of: ".") {
|
||||
if let range = title.range(of: item.dateTimeFormat.decimalSeparator) {
|
||||
let integralPart = String(title[..<range.lowerBound])
|
||||
let fractionalPart = String(title[range.lowerBound...])
|
||||
titleString.append(NSAttributedString(string: integralPart, font: Font.bold(17.0), textColor: titleColor))
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@ public final class WalletPasscodeScreen: ViewController {
|
|||
private let tonContext: TonContext
|
||||
private var presentationData: PresentationData
|
||||
private let mode: WalletPasscodeMode
|
||||
private let randomId: Int64
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, mode: WalletPasscodeMode) {
|
||||
self.context = context
|
||||
self.tonContext = tonContext
|
||||
self.mode = mode
|
||||
|
||||
self.randomId = arc4random64()
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let defaultNavigationPresentationData = NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)
|
||||
|
|
@ -77,7 +80,7 @@ public final class WalletPasscodeScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .sending(walletInfo, address, amount, comment)))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .sending(walletInfo, address, amount, comment, strongSelf.randomId), walletCreatedPreloadState: nil))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollV
|
|||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
self.titleNode.attributedText = NSAttributedString(string: "Scan QR Code", font: Font.bold(32.0), textColor: .white)
|
||||
self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.Wallet_Qr_ScanCode, font: Font.bold(32.0), textColor: .white)
|
||||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.textAlignment = .center
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public final class WalletQrViewScreen: ViewController {
|
|||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
|
||||
self.title = "QR Code"
|
||||
self.title = self.presentationData.strings.Wallet_Qr_Title
|
||||
|
||||
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ private struct WalletReceiveScreenState: Equatable {
|
|||
private func walletReceiveScreenEntries(presentationData: PresentationData, address: String, state: WalletReceiveScreenState) -> [WalletReceiveScreenEntry] {
|
||||
var entries: [WalletReceiveScreenEntry] = []
|
||||
entries.append(.addressCode(presentationData.theme, invoiceUrl(address: address, state: state, escapeComment: true)))
|
||||
entries.append(.addressHeader(presentationData.theme, state.isEmpty ? "YOUR WALLET ADDRESS" : "INVOICE URL"))
|
||||
entries.append(.addressHeader(presentationData.theme, state.isEmpty ? presentationData.strings.Wallet_Receive_AddressHeader : presentationData.strings.Wallet_Receive_InvoiceUrlHeader))
|
||||
|
||||
let addressText: String
|
||||
var addressMonospace = false
|
||||
|
|
@ -278,16 +278,16 @@ private func walletReceiveScreenEntries(presentationData: PresentationData, addr
|
|||
addressText = invoiceUrl(address: address, state: state, escapeComment: false)
|
||||
}
|
||||
entries.append(.address(presentationData.theme, addressText, addressMonospace))
|
||||
entries.append(.copyAddress(presentationData.theme, state.isEmpty ? "Copy Wallet Address" : "Copy Invoice URL"))
|
||||
entries.append(.shareAddressLink(presentationData.theme, state.isEmpty ? "Share Wallet Address" : "Share Invoice URL"))
|
||||
entries.append(.addressInfo(presentationData.theme, "Share this link with other Gram wallet owners to receive Grams from them."))
|
||||
entries.append(.copyAddress(presentationData.theme, state.isEmpty ? presentationData.strings.Wallet_Receive_CopyAddress : presentationData.strings.Wallet_Receive_CopyInvoiceUrl))
|
||||
entries.append(.shareAddressLink(presentationData.theme, state.isEmpty ? presentationData.strings.Wallet_Receive_ShareAddress : presentationData.strings.Wallet_Receive_ShareInvoiceUrl))
|
||||
entries.append(.addressInfo(presentationData.theme, presentationData.strings.Wallet_Receive_ShareUrlInfo))
|
||||
|
||||
let amount = amountValue(state.amount)
|
||||
entries.append(.amountHeader(presentationData.theme, "AMOUNT"))
|
||||
entries.append(.amount(presentationData.theme, presentationData.strings, "Grams to receive", state.amount ?? ""))
|
||||
entries.append(.amountHeader(presentationData.theme, presentationData.strings.Wallet_Receive_AmountHeader))
|
||||
entries.append(.amount(presentationData.theme, presentationData.strings, presentationData.strings.Wallet_Receive_AmountText, state.amount ?? ""))
|
||||
|
||||
entries.append(.commentHeader(presentationData.theme, "COMMENT (OPTIONAL)"))
|
||||
entries.append(.comment(presentationData.theme, "Description of the payment", state.comment))
|
||||
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_Receive_CommentHeader))
|
||||
entries.append(.comment(presentationData.theme, presentationData.strings.Wallet_Receive_CommentInfo, state.comment))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
|
@ -360,10 +360,10 @@ func walletReceiveScreen(context: AccountContext, tonContext: TonContext, wallet
|
|||
let successText: String
|
||||
if state.isEmpty {
|
||||
UIPasteboard.general.string = address
|
||||
successText = "Address copied to clipboard."
|
||||
successText = presentationData.strings.Wallet_Receive_AddressCopied
|
||||
} else {
|
||||
UIPasteboard.general.string = invoiceUrl(address: address, state: state)
|
||||
successText = "Invoice URL copied to clipboard."
|
||||
successText = presentationData.strings.Wallet_Receive_InvoiceUrlCopied
|
||||
}
|
||||
|
||||
if currentStatusController == nil {
|
||||
|
|
@ -401,7 +401,7 @@ func walletReceiveScreen(context: AccountContext, tonContext: TonContext, wallet
|
|||
ensureVisibleItemTag = focusItemTag
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Receive Grams"), leftNavigationButton: ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {}), rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Receive_Title), leftNavigationButton: ItemListNavigationButton(content: .none, style: .regular, enabled: false, action: {}), rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletReceiveScreenEntries(presentationData: presentationData, address: address, state: state), style: .blocks, ensureVisibleItemTag: ensureVisibleItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ final class WalletRefreshNode: ASDisplayNode {
|
|||
title = lastUpdateTimestampString(strings: self.strings, dateTimeFormat: dateTimeFormat, statusTimestamp: ts, relativeTo: Int32(Date().timeIntervalSince1970))
|
||||
pullProgress = progress
|
||||
case .refreshing:
|
||||
title = "updating"
|
||||
title = self.strings.Wallet_Info_Updating
|
||||
}
|
||||
|
||||
if let previousState = previousState {
|
||||
|
|
|
|||
|
|
@ -253,16 +253,16 @@ private struct WalletSendScreenState: Equatable {
|
|||
private func walletSendScreenEntries(presentationData: PresentationData, balance: Int64?, state: WalletSendScreenState) -> [WalletSendScreenEntry] {
|
||||
var entries: [WalletSendScreenEntry] = []
|
||||
|
||||
entries.append(.addressHeader(presentationData.theme, "RECIPIENT WALLET ADDRESS"))
|
||||
entries.append(.address(presentationData.theme, "Enter wallet address...", state.address))
|
||||
entries.append(.addressInfo(presentationData.theme, "Copy the 48-letter address of the recipient here or ask them to send you a ton:// link."))
|
||||
entries.append(.addressHeader(presentationData.theme, presentationData.strings.Wallet_Send_AddressHeader))
|
||||
entries.append(.address(presentationData.theme, presentationData.strings.Wallet_Send_AddressText, state.address))
|
||||
entries.append(.addressInfo(presentationData.theme, presentationData.strings.Wallet_Send_AddressInfo))
|
||||
|
||||
let amount = amountValue(state.amount)
|
||||
entries.append(.amountHeader(presentationData.theme, "AMOUNT", balance.flatMap { "BALANCE: \(formatBalanceText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" }, amount > 0 && (balance ?? 0) < amount))
|
||||
entries.append(.amount(presentationData.theme, presentationData.strings, "Grams to send", state.amount ?? ""))
|
||||
entries.append(.amountHeader(presentationData.theme, presentationData.strings.Wallet_Receive_AmountHeader, balance.flatMap { presentationData.strings.Wallet_Send_Balance(formatBalanceText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator)).0 }, amount > 0 && (balance ?? 0) < amount))
|
||||
entries.append(.amount(presentationData.theme, presentationData.strings, presentationData.strings.Wallet_Send_AmountText, state.amount ?? ""))
|
||||
|
||||
entries.append(.commentHeader(presentationData.theme, "COMMENT (OPTIONAL)"))
|
||||
entries.append(.comment(presentationData.theme, "Description of the payment", state.comment))
|
||||
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_Receive_CommentHeader))
|
||||
entries.append(.comment(presentationData.theme, presentationData.strings.Wallet_Receive_CommentInfo, state.comment))
|
||||
return entries
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +274,7 @@ private final class WalletSendScreenImpl: ItemListController<WalletSendScreenEnt
|
|||
|
||||
}
|
||||
|
||||
public func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, address: String? = nil, amount: Int64? = nil, comment: String? = nil) -> ViewController {
|
||||
public func walletSendScreen(context: AccountContext, tonContext: TonContext, randomId: Int64, walletInfo: WalletInfo, address: String? = nil, amount: Int64? = nil, comment: String? = nil) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let initialState = WalletSendScreenState(address: address ?? "", amount: amount.flatMap { formatBalanceText($0, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) } ?? "", comment: comment ?? "")
|
||||
|
|
@ -361,11 +361,11 @@ public func walletSendScreen(context: AccountContext, tonContext: TonContext, wa
|
|||
return state
|
||||
}
|
||||
|
||||
let title = NSAttributedString(string: "Confirmation", font: Font.semibold(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
let title = NSAttributedString(string: presentationData.strings.Wallet_Send_Confirmation, font: Font.semibold(17.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
|
||||
let address = state.address[state.address.startIndex..<state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)] + " \n " + state.address[state.address.index(state.address.startIndex, offsetBy: walletAddressLength / 2)..<state.address.endIndex]
|
||||
|
||||
let text = "Do you want to send **\(formatBalanceText(amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))** Grams to\n\(address)?"
|
||||
let text = presentationData.strings.Wallet_Send_ConfirmationText(formatBalanceText(amount, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator), String(address)).0
|
||||
let bodyAttributes = MarkdownAttributeSet(font: Font.regular(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
let boldAttributes = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: presentationData.theme.actionSheet.primaryTextColor)
|
||||
let attributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }), textAlignment: .center))
|
||||
|
|
@ -374,10 +374,10 @@ public func walletSendScreen(context: AccountContext, tonContext: TonContext, wa
|
|||
var dismissAlertImpl: ((Bool) -> Void)?
|
||||
let controller = richTextAlertController(context: context, title: title, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissAlertImpl?(true)
|
||||
}), TextAlertAction(type: .defaultAction, title: "Confirm", action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Wallet_Send_ConfirmationConfirm, action: {
|
||||
dismissAlertImpl?(false)
|
||||
dismissInputImpl?()
|
||||
pushImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .sending(walletInfo, state.address, amount, state.comment)))
|
||||
pushImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .sending(walletInfo, state.address, amount, state.comment, randomId), walletCreatedPreloadState: nil))
|
||||
})], allowInputInset: false, dismissAutomatically: false)
|
||||
presentInGlobalOverlayImpl?(controller, nil)
|
||||
|
||||
|
|
@ -423,11 +423,11 @@ public func walletSendScreen(context: AccountContext, tonContext: TonContext, wa
|
|||
if let balance = balance {
|
||||
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= balance.balance && state.comment.count <= 128
|
||||
}
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text("Send"), style: .bold, enabled: sendEnabled, action: {
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Send_Send), style: .bold, enabled: sendEnabled, action: {
|
||||
arguments.proceed()
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Send Grams"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Send_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: balance?.balance, state: state), style: .blocks, focusItemTag: focusItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ private func walletSettingsControllerEntries(presentationData: PresentationData,
|
|||
var entries: [WalletSettingsEntry] = []
|
||||
|
||||
entries.append(.exportWallet(presentationData.theme, "Export Wallet"))
|
||||
entries.append(.deleteWallet(presentationData.theme, "Delete Wallet"))
|
||||
entries.append(.deleteWallet(presentationData.theme, presentationData.strings.Wallet_Settings_DeleteWallet))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
|
@ -94,20 +94,30 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon
|
|||
var replaceAllWalletControllersImpl: ((ViewController) -> Void)?
|
||||
|
||||
let arguments = WalletSettingsControllerArguments(exportWallet: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
presentControllerImpl?(controller, nil)
|
||||
let _ = (walletRestoreWords(network: context.account.network, walletInfo: walletInfo, tonInstance: tonContext.instance, keychain: tonContext.keychain)
|
||||
|> deliverOnMainQueue).start(next: { wordList in
|
||||
pushControllerImpl?(WalletWordDisplayScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, wordList: wordList, mode: .export))
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] wordList in
|
||||
controller?.dismiss()
|
||||
pushControllerImpl?(WalletWordDisplayScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, wordList: wordList, mode: .export, walletCreatedPreloadState: nil))
|
||||
}, error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
})
|
||||
}, deleteWallet: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: "Delete Wallet", color: .destructive, action: { [weak actionSheet] in
|
||||
ActionSheetButtonItem(title: presentationData.strings.Wallet_Settings_DeleteWallet, color: .destructive, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
presentControllerImpl?(controller, nil)
|
||||
let _ = (deleteLocalWalletData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance, keychain: tonContext.keychain, walletInfo: walletInfo)
|
||||
|> deliverOnMainQueue).start(error: { _ in
|
||||
}, completed: {
|
||||
replaceAllWalletControllersImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro))
|
||||
|> deliverOnMainQueue).start(error: { [weak controller] _ in
|
||||
controller?.dismiss()
|
||||
}, completed: { [weak controller] in
|
||||
controller?.dismiss()
|
||||
replaceAllWalletControllersImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
})
|
||||
})
|
||||
]), ActionSheetItemGroup(items: [
|
||||
|
|
@ -120,7 +130,7 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon
|
|||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get())
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<WalletSettingsEntry>, WalletSettingsEntry.ItemGenerationArguments)) in
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Wallet Settings"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Settings_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletSettingsControllerEntries(presentationData: presentationData, state: state), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import OverlayStatusController
|
|||
import ItemListUI
|
||||
import AlertUI
|
||||
import TextFormat
|
||||
import LocalAuth
|
||||
|
||||
public enum WalletSecureStorageResetReason {
|
||||
case notAvailable
|
||||
|
|
@ -25,7 +26,7 @@ public enum WalletSplashMode {
|
|||
case created(WalletInfo, [String])
|
||||
case success(WalletInfo)
|
||||
case restoreFailed
|
||||
case sending(WalletInfo, String, Int64, String)
|
||||
case sending(WalletInfo, String, Int64, String, Int64)
|
||||
case sent(WalletInfo, Int64)
|
||||
case secureStorageNotAvailable
|
||||
case secureStorageReset(WalletSecureStorageResetReason)
|
||||
|
|
@ -37,7 +38,9 @@ public final class WalletSplashScreen: ViewController {
|
|||
private var presentationData: PresentationData
|
||||
private let mode: WalletSplashMode
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, mode: WalletSplashMode) {
|
||||
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult>?
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, mode: WalletSplashMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult>?) {
|
||||
self.context = context
|
||||
self.tonContext = tonContext
|
||||
self.mode = mode
|
||||
|
|
@ -47,6 +50,31 @@ public final class WalletSplashScreen: ViewController {
|
|||
let defaultNavigationPresentationData = NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)
|
||||
let navigationBarTheme = NavigationBarTheme(buttonColor: defaultNavigationPresentationData.theme.buttonColor, disabledButtonColor: defaultNavigationPresentationData.theme.disabledButtonColor, primaryTextColor: defaultNavigationPresentationData.theme.primaryTextColor, backgroundColor: .clear, separatorColor: .clear, badgeBackgroundColor: defaultNavigationPresentationData.theme.badgeBackgroundColor, badgeStrokeColor: defaultNavigationPresentationData.theme.badgeStrokeColor, badgeTextColor: defaultNavigationPresentationData.theme.badgeTextColor)
|
||||
|
||||
switch mode {
|
||||
case let .created(walletInfo, _):
|
||||
if let walletCreatedPreloadState = walletCreatedPreloadState {
|
||||
self.walletCreatedPreloadState = walletCreatedPreloadState
|
||||
} else {
|
||||
self.walletCreatedPreloadState = Promise()
|
||||
self.walletCreatedPreloadState?.set(getCombinedWalletState(postbox: context.account.postbox, walletInfo: walletInfo, tonInstance: tonContext.instance)
|
||||
|> `catch` { _ -> Signal<CombinedWalletStateResult, NoError> in
|
||||
return .complete()
|
||||
})
|
||||
}
|
||||
case let .success(walletInfo):
|
||||
if let walletCreatedPreloadState = walletCreatedPreloadState {
|
||||
self.walletCreatedPreloadState = walletCreatedPreloadState
|
||||
} else {
|
||||
self.walletCreatedPreloadState = Promise()
|
||||
self.walletCreatedPreloadState?.set(getCombinedWalletState(postbox: context.account.postbox, walletInfo: walletInfo, tonInstance: tonContext.instance)
|
||||
|> `catch` { _ -> Signal<CombinedWalletStateResult, NoError> in
|
||||
return .complete()
|
||||
})
|
||||
}
|
||||
default:
|
||||
self.walletCreatedPreloadState = nil
|
||||
}
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: defaultNavigationPresentationData.strings))
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
|
@ -56,45 +84,16 @@ public final class WalletSplashScreen: ViewController {
|
|||
|
||||
switch self.mode {
|
||||
case .intro:
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: "Not Now", style: .plain, target: self, action: #selector(self.backPressed)), animated: false)
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: "Import existing wallet", style: .plain, target: self, action: #selector(self.importPressed)), animated: false)
|
||||
case let .sending(walletInfo, address, amount, textMessage):
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Wallet_Intro_NotNow, style: .plain, target: self, action: #selector(self.backPressed)), animated: false)
|
||||
self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Wallet_Intro_ImportExisting, style: .plain, target: self, action: #selector(self.importPressed)), animated: false)
|
||||
case let .sending(walletInfo, address, amount, textMessage, randomId):
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: ASDisplayNode())!, animated: false)
|
||||
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, keychain: self.tonContext.keychain, walletInfo: walletInfo, toAddress: address, amount: amount, textMessage: textMessage)
|
||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: "Error", actions: [TextAlertAction(type: .defaultAction, title: "OK", action: {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
})])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}, completed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers: [UIViewController] = []
|
||||
for controller in navigationController.viewControllers {
|
||||
if controller is WalletInfoScreen {
|
||||
controllers.append(WalletInfoScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, address: address))
|
||||
} else {
|
||||
controllers.append(controller)
|
||||
}
|
||||
}
|
||||
|
||||
let controller = WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .sent(walletInfo, amount))
|
||||
controllers.append(controller)
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
})
|
||||
self.sendGrams(walletInfo: walletInfo, address: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: false, randomId: randomId)
|
||||
case .sent, .created:
|
||||
self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: ASDisplayNode())!, animated: false)
|
||||
case .success, .restoreFailed, .secureStorageNotAvailable, .secureStorageReset:
|
||||
case .restoreFailed, .secureStorageNotAvailable, .secureStorageReset:
|
||||
break
|
||||
case .success:
|
||||
break
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +109,66 @@ public final class WalletSplashScreen: ViewController {
|
|||
}
|
||||
|
||||
@objc private func importPressed() {
|
||||
self.push(WalletWordCheckScreen(context: self.context, tonContext: self.tonContext, mode: .import))
|
||||
self.push(WalletWordCheckScreen(context: self.context, tonContext: self.tonContext, mode: .import, walletCreatedPreloadState: nil))
|
||||
}
|
||||
|
||||
private func sendGrams(walletInfo: WalletInfo, address: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, randomId: Int64) {
|
||||
let _ = (sendGramsFromWallet(network: self.context.account.network, tonInstance: self.tonContext.instance, keychain: self.tonContext.keychain, walletInfo: walletInfo, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|
||||
|> deliverOnMainQueue).start(error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let text: String
|
||||
switch error {
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .invalidAddress:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorInvalidAddress
|
||||
case .secretDecryptionFailed:
|
||||
text = strongSelf.presentationData.strings.Wallet_Send_ErrorDecryptionFailed
|
||||
case .destinationIsNotInitialized:
|
||||
if !forceIfDestinationNotInitialized {
|
||||
text = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?"
|
||||
let controller = textAlertController(context: strongSelf.context, title: "Warning", text: text, actions: [
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: "Send Anyway", action: {
|
||||
self?.sendGrams(walletInfo: walletInfo, address: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: true, randomId: randomId)
|
||||
})
|
||||
])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
return
|
||||
} else {
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
}
|
||||
let controller = textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
navigationController.popViewController(animated: true)
|
||||
}
|
||||
})])
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}, completed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers: [UIViewController] = []
|
||||
for controller in navigationController.viewControllers {
|
||||
if controller is WalletInfoScreen {
|
||||
controllers.append(WalletInfoScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, address: address))
|
||||
} else {
|
||||
controllers.append(controller)
|
||||
}
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .sent(walletInfo, amount), walletCreatedPreloadState: nil))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
|
|
@ -128,46 +186,87 @@ public final class WalletSplashScreen: ViewController {
|
|||
return
|
||||
}
|
||||
controller.dismiss()
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .created(walletInfo, wordList)), animated: true)
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .created(walletInfo, wordList), walletCreatedPreloadState: nil), animated: true)
|
||||
}, error: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controller.dismiss()
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "Sorry. Please try again.", actions: [
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Intro_CreateErrorTitle, text: strongSelf.presentationData.strings.Wallet_Intro_CreateErrorText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
})
|
||||
case let .created(walletInfo, wordList):
|
||||
strongSelf.push(WalletWordDisplayScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, wordList: wordList, mode: .check))
|
||||
strongSelf.push(WalletWordDisplayScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, wordList: wordList, mode: .check, walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
case let .success(walletInfo):
|
||||
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.tonContext.instance)
|
||||
|> deliverOnMainQueue).start(next: { address in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is WalletSplashScreen {
|
||||
|
||||
var stateSignal: Signal<Bool, NoError>
|
||||
if let walletCreatedPreloadState = strongSelf.walletCreatedPreloadState {
|
||||
stateSignal = walletCreatedPreloadState.get()
|
||||
|> map { state -> Bool in
|
||||
if case .updated = state {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordDisplayScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletInfoScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordCheckScreen {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletInfoScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, address: address))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
|> filter { $0 }
|
||||
} else {
|
||||
stateSignal = .single(true)
|
||||
}
|
||||
|
||||
let presentationData = strongSelf.presentationData
|
||||
let progressSignal = Signal<Never, NoError> { subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
self?.present(controller, in: .window(.root))
|
||||
return ActionDisposable { [weak controller] in
|
||||
Queue.mainQueue().async() {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|> runOn(Queue.mainQueue())
|
||||
|> delay(0.15, queue: Queue.mainQueue())
|
||||
let progressDisposable = progressSignal.start()
|
||||
|
||||
stateSignal = stateSignal
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
progressDisposable.dispose()
|
||||
}
|
||||
}
|
||||
let _ = (stateSignal
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
progressDisposable.dispose()
|
||||
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
var controllers = navigationController.viewControllers
|
||||
controllers = controllers.filter { controller in
|
||||
if controller is WalletSplashScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordDisplayScreen {
|
||||
return false
|
||||
}
|
||||
if controller is WalletWordCheckScreen {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletInfoScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: walletInfo, address: address))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
})
|
||||
})
|
||||
case let .sent(walletInfo, _):
|
||||
if let navigationController = strongSelf.navigationController as? NavigationController {
|
||||
|
|
@ -205,7 +304,7 @@ public final class WalletSplashScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .intro))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
|
|
@ -213,8 +312,13 @@ public final class WalletSplashScreen: ViewController {
|
|||
break
|
||||
case .secureStorageNotAvailable:
|
||||
strongSelf.dismiss()
|
||||
case .secureStorageReset:
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .import))
|
||||
case let .secureStorageReset(reason):
|
||||
switch reason {
|
||||
case .notAvailable:
|
||||
strongSelf.dismiss()
|
||||
case .changed:
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .import, walletCreatedPreloadState: nil))
|
||||
}
|
||||
}
|
||||
}, secondaryAction: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
|
@ -236,18 +340,22 @@ public final class WalletSplashScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .intro))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .intro, walletCreatedPreloadState: nil))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
default:
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .import))
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .import, walletCreatedPreloadState: nil))
|
||||
}
|
||||
}, openTerms: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: "https://telegram.org", forceExternal: true, presentationData: strongSelf.presentationData, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {})
|
||||
var url = strongSelf.presentationData.strings.Wallet_Intro_TermsUrl
|
||||
if url.isEmpty {
|
||||
url = "https://telegram.org/tos/wallet"
|
||||
}
|
||||
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: strongSelf.presentationData, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {})
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
|
@ -306,12 +414,12 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
|
||||
switch mode {
|
||||
case .intro:
|
||||
title = "Gram Wallet"
|
||||
text = NSAttributedString(string: "Gram wallet allows you to make fast and secure blockchain-based payments without intermediaries.", font: textFont, textColor: textColor)
|
||||
buttonText = "Create My Wallet"
|
||||
title = self.presentationData.strings.Wallet_Intro_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_Intro_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Wallet_Intro_CreateWallet
|
||||
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [:])
|
||||
let link = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [NSAttributedString.Key.underlineStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber])
|
||||
termsText = parseMarkdownIntoAttributedString("By creating the wallet you accept\n[Terms of Conditions]().", attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
|
||||
termsText = parseMarkdownIntoAttributedString(self.presentationData.strings.Wallet_Intro_Terms, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "WalletIntroLoading", ofType: "tgs") {
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 248, height: 248, mode: .direct)
|
||||
|
|
@ -320,9 +428,9 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
secondaryActionText = ""
|
||||
case .created:
|
||||
title = "Congratulations"
|
||||
text = NSAttributedString(string: "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode.", font: textFont, textColor: textColor)
|
||||
buttonText = "Proceed"
|
||||
title = self.presentationData.strings.Wallet_Created_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_Created_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Wallet_Created_Proceed
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "WalletCreated", ofType: "tgs") {
|
||||
|
|
@ -332,9 +440,9 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
secondaryActionText = ""
|
||||
case .success:
|
||||
title = "Ready to go!"
|
||||
text = NSAttributedString(string: "You’re all set. Now you have a wallet that only you control - directly, without middlemen or bankers. ", font: textFont, textColor: textColor)
|
||||
buttonText = "View My Wallet"
|
||||
title = self.presentationData.strings.Wallet_Completed_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_Completed_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Wallet_Completed_ViewWallet
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "celebrate", ofType: "tgs") {
|
||||
|
|
@ -344,9 +452,9 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
secondaryActionText = ""
|
||||
case .restoreFailed:
|
||||
title = "Too Bad"
|
||||
text = NSAttributedString(string: "Without the secret words, you can't'nrestore access to the wallet.", font: textFont, textColor: textColor)
|
||||
buttonText = "Create a New Wallet"
|
||||
title = self.presentationData.strings.Wallet_RestoreFailed_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_RestoreFailed_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Wallet_RestoreFailed_CreateWallet
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "sad", ofType: "tgs") {
|
||||
|
|
@ -354,10 +462,10 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
self.animationSize = CGSize(width: 130.0, height: 130.0)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
secondaryActionText = "Enter 24 words"
|
||||
secondaryActionText = self.presentationData.strings.Wallet_RestoreFailed_EnterWords
|
||||
case .sending:
|
||||
title = "Sending Grams"
|
||||
text = NSAttributedString(string: "Please wait a few seconds for your transaction to be processed...", font: textFont, textColor: textColor)
|
||||
title = self.presentationData.strings.Wallet_Sending_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_Sending_Text, font: textFont, textColor: textColor)
|
||||
buttonText = ""
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
|
|
@ -368,11 +476,11 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
secondaryActionText = ""
|
||||
case let .sent(_, amount):
|
||||
title = "Done!"
|
||||
title = self.presentationData.strings.Wallet_Sent_Title
|
||||
let bodyAttributes = MarkdownAttributeSet(font: textFont, textColor: textColor)
|
||||
let boldAttributes = MarkdownAttributeSet(font: Font.semibold(16.0), textColor: textColor)
|
||||
text = parseMarkdownIntoAttributedString("**\(formatBalanceText(amount, decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator)) Grams** have been sent.", attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
buttonText = "View My Wallet"
|
||||
text = parseMarkdownIntoAttributedString(self.presentationData.strings.Wallet_Sent_Text(formatBalanceText(amount, decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator)).0, attributes: MarkdownAttributes(body: bodyAttributes, bold: boldAttributes, link: bodyAttributes, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
buttonText = self.presentationData.strings.Wallet_Sent_ViewWallet
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "celebrate", ofType: "tgs") {
|
||||
|
|
@ -382,32 +490,61 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
|
|||
}
|
||||
secondaryActionText = ""
|
||||
case .secureStorageNotAvailable:
|
||||
title = "Too Bad"
|
||||
text = NSAttributedString(string: "Please set up Passcode on your device to enable secure payments with your Gram wallet.", font: textFont, textColor: textColor)
|
||||
buttonText = "OK"
|
||||
title = self.presentationData.strings.Wallet_SecureStorageNotAvailable_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_SecureStorageNotAvailable_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Common_OK
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "sad", ofType: "tgs") {
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct)
|
||||
if let path = getAppBundle().path(forResource: "WalletKeyLock", ofType: "tgs") {
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, playbackMode: .once, mode: .direct)
|
||||
self.animationSize = CGSize(width: 140.0, height: 140.0)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
secondaryActionText = ""
|
||||
case let .secureStorageReset(reason):
|
||||
title = "Too Bad"
|
||||
title = self.presentationData.strings.Wallet_SecureStorageReset_Title
|
||||
|
||||
let biometricTypeString: String?
|
||||
if let type = LocalAuth.biometricAuthentication {
|
||||
switch type {
|
||||
case .faceId:
|
||||
biometricTypeString = self.presentationData.strings.Wallet_SecureStorageReset_BiometryFaceId
|
||||
case .touchId:
|
||||
biometricTypeString = self.presentationData.strings.Wallet_SecureStorageReset_BiometryTouchId
|
||||
}
|
||||
} else {
|
||||
biometricTypeString = nil
|
||||
}
|
||||
|
||||
switch reason {
|
||||
case .notAvailable:
|
||||
text = NSAttributedString(string: "Unfortunately, your wallet is no longer available because your system Passcode or Touch ID has been turned off.", font: textFont, textColor: textColor)
|
||||
let string: String
|
||||
if let biometricTypeString = biometricTypeString {
|
||||
string = self.presentationData.strings.Wallet_SecureStorageReset_BiometryText(biometricTypeString).0
|
||||
} else {
|
||||
string = self.presentationData.strings.Wallet_SecureStorageReset_PasscodeText
|
||||
}
|
||||
text = NSAttributedString(string: string, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Common_OK
|
||||
secondaryActionText = ""
|
||||
case .changed:
|
||||
text = NSAttributedString(string: "Unfortunately, your wallet is no longer available due to the change in your system security settings (Passcode/Touch ID). To restore your wallet, tap \"import existing wallet\".", font: textFont, textColor: textColor)
|
||||
let string: String
|
||||
if let biometricTypeString = biometricTypeString {
|
||||
string = self.presentationData.strings.Wallet_SecureStorageChanged_BiometryText(biometricTypeString).0
|
||||
} else {
|
||||
string = self.presentationData.strings.Wallet_SecureStorageChanged_PasscodeText
|
||||
}
|
||||
text = NSAttributedString(string: string, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.Wallet_SecureStorageChanged_ImportWallet
|
||||
secondaryActionText = self.presentationData.strings.Wallet_SecureStorageChanged_CreateWallet
|
||||
}
|
||||
buttonText = "Import Existing Wallet"
|
||||
termsText = NSAttributedString(string: "")
|
||||
self.iconNode.image = nil
|
||||
if let path = getAppBundle().path(forResource: "sad", ofType: "tgs") {
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct)
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 260, height: 260, playbackMode: .once, mode: .direct)
|
||||
self.animationSize = CGSize(width: 130.0, height: 130.0)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
secondaryActionText = "Create New Wallet"
|
||||
}
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ private func stringForAddress(strings: PresentationStrings, address: WalletTrans
|
|||
case let .list(addresses):
|
||||
return addresses.map { formatAddress($0) }.joined(separator: "\n\n")
|
||||
case .none:
|
||||
return "No Address"
|
||||
return strings.Wallet_TransactionInfo_NoAddress
|
||||
case .unknown:
|
||||
return "<unknown>"
|
||||
}
|
||||
|
|
@ -184,19 +184,19 @@ private func walletTransactionInfoControllerEntries(presentationData: Presentati
|
|||
let description = extractDescription(walletTransaction)
|
||||
|
||||
if transferredValue <= 0 {
|
||||
entries.append(.infoHeader(presentationData.theme, "RECIPIENT"))
|
||||
entries.append(.infoHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_RecipientHeader))
|
||||
} else {
|
||||
entries.append(.infoHeader(presentationData.theme, "SENDER"))
|
||||
entries.append(.infoHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_SenderHeader))
|
||||
}
|
||||
|
||||
entries.append(.infoAddress(presentationData.theme, text))
|
||||
if case .list = address {
|
||||
entries.append(.infoCopyAddress(presentationData.theme, "Copy Wallet Address"))
|
||||
entries.append(.infoSendGrams(presentationData.theme, "Send Grams"))
|
||||
entries.append(.infoCopyAddress(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_CopyAddress))
|
||||
entries.append(.infoSendGrams(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_SendGrams))
|
||||
}
|
||||
|
||||
if !description.isEmpty {
|
||||
entries.append(.commentHeader(presentationData.theme, "COMMENT"))
|
||||
entries.append(.commentHeader(presentationData.theme, presentationData.strings.Wallet_TransactionInfo_CommentHeader))
|
||||
entries.append(.comment(presentationData.theme, description))
|
||||
}
|
||||
|
||||
|
|
@ -220,13 +220,13 @@ func walletTransactionInfoController(context: AccountContext, tonContext: TonCon
|
|||
if case let .list(addresses) = address, let address = addresses.first {
|
||||
UIPasteboard.general.string = address
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess("Address copied to clipboard.", false)), nil)
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Wallet_TransactionInfo_AddressCopied, false)), nil)
|
||||
}
|
||||
}, sendGrams: {
|
||||
let address = extractAddress(walletTransaction)
|
||||
if case let .list(addresses) = address, let address = addresses.first {
|
||||
dismissImpl?()
|
||||
pushImpl?(walletSendScreen(context: context, tonContext: tonContext, walletInfo: walletInfo, address: address))
|
||||
pushImpl?(walletSendScreen(context: context, tonContext: tonContext, randomId: arc4random64(), walletInfo: walletInfo, address: address))
|
||||
}
|
||||
}, displayContextMenu: { tag, text in
|
||||
displayContextMenuImpl?(tag, text)
|
||||
|
|
@ -234,7 +234,7 @@ func walletTransactionInfoController(context: AccountContext, tonContext: TonCon
|
|||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get())
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<WalletTransactionInfoEntry>, WalletTransactionInfoEntry.ItemGenerationArguments)) in
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Transaction"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_TransactionInfo_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: walletTransactionInfoControllerEntries(presentationData: presentationData, walletTransaction: walletTransaction, state: state), style: .blocks, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
|
@ -400,7 +400,7 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode {
|
|||
}
|
||||
|
||||
let title = NSMutableAttributedString()
|
||||
if let range = balanceString.range(of: ".") {
|
||||
if let range = balanceString.range(of: item.dateTimeFormat.decimalSeparator) {
|
||||
let integralPart = String(balanceString[..<range.lowerBound])
|
||||
let fractionalPart = String(balanceString[range.lowerBound...])
|
||||
title.append(NSAttributedString(string: integralPart, font: Font.bold(48.0), textColor: titleColor))
|
||||
|
|
|
|||
|
|
@ -2078,10 +2078,13 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
|
||||
private let startTime: Double
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, mode: WalletWordCheckMode) {
|
||||
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult>?
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, mode: WalletWordCheckMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult>?) {
|
||||
self.context = context
|
||||
self.tonContext = tonContext
|
||||
self.mode = mode
|
||||
self.walletCreatedPreloadState = walletCreatedPreloadState
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
|
@ -2139,16 +2142,16 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo)))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
} else {
|
||||
strongSelf.view.endEditing(true)
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "Incorrect words!", text: "The secret words you have entered do not match the ones in the list.", actions: [
|
||||
TextAlertAction(type: .defaultAction, title: "Try Again", action: {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_WordCheck_IncorrectHeader, text: strongSelf.presentationData.strings.Wallet_WordCheck_IncorrectText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_WordCheck_TryAgain, action: {
|
||||
}),
|
||||
TextAlertAction(type: .genericAction, title: "View Words", action: {
|
||||
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Wallet_WordCheck_ViewWords, action: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
|
@ -2168,10 +2171,11 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
}
|
||||
if !allWordsAreValid {
|
||||
strongSelf.view.endEditing(true)
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "Incorrect words", text: "Sorry, you have entered incorrect secret words. Please double check and try again.", actions: [
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_WordImport_IncorrectTitle, text: strongSelf.presentationData.strings.Wallet_WordImport_IncorrectText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
return
|
||||
}
|
||||
let _ = (importWallet(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, tonInstance: strongSelf.tonContext.instance, keychain: strongSelf.tonContext.keychain, wordList: enteredWords)
|
||||
|> deliverOnMainQueue).start(next: { walletInfo in
|
||||
|
|
@ -2192,7 +2196,7 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo)))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
strongSelf.view.endEditing(true)
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
|
|
@ -2201,7 +2205,7 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
return
|
||||
}
|
||||
strongSelf.view.endEditing(true)
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "Incorrect words", text: "Sorry, you have entered incorrect secret words. Please double check and try again.", actions: [
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_WordImport_IncorrectTitle, text: strongSelf.presentationData.strings.Wallet_WordImport_IncorrectText, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
})
|
||||
], actionLayout: .vertical), in: .window(.root))
|
||||
|
|
@ -2223,7 +2227,7 @@ public final class WalletWordCheckScreen: ViewController {
|
|||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .restoreFailed))
|
||||
controllers.append(WalletSplashScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .restoreFailed, walletCreatedPreloadState: nil))
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
}
|
||||
})
|
||||
|
|
@ -2553,9 +2557,9 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
|
||||
private let navigationBackgroundNode: ASDisplayNode
|
||||
private let navigationSeparatorNode: ASDisplayNode
|
||||
private let navigationTitleNode: ImmediateTextNode
|
||||
private let scrollNode: ASScrollNode
|
||||
private let animationNode: AnimatedStickerNode
|
||||
private let titleNodeContainer: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let secondaryActionTitleNode: ImmediateTextNode
|
||||
|
|
@ -2596,31 +2600,27 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
switch mode {
|
||||
case let .verify(_, _, indices):
|
||||
wordIndices = indices
|
||||
title = "Test Time!"
|
||||
title = self.presentationData.strings.Wallet_WordCheck_Title
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor, additionalAttributes: [:])
|
||||
let bold = MarkdownAttributeSet(font: Font.bold(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor, additionalAttributes: [NSAttributedString.Key.underlineStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber])
|
||||
text = parseMarkdownIntoAttributedString("Let’s check that you wrote them down correctly. Please enter words\n**\(wordIndices[0] + 1)**, **\(wordIndices[1] + 1)** and **\(wordIndices[2] + 1)** below:", attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center)
|
||||
text = parseMarkdownIntoAttributedString(self.presentationData.strings.Wallet_WordCheck_Text("\(wordIndices[0] + 1)", "\(wordIndices[1] + 1)", "\(wordIndices[2] + 1)").0, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center)
|
||||
|
||||
buttonText = "Continue"
|
||||
buttonText = self.presentationData.strings.Wallet_WordCheck_Continue
|
||||
secondaryActionText = ""
|
||||
if let path = getAppBundle().path(forResource: "WalletWordCheck", ofType: "tgs") {
|
||||
self.animationNode.setup(account: account, resource: .localFile(path), width: 238, height: 238, playbackMode: .once, mode: .direct)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
case .import:
|
||||
title = "24 Secret Words"
|
||||
text = NSAttributedString(string: "Please restore access to your wallet by\nentering the 24 secret words you wrote down when creating the wallet.", font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
buttonText = "Continue"
|
||||
secondaryActionText = "I don't have those"
|
||||
title = self.presentationData.strings.Wallet_WordImport_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.Wallet_WordImport_Text, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
buttonText = self.presentationData.strings.Wallet_WordImport_Continue
|
||||
secondaryActionText = self.presentationData.strings.Wallet_WordImport_CanNotRemember
|
||||
wordIndices = Array(0 ..< 24)
|
||||
}
|
||||
|
||||
self.navigationTitleNode = ImmediateTextNode()
|
||||
self.navigationTitleNode.displaysAsynchronously = false
|
||||
self.navigationTitleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.navigationTitleNode.maximumNumberOfLines = 0
|
||||
self.navigationTitleNode.textAlignment = .center
|
||||
self.titleNodeContainer = ASDisplayNode()
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
|
@ -2669,7 +2669,6 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
self.addSubnode(self.scrollNode)
|
||||
|
||||
self.scrollNode.addSubnode(self.animationNode)
|
||||
self.scrollNode.addSubnode(self.titleNode)
|
||||
self.scrollNode.addSubnode(self.textNode)
|
||||
self.scrollNode.addSubnode(self.secondaryActionTitleNode)
|
||||
self.scrollNode.addSubnode(self.secondaryActionButtonNode)
|
||||
|
|
@ -2680,9 +2679,11 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
}
|
||||
|
||||
self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode)
|
||||
self.navigationBackgroundNode.addSubnode(self.navigationTitleNode)
|
||||
self.addSubnode(self.navigationBackgroundNode)
|
||||
|
||||
self.titleNodeContainer.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.titleNodeContainer)
|
||||
|
||||
self.buttonNode.pressed = {
|
||||
action()
|
||||
}
|
||||
|
|
@ -2719,15 +2720,15 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
}
|
||||
}
|
||||
focused = { [weak self] node in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if node.isLast {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(strongSelf.buttonNode.frame.insetBy(dx: 0.0, dy: -10.0), animated: false)
|
||||
})
|
||||
} else {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(node.frame.insetBy(dx: 0.0, dy: -10.0), animated: true)
|
||||
DispatchQueue.main.async {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if node.isLast {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(strongSelf.buttonNode.frame.insetBy(dx: 0.0, dy: -10.0), animated: true)
|
||||
} else {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(node.frame.insetBy(dx: 0.0, dy: -10.0), animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
pasteWords = { [weak self] wordList in
|
||||
|
|
@ -2762,11 +2763,35 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
self.scrollNode.view.delegate = self
|
||||
}
|
||||
|
||||
private var listTitleFrame: CGRect?
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
guard let listTitleFrame = self.listTitleFrame else {
|
||||
return
|
||||
}
|
||||
let scrollView = self.scrollNode.view
|
||||
|
||||
let navigationHeight = self.navigationHeight ?? 0.0
|
||||
let alpha: CGFloat = scrollView.contentOffset.y >= (self.titleNode.frame.maxY - navigationHeight) ? 1.0 : 0.0
|
||||
let minY = navigationHeight - 44.0 + floor(44.0 / 2.0)
|
||||
let maxY = minY + 44.0
|
||||
let y = max(minY, -scrollView.contentOffset.y + listTitleFrame.midY)
|
||||
var t = (y - minY) / (maxY - minY)
|
||||
t = max(0.0, min(1.0, t))
|
||||
|
||||
let minScale: CGFloat = 0.5
|
||||
let maxScale: CGFloat = 1.0
|
||||
let scale = t * maxScale + (1.0 - t) * minScale
|
||||
|
||||
self.titleNodeContainer.frame = CGRect(origin: CGPoint(x: scrollView.bounds.width / 2.0, y: y), size: CGSize())
|
||||
self.titleNodeContainer.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
|
||||
let alpha: CGFloat = (t <= 0.0 + CGFloat.ulpOfOne) ? 1.0 : 0.0
|
||||
if self.navigationBackgroundNode.alpha != alpha {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.12, curve: .easeInOut)
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.navigationBackgroundNode, alpha: alpha, beginWithCurrentState: true)
|
||||
}
|
||||
}
|
||||
|
|
@ -2801,12 +2826,9 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
}
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height))
|
||||
let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height))
|
||||
let secondaryActionSize = self.secondaryActionTitleNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height))
|
||||
|
||||
transition.updateFrameAdditive(node: self.navigationTitleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floor((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize))
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let contentVerticalOrigin = navigationHeight + 10.0
|
||||
|
|
@ -2814,7 +2836,8 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
let iconFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize)
|
||||
transition.updateFrame(node: self.animationNode, frame: iconFrame)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
self.listTitleFrame = titleFrame
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((-titleFrame.width) / 2.0), y: floor((-titleFrame.height) / 2.0)), size: titleFrame.size))
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
|
|
@ -2853,5 +2876,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||
self.scrollNode.view.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: layout.insets(options: [.input]).bottom + 30.0, right: 0.0)
|
||||
self.scrollNode.view.contentSize = CGSize(width: contentAreaSize.width, height: max(availableAreaSize.height, buttonFrame.maxY + bottomInset) - 30.0)
|
||||
}
|
||||
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,12 +29,15 @@ public final class WalletWordDisplayScreen: ViewController {
|
|||
private let startTime: Double
|
||||
private let idleTimerExtensionDisposable: Disposable
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, wordList: [String], mode: WalletWordDisplayScreenMode) {
|
||||
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult>?
|
||||
|
||||
public init(context: AccountContext, tonContext: TonContext, walletInfo: WalletInfo, wordList: [String], mode: WalletWordDisplayScreenMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult>?) {
|
||||
self.context = context
|
||||
self.tonContext = tonContext
|
||||
self.walletInfo = walletInfo
|
||||
self.wordList = wordList
|
||||
self.mode = mode
|
||||
self.walletCreatedPreloadState = walletCreatedPreloadState
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
|
@ -85,12 +88,12 @@ public final class WalletWordDisplayScreen: ViewController {
|
|||
minimalTimeout = 60.0
|
||||
#endif
|
||||
if deltaTime < minimalTimeout {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "Sure Done?", text: "You didn't have enough time to write those words down.", actions: [TextAlertAction(type: .defaultAction, title: "OK, Sorry", action: {
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Words_NotDoneTitle, text: strongSelf.presentationData.strings.Wallet_Words_NotDoneText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Wallet_Words_NotDoneOk, action: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let path = getAppBundle().path(forResource: "thumbsup", ofType: "tgs") {
|
||||
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: UndoOverlayContent.emoji(account: strongSelf.context.account, path: path, text: "Apologies Accepted"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in }), in: .current)
|
||||
strongSelf.present(UndoOverlayController(context: strongSelf.context, content: UndoOverlayContent.emoji(account: strongSelf.context.account, path: path, text: strongSelf.presentationData.strings.Wallet_Words_NotDoneResponse), elevatedLayout: false, animateInAsReplacement: false, action: { _ in }), in: .current)
|
||||
}
|
||||
})]), in: .window(.root))
|
||||
} else {
|
||||
|
|
@ -102,7 +105,7 @@ public final class WalletWordDisplayScreen: ViewController {
|
|||
}
|
||||
}
|
||||
wordIndices.sort()
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .verify(strongSelf.walletInfo, strongSelf.wordList, wordIndices)))
|
||||
strongSelf.push(WalletWordCheckScreen(context: strongSelf.context, tonContext: strongSelf.tonContext, mode: .verify(strongSelf.walletInfo, strongSelf.wordList, wordIndices), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -123,9 +126,9 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
|
||||
private let navigationBackgroundNode: ASDisplayNode
|
||||
private let navigationSeparatorNode: ASDisplayNode
|
||||
private let navigationTitleNode: ImmediateTextNode
|
||||
private let scrollNode: ASScrollNode
|
||||
private let animationNode: AnimatedStickerNode
|
||||
private let titleNodeContainer: ASDisplayNode
|
||||
private let titleNode: ImmediateTextNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let wordNodes: [(ImmediateTextNode, ImmediateTextNode, ImmediateTextNode)]
|
||||
|
|
@ -152,9 +155,11 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
self.animationNode.visibility = true
|
||||
}
|
||||
|
||||
let title: String = "24 Secret Words"
|
||||
let text: String = "Write down these 24 words in the correct order and store them in a secret place.\n\nUse these secret words to restore access to your wallet if you lose your passcode or Telegram account."
|
||||
let buttonText: String = "Done"
|
||||
let title: String = self.presentationData.strings.Wallet_Words_Title
|
||||
let text: String = self.presentationData.strings.Wallet_Words_Text
|
||||
let buttonText: String = self.presentationData.strings.Wallet_Words_Done
|
||||
|
||||
self.titleNodeContainer = ASDisplayNode()
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.displaysAsynchronously = false
|
||||
|
|
@ -162,12 +167,6 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
self.titleNode.maximumNumberOfLines = 0
|
||||
self.titleNode.textAlignment = .center
|
||||
|
||||
self.navigationTitleNode = ImmediateTextNode()
|
||||
self.navigationTitleNode.displaysAsynchronously = false
|
||||
self.navigationTitleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
self.navigationTitleNode.maximumNumberOfLines = 0
|
||||
self.navigationTitleNode.textAlignment = .center
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor)
|
||||
|
|
@ -210,7 +209,6 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
self.addSubnode(self.scrollNode)
|
||||
|
||||
self.scrollNode.addSubnode(self.animationNode)
|
||||
self.scrollNode.addSubnode(self.titleNode)
|
||||
self.scrollNode.addSubnode(self.textNode)
|
||||
self.scrollNode.addSubnode(self.buttonNode)
|
||||
|
||||
|
|
@ -221,9 +219,11 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
}
|
||||
|
||||
self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode)
|
||||
self.navigationBackgroundNode.addSubnode(self.navigationTitleNode)
|
||||
self.addSubnode(self.navigationBackgroundNode)
|
||||
|
||||
self.titleNodeContainer.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.titleNodeContainer)
|
||||
|
||||
self.buttonNode.pressed = {
|
||||
action()
|
||||
}
|
||||
|
|
@ -252,11 +252,35 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
}
|
||||
}
|
||||
|
||||
private var listTitleFrame: CGRect?
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
guard let listTitleFrame = self.listTitleFrame else {
|
||||
return
|
||||
}
|
||||
let scrollView = self.scrollNode.view
|
||||
|
||||
let navigationHeight = self.navigationHeight ?? 0.0
|
||||
let alpha: CGFloat = scrollView.contentOffset.y >= (self.titleNode.frame.maxY - navigationHeight) ? 1.0 : 0.0
|
||||
let minY = navigationHeight - 44.0 + floor(44.0 / 2.0)
|
||||
let maxY = minY + 44.0
|
||||
let y = max(minY, -scrollView.contentOffset.y + listTitleFrame.midY)
|
||||
var t = (y - minY) / (maxY - minY)
|
||||
t = max(0.0, min(1.0, t))
|
||||
|
||||
let minScale: CGFloat = 0.5
|
||||
let maxScale: CGFloat = 1.0
|
||||
let scale = t * maxScale + (1.0 - t) * minScale
|
||||
|
||||
self.titleNodeContainer.frame = CGRect(origin: CGPoint(x: scrollView.bounds.width / 2.0, y: y), size: CGSize())
|
||||
self.titleNodeContainer.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0)
|
||||
|
||||
let alpha: CGFloat = (t <= 0.0 + CGFloat.ulpOfOne) ? 1.0 : 0.0
|
||||
if self.navigationBackgroundNode.alpha != alpha {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.12, curve: .easeInOut)
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.navigationBackgroundNode, alpha: alpha, beginWithCurrentState: true)
|
||||
}
|
||||
}
|
||||
|
|
@ -283,7 +307,6 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
self.animationNode.updateLayout(size: iconSize)
|
||||
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
|
@ -293,12 +316,11 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0) + 12.0, y: contentVerticalOrigin), size: iconSize)
|
||||
transition.updateFrameAdditive(node: self.animationNode, frame: iconFrame)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize)
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||
self.listTitleFrame = titleFrame
|
||||
transition.updateFrameAdditive(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((-titleFrame.width) / 2.0), y: floor((-titleFrame.height) / 2.0)), size: titleFrame.size))
|
||||
let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
transition.updateFrameAdditive(node: self.navigationTitleNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floor((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize))
|
||||
|
||||
contentHeight = textFrame.maxY + textSpacing
|
||||
|
||||
let rowWidth = layout.size.width - buttonSideInset * 2.0
|
||||
|
|
@ -363,5 +385,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
|
|||
self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition)
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: max(layout.size.height, buttonFrame.maxY + scrollBottomInset))
|
||||
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,6 +312,19 @@
|
|||
<key>explicitFileType</key>
|
||||
<string>archive.ar</string>
|
||||
</dict>
|
||||
<key>1DD70E29A889192100000000</key>
|
||||
<dict>
|
||||
<key>isa</key>
|
||||
<string>PBXFileReference</string>
|
||||
<key>name</key>
|
||||
<string>libLocalAuth.a</string>
|
||||
<key>path</key>
|
||||
<string>libLocalAuth.a</string>
|
||||
<key>sourceTree</key>
|
||||
<string>BUILT_PRODUCTS_DIR</string>
|
||||
<key>explicitFileType</key>
|
||||
<string>archive.ar</string>
|
||||
</dict>
|
||||
<key>1DD70E29CE34063500000000</key>
|
||||
<dict>
|
||||
<key>isa</key>
|
||||
|
|
@ -782,6 +795,7 @@
|
|||
<string>1DD70E29D6F14E1000000000</string>
|
||||
<string>1DD70E295A26607D00000000</string>
|
||||
<string>1DD70E29AC43662400000000</string>
|
||||
<string>1DD70E29A889192100000000</string>
|
||||
<string>1DD70E29CE34063500000000</string>
|
||||
<string>1DD70E2936DE2CF900000000</string>
|
||||
<string>1DD70E29C37F741500000000</string>
|
||||
|
|
@ -1457,6 +1471,13 @@
|
|||
<key>fileRef</key>
|
||||
<string>1DD70E295A26607D00000000</string>
|
||||
</dict>
|
||||
<key>E7A30F04A889192100000000</key>
|
||||
<dict>
|
||||
<key>isa</key>
|
||||
<string>PBXBuildFile</string>
|
||||
<key>fileRef</key>
|
||||
<string>1DD70E29A889192100000000</string>
|
||||
</dict>
|
||||
<key>E7A30F04BBAF750C00000000</key>
|
||||
<dict>
|
||||
<key>isa</key>
|
||||
|
|
@ -1647,6 +1668,7 @@
|
|||
<string>E7A30F0457B5522500000000</string>
|
||||
<string>E7A30F0425BBFEEE00000000</string>
|
||||
<string>E7A30F045A26607D00000000</string>
|
||||
<string>E7A30F04A889192100000000</string>
|
||||
<string>E7A30F04BBAF750C00000000</string>
|
||||
<string>E7A30F0489A0042800000000</string>
|
||||
<string>E7A30F04AA32BBC600000000</string>
|
||||
|
|
|
|||
|
|
@ -368,14 +368,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
|
|||
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & {
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
|
||||
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
|
||||
(last_inmsg_lt > other.last_inmsg_lt ||
|
||||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const & {
|
||||
ton::BlockSeqno other_mc_seqno) const& {
|
||||
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
|
||||
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,33 @@ void bip39Hints::store(td::TlStorerToString &s, const char *field_name) const {
|
|||
}
|
||||
}
|
||||
|
||||
config::config()
|
||||
: config_()
|
||||
, blockchain_name_()
|
||||
, use_callbacks_for_network_()
|
||||
, ignore_cache_()
|
||||
{}
|
||||
|
||||
config::config(std::string const &config_, std::string const &blockchain_name_, bool use_callbacks_for_network_, bool ignore_cache_)
|
||||
: config_(std::move(config_))
|
||||
, blockchain_name_(std::move(blockchain_name_))
|
||||
, use_callbacks_for_network_(use_callbacks_for_network_)
|
||||
, ignore_cache_(ignore_cache_)
|
||||
{}
|
||||
|
||||
const std::int32_t config::ID;
|
||||
|
||||
void config::store(td::TlStorerToString &s, const char *field_name) const {
|
||||
if (!LOG_IS_STRIPPED(ERROR)) {
|
||||
s.store_class_begin(field_name, "config");
|
||||
s.store_field("config", config_);
|
||||
s.store_field("blockchain_name", blockchain_name_);
|
||||
s.store_field("use_callbacks_for_network", use_callbacks_for_network_);
|
||||
s.store_field("ignore_cache", ignore_cache_);
|
||||
s.store_class_end();
|
||||
}
|
||||
}
|
||||
|
||||
error::error()
|
||||
: code_()
|
||||
, message_()
|
||||
|
|
@ -268,13 +295,11 @@ void ok::store(td::TlStorerToString &s, const char *field_name) const {
|
|||
options::options()
|
||||
: config_()
|
||||
, keystore_directory_()
|
||||
, use_callbacks_for_network_()
|
||||
{}
|
||||
|
||||
options::options(std::string const &config_, std::string const &keystore_directory_, bool use_callbacks_for_network_)
|
||||
options::options(object_ptr<config> &&config_, std::string const &keystore_directory_)
|
||||
: config_(std::move(config_))
|
||||
, keystore_directory_(std::move(keystore_directory_))
|
||||
, use_callbacks_for_network_(use_callbacks_for_network_)
|
||||
{}
|
||||
|
||||
const std::int32_t options::ID;
|
||||
|
|
@ -282,9 +307,26 @@ const std::int32_t options::ID;
|
|||
void options::store(td::TlStorerToString &s, const char *field_name) const {
|
||||
if (!LOG_IS_STRIPPED(ERROR)) {
|
||||
s.store_class_begin(field_name, "options");
|
||||
s.store_field("config", config_);
|
||||
if (config_ == nullptr) { s.store_field("config", "null"); } else { config_->store(s, "config"); }
|
||||
s.store_field("keystore_directory", keystore_directory_);
|
||||
s.store_field("use_callbacks_for_network", use_callbacks_for_network_);
|
||||
s.store_class_end();
|
||||
}
|
||||
}
|
||||
|
||||
sendGramsResult::sendGramsResult()
|
||||
: sent_until_()
|
||||
{}
|
||||
|
||||
sendGramsResult::sendGramsResult(std::int64_t sent_until_)
|
||||
: sent_until_(sent_until_)
|
||||
{}
|
||||
|
||||
const std::int32_t sendGramsResult::ID;
|
||||
|
||||
void sendGramsResult::store(td::TlStorerToString &s, const char *field_name) const {
|
||||
if (!LOG_IS_STRIPPED(ERROR)) {
|
||||
s.store_class_begin(field_name, "sendGramsResult");
|
||||
s.store_field("sent_until", sent_until_);
|
||||
s.store_class_end();
|
||||
}
|
||||
}
|
||||
|
|
@ -873,14 +915,18 @@ generic_sendGrams::generic_sendGrams()
|
|||
, source_()
|
||||
, destination_()
|
||||
, amount_()
|
||||
, timeout_()
|
||||
, allow_send_to_uninited_()
|
||||
, message_()
|
||||
{}
|
||||
|
||||
generic_sendGrams::generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::string const &message_)
|
||||
generic_sendGrams::generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::int32_t timeout_, bool allow_send_to_uninited_, std::string const &message_)
|
||||
: private_key_(std::move(private_key_))
|
||||
, source_(std::move(source_))
|
||||
, destination_(std::move(destination_))
|
||||
, amount_(amount_)
|
||||
, timeout_(timeout_)
|
||||
, allow_send_to_uninited_(allow_send_to_uninited_)
|
||||
, message_(std::move(message_))
|
||||
{}
|
||||
|
||||
|
|
@ -893,6 +939,8 @@ void generic_sendGrams::store(td::TlStorerToString &s, const char *field_name) c
|
|||
if (source_ == nullptr) { s.store_field("source", "null"); } else { source_->store(s, "source"); }
|
||||
if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); }
|
||||
s.store_field("amount", amount_);
|
||||
s.store_field("timeout", timeout_);
|
||||
s.store_field("allow_send_to_uninited", allow_send_to_uninited_);
|
||||
s.store_bytes_field("message", message_);
|
||||
s.store_class_end();
|
||||
}
|
||||
|
|
@ -1106,7 +1154,7 @@ options_setConfig::options_setConfig()
|
|||
: config_()
|
||||
{}
|
||||
|
||||
options_setConfig::options_setConfig(std::string const &config_)
|
||||
options_setConfig::options_setConfig(object_ptr<config> &&config_)
|
||||
: config_(std::move(config_))
|
||||
{}
|
||||
|
||||
|
|
@ -1115,7 +1163,7 @@ const std::int32_t options_setConfig::ID;
|
|||
void options_setConfig::store(td::TlStorerToString &s, const char *field_name) const {
|
||||
if (!LOG_IS_STRIPPED(ERROR)) {
|
||||
s.store_class_begin(field_name, "options_setConfig");
|
||||
s.store_field("config", config_);
|
||||
if (config_ == nullptr) { s.store_field("config", "null"); } else { config_->store(s, "config"); }
|
||||
s.store_class_end();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ class accountAddress;
|
|||
|
||||
class bip39Hints;
|
||||
|
||||
class config;
|
||||
|
||||
class error;
|
||||
|
||||
class exportedEncryptedKey;
|
||||
|
|
@ -70,6 +72,8 @@ class ok;
|
|||
|
||||
class options;
|
||||
|
||||
class sendGramsResult;
|
||||
|
||||
class updateSendLiteServerQuery;
|
||||
|
||||
class generic_AccountState;
|
||||
|
|
@ -140,6 +144,25 @@ class bip39Hints final : public Object {
|
|||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
||||
class config final : public Object {
|
||||
public:
|
||||
std::string config_;
|
||||
std::string blockchain_name_;
|
||||
bool use_callbacks_for_network_;
|
||||
bool ignore_cache_;
|
||||
|
||||
config();
|
||||
|
||||
config(std::string const &config_, std::string const &blockchain_name_, bool use_callbacks_for_network_, bool ignore_cache_);
|
||||
|
||||
static const std::int32_t ID = -1538391496;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
||||
class error final : public Object {
|
||||
public:
|
||||
std::int32_t code_;
|
||||
|
|
@ -333,15 +356,30 @@ class ok final : public Object {
|
|||
|
||||
class options final : public Object {
|
||||
public:
|
||||
std::string config_;
|
||||
object_ptr<config> config_;
|
||||
std::string keystore_directory_;
|
||||
bool use_callbacks_for_network_;
|
||||
|
||||
options();
|
||||
|
||||
options(std::string const &config_, std::string const &keystore_directory_, bool use_callbacks_for_network_);
|
||||
options(object_ptr<config> &&config_, std::string const &keystore_directory_);
|
||||
|
||||
static const std::int32_t ID = -952483001;
|
||||
static const std::int32_t ID = 789823302;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
||||
class sendGramsResult final : public Object {
|
||||
public:
|
||||
std::int64_t sent_until_;
|
||||
|
||||
sendGramsResult();
|
||||
|
||||
explicit sendGramsResult(std::int64_t sent_until_);
|
||||
|
||||
static const std::int32_t ID = -858318471;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
|
@ -839,18 +877,20 @@ class generic_sendGrams final : public Function {
|
|||
object_ptr<accountAddress> source_;
|
||||
object_ptr<accountAddress> destination_;
|
||||
std::int64_t amount_;
|
||||
std::int32_t timeout_;
|
||||
bool allow_send_to_uninited_;
|
||||
std::string message_;
|
||||
|
||||
generic_sendGrams();
|
||||
|
||||
generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::string const &message_);
|
||||
generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::int32_t timeout_, bool allow_send_to_uninited_, std::string const &message_);
|
||||
|
||||
static const std::int32_t ID = 1523427648;
|
||||
static const std::int32_t ID = -758801136;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
using ReturnType = object_ptr<ok>;
|
||||
using ReturnType = object_ptr<sendGramsResult>;
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
|
@ -1054,13 +1094,13 @@ class onLiteServerQueryResult final : public Function {
|
|||
|
||||
class options_setConfig final : public Function {
|
||||
public:
|
||||
std::string config_;
|
||||
object_ptr<config> config_;
|
||||
|
||||
options_setConfig();
|
||||
|
||||
explicit options_setConfig(std::string const &config_);
|
||||
explicit options_setConfig(object_ptr<config> &&config_);
|
||||
|
||||
static const std::int32_t ID = 21225546;
|
||||
static const std::int32_t ID = 646497241;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
|
@ -1259,12 +1299,12 @@ class testGiver_sendGrams final : public Function {
|
|||
|
||||
testGiver_sendGrams(object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_);
|
||||
|
||||
static const std::int32_t ID = -1361914347;
|
||||
static const std::int32_t ID = -1785750375;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
using ReturnType = object_ptr<ok>;
|
||||
using ReturnType = object_ptr<sendGramsResult>;
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
|
@ -1335,12 +1375,12 @@ class testWallet_sendGrams final : public Function {
|
|||
|
||||
testWallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_);
|
||||
|
||||
static const std::int32_t ID = 43200674;
|
||||
static const std::int32_t ID = 1290131585;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
using ReturnType = object_ptr<ok>;
|
||||
using ReturnType = object_ptr<sendGramsResult>;
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
|
@ -1412,12 +1452,12 @@ class wallet_sendGrams final : public Function {
|
|||
|
||||
wallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t valid_until_, std::int64_t amount_, std::string const &message_);
|
||||
|
||||
static const std::int32_t ID = 789731197;
|
||||
static const std::int32_t ID = -1837893526;
|
||||
std::int32_t get_id() const final {
|
||||
return ID;
|
||||
}
|
||||
|
||||
using ReturnType = object_ptr<ok>;
|
||||
using ReturnType = object_ptr<sendGramsResult>;
|
||||
|
||||
void store(td::TlStorerToString &s, const char *field_name) const final;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ bool downcast_call(Object &obj, const T &func) {
|
|||
case bip39Hints::ID:
|
||||
func(static_cast<bip39Hints &>(obj));
|
||||
return true;
|
||||
case config::ID:
|
||||
func(static_cast<config &>(obj));
|
||||
return true;
|
||||
case error::ID:
|
||||
func(static_cast<error &>(obj));
|
||||
return true;
|
||||
|
|
@ -59,6 +62,9 @@ bool downcast_call(Object &obj, const T &func) {
|
|||
case options::ID:
|
||||
func(static_cast<options &>(obj));
|
||||
return true;
|
||||
case sendGramsResult::ID:
|
||||
func(static_cast<sendGramsResult &>(obj));
|
||||
return true;
|
||||
case updateSendLiteServerQuery::ID:
|
||||
func(static_cast<updateSendLiteServerQuery &>(obj));
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
|
|||
static const std::unordered_map<Slice, int32, SliceHash> m = {
|
||||
{"accountAddress", 755613099},
|
||||
{"bip39Hints", 1012243456},
|
||||
{"config", -1538391496},
|
||||
{"error", -1679978726},
|
||||
{"exportedEncryptedKey", 2024406612},
|
||||
{"exportedKey", -1449248297},
|
||||
|
|
@ -56,7 +57,8 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
|
|||
{"logTags", -1604930601},
|
||||
{"logVerbosityLevel", 1734624234},
|
||||
{"ok", -722616727},
|
||||
{"options", -952483001},
|
||||
{"options", 789823302},
|
||||
{"sendGramsResult", -858318471},
|
||||
{"updateSendLiteServerQuery", -1555130916},
|
||||
{"generic.accountStateRaw", -1387096685},
|
||||
{"generic.accountStateTestWallet", -1041955397},
|
||||
|
|
@ -93,7 +95,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
|
|||
{"exportKey", 399723440},
|
||||
{"exportPemKey", -2047752448},
|
||||
{"generic.getAccountState", -657000446},
|
||||
{"generic.sendGrams", 1523427648},
|
||||
{"generic.sendGrams", -758801136},
|
||||
{"getBip39Hints", -1889640982},
|
||||
{"getLogStream", 1167608667},
|
||||
{"getLogTagVerbosityLevel", 951004547},
|
||||
|
|
@ -105,7 +107,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
|
|||
{"init", -2014661877},
|
||||
{"onLiteServerQueryError", -677427533},
|
||||
{"onLiteServerQueryResult", 2056444510},
|
||||
{"options.setConfig", 21225546},
|
||||
{"options.setConfig", 646497241},
|
||||
{"raw.getAccountAddress", -521283849},
|
||||
{"raw.getAccountState", 663706721},
|
||||
{"raw.getTransactions", 935377269},
|
||||
|
|
@ -116,15 +118,15 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
|
|||
{"setLogVerbosityLevel", -303429678},
|
||||
{"testGiver.getAccountAddress", -540100768},
|
||||
{"testGiver.getAccountState", 267738275},
|
||||
{"testGiver.sendGrams", -1361914347},
|
||||
{"testGiver.sendGrams", -1785750375},
|
||||
{"testWallet.getAccountAddress", -1557748223},
|
||||
{"testWallet.getAccountState", 654082364},
|
||||
{"testWallet.init", 419055225},
|
||||
{"testWallet.sendGrams", 43200674},
|
||||
{"testWallet.sendGrams", 1290131585},
|
||||
{"wallet.getAccountAddress", -1004103180},
|
||||
{"wallet.getAccountState", 462294850},
|
||||
{"wallet.init", 1528056782},
|
||||
{"wallet.sendGrams", 789731197}
|
||||
{"wallet.sendGrams", -1837893526}
|
||||
};
|
||||
auto it = m.find(str);
|
||||
if (it == m.end()) {
|
||||
|
|
@ -150,6 +152,33 @@ Status from_json(tonlib_api::bip39Hints &to, JsonObject &from) {
|
|||
}
|
||||
return Status::OK();
|
||||
}
|
||||
Status from_json(tonlib_api::config &to, JsonObject &from) {
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "config", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.config_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "blockchain_name", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.blockchain_name_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "use_callbacks_for_network", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.use_callbacks_for_network_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "ignore_cache", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.ignore_cache_, value));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
Status from_json(tonlib_api::error &to, JsonObject &from) {
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "code", JsonValue::Type::Null, true));
|
||||
|
|
@ -277,10 +306,13 @@ Status from_json(tonlib_api::options &to, JsonObject &from) {
|
|||
TRY_STATUS(from_json(to.keystore_directory_, value));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
Status from_json(tonlib_api::sendGramsResult &to, JsonObject &from) {
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "use_callbacks_for_network", JsonValue::Type::Null, true));
|
||||
TRY_RESULT(value, get_json_object_field(from, "sent_until", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.use_callbacks_for_network_, value));
|
||||
TRY_STATUS(from_json(to.sent_until_, value));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
|
|
@ -745,6 +777,18 @@ Status from_json(tonlib_api::generic_sendGrams &to, JsonObject &from) {
|
|||
TRY_STATUS(from_json(to.amount_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "timeout", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.timeout_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "allow_send_to_uninited", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
TRY_STATUS(from_json(to.allow_send_to_uninited_, value));
|
||||
}
|
||||
}
|
||||
{
|
||||
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
|
||||
if (value.type() != JsonValue::Type::Null) {
|
||||
|
|
@ -1156,6 +1200,14 @@ void to_json(JsonValueScope &jv, const tonlib_api::bip39Hints &object) {
|
|||
jo << ctie("@type", "bip39Hints");
|
||||
jo << ctie("words", ToJson(object.words_));
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::config &object) {
|
||||
auto jo = jv.enter_object();
|
||||
jo << ctie("@type", "config");
|
||||
jo << ctie("config", ToJson(object.config_));
|
||||
jo << ctie("blockchain_name", ToJson(object.blockchain_name_));
|
||||
jo << ctie("use_callbacks_for_network", ToJson(object.use_callbacks_for_network_));
|
||||
jo << ctie("ignore_cache", ToJson(object.ignore_cache_));
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::error &object) {
|
||||
auto jo = jv.enter_object();
|
||||
jo << ctie("@type", "error");
|
||||
|
|
@ -1225,9 +1277,15 @@ void to_json(JsonValueScope &jv, const tonlib_api::ok &object) {
|
|||
void to_json(JsonValueScope &jv, const tonlib_api::options &object) {
|
||||
auto jo = jv.enter_object();
|
||||
jo << ctie("@type", "options");
|
||||
jo << ctie("config", ToJson(object.config_));
|
||||
if (object.config_) {
|
||||
jo << ctie("config", ToJson(object.config_));
|
||||
}
|
||||
jo << ctie("keystore_directory", ToJson(object.keystore_directory_));
|
||||
jo << ctie("use_callbacks_for_network", ToJson(object.use_callbacks_for_network_));
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::sendGramsResult &object) {
|
||||
auto jo = jv.enter_object();
|
||||
jo << ctie("@type", "sendGramsResult");
|
||||
jo << ctie("sent_until", ToJson(object.sent_until_));
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::updateSendLiteServerQuery &object) {
|
||||
auto jo = jv.enter_object();
|
||||
|
|
@ -1450,6 +1508,8 @@ void to_json(JsonValueScope &jv, const tonlib_api::generic_sendGrams &object) {
|
|||
jo << ctie("destination", ToJson(object.destination_));
|
||||
}
|
||||
jo << ctie("amount", ToJson(JsonInt64{object.amount_}));
|
||||
jo << ctie("timeout", ToJson(object.timeout_));
|
||||
jo << ctie("allow_send_to_uninited", ToJson(object.allow_send_to_uninited_));
|
||||
jo << ctie("message", ToJson(JsonBytes{object.message_}));
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::getBip39Hints &object) {
|
||||
|
|
@ -1525,7 +1585,9 @@ void to_json(JsonValueScope &jv, const tonlib_api::onLiteServerQueryResult &obje
|
|||
void to_json(JsonValueScope &jv, const tonlib_api::options_setConfig &object) {
|
||||
auto jo = jv.enter_object();
|
||||
jo << ctie("@type", "options.setConfig");
|
||||
jo << ctie("config", ToJson(object.config_));
|
||||
if (object.config_) {
|
||||
jo << ctie("config", ToJson(object.config_));
|
||||
}
|
||||
}
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::raw_getAccountAddress &object) {
|
||||
auto jo = jv.enter_object();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
|
|||
Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std::string &str);
|
||||
Status from_json(tonlib_api::accountAddress &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::bip39Hints &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::config &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::error &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::exportedEncryptedKey &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::exportedKey &to, JsonObject &from);
|
||||
|
|
@ -30,6 +31,7 @@ Status from_json(tonlib_api::logTags &to, JsonObject &from);
|
|||
Status from_json(tonlib_api::logVerbosityLevel &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::ok &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::options &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::sendGramsResult &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::updateSendLiteServerQuery &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::generic_accountStateRaw &to, JsonObject &from);
|
||||
Status from_json(tonlib_api::generic_accountStateTestWallet &to, JsonObject &from);
|
||||
|
|
@ -91,6 +93,7 @@ Status from_json(tonlib_api::wallet_init &to, JsonObject &from);
|
|||
Status from_json(tonlib_api::wallet_sendGrams &to, JsonObject &from);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::accountAddress &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::bip39Hints &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::config &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::error &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::exportedEncryptedKey &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::exportedKey &object);
|
||||
|
|
@ -105,6 +108,7 @@ void to_json(JsonValueScope &jv, const tonlib_api::logTags &object);
|
|||
void to_json(JsonValueScope &jv, const tonlib_api::logVerbosityLevel &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::ok &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::options &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::sendGramsResult &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::updateSendLiteServerQuery &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::generic_AccountState &object);
|
||||
void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateRaw &object);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ vector {t:Type} # [ t ] = Vector t;
|
|||
error code:int32 message:string = Error;
|
||||
ok = Ok;
|
||||
|
||||
options config:string keystore_directory:string use_callbacks_for_network:Bool = Options;
|
||||
config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config;
|
||||
options config:config keystore_directory:string = Options;
|
||||
|
||||
key public_key:string secret:secureBytes = Key;
|
||||
inputKey key:key local_password:secureBytes = InputKey;
|
||||
|
|
@ -56,7 +57,7 @@ generic.accountStateWallet account_state:wallet.accountState = generic.AccountSt
|
|||
generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState;
|
||||
generic.accountStateUninited account_state:uninited.accountState = generic.AccountState;
|
||||
|
||||
//generic.sendGramsResult sent_until:int53 = generic.SendGramsResult;
|
||||
sendGramsResult sent_until:int53 = SendGramsResult;
|
||||
|
||||
updateSendLiteServerQuery id:int64 data:bytes = Update;
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ logTags tags:vector<string> = LogTags;
|
|||
init options:options = Ok;
|
||||
close = Ok;
|
||||
|
||||
options.setConfig config:string = Ok;
|
||||
options.setConfig config:config = Ok;
|
||||
|
||||
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
|
||||
deleteKey key:key = Ok;
|
||||
|
|
@ -106,20 +107,20 @@ raw.getTransactions account_address:accountAddress from_transaction_id:internal.
|
|||
testWallet.init private_key:inputKey = Ok;
|
||||
testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress;
|
||||
testWallet.getAccountState account_address:accountAddress = testWallet.AccountState;
|
||||
testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok;
|
||||
testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult;
|
||||
|
||||
wallet.init private_key:inputKey = Ok;
|
||||
wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress;
|
||||
wallet.getAccountState account_address:accountAddress = wallet.AccountState;
|
||||
wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = Ok;
|
||||
wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult;
|
||||
|
||||
testGiver.getAccountState = testGiver.AccountState;
|
||||
testGiver.getAccountAddress = AccountAddress;
|
||||
testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok;
|
||||
testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult;
|
||||
|
||||
//generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress;
|
||||
generic.getAccountState account_address:accountAddress = generic.AccountState;
|
||||
generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 message:bytes = Ok;
|
||||
generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult;
|
||||
|
||||
onLiteServerQueryResult id:int64 bytes:bytes = Ok;
|
||||
onLiteServerQueryError id:int64 error:error = Ok;
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -267,20 +267,21 @@ static auto sync_send = [](auto &client, auto query) {
|
|||
|
||||
TEST(Tonlib, InitClose) {
|
||||
using tonlib_api::make_object;
|
||||
auto cfg = [](auto str) { return make_object<tonlib_api::config>(str, "", false, false); };
|
||||
{
|
||||
Client client;
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
}
|
||||
{
|
||||
Client client;
|
||||
sync_send(client, make_object<tonlib_api::init>(nullptr)).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("fdajkfldsjkafld", ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(cfg("fdajkfldsjkafld"), ".")))
|
||||
.ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", "fdhskfds", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "fdhskfds")))
|
||||
.ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
|
||||
td::Slice bad_config = R"abc(
|
||||
{
|
||||
|
|
@ -289,11 +290,11 @@ TEST(Tonlib, InitClose) {
|
|||
}
|
||||
)abc";
|
||||
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(bad_config.str())).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(cfg(bad_config.str()))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure();
|
||||
sync_send(client, make_object<tonlib_api::close>()).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure_error();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -388,7 +389,7 @@ TEST(Tonlib, KeysApi) {
|
|||
Client client;
|
||||
|
||||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>("", ".", false))).ensure();
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
|
||||
auto local_password = td::SecureString("local password");
|
||||
auto mnemonic_password = td::SecureString("mnemonic password");
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ void transfer_grams(Client& client, std::string from, std::string to, td::int64
|
|||
auto balance = get_balance(client, to);
|
||||
sync_send(client, tonlib_api::make_object<tonlib_api::generic_sendGrams>(
|
||||
std::move(input_key), tonlib_api::make_object<tonlib_api::accountAddress>(from),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount, "GIFT"))
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount, 0, true, "GIFT"))
|
||||
.ensure();
|
||||
while (balance == get_balance(client, to)) {
|
||||
client.receive(1);
|
||||
|
|
@ -196,7 +196,8 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
Client client;
|
||||
{
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
|
||||
.ensure();
|
||||
}
|
||||
//dump_transaction_history(client, get_test_giver_address(client));
|
||||
|
|
@ -209,7 +210,8 @@ int main(int argc, char* argv[]) {
|
|||
return 0;
|
||||
{
|
||||
// init
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(global_config_str, ".", false)))
|
||||
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
|
||||
.ensure();
|
||||
|
||||
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(
|
||||
|
|
@ -224,7 +226,9 @@ int main(int argc, char* argv[]) {
|
|||
auto public_key_raw = key->public_key_;
|
||||
td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw});
|
||||
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(global_config_str)).ensure();
|
||||
sync_send(client, make_object<tonlib_api::options_setConfig>(
|
||||
make_object<tonlib_api::config>(global_config_str, "", false, false)))
|
||||
.ensure();
|
||||
|
||||
auto wallet_addr = GenericAccount::get_address(0, TestWallet::get_init_state(public_key_std));
|
||||
{
|
||||
|
|
@ -307,10 +311,10 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
{
|
||||
sync_send(client,
|
||||
make_object<tonlib_api::generic_sendGrams>(
|
||||
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
|
||||
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000, "GIFT"))
|
||||
sync_send(client, make_object<tonlib_api::generic_sendGrams>(
|
||||
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
|
||||
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000, 0,
|
||||
true, "GIFT"))
|
||||
.ensure();
|
||||
}
|
||||
while (true) {
|
||||
|
|
|
|||
BIN
submodules/ton/tonlib-src/tonlib/tonlib/.GenericAccount.cpp.swp
Normal file
BIN
submodules/ton/tonlib-src/tonlib/tonlib/.GenericAccount.cpp.swp
Normal file
Binary file not shown.
|
|
@ -21,6 +21,45 @@
|
|||
#include "td/utils/JsonBuilder.h"
|
||||
|
||||
namespace tonlib {
|
||||
td::Result<ton::BlockIdExt> parse_block_id_ext(td::JsonObject &obj) {
|
||||
ton::WorkchainId zero_workchain_id;
|
||||
{
|
||||
TRY_RESULT(wc, td::get_json_object_int_field(obj, "workchain"));
|
||||
zero_workchain_id = wc;
|
||||
}
|
||||
ton::ShardId zero_shard_id; // uint64
|
||||
{
|
||||
TRY_RESULT(shard_id, td::get_json_object_long_field(obj, "shard"));
|
||||
zero_shard_id = static_cast<ton::ShardId>(shard_id);
|
||||
}
|
||||
ton::BlockSeqno zero_seqno;
|
||||
{
|
||||
TRY_RESULT(seqno, td::get_json_object_int_field(obj, "seqno"));
|
||||
zero_seqno = seqno;
|
||||
}
|
||||
|
||||
ton::RootHash zero_root_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "root_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::RootHash::size()) {
|
||||
return td::Status::Error("Invalid config (8)");
|
||||
}
|
||||
zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
ton::FileHash zero_file_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "file_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::FileHash::size()) {
|
||||
return td::Status::Error("Invalid config (9)");
|
||||
}
|
||||
zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
|
||||
return ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash),
|
||||
std::move(zero_file_hash));
|
||||
}
|
||||
td::Result<Config> Config::parse(std::string str) {
|
||||
TRY_RESULT(json, td::json_decode(str));
|
||||
if (json.type() != td::JsonValue::Type::Object) {
|
||||
|
|
@ -74,45 +113,13 @@ td::Result<Config> Config::parse(std::string str) {
|
|||
return td::Status::Error("Invalid config (7)");
|
||||
}
|
||||
TRY_RESULT(zero_state_obj, td::get_json_object_field(validator, "zero_state", td::JsonValue::Type::Object, false));
|
||||
auto &zero_state = zero_state_obj.get_object();
|
||||
|
||||
ton::WorkchainId zero_workchain_id;
|
||||
{
|
||||
TRY_RESULT(wc, td::get_json_object_int_field(zero_state, "workchain"));
|
||||
zero_workchain_id = wc;
|
||||
TRY_RESULT(zero_state_id, parse_block_id_ext(zero_state_obj.get_object()));
|
||||
res.zero_state_id = zero_state_id;
|
||||
auto r_init_block_obj = td::get_json_object_field(validator, "init_block", td::JsonValue::Type::Object, false);
|
||||
if (r_init_block_obj.is_ok()) {
|
||||
TRY_RESULT(init_block_id, parse_block_id_ext(r_init_block_obj.move_as_ok().get_object()));
|
||||
res.init_block_id = init_block_id;
|
||||
}
|
||||
ton::ShardId zero_shard_id; // uint64
|
||||
{
|
||||
TRY_RESULT(shard_id, td::get_json_object_long_field(zero_state, "shard"));
|
||||
zero_shard_id = static_cast<ton::ShardId>(shard_id);
|
||||
}
|
||||
ton::BlockSeqno zero_seqno;
|
||||
{
|
||||
TRY_RESULT(seqno, td::get_json_object_int_field(zero_state, "seqno"));
|
||||
zero_seqno = seqno;
|
||||
}
|
||||
|
||||
ton::RootHash zero_root_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "root_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::RootHash::size()) {
|
||||
return td::Status::Error("Invalid config (8)");
|
||||
}
|
||||
zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
ton::FileHash zero_file_hash;
|
||||
{
|
||||
TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "file_hash"));
|
||||
TRY_RESULT(hash, td::base64_decode(hash_b64));
|
||||
if (hash.size() * 8 != ton::FileHash::size()) {
|
||||
return td::Status::Error("Invalid config (9)");
|
||||
}
|
||||
zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin()));
|
||||
}
|
||||
|
||||
res.zero_state_id = ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash),
|
||||
std::move(zero_file_hash));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct Config {
|
|||
td::IPAddress address;
|
||||
};
|
||||
ton::BlockIdExt zero_state_id;
|
||||
ton::BlockIdExt init_block_id;
|
||||
std::vector<LiteClient> lite_clients;
|
||||
static td::Result<Config> parse(std::string str);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,27 +29,70 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state
|
|||
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
|
||||
}
|
||||
|
||||
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback)
|
||||
: state_(std::move(state)), callback_(std::move(callback)) {
|
||||
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback)
|
||||
: state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) {
|
||||
client_.set_client(client);
|
||||
if (!config_.init_block_id.is_valid()) {
|
||||
check_init_block_state_ = QueryState::Done;
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
|
||||
if (has_fatal_error()) {
|
||||
promise.set_error(fatal_error_.clone());
|
||||
return;
|
||||
}
|
||||
if (promises_.empty() && get_last_block_state_ == QueryState::Done) {
|
||||
get_last_block_state_ = QueryState::Empty;
|
||||
}
|
||||
promises_.push_back(std::move(promise));
|
||||
sync_loop();
|
||||
}
|
||||
|
||||
void LastBlock::sync_loop() {
|
||||
if (promises_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_zero_state(state_.zero_state_id);
|
||||
update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
|
||||
config_.zero_state_id.file_hash));
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Empty) {
|
||||
get_mc_info_state_ = QueryState::Active;
|
||||
client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
|
||||
[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
|
||||
}
|
||||
|
||||
if (get_last_block_state_ == QueryState::Empty) {
|
||||
get_last_block_state_ = QueryState::Active;
|
||||
total_sync_ = td::Timer();
|
||||
validate_ = td::Timer(true);
|
||||
queries_ = 0;
|
||||
LOG(INFO) << "Begin last block synchronization " << state_;
|
||||
do_get_last_block();
|
||||
}
|
||||
promises_.push_back(std::move(promise));
|
||||
|
||||
if (check_init_block_state_ == QueryState::Empty) {
|
||||
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
|
||||
check_init_block_state_ = QueryState::Active;
|
||||
// validate
|
||||
//total_sync_ = td::Timer();
|
||||
//validate_ = td::Timer(true);
|
||||
//queries_ = 0;
|
||||
LOG(INFO) << "Begin last block synchronization (check init_block)" << state_;
|
||||
do_check_init_block(state_.last_key_block_id);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done &&
|
||||
check_init_block_state_ == QueryState::Done) {
|
||||
on_sync_ok();
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::do_get_last_block() {
|
||||
//client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
|
||||
//[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
|
||||
//return;
|
||||
|
||||
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
queries_++;
|
||||
client_.send_query(
|
||||
|
|
@ -59,14 +102,19 @@ void LastBlock::do_get_last_block() {
|
|||
});
|
||||
}
|
||||
|
||||
td::Result<bool> LastBlock::process_block_proof(
|
||||
void LastBlock::do_check_init_block(ton::BlockIdExt from) {
|
||||
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
//queries_++;
|
||||
client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from),
|
||||
create_tl_lite_block_id(config_.init_block_id)),
|
||||
[this, from = state_.last_key_block_id](auto r_block_proof) {
|
||||
this->on_init_block_proof(from, std::move(r_block_proof));
|
||||
});
|
||||
}
|
||||
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
validate_.resume();
|
||||
SCOPE_EXIT {
|
||||
validate_.pause();
|
||||
};
|
||||
|
||||
TRY_RESULT(block_proof, std::move(r_block_proof));
|
||||
LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
|
||||
TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof)));
|
||||
|
|
@ -86,51 +134,82 @@ td::Result<bool> LastBlock::process_block_proof(
|
|||
if (is_changed) {
|
||||
callback_->on_state_changed(state_);
|
||||
}
|
||||
return chain->complete;
|
||||
return chain;
|
||||
}
|
||||
|
||||
void LastBlock::on_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
auto r_is_ready = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
bool is_ready;
|
||||
if (r_is_ready.is_error()) {
|
||||
LOG(WARNING) << "Error during last block synchronization " << r_is_ready.error();
|
||||
if (r_chain.is_error()) {
|
||||
LOG(WARNING) << "Error during last block synchronization " << r_chain.error();
|
||||
if (config_.init_block_id.is_valid()) {
|
||||
if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) {
|
||||
on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: "
|
||||
<< r_chain.move_as_error()));
|
||||
}
|
||||
}
|
||||
is_ready = true;
|
||||
} else {
|
||||
is_ready = r_is_ready.move_as_ok();
|
||||
is_ready = r_chain.ok()->complete;
|
||||
}
|
||||
if (is_ready) {
|
||||
LOG(INFO) << "End last block synchronization " << state_ << "\n"
|
||||
<< " net queries: " << queries_ << "\n"
|
||||
<< " total: " << total_sync_ << " validation: " << validate_;
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
get_last_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
do_get_last_block();
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
|
||||
validate_.resume();
|
||||
auto r_chain = process_block_proof(from, std::move(r_block_proof));
|
||||
validate_.pause();
|
||||
if (r_chain.is_error()) {
|
||||
check_init_block_state_ = QueryState::Empty;
|
||||
on_sync_error(
|
||||
td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error()));
|
||||
return;
|
||||
}
|
||||
auto chain = r_chain.move_as_ok();
|
||||
if (chain->complete) {
|
||||
LOG(INFO) << "End last block synchronization " << state_ << "\n"
|
||||
<< " net queries: " << queries_ << "\n"
|
||||
<< " total: " << total_sync_ << " validation: " << validate_;
|
||||
get_last_block_state_ = QueryState::Done;
|
||||
sync_loop();
|
||||
} else {
|
||||
do_check_init_block(chain->to);
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_masterchain_info(
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info) {
|
||||
if (r_info.is_ok()) {
|
||||
auto info = r_info.move_as_ok();
|
||||
update_zero_state(create_zero_state_id(info->init_));
|
||||
update_mc_last_block(create_block_id(info->last_));
|
||||
get_mc_info_state_ = QueryState::Done;
|
||||
} else {
|
||||
get_mc_info_state_ = QueryState::Empty;
|
||||
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
|
||||
on_sync_error(r_info.move_as_error());
|
||||
}
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
sync_loop();
|
||||
}
|
||||
|
||||
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
||||
if (has_fatal_error()) {
|
||||
return;
|
||||
}
|
||||
if (!zero_state_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid zero state update";
|
||||
return;
|
||||
|
|
@ -142,17 +221,18 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (state_.zero_state_id == state_.zero_state_id) {
|
||||
if (state_.zero_state_id == zero_state_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Masterchain zerostate mismatch: expected: " << state_.zero_state_id.to_str() << ", found "
|
||||
<< zero_state_id.to_str();
|
||||
// TODO: all other updates will be inconsitent.
|
||||
// One will have to restart ton client
|
||||
on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: "
|
||||
<< state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str()));
|
||||
}
|
||||
|
||||
bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
|
||||
if (has_fatal_error()) {
|
||||
return false;
|
||||
}
|
||||
if (!mc_block_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid masterchain block";
|
||||
return false;
|
||||
|
|
@ -166,6 +246,9 @@ bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
|
|||
}
|
||||
|
||||
bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
|
||||
if (has_fatal_error()) {
|
||||
return false;
|
||||
}
|
||||
if (!mc_key_block_id.is_valid()) {
|
||||
LOG(ERROR) << "Ignore invalid masterchain block";
|
||||
return false;
|
||||
|
|
@ -183,4 +266,26 @@ void LastBlock::update_utime(td::int64 utime) {
|
|||
state_.utime = utime;
|
||||
}
|
||||
}
|
||||
|
||||
void LastBlock::on_sync_ok() {
|
||||
for (auto& promise : promises_) {
|
||||
auto state = state_;
|
||||
promise.set_value(std::move(state));
|
||||
}
|
||||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_sync_error(td::Status status) {
|
||||
for (auto& promise : promises_) {
|
||||
promise.set_error(status.clone());
|
||||
}
|
||||
promises_.clear();
|
||||
}
|
||||
void LastBlock::on_fatal_error(td::Status status) {
|
||||
fatal_error_ = std::move(status);
|
||||
on_sync_error(fatal_error_.clone());
|
||||
}
|
||||
|
||||
bool LastBlock::has_fatal_error() const {
|
||||
return fatal_error_.is_error();
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
|||
|
|
@ -19,8 +19,12 @@
|
|||
#pragma once
|
||||
#include "td/actor/actor.h"
|
||||
|
||||
#include "tonlib/Config.h"
|
||||
#include "tonlib/ExtClient.h"
|
||||
|
||||
namespace block {
|
||||
struct BlockProofChain;
|
||||
}
|
||||
namespace tonlib {
|
||||
td::StringBuilder &operator<<(td::StringBuilder &sb, const LastBlockState &state);
|
||||
template <unsigned int N, class StorerT>
|
||||
|
|
@ -116,14 +120,22 @@ class LastBlock : public td::actor::Actor {
|
|||
virtual void on_state_changed(LastBlockState state) = 0;
|
||||
};
|
||||
|
||||
explicit LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback);
|
||||
explicit LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback);
|
||||
void get_last_block(td::Promise<LastBlockState> promise);
|
||||
|
||||
private:
|
||||
ExtClient client_;
|
||||
LastBlockState state_;
|
||||
Config config_;
|
||||
td::unique_ptr<Callback> callback_;
|
||||
|
||||
td::Status fatal_error_;
|
||||
|
||||
enum class QueryState { Empty, Active, Done };
|
||||
QueryState get_mc_info_state_{QueryState::Empty};
|
||||
QueryState get_last_block_state_{QueryState::Empty};
|
||||
QueryState check_init_block_state_{QueryState::Empty};
|
||||
|
||||
// stats
|
||||
td::Timer total_sync_;
|
||||
td::Timer validate_;
|
||||
|
|
@ -131,11 +143,15 @@ class LastBlock : public td::actor::Actor {
|
|||
|
||||
std::vector<td::Promise<LastBlockState>> promises_;
|
||||
|
||||
void do_get_last_block();
|
||||
void do_check_init_block(ton::BlockIdExt from);
|
||||
void on_init_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
|
||||
void do_get_last_block();
|
||||
void on_block_proof(ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
td::Result<bool> process_block_proof(
|
||||
td::Result<std::unique_ptr<block::BlockProofChain>> process_block_proof(
|
||||
ton::BlockIdExt from,
|
||||
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
|
||||
|
||||
|
|
@ -144,5 +160,12 @@ class LastBlock : public td::actor::Actor {
|
|||
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
|
||||
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);
|
||||
void update_utime(td::int64 utime);
|
||||
|
||||
void on_sync_ok();
|
||||
void on_sync_error(td::Status status);
|
||||
void on_fatal_error(td::Status status);
|
||||
bool has_fatal_error() const;
|
||||
|
||||
void sync_loop();
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
|||
|
|
@ -306,44 +306,54 @@ void TonlibClient::init_ext_client() {
|
|||
private:
|
||||
td::actor::ActorShared<> parent_;
|
||||
};
|
||||
ext_client_outbound_ = {};
|
||||
ref_cnt_++;
|
||||
raw_client_ = ExtClientLazy::create(lite_client.adnl_id, lite_client.address,
|
||||
td::make_unique<Callback>(td::actor::actor_shared()));
|
||||
}
|
||||
}
|
||||
|
||||
void TonlibClient::update_last_block_state(LastBlockState state) {
|
||||
last_block_storage_.save_state("none", state);
|
||||
void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 config_generation) {
|
||||
if (config_generation == config_generation_) {
|
||||
last_block_storage_.save_state(blockchain_name_, state);
|
||||
}
|
||||
}
|
||||
|
||||
void TonlibClient::init_last_block() {
|
||||
ref_cnt_++;
|
||||
class Callback : public LastBlock::Callback {
|
||||
public:
|
||||
Callback(td::actor::ActorShared<TonlibClient> client) : client_(std::move(client)) {
|
||||
Callback(td::actor::ActorShared<TonlibClient> client, td::uint32 config_generation)
|
||||
: client_(std::move(client)), config_generation_(config_generation) {
|
||||
}
|
||||
void on_state_changed(LastBlockState state) override {
|
||||
send_closure(client_, &TonlibClient::update_last_block_state, std::move(state));
|
||||
send_closure(client_, &TonlibClient::update_last_block_state, std::move(state), config_generation_);
|
||||
}
|
||||
|
||||
private:
|
||||
td::actor::ActorShared<TonlibClient> client_;
|
||||
td::uint32 config_generation_;
|
||||
};
|
||||
LastBlockState state;
|
||||
|
||||
auto r_state = last_block_storage_.get_state("none");
|
||||
if (r_state.is_error()) {
|
||||
LOG(WARNING) << "Unknown LastBlockState: " << r_state.error();
|
||||
td::Result<LastBlockState> r_state;
|
||||
if (!ignore_cache_) {
|
||||
r_state = last_block_storage_.get_state(blockchain_name_);
|
||||
}
|
||||
if (ignore_cache_ || r_state.is_error()) {
|
||||
LOG_IF(WARNING, !ignore_cache_) << "Unknown LastBlockState: " << r_state.error();
|
||||
state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
|
||||
config_.zero_state_id.file_hash),
|
||||
state.last_block_id = config_.zero_state_id;
|
||||
state.last_key_block_id = config_.zero_state_id;
|
||||
last_block_storage_.save_state(blockchain_name_, state);
|
||||
} else {
|
||||
state = r_state.move_as_ok();
|
||||
}
|
||||
|
||||
raw_last_block_ = td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state),
|
||||
td::make_unique<Callback>(td::actor::actor_shared(this)));
|
||||
raw_last_block_ =
|
||||
td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state), config_,
|
||||
td::make_unique<Callback>(td::actor::actor_shared(this), config_generation_));
|
||||
client_.set_client(get_client_ref());
|
||||
}
|
||||
|
||||
|
|
@ -471,7 +481,7 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::testWallet_getAccountAddress& request) {
|
||||
|
|
@ -479,7 +489,7 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::wallet_getAccountAddress& request) {
|
||||
|
|
@ -487,11 +497,11 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
|||
if (r_account_address.is_error()) {
|
||||
return status_to_tonlib_api(r_account_address.error());
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
|
||||
}
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
|
||||
const tonlib_api::testGiver_getAccountAddress& request) {
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize());
|
||||
return tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize(true));
|
||||
}
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(tonlib_api::getBip39Hints& request) {
|
||||
|
|
@ -509,8 +519,7 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
|
|||
}
|
||||
TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_));
|
||||
TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_));
|
||||
use_callbacks_for_network_ = request.options_->use_callbacks_for_network_;
|
||||
if (!request.options_->config_.empty()) {
|
||||
if (request.options_->config_) {
|
||||
TRY_STATUS(set_config(std::move(request.options_->config_)));
|
||||
}
|
||||
state_ = State::Running;
|
||||
|
|
@ -518,15 +527,26 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::set_config(std::string config) {
|
||||
if (config.empty()) {
|
||||
return td::Status::Error("config is empty");
|
||||
td::Status TonlibClient::set_config(object_ptr<tonlib_api::config> config) {
|
||||
if (!config) {
|
||||
return td::Status::Error(400, "config is empty");
|
||||
}
|
||||
TRY_RESULT(new_config, Config::parse(std::move(config)));
|
||||
if (new_config.lite_clients.empty()) {
|
||||
if (config->config_.empty()) {
|
||||
return td::Status::Error(400, "config is empty");
|
||||
}
|
||||
TRY_RESULT(new_config, Config::parse(std::move(config->config_)));
|
||||
if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) {
|
||||
return td::Status::Error("No lite clients in config");
|
||||
}
|
||||
config_ = std::move(new_config);
|
||||
config_generation_++;
|
||||
if (config->blockchain_name_.empty()) {
|
||||
blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16);
|
||||
} else {
|
||||
blockchain_name_ = config->blockchain_name_;
|
||||
}
|
||||
use_callbacks_for_network_ = config->use_callbacks_for_network_;
|
||||
ignore_cache_ = config->ignore_cache_;
|
||||
init_ext_client();
|
||||
init_last_block();
|
||||
return td::Status::OK();
|
||||
|
|
@ -540,9 +560,9 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request,
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::options_setConfig& request,
|
||||
td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
TRY_STATUS(set_config(request.config_));
|
||||
TRY_STATUS(set_config(std::move(request.config_)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
|
@ -587,7 +607,7 @@ td::Result<std::string> to_std_address_or_throw(td::Ref<vm::CellSlice> cs) {
|
|||
if (!tlb::csr_unpack(cs, addr)) {
|
||||
return td::Status::Error("Failed to unpack MsgAddressInt");
|
||||
}
|
||||
return block::StdAddress(addr.workchain_id, addr.address).rserialize();
|
||||
return block::StdAddress(addr.workchain_id, addr.address).rserialize(true);
|
||||
}
|
||||
|
||||
td::Result<std::string> to_std_address(td::Ref<vm::CellSlice> cs) {
|
||||
|
|
@ -891,14 +911,14 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request,
|
|||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto init_message = TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)));
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()),
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)),
|
||||
vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(),
|
||||
vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
|
|
@ -920,15 +940,23 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ
|
|||
TRY_RESULT(public_key, private_key.get_public_key());
|
||||
init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str();
|
||||
}
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise)](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(0));
|
||||
}
|
||||
};
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), std::move(init_state),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)), std::move(init_state),
|
||||
vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request,
|
||||
|
|
@ -961,14 +989,14 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request,
|
|||
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
|
||||
auto init_message = Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)));
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()),
|
||||
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)),
|
||||
vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(),
|
||||
vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
|
|
@ -991,15 +1019,23 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request,
|
|||
TRY_RESULT(public_key, private_key.get_public_key());
|
||||
init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str();
|
||||
}
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise), valid_until](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(valid_until));
|
||||
}
|
||||
};
|
||||
return do_request(
|
||||
tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), std::move(init_state),
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize(true)), std::move(init_state),
|
||||
vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request,
|
||||
|
|
@ -1022,7 +1058,7 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request,
|
|||
|
||||
// TestGiver
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (!request.destination_) {
|
||||
return td::Status::Error(400, "Field destination must not be empty");
|
||||
}
|
||||
|
|
@ -1031,14 +1067,22 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque
|
|||
}
|
||||
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
|
||||
account_address.bounceable = false;
|
||||
td::Promise<object_ptr<tonlib_api::ok>> new_promise =
|
||||
[promise = std::move(promise)](td::Result<object_ptr<tonlib_api::ok>> res) mutable {
|
||||
if (res.is_error()) {
|
||||
promise.set_error(res.move_as_error());
|
||||
} else {
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::sendGramsResult>(0));
|
||||
}
|
||||
};
|
||||
return do_request(tonlib_api::raw_sendMessage(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()), "",
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize(true)), "",
|
||||
vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_,
|
||||
request.message_, account_address))
|
||||
.move_as_ok()
|
||||
.as_slice()
|
||||
.str()),
|
||||
std::move(promise));
|
||||
std::move(new_promise));
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request,
|
||||
|
|
@ -1095,13 +1139,13 @@ class TonlibQueryActor : public td::actor::Actor {
|
|||
class GenericSendGrams : public TonlibQueryActor {
|
||||
public:
|
||||
GenericSendGrams(td::actor::ActorShared<TonlibClient> client, tonlib_api::generic_sendGrams send_grams,
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::ok>>&& promise)
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::sendGramsResult>>&& promise)
|
||||
: TonlibQueryActor(std::move(client)), send_grams_(std::move(send_grams)), promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
private:
|
||||
tonlib_api::generic_sendGrams send_grams_;
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::ok>> promise_;
|
||||
td::Promise<tonlib_api::object_ptr<tonlib_api::sendGramsResult>> promise_;
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::generic_AccountState> source_state_;
|
||||
block::StdAddress source_address_;
|
||||
|
|
@ -1193,9 +1237,9 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
td::Status do_on_destination_state(td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> r_state) {
|
||||
TRY_RESULT(state, std::move(r_state));
|
||||
destination_state_ = std::move(state);
|
||||
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID) {
|
||||
//FIXME
|
||||
//return td::Status::Error("Transfer to uninited wallet");
|
||||
if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ &&
|
||||
!send_grams_.allow_send_to_uninited_) {
|
||||
return td::Status::Error(400, "DANGEROUS_TRANSACTION: Transfer to uninited wallet");
|
||||
}
|
||||
return do_loop();
|
||||
}
|
||||
|
|
@ -1227,7 +1271,10 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
[&](tonlib_api::generic_accountStateWallet& test_wallet_state) {
|
||||
send_query(tonlib_api::wallet_sendGrams(
|
||||
std::move(send_grams_.private_key_), std::move(send_grams_.destination_),
|
||||
test_wallet_state.account_state_->seqno_, std::numeric_limits<td::uint32>::max(),
|
||||
test_wallet_state.account_state_->seqno_,
|
||||
send_grams_.timeout_ == 0
|
||||
? 60 + test_wallet_state.account_state_->sync_utime_
|
||||
: send_grams_.timeout_ + test_wallet_state.account_state_->sync_utime_,
|
||||
send_grams_.amount_, std::move(send_grams_.message_)),
|
||||
std::move(promise_));
|
||||
stop();
|
||||
|
|
@ -1245,7 +1292,10 @@ class GenericSendGrams : public TonlibQueryActor {
|
|||
};
|
||||
|
||||
td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise) {
|
||||
if (request.timeout_ < 0 || request.timeout_ > 300) {
|
||||
return td::Status::Error(400, "Invalid timeout: must be between 0 and 300");
|
||||
}
|
||||
auto id = actor_id_++;
|
||||
actors_[id] = td::actor::create_actor<GenericSendGrams>("GenericSendGrams", actor_shared(this, id),
|
||||
std::move(request), std::move(promise));
|
||||
|
|
@ -1257,7 +1307,7 @@ td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request,
|
|||
TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
|
||||
std::move(request.random_extra_seed_)));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
|
@ -1294,7 +1344,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importKey& request,
|
|||
TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_),
|
||||
KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
|
@ -1323,7 +1373,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request,
|
|||
TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_),
|
||||
KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
|
@ -1347,7 +1397,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques
|
|||
std::move(request.local_password_), std::move(request.key_password_),
|
||||
KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)}));
|
||||
TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice()));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(), std::move(key.secret)));
|
||||
promise.set_value(tonlib_api::make_object<tonlib_api::key>(key_bytes.serialize(true), std::move(key.secret)));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ class TonlibClient : public td::actor::Actor {
|
|||
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
|
||||
td::unique_ptr<TonlibCallback> callback_;
|
||||
Config config_;
|
||||
td::uint32 config_generation_{0};
|
||||
std::string blockchain_name_;
|
||||
bool ignore_cache_{false};
|
||||
|
||||
bool use_callbacks_for_network_{false};
|
||||
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
|
||||
|
|
@ -85,7 +88,7 @@ class TonlibClient : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void update_last_block_state(LastBlockState state);
|
||||
void update_last_block_state(LastBlockState state, td::uint32 config_generation_);
|
||||
void on_result(td::uint64 id, object_ptr<tonlib_api::Object> response);
|
||||
static bool is_static_request(td::int32 id);
|
||||
static bool is_uninited_request(td::int32 id);
|
||||
|
|
@ -114,12 +117,10 @@ class TonlibClient : public td::actor::Actor {
|
|||
return td::Status::Error(400, "Function is unsupported");
|
||||
}
|
||||
|
||||
td::Status set_config(std::string config);
|
||||
|
||||
td::Status set_config(object_ptr<tonlib_api::config> config);
|
||||
td::Status do_request(const tonlib_api::init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::close& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::options_setConfig& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::options_setConfig& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::raw_getAccountState& request,
|
||||
|
|
@ -129,23 +130,25 @@ class TonlibClient : public td::actor::Actor {
|
|||
|
||||
td::Status do_request(const tonlib_api::testWallet_init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::testWallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
td::Status do_request(tonlib_api::testWallet_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::testWallet_accountState>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::wallet_init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::wallet_sendGrams& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(const tonlib_api::wallet_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
td::Status do_request(tonlib_api::wallet_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::wallet_accountState>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::testGiver_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::testGiver_accountState>>&& promise);
|
||||
td::Status do_request(const tonlib_api::testGiver_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::generic_getAccountState& request,
|
||||
td::Promise<object_ptr<tonlib_api::generic_AccountState>>&& promise);
|
||||
td::Status do_request(tonlib_api::generic_sendGrams& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
|
||||
td::Status do_request(tonlib_api::generic_sendGrams& request,
|
||||
td::Promise<object_ptr<tonlib_api::sendGramsResult>>&& promise);
|
||||
|
||||
td::Status do_request(const tonlib_api::createNewKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
|
||||
td::Status do_request(const tonlib_api::exportKey& request,
|
||||
|
|
|
|||
|
|
@ -25,9 +25,14 @@ class TonlibCli : public td::actor::Actor {
|
|||
struct Options {
|
||||
bool enable_readline{true};
|
||||
std::string config;
|
||||
std::string name;
|
||||
std::string key_dir{"."};
|
||||
bool use_callbacks_for_network{false};
|
||||
bool use_simple_wallet{false};
|
||||
bool ignore_cache{false};
|
||||
|
||||
bool one_shot{false};
|
||||
std::string cmd;
|
||||
};
|
||||
TonlibCli(Options options) : options_(std::move(options)) {
|
||||
}
|
||||
|
|
@ -65,8 +70,10 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::actor::ActorShared<TonlibCli> id_;
|
||||
};
|
||||
ref_cnt_++;
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique<Cb>(actor_shared(this)));
|
||||
td::actor::send_closure(io_, &td::TerminalIO::set_log_interface);
|
||||
if (!options_.one_shot) {
|
||||
io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique<Cb>(actor_shared(this)));
|
||||
td::actor::send_closure(io_, &td::TerminalIO::set_log_interface);
|
||||
}
|
||||
|
||||
class TonlibCb : public tonlib::TonlibCallback {
|
||||
public:
|
||||
|
|
@ -109,13 +116,20 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(options_.config, options_.key_dir,
|
||||
options_.use_callbacks_for_network)),
|
||||
auto config = !options_.config.empty()
|
||||
? make_object<tonlib_api::config>(options_.config, options_.name,
|
||||
options_.use_callbacks_for_network, options_.ignore_cache)
|
||||
: nullptr;
|
||||
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(std::move(config), options_.key_dir)),
|
||||
[](auto r_ok) {
|
||||
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error();
|
||||
td::TerminalIO::out() << "Tonlib is inited\n";
|
||||
});
|
||||
if (options_.one_shot) {
|
||||
td::actor::send_closure(actor_id(this), &TonlibCli::parse_line, td::BufferSlice(options_.cmd));
|
||||
}
|
||||
}
|
||||
|
||||
void hangup_shared() override {
|
||||
CHECK(ref_cnt_ > 0);
|
||||
ref_cnt_--;
|
||||
|
|
@ -130,6 +144,26 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::actor::SchedulerContext::get()->stop();
|
||||
}
|
||||
|
||||
void on_wait() {
|
||||
if (options_.one_shot) {
|
||||
LOG(ERROR) << "FAILED (not enough data)";
|
||||
std::_Exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
void on_error() {
|
||||
if (options_.one_shot) {
|
||||
LOG(ERROR) << "FAILED";
|
||||
std::_Exit(1);
|
||||
}
|
||||
}
|
||||
void on_ok() {
|
||||
if (options_.one_shot) {
|
||||
LOG(INFO) << "OK";
|
||||
std::_Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void parse_line(td::BufferSlice line) {
|
||||
if (is_closing_) {
|
||||
return;
|
||||
|
|
@ -148,13 +182,14 @@ class TonlibCli : public td::actor::Actor {
|
|||
td::TerminalIO::out() << "help - show this help\n";
|
||||
td::TerminalIO::out() << "genkey - generate new secret key\n";
|
||||
td::TerminalIO::out() << "keys - show all stored keys\n";
|
||||
td::TerminalIO::out() << "exportkey [key_id] - export key\n";
|
||||
td::TerminalIO::out() << "setconfig <path> - set lite server config\n";
|
||||
td::TerminalIO::out() << "importkey - import key\n";
|
||||
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
|
||||
td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\n";
|
||||
td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n";
|
||||
td::TerminalIO::out()
|
||||
<< "gethistory <key_id> - get history fo simple wallet with requested key (last 10 transactions)\n";
|
||||
td::TerminalIO::out() << "init <key_id> - init simple wallet with requested key\n";
|
||||
td::TerminalIO::out() << "transfer <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from "
|
||||
td::TerminalIO::out() << "transfer[f] <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from "
|
||||
"<from_key_id> to <to_key_id>.\n"
|
||||
<< "\t<from_key_id> could also be 'giver'\n"
|
||||
<< "\t<to_key_id> could also be 'giver' or smartcontract address\n";
|
||||
|
|
@ -174,18 +209,32 @@ class TonlibCli : public td::actor::Actor {
|
|||
} else if (cmd == "importkey") {
|
||||
import_key(parser.read_all());
|
||||
} else if (cmd == "setconfig") {
|
||||
set_config(parser.read_word());
|
||||
auto config = parser.read_word();
|
||||
auto name = parser.read_word();
|
||||
auto to_bool = [](td::Slice word) {
|
||||
if (word.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (word == "0" || word == "FALSE" || word == "false") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto use_callback = parser.read_word();
|
||||
auto force = parser.read_word();
|
||||
set_config(config, name, to_bool(use_callback), to_bool(force));
|
||||
} else if (cmd == "getstate") {
|
||||
get_state(parser.read_word());
|
||||
} else if (cmd == "gethistory") {
|
||||
get_history(parser.read_word());
|
||||
} else if (cmd == "init") {
|
||||
init_simple_wallet(parser.read_word());
|
||||
} else if (cmd == "transfer") {
|
||||
} else if (cmd == "transfer" || cmd == "transferf") {
|
||||
auto from = parser.read_word();
|
||||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
transfer(from, to, grams);
|
||||
auto message = parser.read_word();
|
||||
transfer(from, to, grams, message, cmd == "transferf");
|
||||
} else if (cmd == "hint") {
|
||||
get_hints(parser.read_word());
|
||||
}
|
||||
|
|
@ -282,7 +331,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
info.public_key = key->public_key_;
|
||||
info.secret = std::move(key->secret_);
|
||||
keys_.push_back(std::move(info));
|
||||
//export_key(key->public_key_, keys_.size() - 1, std::move(password));
|
||||
export_key(key->public_key_, keys_.size() - 1, std::move(password));
|
||||
store_keys();
|
||||
});
|
||||
}
|
||||
|
|
@ -305,7 +354,13 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto db = r_db.move_as_ok();
|
||||
td::ConstParser parser(db.as_slice());
|
||||
while (true) {
|
||||
auto public_key = parser.read_word();
|
||||
auto public_key = parser.read_word().str();
|
||||
{
|
||||
auto tmp = td::base64_decode(public_key);
|
||||
if (tmp.is_ok()) {
|
||||
public_key = td::base64url_encode(tmp.move_as_ok());
|
||||
}
|
||||
}
|
||||
auto secret_b64 = parser.read_word();
|
||||
if (secret_b64.empty()) {
|
||||
break;
|
||||
|
|
@ -316,7 +371,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
|
||||
KeyInfo info;
|
||||
info.public_key = public_key.str();
|
||||
info.public_key = public_key;
|
||||
info.secret = r_secret.move_as_ok();
|
||||
LOG(INFO) << info.public_key;
|
||||
|
||||
|
|
@ -324,13 +379,16 @@ class TonlibCli : public td::actor::Actor {
|
|||
}
|
||||
}
|
||||
|
||||
void dump_key(size_t i) {
|
||||
td::TerminalIO::out() << " #" << i << ": Public key: " << keys_[i].public_key << " "
|
||||
<< " Address: "
|
||||
<< to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_ << "\n";
|
||||
}
|
||||
void dump_keys() {
|
||||
td::TerminalIO::out() << "Got " << keys_.size() << " keys"
|
||||
<< "\n";
|
||||
for (size_t i = 0; i < keys_.size(); i++) {
|
||||
td::TerminalIO::out() << " #" << i << ": " << keys_[i].public_key << "\n";
|
||||
td::TerminalIO::out() << " " << to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_
|
||||
<< "\n";
|
||||
dump_key(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -447,12 +505,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
send_query(make_object<tonlib_api::exportKey>(make_object<tonlib_api::inputKey>(
|
||||
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
|
||||
td::SecureString(password))),
|
||||
[key = std::move(key)](auto r_res) {
|
||||
[this, key = std::move(key), key_i](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
dump_key(key_i);
|
||||
for (auto& word : r_res.ok()->word_list_) {
|
||||
td::TerminalIO::out() << " " << word.as_slice() << "\n";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -496,7 +557,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
});
|
||||
}
|
||||
|
||||
void set_config(td::Slice path) {
|
||||
void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) {
|
||||
auto r_data = td::read_file_str(path.str());
|
||||
if (r_data.is_error()) {
|
||||
td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n";
|
||||
|
|
@ -505,13 +566,15 @@ class TonlibCli : public td::actor::Actor {
|
|||
|
||||
auto data = r_data.move_as_ok();
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::options_setConfig>(data), [](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
send_query(make_object<tonlib_api::options_setConfig>(
|
||||
make_object<tonlib_api::config>(std::move(data), name.str(), use_callback, ignore_cache)),
|
||||
[](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
}
|
||||
|
||||
void get_state(td::Slice key) {
|
||||
|
|
@ -519,23 +582,27 @@ class TonlibCli : public td::actor::Actor {
|
|||
dump_keys();
|
||||
td::TerminalIO::out() << "Choose public key (hex prefix or #N)";
|
||||
cont_ = [this](td::Slice key) { this->get_state(key); };
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
auto r_address = to_account_address(key, false);
|
||||
if (r_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << key << "]\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
auto address = r_address.move_as_ok();
|
||||
using tonlib_api::make_object;
|
||||
send_query(make_object<tonlib_api::generic_getAccountState>(
|
||||
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address))),
|
||||
[](auto r_res) {
|
||||
[this](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
on_ok();
|
||||
});
|
||||
}
|
||||
void get_history(td::Slice key) {
|
||||
|
|
@ -586,54 +653,76 @@ class TonlibCli : public td::actor::Actor {
|
|||
});
|
||||
}
|
||||
|
||||
void transfer(td::Slice from, td::Slice to, td::Slice grams) {
|
||||
void transfer(td::Slice from, td::Slice to, td::Slice grams, td::Slice message, bool allow_send_to_uninited) {
|
||||
auto r_from_address = to_account_address(from, true);
|
||||
if (r_from_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << from << "] : " << r_from_address.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
auto r_to_address = to_account_address(to, false);
|
||||
if (r_to_address.is_error()) {
|
||||
td::TerminalIO::out() << "Unknown key id: [" << to << "] : " << r_to_address.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
auto r_grams = td::to_integer_safe<td::uint64>(grams);
|
||||
if (r_grams.is_error()) {
|
||||
td::TerminalIO::out() << "Invalid grams amount: [" << grams << "]\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
if (from != "giver") {
|
||||
if (options_.one_shot) {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", "",
|
||||
allow_send_to_uninited);
|
||||
return;
|
||||
}
|
||||
if (from != "giver" && message.empty()) {
|
||||
td::TerminalIO::out() << "Enter password (could be empty)";
|
||||
cont_ = [this, from = r_from_address.move_as_ok(), to = r_to_address.move_as_ok(), grams = r_grams.move_as_ok()](
|
||||
td::Slice password) mutable { this->transfer(std::move(from), std::move(to), grams, password); };
|
||||
cont_ = [this, from = r_from_address.move_as_ok(), to = r_to_address.move_as_ok(), grams = r_grams.move_as_ok(),
|
||||
allow_send_to_uninited](td::Slice password) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, allow_send_to_uninited);
|
||||
};
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "");
|
||||
if (message.empty()) {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "",
|
||||
allow_send_to_uninited);
|
||||
} else {
|
||||
transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", message,
|
||||
allow_send_to_uninited);
|
||||
}
|
||||
}
|
||||
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password) {
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, bool allow_send_to_uninited) {
|
||||
td::TerminalIO::out() << "Enter message (could be empty)";
|
||||
cont_ = [this, from = std::move(from), to = std::move(to), grams,
|
||||
password = password.str()](td::Slice message) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, message);
|
||||
cont_ = [this, from = std::move(from), to = std::move(to), grams, password = password.str(),
|
||||
allow_send_to_uninited](td::Slice message) mutable {
|
||||
this->transfer(std::move(from), std::move(to), grams, password, message, allow_send_to_uninited);
|
||||
};
|
||||
on_wait();
|
||||
return;
|
||||
}
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message) {
|
||||
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message,
|
||||
bool allow_send_to_uninited) {
|
||||
using tonlib_api::make_object;
|
||||
auto key = !from.secret.empty()
|
||||
? make_object<tonlib_api::inputKey>(
|
||||
make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password))
|
||||
: nullptr;
|
||||
send_query(make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address),
|
||||
std::move(to.address), grams, message.str()),
|
||||
[](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
});
|
||||
send_query(
|
||||
make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address), std::move(to.address),
|
||||
grams, 30, allow_send_to_uninited, message.str()),
|
||||
[this](auto r_res) {
|
||||
if (r_res.is_error()) {
|
||||
td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n";
|
||||
on_error();
|
||||
return;
|
||||
}
|
||||
td::TerminalIO::out() << to_string(r_res.ok());
|
||||
on_ok();
|
||||
});
|
||||
}
|
||||
|
||||
void init_simple_wallet(td::Slice key) {
|
||||
|
|
@ -719,17 +808,28 @@ int main(int argc, char* argv[]) {
|
|||
options.key_dir = arg.str();
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('E', "execute", "execute one command", [&](td::Slice arg) {
|
||||
options.one_shot = true;
|
||||
options.cmd = arg.str();
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
|
||||
auto verbosity = td::to_integer<int>(arg);
|
||||
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);
|
||||
return (verbosity >= 0 && verbosity <= 20) ? td::Status::OK() : td::Status::Error("verbosity must be 0..20");
|
||||
});
|
||||
p.add_option('C', "config", "set lite server config", [&](td::Slice arg) {
|
||||
p.add_option('C', "config-force", "set lite server config, drop config related blockchain cache", [&](td::Slice arg) {
|
||||
TRY_RESULT(data, td::read_file_str(arg.str()));
|
||||
options.config = std::move(data);
|
||||
options.ignore_cache = true;
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('c', "config", "set lite server config", [&](td::Slice arg) {
|
||||
TRY_RESULT(data, td::read_file_str(arg.str()));
|
||||
options.config = std::move(data);
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('c', "use-callbacks-for-network", "do not use this", [&]() {
|
||||
p.add_option('n', "use-callbacks-for-network", "do not use this", [&]() {
|
||||
options.use_callbacks_for_network = true;
|
||||
return td::Status::OK();
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue