Herman Schoenfeld преди 7 години
родител
ревизия
38af7e2fd4
променени са 35 файла, в които са добавени 2166 реда и са изтрити 289 реда
  1. 28 0
      src/core.utils/UCoreUtils.pas
  2. 1 9
      src/gui/UCTRLWallet.lfm
  3. 11 38
      src/gui/UCTRLWallet.pas
  4. 46 23
      src/gui/wizards/UWIZModels.pas
  5. 476 0
      src/gui/wizards/operations/UWIZChangeKey.pas
  6. 50 0
      src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.lfm
  7. 144 0
      src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.pas
  8. 48 0
      src/gui/wizards/operations/UWIZChangeKey_Confirmation.lfm
  9. 178 0
      src/gui/wizards/operations/UWIZChangeKey_Confirmation.pas
  10. 53 0
      src/gui/wizards/operations/UWIZChangeKey_EnterKey.lfm
  11. 97 0
      src/gui/wizards/operations/UWIZChangeKey_EnterKey.pas
  12. 65 0
      src/gui/wizards/operations/UWIZChangeKey_SelectKey.lfm
  13. 163 0
      src/gui/wizards/operations/UWIZChangeKey_SelectKey.pas
  14. 63 0
      src/gui/wizards/operations/UWIZChangeKey_SelectOption.lfm
  15. 63 0
      src/gui/wizards/operations/UWIZChangeKey_SelectOption.pas
  16. 453 0
      src/gui/wizards/operations/UWIZConfirmAccount.pas
  17. 1 1
      src/gui/wizards/operations/UWIZEnlistAccountForSale_Start.lfm
  18. 5 5
      src/gui/wizards/operations/UWIZFeeOverride.lfm
  19. 5 7
      src/gui/wizards/operations/UWIZFeeOverride.pas
  20. 2 3
      src/gui/wizards/operations/UWIZPayloadContentOverride.pas
  21. 1 0
      src/gui/wizards/operations/UWIZPayloadOverride.lfm
  22. 3 5
      src/gui/wizards/operations/UWIZPayloadOverride.pas
  23. 1 1
      src/gui/wizards/operations/UWIZPayloadPasswordOverride.lfm
  24. 2 3
      src/gui/wizards/operations/UWIZPayloadPasswordOverride.pas
  25. 7 9
      src/gui/wizards/operations/UWIZSelectSignerOverride.pas
  26. 48 42
      src/gui/wizards/operations/UWIZSendPASC.pas
  27. 1 1
      src/gui/wizards/operations/UWIZSendPASC_ConfirmSender.lfm
  28. 40 24
      src/gui/wizards/operations/UWIZSendPASC_ConfirmSender.pas
  29. 43 39
      src/gui/wizards/operations/UWIZSendPASC_Confirmation.pas
  30. 3 1
      src/gui/wizards/operations/UWIZSendPASC_EnterQuantity.pas
  31. 1 0
      src/gui/wizards/operations/UWIZSendPASC_EnterRecipient.lfm
  32. 1 1
      src/gui/wizards/operations/UWIZSendPASC_EnterRecipient.pas
  33. 0 19
      src/gui/wizards/operations/UWIZSendPASC_FeeOverride.lfm
  34. 2 0
      src/gui/wizards/operations/UWIZTransferAccount.pas
  35. 61 58
      src/pascalcoin_wallet.lpi

+ 28 - 0
src/core.utils/UCoreUtils.pas

@@ -34,6 +34,12 @@ TAccountKeyEqualityComparer = class(TEqualityComparer<TAccountKey>)
     class function CalcHashCode(constref AValue: TAccountKey): UInt32;
 end;
 
+{ TCoreTool }
+
+TCoreTool = class(TObject)
+  class function GetSignerCandidates(ANumOps : Integer; const ACandidates : TArray<TAccount>) : TArray<TAccount>; static;
+  end;
+
 TAccountHelper = record helper for TAccount
   function GetAccountString : AnsiString;
   function GetInfoText(const ABank : TPCBank) : utf8string;
@@ -53,6 +59,28 @@ implementation
 uses
   UMemory, UConst;
 
+{ TCoreTool }
+
+class function TCoreTool.GetSignerCandidates(ANumOps: Integer;
+  const ACandidates: TArray<TAccount>): TArray<TAccount>;
+var
+  i: Integer;
+  Fee: Int64;
+  acc: TAccount;
+begin
+  //make deep copy of accounts!!! Very Important
+  Result := Copy(ACandidates);
+  Fee := ANumOps * CT_MOLINA;
+  for i := High(Result) downto Low(Result) do
+    begin
+      acc := Result[i];
+      if not (acc.Balance >= Fee) then
+      begin
+        TArrayTool<TAccount>.RemoveAt(Result, i);
+      end;
+    end;
+end;
+
 { TAccountKeyComparer }
 
 function TAccountKeyComparer.Compare(constref ALeft, ARight: T): Integer;

+ 1 - 9
src/gui/UCTRLWallet.lfm

@@ -10,7 +10,6 @@ object CTRLWallet: TCTRLWallet
   ClientWidth = 1151
   OnCreate = FormCreate
   OnResize = FormResize
-  LCLVersion = '1.8.2.0'
   Visible = False
   object PairSplitter1: TPairSplitter
     Left = 0
@@ -168,14 +167,7 @@ object CTRLWallet: TCTRLWallet
     end
     object miChangeKey: TMenuItem
       Caption = 'Change Key'
-      object miTransferAccounts: TMenuItem
-        Caption = 'Transfer Account(s)'
-        OnClick = miTransferAccountsClick
-      end
-      object miChangeAccountsPrivateKey: TMenuItem
-        Caption = 'Change Account(s) Private Key'
-        OnClick = miChangeAccountsPrivateKeyClick
-      end
+      OnClick = miChangeKeyClick
     end
     object miAccountsMarket: TMenuItem
       Caption = 'Account Market'

+ 11 - 38
src/gui/UCTRLWallet.pas

@@ -9,8 +9,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Menus,
   ExtCtrls, PairSplitter, Buttons, UVisualGrid, UCommon.UI, Generics.Collections,
-  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZTransferAccount,
-  UWIZChangeAccountPrivateKey;
+  UAccounts, UDataSources, UNode, UWIZSendPASC, UWIZChangeKey;
 
 type
 
@@ -33,8 +32,6 @@ type
     miOperationInfo: TMenuItem;
     miSendPASC: TMenuItem;
     miChangeKey: TMenuItem;
-    miTransferAccounts: TMenuItem;
-    miChangeAccountsPrivateKey: TMenuItem;
     miAccountsMarket: TMenuItem;
     miEnlistAccountsForSale: TMenuItem;
     miDelistAccountsFromSale: TMenuItem;
@@ -55,11 +52,10 @@ type
     procedure FormDestroy(Sender: TObject);
     procedure FormResize(Sender: TObject);
     procedure miAccountInfoClick(Sender: TObject);
+    procedure miChangeKeyClick(Sender: TObject);
     procedure miCopyOphashClick(Sender: TObject);
     procedure miOperationInfoClick(Sender: TObject);
     procedure miSendPASCClick(Sender: TObject);
-    procedure miTransferAccountsClick(Sender: TObject);
-    procedure miChangeAccountsPrivateKeyClick(Sender: TObject);
     procedure miEnlistAccountsForSaleClick(Sender: TObject);
     procedure miDelistAccountsFromSaleClick(Sender: TObject);
   private
@@ -608,10 +604,8 @@ begin
   miAccountInfo.Visible := ASelection.RowCount = 1;
   miSendPASC.Caption :=
     IIF(ASelection.RowCount = 1, 'Send PASC', 'Send All PASC');
-  miTransferAccounts.Caption :=
-    IIF(ASelection.RowCount = 1, 'Transfer Account', 'Transfer All Account');
-  miChangeAccountsPrivateKey.Caption :=
-    IIF(ASelection.RowCount = 1, 'Change Account Private Key', 'Change All Account Private Key');
+   miChangeKey.Caption :=
+    IIF(ASelection.RowCount = 1, 'Change Key', 'Change All Key');
   miEnlistAccountsForSale.Caption :=
     IIF(ASelection.RowCount = 1, 'Enlist Account For Sale',
     'Enlist All Account For Sale');
@@ -654,46 +648,25 @@ begin
     TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows,
     GetAccNoWithoutChecksum);
 
-  model.SendPASC.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
-  model.SendPASC.SelectedIndex := 0;
+  model.Account.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
   wiz.Start(model);
 end;
 
-procedure TCTRLWallet.miTransferAccountsClick(Sender: TObject);
+procedure TCTRLWallet.miChangeKeyClick(Sender: TObject);
 var
   Scoped: TDisposables;
-  wiz: TWIZTransferAccountWizard;
+  wiz: TWIZChangeKeyWizard;
   model: TWIZOperationsModel;
   AccountNumbersWithoutChecksum: TArray<cardinal>;
 begin
-  wiz := Scoped.AddObject(TWIZTransferAccountWizard.Create(nil)) as
-    TWIZTransferAccountWizard;
-  model := TWIZOperationsModel.Create(wiz, omtTransferAccount);
+   wiz := Scoped.AddObject(TWIZChangeKeyWizard.Create(nil)) as
+    TWIZChangeKeyWizard;
+  model := TWIZOperationsModel.Create(wiz, omtChangeKey);
   AccountNumbersWithoutChecksum :=
     TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows,
     GetAccNoWithoutChecksum);
 
-  model.TransferAccount.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
-  model.TransferAccount.SelectedIndex := 0;
-  wiz.Start(model);
-end;
-
-procedure TCTRLWallet.miChangeAccountsPrivateKeyClick(Sender: TObject);
-var
-  Scoped: TDisposables;
-  wiz: TWIZChangeAccountPrivateKeyWizard;
-  model: TWIZOperationsModel;
-  AccountNumbersWithoutChecksum: TArray<cardinal>;
-begin
-  wiz := Scoped.AddObject(TWIZChangeAccountPrivateKeyWizard.Create(nil)) as TWIZChangeAccountPrivateKeyWizard;
-  model := TWIZOperationsModel.Create(wiz, omtChangeAccountPrivateKey);
-
-  AccountNumbersWithoutChecksum :=
-    TListTool<variant, cardinal>.Transform(FAccountsGrid.SelectedRows,
-    GetAccNoWithoutChecksum);
-
-  model.ChangeAccountPrivateKey.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
-  model.ChangeAccountPrivateKey.SelectedIndex := 0;
+  model.Account.SelectedAccounts := GetAccounts(AccountNumbersWithoutChecksum);
   wiz.Start(model);
 end;
 

+ 46 - 23
src/gui/wizards/UWIZModels.pas

@@ -30,11 +30,12 @@ type
   { TWIZOperationsModel }
 
   TWIZOperationsModel = class(TComponent)
-  public type
+  public
+    type
 
     { TModelType }
 
-    TModelType = (omtSendPasc, omtTransferAccount, omtChangeAccountPrivateKey, omtAddKey);
+    TModelType = (omtAccount, omtSendPasc, omtChangeKey, omtTransferAccount, omtChangeAccountPrivateKey, omtAddKey);
 
     { TPayloadEncryptionMode }
 
@@ -44,35 +45,50 @@ type
 
     TOperationSigningMode = (akaPrimary, akaSecondary);
 
+    { TChangeKeyMode }
+
+    TChangeKeyMode = (akaTransferAccountOwnership, akaChangeAccountPrivateKey);
+
+     { TSendPASCMode }
+
+    TSendPASCMode = (akaAllBalance, akaSpecifiedAmount);
+
+    { TAccountModel }
+
+    TAccountModel = class(TComponent)
+    public
+      SelectedAccounts: TArray<TAccount>;
+    end;
+
     { TSendPASCModel }
 
     TSendPASCModel = class(TComponent)
     public
-      SelectedIndex: integer;
       SingleAmountToSend: int64;
       DestinationAccount: TAccount;
-      SelectedAccounts: TArray<TAccount>;
+      SendPASCMode: TSendPASCMode;
+    end;
+
+    { TChangeKeyModel }
+
+    TChangeKeyModel = class(TComponent)
+    public
+      ChangeKeyMode: TChangeKeyMode;
     end;
 
     { TTransferAccountModel }
 
     TTransferAccountModel = class(TComponent)
     public
-      NewPublicKey : string;
-      SelectedIndex: integer;
       AccountKey: TAccountKey;
-      SelectedAccounts: TArray<TAccount>;
     end;
 
     { TChangeAccountPrivateKeyModel }
 
     TChangeAccountPrivateKeyModel = class(TComponent)
     public
-      NewPublicKey : string;
-      SelectedIndex, PrivateKeySelectedIndex: integer;
+      SelectedIndex: integer;
       NewWalletKey: TWalletKey;
-      EncodedPayload: TRawBytes;
-      SelectedAccounts: TArray<TAccount>;
     end;
 
     { TFeeModel }
@@ -88,6 +104,8 @@ type
     public
       OperationSigningMode: TOperationSigningMode;
       SignerAccount: TAccount;
+      SignerCandidates: TArray<TAccount>;
+      SelectedIndex: integer;
     end;
 
     { TPayloadModel }
@@ -95,28 +113,32 @@ type
     TPayloadModel = class(TComponent)
     public
       HasPayload: boolean;
-      Content, Password : string;
+      Content, Password: string;
       Mode: TPayloadEncryptionMode;
       EncodedBytes: TRawBytes;
     end;
 
   private
     FModelType: TModelType;
-    FSendPASC : TSendPASCModel;
-    FTransferAccount : TTransferAccountModel;
-    FChangeAccountPrivateKey : TChangeAccountPrivateKeyModel;
-    FFee : TFeeModel;
-    FSigner : TSignerModel;
-    FPayload : TPayloadModel;
+    FAccount: TAccountModel;
+    FSendPASC: TSendPASCModel;
+    FChangeKey: TChangeKeyModel;
+    FTransferAccount: TTransferAccountModel;
+    FChangeAccountPrivateKey: TChangeAccountPrivateKeyModel;
+    FFee: TFeeModel;
+    FSigner: TSignerModel;
+    FPayload: TPayloadModel;
   public
     constructor Create(AOwner: TComponent; AType: TModelType); overload;
     property ModelType: TModelType read FModelType;
+    property Account: TAccountModel read FAccount;
     property SendPASC: TSendPASCModel read FSendPASC;
+    property ChangeKey: TChangeKeyModel read FChangeKey;
     property TransferAccount: TTransferAccountModel read FTransferAccount;
-    property ChangeAccountPrivateKey : TChangeAccountPrivateKeyModel read FChangeAccountPrivateKey;
-    property Fee : TFeeModel read FFee;
-    property Signer : TSignerModel read FSigner;
-    property Payload : TPayloadModel read FPayload;
+    property ChangeAccountPrivateKey: TChangeAccountPrivateKeyModel read FChangeAccountPrivateKey;
+    property Fee: TFeeModel read FFee;
+    property Signer: TSignerModel read FSigner;
+    property Payload: TPayloadModel read FPayload;
   end;
 
 implementation
@@ -125,10 +147,11 @@ constructor TWIZOperationsModel.Create(AOwner: TComponent; AType: TWIZOperations
 begin
   inherited Create(AOwner);
   FModelType := AType;
+  FAccount := TAccountModel.Create(Self);
   FSendPASC := TSendPASCModel.Create(Self);
+  FChangeKey := TChangeKeyModel.Create(Self);
   FTransferAccount := TTransferAccountModel.Create(Self);
   FChangeAccountPrivateKey := TChangeAccountPrivateKeyModel.Create(Self);
-  FChangeAccountPrivateKey := TChangeAccountPrivateKeyModel.Create(Self);
   FFee := TFeeModel.Create(Self);
   FSigner := TSignerModel.Create(Self);
   FPayload := TPayloadModel.Create(Self);

+ 476 - 0
src/gui/wizards/operations/UWIZChangeKey.pas

@@ -0,0 +1,476 @@
+unit UWIZChangeKey;
+
+{$mode delphi}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Dialogs, UCrypto, UCommon, UWizard, UAccounts, LCLType, UWIZModels;
+
+type
+
+  { TWIZChangeKeyWizard }
+
+  TWIZChangeKeyWizard = class(TWizard<TWIZOperationsModel>)
+  private
+    function UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
+    function UpdateOperationOptions(var errors: string): boolean;
+    function UpdateOpChangeKey(const TargetAccount: TAccount; var SignerAccount: TAccount; var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
+    procedure WIZChangeKeyOwnership();
+  public
+    constructor Create(AOwner: TComponent); override;
+    function DetermineHasNext: boolean; override;
+    function DetermineHasPrevious: boolean; override;
+    function FinishRequested(out message: ansistring): boolean; override;
+    function CancelRequested(out message: ansistring): boolean; override;
+  end;
+
+implementation
+
+uses
+  UBlockChain,
+  UOpTransaction,
+  UNode,
+  UConst,
+  UWallet,
+  UECIES,
+  UAES,
+  UWIZChangeKey_ConfirmAccount,
+  UWIZChangeKey_Confirmation;
+
+{ TWIZChangeKeyWizard }
+
+function TWIZChangeKeyWizard.UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
+var
+  valid: boolean;
+  payload_encrypted, payload_u: string;
+  account: TAccount;
+  public_key: TAccountKey;
+begin
+  valid := False;
+  payload_encrypted := '';
+  Model.Payload.EncodedBytes := '';
+  errors := 'Unknown error';
+  payload_u := Model.Payload.Content;
+
+  try
+    if (payload_u = '') then
+    begin
+      valid := True;
+      Exit;
+    end;
+    case Model.Payload.Mode of
+
+      akaEncryptWithSender:
+      begin
+        // Use sender
+        errors := 'Error encrypting';
+        account := SenderAccount;
+        payload_encrypted := ECIESEncrypt(account.accountInfo.accountKey, payload_u);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaEncryptWithReceiver:
+      begin
+
+        case Model.ChangeKey.ChangeKeyMode of
+          akaTransferAccountOwnership:
+          begin
+            public_key := Model.TransferAccount.AccountKey;
+          end;
+
+          akaChangeAccountPrivateKey:
+          begin
+            public_key := Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey;
+          end;
+
+        end;
+
+        errors := 'Public key: ' + 'Error encrypting';
+
+        if Model.TransferAccount.AccountKey.EC_OpenSSL_NID <>
+          CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then
+        begin
+          payload_encrypted := ECIESEncrypt(public_key, payload_u);
+          valid := payload_encrypted <> '';
+        end
+        else
+        begin
+          valid := False;
+          errors := 'Selected private key is not valid to encode';
+          exit;
+        end;
+      end;
+
+      akaEncryptWithPassword:
+      begin
+        payload_encrypted := TAESComp.EVP_Encrypt_AES256(
+          payload_u, Model.Payload.Password);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaNotEncrypt:
+      begin
+        payload_encrypted := payload_u;
+        valid := True;
+      end
+
+      else
+      begin
+        raise Exception.Create('Invalid Encryption Selection');
+      end;
+    end;
+
+  finally
+    if valid then
+    begin
+      if length(payload_encrypted) > CT_MaxPayloadSize then
+      begin
+        valid := False;
+        errors := 'Payload size is bigger than ' + IntToStr(CT_MaxPayloadSize) +
+          ' (' + IntToStr(length(payload_encrypted)) + ')';
+      end;
+
+    end;
+    Model.Payload.EncodedBytes := payload_encrypted;
+    Result := valid;
+  end;
+
+end;
+
+function TWIZChangeKeyWizard.UpdateOperationOptions(var errors: string): boolean;
+var
+  iAcc, iWallet: integer;
+  sender_account, signer_account: TAccount;
+  publicKey: TAccountKey;
+  wk: TWalletKey;
+  e: string;
+  amount: int64;
+begin
+  Result := False;
+  errors := '';
+  if not Assigned(TWallet.Keys) then
+  begin
+    errors := 'No wallet keys';
+    Exit;
+  end;
+
+  if Length(Model.Account.SelectedAccounts) = 0 then
+  begin
+    errors := 'No sender account';
+    Exit;
+  end
+  else
+  begin
+
+    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+    begin
+      sender_account := Model.Account.SelectedAccounts[iAcc];
+      iWallet := TWallet.Keys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
+      if (iWallet < 0) then
+      begin
+        errors := 'Private key of account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
+          ' not found in wallet';
+        Exit;
+      end;
+      wk := TWallet.Keys.Key[iWallet];
+      if not assigned(wk.PrivateKey) then
+      begin
+        if wk.CryptedKey <> '' then
+        begin
+          // TODO: handle unlocking of encrypted wallet here
+          errors := 'Wallet is password protected. Need password';
+        end
+        else
+        begin
+          errors := 'Only public key of account ' +
+            TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
+            ' found in wallet. You cannot operate with this account';
+        end;
+        Exit;
+      end;
+    end;
+  end;
+
+  Result := UpdateOpChangeKey(Model.Account.SelectedAccounts[0], signer_account,
+    publicKey, errors);
+  UpdatePayload(sender_account, e);
+end;
+
+function TWIZChangeKeyWizard.UpdateOpChangeKey(const TargetAccount: TAccount; var SignerAccount: TAccount; var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
+
+begin
+  Result := False;
+  errors := '';
+  try
+
+    case Model.ChangeKey.ChangeKeyMode of
+      akaTransferAccountOwnership:
+      begin
+        NewPublicKey := Model.TransferAccount.AccountKey;
+      end;
+
+      akaChangeAccountPrivateKey:
+      begin
+        NewPublicKey := Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey;
+      end;
+
+    end;
+
+    if TNode.Node.Bank.SafeBox.CurrentProtocol >= 1 then
+    begin
+      // Signer:
+      SignerAccount := Model.Signer.SignerAccount;
+      if (TAccountComp.IsAccountLocked(SignerAccount.accountInfo,
+        TNode.Node.Bank.BlocksCount)) then
+      begin
+        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
+          SignerAccount.account) + ' is locked until block ' + IntToStr(
+          SignerAccount.accountInfo.locked_until_block);
+        exit;
+      end;
+      if (not TAccountComp.EqualAccountKeys(
+        SignerAccount.accountInfo.accountKey, TargetAccount.accountInfo.accountKey)) then
+      begin
+        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
+          SignerAccount.account) + ' is not owner of account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(TargetAccount.account);
+        exit;
+      end;
+    end
+    else
+    begin
+      SignerAccount := TargetAccount;
+    end;
+
+    if (TAccountComp.EqualAccountKeys(TargetAccount.accountInfo.accountKey,
+      NewPublicKey)) then
+    begin
+      errors := 'New key is same as current key';
+      exit;
+    end;
+
+  finally
+    Result := errors = '';
+  end;
+end;
+
+procedure TWIZChangeKeyWizard.WIZChangeKeyOwnership();
+var
+  _V2, dooperation: boolean;
+  iAcc, i: integer;
+  _totalamount, _totalfee, _totalSignerFee, _amount, _fee: int64;
+  _signer_n_ops: cardinal;
+  operationstxt, operation_to_string, errors, auxs: string;
+  wk: TWalletKey;
+  ops: TOperationsHashTree;
+  op: TPCOperation;
+  account, signerAccount: TAccount;
+  _newOwnerPublicKey: TECDSA_Public;
+label
+  loop_start;
+begin
+  if not Assigned(TWallet.Keys) then
+    raise Exception.Create('No wallet keys');
+  if not UpdateOperationOptions(errors) then
+    raise Exception.Create(errors);
+  ops := TOperationsHashTree.Create;
+
+  try
+    _V2 := TNode.Node.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
+    _totalamount := 0;
+    _totalfee := 0;
+    _totalSignerFee := 0;
+    _signer_n_ops := 0;
+    operationstxt := '';
+    operation_to_string := '';
+    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+    begin
+      loop_start:
+        op := nil;
+      account := Model.Account.SelectedAccounts[iAcc];
+      if not UpdatePayload(account, errors) then
+      begin
+        raise Exception.Create('Error encoding payload of sender account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(account.account) + ': ' + errors);
+      end;
+      i := TWallet.Keys.IndexOfAccountKey(account.accountInfo.accountKey);
+      if i < 0 then
+      begin
+        raise Exception.Create('Sender account private key not found in Wallet');
+      end;
+
+      wk := TWallet.Keys.Key[i];
+      dooperation := True;
+      // Default fee
+      if account.balance > uint64(Model.Fee.DefaultFee) then
+        _fee := Model.Fee.DefaultFee
+      else
+        _fee := account.balance;
+
+      if not UpdateOpChangeKey(account, signerAccount, _newOwnerPublicKey, errors) then
+      begin
+        raise Exception.Create(errors);
+      end;
+      if _V2 then
+      begin
+        // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+        if (iAcc < Length(Model.Account.SelectedAccounts) - 1) and
+          (account.account = signerAccount.account) then
+        begin
+          TArrayTool<TAccount>.Swap(Model.Account.SelectedAccounts, iAcc,
+            Length(Model.Account.SelectedAccounts) - 1); // ensure signer account processed last
+          // TArrayTool_internal<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1);
+          goto loop_start; // TODO: remove ugly hack with refactoring!
+        end;
+
+        // Maintain correct signer fee distribution
+        if uint64(_totalSignerFee) >= signerAccount.balance then
+          _fee := 0
+        else if signerAccount.balance - uint64(_totalSignerFee) >
+          uint64(Model.Fee.DefaultFee) then
+          _fee := Model.Fee.DefaultFee
+        else
+          _fee := signerAccount.balance - uint64(_totalSignerFee);
+        op := TOpChangeKeySigned.Create(signerAccount.account,
+          signerAccount.n_operation + _signer_n_ops + 1, account.account,
+          wk.PrivateKey, _newOwnerPublicKey, _fee, Model.Payload.EncodedBytes);
+        Inc(_signer_n_ops);
+        Inc(_totalSignerFee, _fee);
+      end
+      else
+      begin
+        op := TOpChangeKey.Create(account.account, account.n_operation +
+          1, account.account, wk.PrivateKey, _newOwnerPublicKey, _fee, Model.Payload.EncodedBytes);
+      end;
+      Inc(_totalfee, _fee);
+      operationstxt :=
+        'Change private key to ' + TAccountComp.GetECInfoTxt(
+        _newOwnerPublicKey.EC_OpenSSL_NID);
+
+      if Assigned(op) and (dooperation) then
+      begin
+        ops.AddOperationToHashTree(op);
+        if operation_to_string <> '' then
+          operation_to_string := operation_to_string + #10;
+        operation_to_string := operation_to_string + op.ToString;
+      end;
+      FreeAndNil(op);
+    end;
+
+    if (ops.OperationsCount = 0) then
+      raise Exception.Create('No valid operation to execute');
+
+    if (Length(Model.Account.SelectedAccounts) > 1) then
+    begin
+      auxs := '';
+      if Application.MessageBox(
+        PChar('Execute ' + IntToStr(Length(Model.Account.SelectedAccounts)) +
+        ' operations?' + #10 + 'Operation: ' + operationstxt + #10 +
+        auxs + 'Total fee: ' + TAccountComp.FormatMoney(_totalfee) +
+        #10 + #10 + 'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title), MB_YESNO + MB_ICONINFORMATION + MB_DEFBUTTON2) <>
+        idYes then
+      begin
+        Exit;
+      end;
+    end
+    else
+    begin
+      if Application.MessageBox(PChar('Execute this operation:' + #10 +
+        #10 + operation_to_string + #10 + #10 +
+        'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title), MB_YESNO + MB_ICONINFORMATION + MB_DEFBUTTON2) <> idYes then
+      begin
+        Exit;
+      end;
+    end;
+    i := TNode.Node.AddOperations(nil, ops, nil, errors);
+    if (i = ops.OperationsCount) then
+    begin
+      operationstxt := 'Successfully executed ' + IntToStr(i) +
+        ' operations!' + #10 + #10 + operation_to_string;
+      if i > 1 then
+      begin
+
+        ShowMessage(operationstxt);
+      end
+      else
+      begin
+        Application.MessageBox(
+          PChar('Successfully executed ' + IntToStr(i) + ' operations!' +
+          #10 + #10 + operation_to_string),
+          PChar(Application.Title), MB_OK + MB_ICONINFORMATION);
+      end;
+
+    end
+    else if (i > 0) then
+    begin
+      operationstxt := 'One or more of your operations has not been executed:' +
+        #10 + 'Errors:' + #10 + errors + #10 + #10 +
+        'Total successfully executed operations: ' + IntToStr(i);
+
+      ShowMessage(operationstxt);
+    end
+    else
+    begin
+      raise Exception.Create(errors);
+    end;
+
+
+  finally
+    ops.Free;
+  end;
+
+end;
+
+constructor TWIZChangeKeyWizard.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner, [TWIZChangeKey_ConfirmAccount,
+    TWIZChangeKey_Confirmation]);
+  TitleText := 'Change Key';
+  FinishText := 'Change Key';
+end;
+
+function TWIZChangeKeyWizard.DetermineHasNext: boolean;
+begin
+  Result := not (CurrentScreen is TWIZChangeKey_Confirmation);
+end;
+
+function TWIZChangeKeyWizard.DetermineHasPrevious: boolean;
+begin
+  Result := inherited DetermineHasPrevious;
+end;
+
+function TWIZChangeKeyWizard.FinishRequested(out message: ansistring): boolean;
+begin
+  // Execute the Change Key Action here
+  try
+    Result := True;
+    WIZChangeKeyOwnership();
+  except
+    On E: Exception do
+    begin
+      Result := False;
+      message := E.ToString;
+    end;
+  end;
+end;
+
+function TWIZChangeKeyWizard.CancelRequested(out message: ansistring): boolean;
+begin
+  Result := True;
+end;
+
+end.

+ 50 - 0
src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.lfm

@@ -0,0 +1,50 @@
+object WIZChangeKey_ConfirmAccount: TWIZChangeKey_ConfirmAccount
+  Left = 0
+  Height = 261
+  Top = 0
+  Width = 429
+  Caption = 'ChangeKey_ConfirmAccount'
+  ClientHeight = 261
+  ClientWidth = 429
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object gpChangeKey: TGroupBox
+    Left = 4
+    Height = 256
+    Top = 0
+    Width = 420
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Caption = 'Change Key Accounts'
+    ClientHeight = 236
+    ClientWidth = 416
+    TabOrder = 0
+    object paGrid: TPanel
+      Left = 8
+      Height = 192
+      Top = 24
+      Width = 400
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BevelOuter = bvNone
+      Caption = 'GRID PANEL'
+      TabOrder = 0
+    end
+    object lblTotalBalances: TLabel
+      Left = 8
+      Height = 15
+      Top = 0
+      Width = 176
+      Caption = 'Selected Accounts Total Balance: '
+      ParentColor = False
+    end
+    object lblTotalBalanceValue: TLabel
+      Left = 192
+      Height = 15
+      Top = 0
+      Width = 70
+      Caption = 'Total Balance'
+      Font.Color = clGreen
+      ParentColor = False
+      ParentFont = False
+    end
+  end
+end

+ 144 - 0
src/gui/wizards/operations/UWIZChangeKey_ConfirmAccount.pas

@@ -0,0 +1,144 @@
+unit UWIZChangeKey_ConfirmAccount;
+
+{$mode delphi}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, UVisualGrid, UCommon.Data, UCellRenderers,
+  UWizard, UWIZSendPASC, UWIZChangeKey_SelectOption, UWIZChangeKey_Confirmation, UWIZModels;
+
+type
+
+  { TWIZChangeKey_ConfirmAccount }
+
+  TWIZChangeKey_ConfirmAccount = class(TWizardForm<TWIZOperationsModel>)
+    gpChangeKey: TGroupBox;
+    lblTotalBalances: TLabel;
+    lblTotalBalanceValue: TLabel;
+    paGrid: TPanel;
+  private
+    FChangeKeyGrid: TVisualGrid;
+  public
+    procedure OnPresent; override;
+    procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
+  end;
+
+
+
+implementation
+
+{$R *.lfm}
+
+uses UAccounts, UCoreUtils, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+
+type
+
+  { TAccountChangeKeyDataSource }
+
+  TAccountChangeKeyDataSource = class(TAccountsDataSourceBase)
+  private
+    FModel: TWIZOperationsModel;
+  public
+    property Model: TWIZOperationsModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
+  end;
+
+{ TWIZChangeKey_ConfirmAccount }
+
+procedure TWIZChangeKey_ConfirmAccount.OnPresent;
+var
+  Data: TAccountChangeKeyDataSource;
+  i: integer;
+  acc: TAccount;
+  totalBalance: int64;
+begin
+  FChangeKeyGrid := TVisualGrid.Create(Self);
+  FChangeKeyGrid.CanSearch := False;
+  FChangeKeyGrid.SortMode := smMultiColumn;
+  FChangeKeyGrid.FetchDataInThread := False;
+  FChangeKeyGrid.AutoPageSize := True;
+  FChangeKeyGrid.SelectionType := stNone;
+  FChangeKeyGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
+  with FChangeKeyGrid.AddColumn('Account') do
+  begin
+    StretchedToFill := True;
+    Binding := 'AccountNumber';
+    SortBinding := 'AccountNumber';
+    DisplayBinding := 'Account';
+    Width := 100;
+    HeaderFontStyles := [fsBold];
+    DataFontStyles := [fsBold];
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  with FChangeKeyGrid.AddColumn('Balance') do
+  begin
+    Binding := 'BalanceDecimal';
+    SortBinding := 'Balance';
+    DisplayBinding := 'Balance';
+    Width := 100;
+    HeaderAlignment := taRightJustify;
+    DataAlignment := taRightJustify;
+    Renderer := TCellRenderers.PASC;
+    Filters := SORTABLE_NUMERIC_FILTER;
+  end;
+  Data := TAccountChangeKeyDataSource.Create(FChangeKeyGrid);
+  Data.Model := Model;
+  FChangeKeyGrid.DataSource := Data;
+  paGrid.AddControlDockCenter(FChangeKeyGrid);
+
+  totalBalance := 0;
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  begin
+    acc := Model.Account.SelectedAccounts[i];
+    totalBalance := totalBalance + acc.balance;
+  end;
+
+  lblTotalBalanceValue.Caption :=
+    Format('%s PASC', [TAccountComp.FormatMoney(totalBalance)]);
+end;
+
+procedure TWIZChangeKey_ConfirmAccount.OnNext;
+begin
+  UpdatePath(ptReplaceAllNext, [TWIZChangeKey_SelectOption, TWIZChangeKey_Confirmation]);
+end;
+
+function TWIZChangeKey_ConfirmAccount.Validate(out message: ansistring): boolean;
+begin
+  Result := True;
+  // get signer accounts from selected accounts
+  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Account.SelectedAccounts);
+
+  if Length(Model.Signer.SignerCandidates) < 1 then
+  begin
+    Result := False;
+    message := 'no valid signer account was found.';
+  end;
+
+end;
+
+{ TAccountChangeKeyDataSource }
+
+procedure TAccountChangeKeyDataSource.FetchAll(const AContainer: TList<TAccount>);
+var
+  i: integer;
+begin
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  begin
+    AContainer.Add(Model.Account.SelectedAccounts[i]);
+  end;
+end;
+
+end.

+ 48 - 0
src/gui/wizards/operations/UWIZChangeKey_Confirmation.lfm

@@ -0,0 +1,48 @@
+object WIZChangeKey_Confirmation: TWIZChangeKey_Confirmation
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  Caption = 'WIZChangeKey_Confirmation'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object GroupBox1: TGroupBox
+    Left = 10
+    Height = 232
+    Top = 8
+    Width = 408
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Caption = 'Confirm Change Key Transaction'
+    ClientHeight = 212
+    ClientWidth = 404
+    TabOrder = 0
+    object paGrid: TPanel
+      Left = 8
+      Height = 152
+      Top = 48
+      Width = 382
+      Anchors = [akTop, akLeft, akRight, akBottom]
+      BevelOuter = bvNone
+      Caption = 'GRID PANEL'
+      TabOrder = 0
+    end
+    object lblSgnAcc: TLabel
+      Left = 112
+      Height = 15
+      Top = 10
+      Width = 53
+      Caption = 'lblSgnAcc'
+      ParentColor = False
+    end
+    object Label1: TLabel
+      Left = 8
+      Height = 15
+      Top = 10
+      Width = 84
+      Caption = 'Signer Account:'
+      ParentColor = False
+    end
+  end
+end

+ 178 - 0
src/gui/wizards/operations/UWIZChangeKey_Confirmation.pas

@@ -0,0 +1,178 @@
+unit UWIZChangeKey_Confirmation;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, UVisualGrid, UCellRenderers, UCommon.Data, UWizard, UWIZChangeKey, UWIZModels;
+
+type
+
+  { TWIZChangeKey_Confirmation }
+
+  TWIZChangeKey_Confirmation = class(TWizardForm<TWIZOperationsModel>)
+    GroupBox1: TGroupBox;
+    Label1: TLabel;
+    lblSgnAcc: TLabel;
+    paGrid: TPanel;
+  private
+    FChangeKeyGrid: TVisualGrid;
+  public
+    procedure OnPresent; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses UAccounts, UCrypto, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+
+type
+
+  { TAccountChangeKeyDataSource }
+
+  TAccountChangeKeyDataSource = class(TAccountsDataSourceBase)
+  private
+    FModel: TWIZOperationsModel;
+  protected
+    function GetColumns: TDataColumns; override;
+  public
+    property Model: TWIZOperationsModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
+    function GetItemField(constref AItem: TAccount; const ABindingName: ansistring): variant; override;
+  end;
+
+{ TWIZChangeKey_Confirmation }
+
+procedure TWIZChangeKey_Confirmation.OnPresent;
+var
+  Data: TAccountChangeKeyDataSource;
+begin
+  FChangeKeyGrid := TVisualGrid.Create(Self);
+  FChangeKeyGrid.CanSearch := False;
+  FChangeKeyGrid.SortMode := smMultiColumn;
+  FChangeKeyGrid.FetchDataInThread := False;
+  FChangeKeyGrid.AutoPageSize := True;
+  FChangeKeyGrid.SelectionType := stNone;
+  FChangeKeyGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
+
+  with FChangeKeyGrid.AddColumn('Account') do
+  begin
+    Binding := 'Account';
+    Filters := SORTABLE_NUMERIC_FILTER;
+    Width := 100;
+    HeaderFontStyles := [fsBold];
+    DataFontStyles := [fsBold];
+  end;
+
+  with FChangeKeyGrid.AddColumn('Current Key') do
+  begin
+    Binding := 'CurrentKey';
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+
+  with FChangeKeyGrid.AddColumn('New Key') do
+  begin
+    Binding := 'NewKey';
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+
+  with FChangeKeyGrid.AddColumn('Fee') do
+  begin
+    Filters := SORTABLE_TEXT_FILTER;
+    Width := 100;
+  end;
+
+  Data := TAccountChangeKeyDataSource.Create(FChangeKeyGrid);
+  Data.Model := Model;
+  FChangeKeyGrid.DataSource := Data;
+  paGrid.AddControlDockCenter(FChangeKeyGrid);
+  lblSgnAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Model.Signer.SignerAccount.account);
+end;
+
+{ TAccountChangeKeyDataSource }
+
+function TAccountChangeKeyDataSource.GetColumns: TDataColumns;
+begin
+  Result := TDataColumns.Create(
+    TDataColumn.From('Account'),
+    TDataColumn.From('CurrentKey'),
+    TDataColumn.From('NewKey'),
+    TDataColumn.From('Fee')
+    );
+end;
+
+function TAccountChangeKeyDataSource.GetItemField(constref AItem: TAccount; const ABindingName: ansistring): variant;
+var
+  index: integer;
+begin
+  if ABindingName = 'Account' then
+    Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account)
+  else if ABindingName = 'Fee' then
+    Result := TAccountComp.FormatMoney(Model.Fee.SingleOperationFee)
+  else
+  begin
+    case Model.ChangeKey.ChangeKeyMode of
+      akaTransferAccountOwnership:
+      begin
+        if ABindingName = 'CurrentKey' then
+          Result := TAccountComp.AccountPublicKeyExport(AItem.accountInfo.accountKey)
+        else if ABindingName = 'NewKey' then
+          Result := TAccountComp.AccountPublicKeyExport(Model.TransferAccount.AccountKey)
+        else
+          raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
+      end;
+
+      akaChangeAccountPrivateKey:
+      begin
+        if ABindingName = 'CurrentKey' then
+          { TODO : Check how to get the wallet name an account is in }
+          Result := '??? unknown'
+        else if ABindingName = 'NewKey' then
+        begin
+          Result := IIF(Model.ChangeAccountPrivateKey.NewWalletKey.Name = '',
+            TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(
+            Model.ChangeAccountPrivateKey.NewWalletKey.AccountKey)), Model.ChangeAccountPrivateKey.NewWalletKey.Name);
+          if not Assigned(Model.ChangeAccountPrivateKey.NewWalletKey.PrivateKey) then
+          begin
+            Result := Result + '(*)';
+          end;
+        end
+        else
+          raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
+      end;
+
+    end;
+  end;
+
+end;
+
+
+procedure TAccountChangeKeyDataSource.FetchAll(const AContainer: TList<TAccount>);
+var
+  i: integer;
+begin
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  begin
+    AContainer.Add(Model.Account.SelectedAccounts[i]);
+  end;
+end;
+
+
+end.

+ 53 - 0
src/gui/wizards/operations/UWIZChangeKey_EnterKey.lfm

@@ -0,0 +1,53 @@
+object WIZChangeKey_EnterKey: TWIZChangeKey_EnterKey
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  ActiveControl = chkChooseFee
+  Caption = 'ChangeKey_EnterKey'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object gbNewPublicKey: TGroupBox
+    Left = 16
+    Height = 232
+    Top = 8
+    Width = 400
+    Caption = 'New Public Key'
+    ClientHeight = 212
+    ClientWidth = 396
+    TabOrder = 0
+    object lblPublicKeyNotice: TLabel
+      Left = 8
+      Height = 30
+      Top = 7
+      Width = 348
+      Caption = 'Please enter the public key of the new owner who will control this '#13#10'account'
+      ParentColor = False
+    end
+    object chkChooseFee: TCheckBox
+      Left = 8
+      Height = 19
+      Top = 152
+      Width = 226
+      Caption = 'Let me choose fee''s for this transaction'
+      TabOrder = 0
+    end
+    object chkAttachPayload: TCheckBox
+      Left = 8
+      Height = 19
+      Top = 184
+      Width = 303
+      Caption = 'I want to attach a message payload to this transaction'
+      TabOrder = 1
+    end
+    object mmoNewPrivateKey: TMemo
+      Left = 8
+      Height = 102
+      Top = 42
+      Width = 376
+      TabOrder = 2
+    end
+  end
+end

+ 97 - 0
src/gui/wizards/operations/UWIZChangeKey_EnterKey.pas

@@ -0,0 +1,97 @@
+unit UWIZChangeKey_EnterKey;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, Buttons, UCommon, UCommon.Collections, UWallet,
+  UFRMAccountSelect, UNode, UWizard, UWIZFeeOverride, UWIZPayloadOverride, UWIZSelectSignerOverride,
+  UWIZChangeKey_Confirmation, UWIZModels;
+
+type
+
+  { TWIZChangeKey_EnterKey }
+
+  TWIZChangeKey_EnterKey = class(TWizardForm<TWIZOperationsModel>)
+    chkChooseFee: TCheckBox;
+    chkAttachPayload: TCheckBox;
+    gbNewPublicKey: TGroupBox;
+    lblPublicKeyNotice: TLabel;
+    mmoNewPrivateKey: TMemo;
+
+  public
+    procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses
+  UAccounts, UUserInterface, USettings;
+
+{ TWIZChangeKey_EnterKey }
+
+procedure TWIZChangeKey_EnterKey.OnNext;
+begin
+  Model.Payload.HasPayload := chkAttachPayload.Checked;
+
+  if chkChooseFee.Checked then
+  begin
+    UpdatePath(ptReplaceAllNext, [TWIZFeeOverride, TWIZChangeKey_Confirmation]);
+  end
+  else
+  begin
+    Model.Fee.SingleOperationFee := TSettings.DefaultFee;
+    if Model.Payload.HasPayload then
+    begin
+      UpdatePath(ptReplaceAllNext, [TWIZPayloadOverride, TWIZChangeKey_Confirmation]);
+    end
+    else
+    begin
+      UpdatePath(ptReplaceAllNext, [TWIZSelectSignerOverride, TWIZChangeKey_Confirmation]);
+    end;
+  end;
+end;
+
+function TWIZChangeKey_EnterKey.Validate(out message: ansistring): boolean;
+var
+  tempAccountKey: TAccountKey;
+  i: Integer;
+begin
+  Result := True;
+  if not TAccountComp.AccountKeyFromImport(mmoNewPrivateKey.Lines.Text,
+    tempAccountKey, message) then
+  begin
+    Result := False;
+    Exit;
+  end;
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  begin
+    if TAccountComp.EqualAccountKeys(Model.Account.SelectedAccounts[i].accountInfo.accountKey,
+      tempAccountKey) then
+    begin
+      Result := False;
+      message := 'New key is same as current key';
+      Exit;
+    end;
+  end;
+  Model.TransferAccount.AccountKey := tempAccountKey;
+end;
+
+end.

+ 65 - 0
src/gui/wizards/operations/UWIZChangeKey_SelectKey.lfm

@@ -0,0 +1,65 @@
+object WIZChangeKey_SelectKey: TWIZChangeKey_SelectKey
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  Caption = 'WIZChangeKey_SelectKey'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object gbNewPrivateKey: TGroupBox
+    Left = 16
+    Height = 232
+    Top = 8
+    Width = 400
+    Caption = 'New Private Key'
+    ClientHeight = 212
+    ClientWidth = 396
+    TabOrder = 0
+    object lblPrivateKeyNote: TLabel
+      Left = 8
+      Height = 15
+      Top = 8
+      Width = 329
+      Caption = 'Please select the new private key to update your accounts with'
+      ParentColor = False
+    end
+    object cbNewPrivateKey: TComboBox
+      Left = 8
+      Height = 23
+      Top = 40
+      Width = 168
+      ItemHeight = 15
+      OnChange = cbNewPrivateKeyChange
+      TabOrder = 0
+      Text = 'Select Private Key'
+    end
+    object lblKeyName: TLabel
+      Left = 192
+      Height = 15
+      Top = 45
+      Width = 128
+      Caption = 'Please Select Private Key'
+      Font.Color = clRed
+      ParentColor = False
+      ParentFont = False
+    end
+    object chkChooseFee: TCheckBox
+      Left = 8
+      Height = 19
+      Top = 80
+      Width = 226
+      Caption = 'Let me choose fee''s for this transaction'
+      TabOrder = 1
+    end
+    object chkAttachPayload: TCheckBox
+      Left = 8
+      Height = 19
+      Top = 112
+      Width = 303
+      Caption = 'I want to attach a message payload to this transaction'
+      TabOrder = 2
+    end
+  end
+end

+ 163 - 0
src/gui/wizards/operations/UWIZChangeKey_SelectKey.pas

@@ -0,0 +1,163 @@
+unit UWIZChangeKey_SelectKey;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, Buttons, UCommon, UCommon.Collections, UWallet,
+  UFRMAccountSelect, UNode, UWizard,
+  UWIZChangeKey_Confirmation, UWIZFeeOverride, UWIZSelectSignerOverride, UWIZPayloadOverride, UWIZModels;
+
+type
+
+  { TWIZChangeKey_SelectKey }
+
+  TWIZChangeKey_SelectKey = class(TWizardForm<TWIZOperationsModel>)
+    cbNewPrivateKey: TComboBox;
+    chkAttachPayload: TCheckBox;
+    chkChooseFee: TCheckBox;
+    gbNewPrivateKey: TGroupBox;
+    lblKeyName: TLabel;
+    lblPrivateKeyNote: TLabel;
+    procedure cbNewPrivateKeyChange(Sender: TObject);
+  private
+    procedure UpdateWalletKeys();
+  public
+    procedure OnPresent; override;
+    procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
+  end;
+
+
+implementation
+
+{$R *.lfm}
+
+uses
+  UAccounts, UCrypto, UUserInterface, USettings;
+
+{ TWIZChangeKey_SelectKey }
+
+procedure TWIZChangeKey_SelectKey.cbNewPrivateKeyChange(Sender: TObject);
+var
+  i: integer;
+  wk: TWalletKey;
+begin
+  if cbNewPrivateKey.ItemIndex < 1 then
+  begin
+    lblKeyName.Font.Color := clRed;
+    lblKeyName.Caption := 'Please Select Private Key';
+  end
+  else
+  begin
+    lblKeyName.Font.Color := clGreen;
+    i := PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex]);
+    wk := TWallet.Keys.Key[i];
+    lblKeyName.Caption := Format('%s ',
+      [IIF(wk.Name = '', TCrypto.ToHexaString(
+      TAccountComp.AccountKey2RawString(wk.AccountKey)), wk.Name)]);
+  end;
+end;
+
+procedure TWIZChangeKey_SelectKey.UpdateWalletKeys();
+var
+  i: integer;
+  wk: TWalletKey;
+  s: string;
+begin
+  cbNewPrivateKey.items.BeginUpdate;
+  try
+    cbNewPrivateKey.Items.Clear;
+    cbNewPrivateKey.Items.Add('Select Private Key');
+    if not Assigned(TWallet.Keys) then
+    begin
+      Exit;
+    end;
+    for i := 0 to TWallet.Keys.Count - 1 do
+    begin
+      wk := TWallet.Keys.Key[i];
+      s := IIF(wk.Name = '', TCrypto.ToHexaString(TAccountComp.AccountKey2RawString(wk.AccountKey)), wk.Name);
+      if not Assigned(wk.PrivateKey) then
+      begin
+        s := s + '(*)';
+      end;
+      cbNewPrivateKey.Items.AddObject(s, TObject(i));
+    end;
+    cbNewPrivateKey.Sorted := True;
+  finally
+    cbNewPrivateKey.Items.EndUpdate;
+  end;
+end;
+
+procedure TWIZChangeKey_SelectKey.OnPresent;
+begin
+  UpdateWalletKeys();
+  cbNewPrivateKey.ItemIndex := Model.ChangeAccountPrivateKey.SelectedIndex;
+  cbNewPrivateKeyChange(Self);
+end;
+
+procedure TWIZChangeKey_SelectKey.OnNext;
+begin
+  Model.ChangeAccountPrivateKey.SelectedIndex := cbNewPrivateKey.ItemIndex;
+  Model.ChangeAccountPrivateKey.NewWalletKey := TWallet.Keys.Key[PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex])];
+  Model.Payload.HasPayload := chkAttachPayload.Checked;
+
+  if chkChooseFee.Checked then
+  begin
+    UpdatePath(ptReplaceAllNext, [TWIZFeeOverride, TWIZChangeKey_Confirmation]);
+  end
+  else
+  begin
+    Model.Fee.SingleOperationFee := TSettings.DefaultFee;
+    if Model.Payload.HasPayload then
+    begin
+      UpdatePath(ptReplaceAllNext, [TWIZPayloadOverride, TWIZChangeKey_Confirmation]);
+    end
+    else
+    begin
+      UpdatePath(ptReplaceAllNext, [TWIZSelectSignerOverride, TWIZChangeKey_Confirmation]);
+    end;
+  end;
+end;
+
+function TWIZChangeKey_SelectKey.Validate(out message: ansistring): boolean;
+var
+  i: integer;
+  tempAccountKey: TAccountKey;
+begin
+  Result := True;
+  if cbNewPrivateKey.ItemIndex < 1 then
+  begin
+    message := 'A key must be selected';
+    Result := False;
+    Exit;
+  end;
+
+  tempAccountKey := TWallet.Keys.Key[PtrInt(cbNewPrivateKey.Items.Objects[cbNewPrivateKey.ItemIndex])].AccountKey;
+
+   for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
+  begin
+    if TAccountComp.EqualAccountKeys(Model.Account.SelectedAccounts[i].accountInfo.accountKey,
+      tempAccountKey) then
+    begin
+      Result := False;
+      message := 'New key is same as current key';
+      Exit;
+    end;
+  end;
+end;
+
+end.

+ 63 - 0
src/gui/wizards/operations/UWIZChangeKey_SelectOption.lfm

@@ -0,0 +1,63 @@
+object WIZChangeKey_SelectOption: TWIZChangeKey_SelectOption
+  Left = 0
+  Height = 253
+  Top = 0
+  Width = 429
+  Caption = 'WIZChangeKey_SelectOption'
+  ClientHeight = 253
+  ClientWidth = 429
+  LCLVersion = '1.8.2.0'
+  Visible = False
+  object gbChangeKeyOptions: TGroupBox
+    Left = 8
+    Height = 245
+    Top = 2
+    Width = 410
+    Caption = 'Change Key Options'
+    ClientHeight = 225
+    ClientWidth = 406
+    TabOrder = 0
+    object lblNote: TLabel
+      Left = 8
+      Height = 15
+      Top = 8
+      Width = 310
+      Caption = 'How would you like to change the keys of your account(s).'
+      ParentColor = False
+    end
+    object rbTransferAccountOwnership: TRadioButton
+      Left = 8
+      Height = 19
+      Top = 40
+      Width = 192
+      Caption = 'Transfer account to another user'
+      Checked = True
+      TabOrder = 1
+      TabStop = True
+    end
+    object lblTransferAccountOwnership: TLabel
+      Left = 8
+      Height = 15
+      Top = 72
+      Width = 351
+      Caption = 'Use this to transfer ownership of the account to another user''s key.'
+      ParentColor = False
+    end
+    object rbChangeAccountPrivateKey: TRadioButton
+      Left = 8
+      Height = 19
+      Top = 120
+      Width = 207
+      Caption = 'Change to another key in my wallet'
+      TabOrder = 0
+    end
+    object lblChangeAccountPrivateKey: TLabel
+      Left = 8
+      Height = 15
+      Top = 152
+      Width = 383
+      Caption = 'This will change the key of your account to another key that you control.'
+      ParentColor = False
+    end
+  end
+end

+ 63 - 0
src/gui/wizards/operations/UWIZChangeKey_SelectOption.pas

@@ -0,0 +1,63 @@
+unit UWIZChangeKey_SelectOption;
+
+{$mode delphi}
+{$modeswitch nestedprocvars}
+
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer
+}
+
+interface
+
+uses
+  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
+  ExtCtrls, UWizard, UWIZChangeKey, UWIZModels, UWIZChangeKey_EnterKey, UWIZChangeKey_SelectKey, UWIZChangeKey_Confirmation;
+
+type
+
+  { TWIZChangeKey_SelectOption }
+
+  TWIZChangeKey_SelectOption = class(TWizardForm<TWIZOperationsModel>)
+    gbChangeKeyOptions: TGroupBox;
+    lblTransferAccountOwnership: TLabel;
+    lblNote: TLabel;
+    lblChangeAccountPrivateKey: TLabel;
+    rbTransferAccountOwnership: TRadioButton;
+    rbChangeAccountPrivateKey: TRadioButton;
+  public
+    procedure OnNext; override;
+  end;
+
+
+
+implementation
+
+{$R *.lfm}
+
+uses UAccounts, USettings, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+
+{ TWIZChangeKey_SelectOption }
+
+procedure TWIZChangeKey_SelectOption.OnNext;
+begin
+  if rbTransferAccountOwnership.Checked then
+  begin
+    Model.ChangeKey.ChangeKeyMode := akaTransferAccountOwnership;
+    UpdatePath(ptReplaceAllNext, [TWIZChangeKey_EnterKey,
+      TWIZChangeKey_Confirmation]);
+  end
+  else if rbChangeAccountPrivateKey.Checked then
+  begin
+    Model.ChangeKey.ChangeKeyMode := akaChangeAccountPrivateKey;
+    UpdatePath(ptReplaceAllNext, [TWIZChangeKey_SelectKey,
+      TWIZChangeKey_Confirmation]);
+  end;
+end;
+
+end.

+ 453 - 0
src/gui/wizards/operations/UWIZConfirmAccount.pas

@@ -0,0 +1,453 @@
+unit UWIZChangeKey;
+
+{$mode delphi}
+
+{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  Acknowledgements:
+    Herman Schoenfeld <[email protected]>: added grid-based layout
+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Dialogs, UCrypto, UCommon, UWizard, UAccounts, LCLType, UWIZModels;
+
+type
+
+  { TWIZChangeKeyWizard }
+
+  TWIZChangeKeyWizard = class(TWizard<TWIZOperationsModel>)
+  private
+    function UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
+    function UpdateOperationOptions(var errors: string): boolean;
+    function UpdateOpChangeKey(const TargetAccount: TAccount; var SignerAccount: TAccount; var NewPublicKey: TAccountKey; var errors: ansistring): boolean;
+    procedure WIZChangeKeyOwnership();
+  public
+    constructor Create(AOwner: TComponent); override;
+    function DetermineHasNext: boolean; override;
+    function DetermineHasPrevious: boolean; override;
+    function FinishRequested(out message: ansistring): boolean; override;
+    function CancelRequested(out message: ansistring): boolean; override;
+  end;
+
+implementation
+
+uses
+  UBlockChain,
+  UOpTransaction,
+  UNode,
+  UConst,
+  UWallet,
+  UECIES,
+  UAES,
+  UWIZChangeKey_Start,
+  UWIZChangeKey_Confirmation;
+
+{ TWIZChangeKeyWizard }
+
+function TWIZChangeKeyWizard.UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
+var
+  valid: boolean;
+  payload_encrypted, payload_u: string;
+  account: TAccount;
+begin
+  valid := False;
+  payload_encrypted := '';
+  Model.Payload.EncodedBytes := '';
+  errors := 'Unknown error';
+  payload_u := Model.Payload.Content;
+
+  try
+    if (payload_u = '') then
+    begin
+      valid := True;
+      Exit;
+    end;
+    case Model.Payload.Mode of
+
+      akaEncryptWithSender:
+      begin
+        // Use sender
+        errors := 'Error encrypting';
+        account := SenderAccount;
+        payload_encrypted := ECIESEncrypt(account.accountInfo.accountKey, payload_u);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaEncryptWithReceiver:
+      begin
+        errors := 'Public key: ' + 'Error encrypting';
+
+        if Model.WIZChangeKey.AccountKey.EC_OpenSSL_NID <>
+          CT_Account_NUL.accountInfo.accountKey.EC_OpenSSL_NID then
+        begin
+          payload_encrypted := ECIESEncrypt(Model.WIZChangeKey.AccountKey, payload_u);
+          valid := payload_encrypted <> '';
+        end
+        else
+        begin
+          valid := False;
+          errors := 'Selected private key is not valid to encode';
+          exit;
+        end;
+      end;
+
+      akaEncryptWithPassword:
+      begin
+        payload_encrypted := TAESComp.EVP_Encrypt_AES256(
+          payload_u, Model.Payload.Password);
+        valid := payload_encrypted <> '';
+      end;
+
+      akaNotEncrypt:
+      begin
+        payload_encrypted := payload_u;
+        valid := True;
+      end
+
+      else
+      begin
+        raise Exception.Create('Invalid Encryption Selection');
+      end;
+    end;
+
+  finally
+    if valid then
+    begin
+      if length(payload_encrypted) > CT_MaxPayloadSize then
+      begin
+        valid := False;
+        errors := 'Payload size is bigger than ' + IntToStr(CT_MaxPayloadSize) +
+          ' (' + IntToStr(length(payload_encrypted)) + ')';
+      end;
+
+    end;
+    Model.Payload.EncodedBytes := payload_encrypted;
+    Result := valid;
+  end;
+
+end;
+
+function TWIZChangeKeyWizard.UpdateOperationOptions(var errors: string): boolean;
+var
+  iAcc, iWallet: integer;
+  sender_account, signer_account: TAccount;
+  publicKey: TAccountKey;
+  wk: TWalletKey;
+  e: string;
+  amount: int64;
+begin
+  Result := False;
+  errors := '';
+  if not Assigned(TWallet.Keys) then
+  begin
+    errors := 'No wallet keys';
+    Exit;
+  end;
+
+  if Length(Model.WIZChangeKey.SelectedAccounts) = 0 then
+  begin
+    errors := 'No sender account';
+    Exit;
+  end
+  else
+  begin
+
+    for iAcc := Low(Model.WIZChangeKey.SelectedAccounts) to High(Model.WIZChangeKey.SelectedAccounts) do
+    begin
+      sender_account := Model.WIZChangeKey.SelectedAccounts[iAcc];
+      iWallet := TWallet.Keys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
+      if (iWallet < 0) then
+      begin
+        errors := 'Private key of account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
+          ' not found in wallet';
+        Exit;
+      end;
+      wk := TWallet.Keys.Key[iWallet];
+      if not assigned(wk.PrivateKey) then
+      begin
+        if wk.CryptedKey <> '' then
+        begin
+          // TODO: handle unlocking of encrypted wallet here
+          errors := 'Wallet is password protected. Need password';
+        end
+        else
+        begin
+          errors := 'Only public key of account ' +
+            TAccountComp.AccountNumberToAccountTxtNumber(sender_account.account) +
+            ' found in wallet. You cannot operate with this account';
+        end;
+        Exit;
+      end;
+    end;
+  end;
+
+  Result := UpdateOpChangeKey(Model.WIZChangeKey.SelectedAccounts[0], signer_account,
+    publicKey, errors);
+  UpdatePayload(sender_account, e);
+end;
+
+function TWIZChangeKeyWizard.UpdateOpChangeKey(const TargetAccount: TAccount;
+  var SignerAccount: TAccount; var NewPublicKey: TAccountKey;
+  var errors: ansistring): boolean;
+begin
+  Result := False;
+  errors := '';
+  try
+    if not TAccountComp.AccountKeyFromImport(Model.WIZChangeKey.NewPublicKey,
+      NewPublicKey, errors) then
+    begin
+      Exit;
+    end;
+
+    if TNode.Node.Bank.SafeBox.CurrentProtocol >= 1 then
+    begin
+      // Signer:
+      SignerAccount := Model.Signer.SignerAccount;
+      if (TAccountComp.IsAccountLocked(SignerAccount.accountInfo,
+        TNode.Node.Bank.BlocksCount)) then
+      begin
+        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
+          SignerAccount.account) + ' is locked until block ' + IntToStr(
+          SignerAccount.accountInfo.locked_until_block);
+        exit;
+      end;
+      if (not TAccountComp.EqualAccountKeys(
+        SignerAccount.accountInfo.accountKey, TargetAccount.accountInfo.accountKey)) then
+      begin
+        errors := 'Signer account ' + TAccountComp.AccountNumberToAccountTxtNumber(
+          SignerAccount.account) + ' is not owner of account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(TargetAccount.account);
+        exit;
+      end;
+    end
+    else
+    begin
+      SignerAccount := TargetAccount;
+    end;
+
+    if (TAccountComp.EqualAccountKeys(TargetAccount.accountInfo.accountKey,
+      NewPublicKey)) then
+    begin
+      errors := 'New public key is the same public key';
+      exit;
+    end;
+
+  finally
+    Result := errors = '';
+  end;
+end;
+
+procedure TWIZChangeKeyWizard.WIZChangeKeyOwnership();
+var
+  _V2, dooperation: boolean;
+  iAcc, i: integer;
+  _totalamount, _totalfee, _totalSignerFee, _amount, _fee: int64;
+  _signer_n_ops: cardinal;
+  operationstxt, operation_to_string, errors, auxs: string;
+  wk: TWalletKey;
+  ops: TOperationsHashTree;
+  op: TPCOperation;
+  account, signerAccount: TAccount;
+  _newOwnerPublicKey: TECDSA_Public;
+label
+  loop_start;
+begin
+  if not Assigned(TWallet.Keys) then
+    raise Exception.Create('No wallet keys');
+  if not UpdateOperationOptions(errors) then
+    raise Exception.Create(errors);
+  ops := TOperationsHashTree.Create;
+
+  try
+    _V2 := TNode.Node.Bank.SafeBox.CurrentProtocol >= CT_PROTOCOL_2;
+    _totalamount := 0;
+    _totalfee := 0;
+    _totalSignerFee := 0;
+    _signer_n_ops := 0;
+    operationstxt := '';
+    operation_to_string := '';
+    for iAcc := Low(Model.WIZChangeKey.SelectedAccounts) to High(Model.WIZChangeKey.SelectedAccounts) do
+    begin
+      loop_start:
+        op := nil;
+      account := Model.WIZChangeKey.SelectedAccounts[iAcc];
+      if not UpdatePayload(account, errors) then
+      begin
+        raise Exception.Create('Error encoding payload of sender account ' +
+          TAccountComp.AccountNumberToAccountTxtNumber(account.account) + ': ' + errors);
+      end;
+      i := TWallet.Keys.IndexOfAccountKey(account.accountInfo.accountKey);
+      if i < 0 then
+      begin
+        raise Exception.Create('Sender account private key not found in Wallet');
+      end;
+
+      wk := TWallet.Keys.Key[i];
+      dooperation := True;
+      // Default fee
+      if account.balance > uint64(Model.Fee.DefaultFee) then
+        _fee := Model.Fee.DefaultFee
+      else
+        _fee := account.balance;
+
+      if not UpdateOpChangeKey(account, signerAccount, _newOwnerPublicKey, errors) then
+      begin
+        raise Exception.Create(errors);
+      end;
+      if _V2 then
+      begin
+        // must ensure is Signer account last if included in sender accounts (not necessarily ordered enumeration)
+        if (iAcc < Length(Model.WIZChangeKey.SelectedAccounts) - 1) and
+          (account.account = signerAccount.account) then
+        begin
+          TArrayTool<TAccount>.Swap(Model.WIZChangeKey.SelectedAccounts, iAcc,
+            Length(Model.WIZChangeKey.SelectedAccounts) - 1); // ensure signer account processed last
+          // TArrayTool_internal<Cardinal>.Swap(_senderAccounts, iAcc, Length(_senderAccounts) - 1);
+          goto loop_start; // TODO: remove ugly hack with refactoring!
+        end;
+
+        // Maintain correct signer fee distribution
+        if uint64(_totalSignerFee) >= signerAccount.balance then
+          _fee := 0
+        else if signerAccount.balance - uint64(_totalSignerFee) >
+          uint64(Model.Fee.DefaultFee) then
+          _fee := Model.Fee.DefaultFee
+        else
+          _fee := signerAccount.balance - uint64(_totalSignerFee);
+        op := TOpChangeKeySigned.Create(signerAccount.account,
+          signerAccount.n_operation + _signer_n_ops + 1, account.account,
+          wk.PrivateKey, _newOwnerPublicKey, _fee, Model.Payload.EncodedBytes);
+        Inc(_signer_n_ops);
+        Inc(_totalSignerFee, _fee);
+      end
+      else
+      begin
+        op := TOpChangeKey.Create(account.account, account.n_operation +
+          1, account.account, wk.PrivateKey, _newOwnerPublicKey, _fee, Model.Payload.EncodedBytes);
+      end;
+      Inc(_totalfee, _fee);
+      operationstxt :=
+        'Change private key to ' + TAccountComp.GetECInfoTxt(
+        _newOwnerPublicKey.EC_OpenSSL_NID);
+
+      if Assigned(op) and (dooperation) then
+      begin
+        ops.AddOperationToHashTree(op);
+        if operation_to_string <> '' then
+          operation_to_string := operation_to_string + #10;
+        operation_to_string := operation_to_string + op.ToString;
+      end;
+      FreeAndNil(op);
+    end;
+
+    if (ops.OperationsCount = 0) then
+      raise Exception.Create('No valid operation to execute');
+
+    if (Length(Model.WIZChangeKey.SelectedAccounts) > 1) then
+    begin
+      auxs := '';
+      if Application.MessageBox(
+        PChar('Execute ' + IntToStr(Length(Model.WIZChangeKey.SelectedAccounts)) +
+        ' operations?' + #10 + 'Operation: ' + operationstxt + #10 +
+        auxs + 'Total fee: ' + TAccountComp.FormatMoney(_totalfee) +
+        #10 + #10 + 'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title), MB_YESNO + MB_ICONINFORMATION + MB_DEFBUTTON2) <>
+        idYes then
+      begin
+        Exit;
+      end;
+    end
+    else
+    begin
+      if Application.MessageBox(PChar('Execute this operation:' + #10 +
+        #10 + operation_to_string + #10 + #10 +
+        'Note: This operation will be transmitted to the network!'),
+        PChar(Application.Title), MB_YESNO + MB_ICONINFORMATION + MB_DEFBUTTON2) <> idYes then
+      begin
+        Exit;
+      end;
+    end;
+    i := TNode.Node.AddOperations(nil, ops, nil, errors);
+    if (i = ops.OperationsCount) then
+    begin
+      operationstxt := 'Successfully executed ' + IntToStr(i) +
+        ' operations!' + #10 + #10 + operation_to_string;
+      if i > 1 then
+      begin
+
+        ShowMessage(operationstxt);
+      end
+      else
+      begin
+        Application.MessageBox(
+          PChar('Successfully executed ' + IntToStr(i) + ' operations!' +
+          #10 + #10 + operation_to_string),
+          PChar(Application.Title), MB_OK + MB_ICONINFORMATION);
+      end;
+
+    end
+    else if (i > 0) then
+    begin
+      operationstxt := 'One or more of your operations has not been executed:' +
+        #10 + 'Errors:' + #10 + errors + #10 + #10 +
+        'Total successfully executed operations: ' + IntToStr(i);
+
+      ShowMessage(operationstxt);
+    end
+    else
+    begin
+      raise Exception.Create(errors);
+    end;
+
+
+  finally
+    ops.Free;
+  end;
+
+end;
+
+constructor TWIZChangeKeyWizard.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner, [TWIZChangeKey_Start,
+    TWIZChangeKey_Confirmation]);
+  TitleText := 'Transfer Account';
+  FinishText := 'Transfer Account';
+end;
+
+function TWIZChangeKeyWizard.DetermineHasNext: boolean;
+begin
+  Result := not (CurrentScreen is TWIZChangeKey_Confirmation);
+end;
+
+function TWIZChangeKeyWizard.DetermineHasPrevious: boolean;
+begin
+  Result := inherited DetermineHasPrevious;
+end;
+
+function TWIZChangeKeyWizard.FinishRequested(out message: ansistring): boolean;
+begin
+  // Execute the Transfer Account Action here
+  try
+    Result := True;
+    WIZChangeKeyOwnership();
+  except
+    On E: Exception do
+    begin
+      Result := False;
+      message := E.ToString;
+    end;
+  end;
+end;
+
+function TWIZChangeKeyWizard.CancelRequested(out message: ansistring): boolean;
+begin
+  Result := True;
+end;
+
+end.

+ 1 - 1
src/gui/wizards/operations/UWIZEnlistAccountForSale_Start.lfm

@@ -3,10 +3,10 @@ object WIZEnlistAccountForSale_Start: TWIZEnlistAccountForSale_Start
   Height = 253
   Top = 0
   Width = 429
+  ActiveControl = rbPublicSale
   Caption = 'WIZEnlistAccountForSale_Start'
   ClientHeight = 253
   ClientWidth = 429
-  LCLVersion = '1.8.1.0'
   Visible = False
   object Label1: TLabel
     Left = 24

+ 5 - 5
src/gui/wizards/operations/UWIZFeeOverride.lfm

@@ -55,20 +55,20 @@ object WIZFeeOverride: TWIZFeeOverride
       ParentColor = False
       ParentFont = False
     end
-    object lbltotalfee: TLabel
+    object lblestimatedfee: TLabel
       Left = 11
       Height = 15
       Top = 66
-      Width = 162
-      Caption = 'Total Fee For All Transcations:'
+      Width = 190
+      Caption = 'Estimated Fee For All Transcations:'
       Font.Style = [fsBold]
       ParentColor = False
       ParentFont = False
     end
     object lblTotalFeeValue: TLabel
-      Left = 192
+      Left = 208
       Height = 15
-      Top = 64
+      Top = 66
       Width = 28
       Caption = 'xxxx'
       Font.Style = [fsBold]

+ 5 - 7
src/gui/wizards/operations/UWIZFeeOverride.pas

@@ -18,7 +18,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, Spin, UCommon, UCommon.Collections, UWallet,
-  UFRMAccountSelect, UNode, UWizard, UWIZSendPASC, UWIZPayloadOverride, UWIZSelectSignerOverride, UWIZSendPASC_Confirmation, UWIZModels;
+  UFRMAccountSelect, UNode, UWizard, UWIZPayloadOverride, UWIZSelectSignerOverride, UWIZModels;
 
 type
 
@@ -27,7 +27,7 @@ type
   TWIZFeeOverride = class(TWizardForm<TWIZOperationsModel>)
     fseFee: TFloatSpinEdit;
     gbTransactionFee: TGroupBox;
-    lbltotalfee: TLabel;
+    lblestimatedfee: TLabel;
     lblPASC: TLabel;
     lblNote1: TLabel;
     lblNote2: TLabel;
@@ -58,7 +58,7 @@ var
 begin
   TAccountComp.TxtToMoney(Trim(fseFee.ValueToStr(fseFee.Value)), opfee);
   lblTotalFeeValue.Caption := Format('%s PASC', [TAccountComp.FormatMoney(opfee *
-    Length(Model.SendPASC.SelectedAccounts))]);
+    Length(Model.Account.SelectedAccounts))]);
 end;
 
 procedure TWIZFeeOverride.fseFeeChange(Sender: TObject);
@@ -77,13 +77,11 @@ begin
     Model.Fee.SingleOperationFee);
   if Model.Payload.HasPayload then
   begin
-    UpdatePath(ptReplaceAllNext, [TWIZPayloadOverride,
-      TWIZSendPASC_Confirmation]);
+    UpdatePath(ptInject, [TWIZPayloadOverride]);
   end
   else
   begin
-    UpdatePath(ptReplaceAllNext, [TWIZSelectSignerOverride,
-      TWIZSendPASC_Confirmation]);
+  UpdatePath(ptInject, [TWIZSelectSignerOverride]);
   end;
 end;
 

+ 2 - 3
src/gui/wizards/operations/UWIZPayloadContentOverride.pas

@@ -18,7 +18,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, UCommon, UCommon.Collections,
-  UWizard, UWIZSendPASC, UWIZSelectSignerOverride, UWIZSendPASC_Confirmation, UWIZModels;
+  UWizard, UWIZSelectSignerOverride, UWIZModels;
 
 type
 
@@ -46,8 +46,7 @@ uses
 procedure TWIZPayloadContentOverride.OnNext;
 begin
   Model.Payload.Content := mmoPayload.Lines.Text;
-   UpdatePath(ptReplaceAllNext, [TWIZSelectSignerOverride,
-      TWIZSendPASC_Confirmation]);
+   UpdatePath(ptInject, [TWIZSelectSignerOverride]);
 end;
 
 end.

+ 1 - 0
src/gui/wizards/operations/UWIZPayloadOverride.lfm

@@ -7,6 +7,7 @@ object WIZPayloadOverride: TWIZPayloadOverride
   Caption = 'Form1'
   ClientHeight = 253
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object grpPayload: TGroupBox
     Left = 8

+ 3 - 5
src/gui/wizards/operations/UWIZPayloadOverride.pas

@@ -18,7 +18,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, UCommon, UCommon.Collections,
-  UWizard, UWIZSendPASC, UWIZPayloadContentOverride, UWIZPayloadPasswordOverride, UWIZSendPASC_Confirmation, UWIZModels;
+  UWizard, UWIZPayloadContentOverride, UWIZPayloadPasswordOverride, UWIZModels;
 
 type
 
@@ -75,13 +75,11 @@ begin
   case Model.Payload.Mode of
     akaEncryptWithPassword:
     begin
-      UpdatePath(ptReplaceAllNext, [TWIZPayloadPasswordOverride,
-        TWIZSendPASC_Confirmation]);
+      UpdatePath(ptInject, [TWIZPayloadPasswordOverride]);
     end
     else
     begin
-      UpdatePath(ptReplaceAllNext, [TWIZPayloadContentOverride,
-        TWIZSendPASC_Confirmation]);
+      UpdatePath(ptInject, [TWIZPayloadContentOverride]);
     end;
   end;
 end;

+ 1 - 1
src/gui/wizards/operations/UWIZPayloadPasswordOverride.lfm

@@ -6,7 +6,7 @@ object WIZPayloadPasswordOverride: TWIZPayloadPasswordOverride
   Caption = 'Form1'
   ClientHeight = 253
   ClientWidth = 429
-  LCLVersion = '1.8.1.0'
+  LCLVersion = '1.8.2.0'
   Visible = False
   object grpPayload: TGroupBox
     Left = 8

+ 2 - 3
src/gui/wizards/operations/UWIZPayloadPasswordOverride.pas

@@ -18,7 +18,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, UCommon, UCommon.Collections,
-  UWizard, UWIZSendPASC, UWIZSelectSignerOverride, UWIZSendPASC_Confirmation, UWIZModels;
+  UWizard, UWIZSelectSignerOverride, UWIZModels;
 
 type
 
@@ -48,8 +48,7 @@ uses
 procedure TWIZPayloadPasswordOverride.OnNext;
 begin
   Model.Payload.Password := edtPassword.Text;
-  UpdatePath(ptReplaceAllNext, [TWIZSelectSignerOverride,
-    TWIZSendPASC_Confirmation]);
+   UpdatePath(ptInject, [TWIZSelectSignerOverride]);
 end;
 
 function TWIZPayloadPasswordOverride.Validate(out message: ansistring): boolean;

+ 7 - 9
src/gui/wizards/operations/UWIZSelectSignerOverride.pas

@@ -18,7 +18,7 @@ interface
 uses
   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
   ExtCtrls, Buttons, UCommon, UCommon.Collections, UWallet,
-  UFRMAccountSelect, UNode, UWizard, UWIZSendPASC, UWIZSendPASC_Confirmation, UWIZModels;
+  UFRMAccountSelect, UNode, UWizard, UWIZModels;
 
 type
 
@@ -33,8 +33,6 @@ type
     rbSecondary: TRadioButton;
     procedure cbSignerAccountChange(Sender: TObject);
 
-
-
   public
     procedure OnPresent; override;
     procedure OnNext; override;
@@ -62,7 +60,7 @@ begin
   begin
     lblBalance.Font.Color := clGreen;
     lblBalance.Caption := Format('%s PASC',
-      [TAccountComp.FormatMoney(Model.SendPASC.SelectedAccounts[PtrInt(
+      [TAccountComp.FormatMoney(Model.Signer.SignerCandidates[PtrInt(
       cbSignerAccount.Items.Objects[cbSignerAccount.ItemIndex])].Balance)]);
   end;
 end;
@@ -83,23 +81,23 @@ begin
   try
     cbSignerAccount.Items.Clear;
     cbSignerAccount.Items.Add('Select Signer Account');
-    for i := Low(Model.SendPASC.SelectedAccounts) to High(Model.SendPASC.SelectedAccounts) do
+    for i := Low(Model.Signer.SignerCandidates) to High(Model.Signer.SignerCandidates) do
     begin
-      acc := Model.SendPASC.SelectedAccounts[i];
+      acc := Model.Signer.SignerCandidates[i];
       accNumberwithChecksum := GetAccNoWithChecksum(acc.account);
       cbSignerAccount.Items.AddObject(accNumberwithChecksum, TObject(i));
     end;
   finally
     cbSignerAccount.Items.EndUpdate;
   end;
-  cbSignerAccount.ItemIndex := Model.SendPASC.SelectedIndex;
+  cbSignerAccount.ItemIndex := Model.Signer.SelectedIndex;
   cbSignerAccountChange(Self);
 end;
 
 procedure TWIZSelectSignerOverride.OnNext;
 begin
-  Model.SendPASC.SelectedIndex := cbSignerAccount.ItemIndex;
-  Model.Signer.SignerAccount := Model.SendPASC.SelectedAccounts[PtrInt(
+  Model.Signer.SelectedIndex := cbSignerAccount.ItemIndex;
+  Model.Signer.SignerAccount := Model.Signer.SignerCandidates[PtrInt(
     cbSignerAccount.Items.Objects[cbSignerAccount.ItemIndex])];
   if rbPrimary.Checked then
   begin

+ 48 - 42
src/gui/wizards/operations/UWIZSendPASC.pas

@@ -2,13 +2,14 @@ unit UWIZSendPASC;
 
 {$mode delphi}
 
-{ Copyright (c) 2018 by Ugochukwu Mmaduekwe
+{ Copyright (c) 2018 Sphere 10 Software (http://www.sphere10.com/)
 
   Distributed under the MIT software license, see the accompanying file LICENSE
   or visit http://www.opensource.org/licenses/mit-license.php.
 
   Acknowledgements:
-    Herman Schoenfeld <[email protected]>: added grid-based layout
+  Ugochukwu Mmaduekwe - main developer
+  Herman Schoenfeld - designer <[email protected]>: added grid-based layout
 }
 
 interface
@@ -22,11 +23,9 @@ type
 
   TWIZSendPASCWizard = class(TWizard<TWIZOperationsModel>)
   private
-    function UpdatePayload(const SenderAccount: TAccount;
-      var errors: string): boolean;
+    function UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
     function UpdateOperationOptions(var errors: string): boolean;
-    function UpdateOpTransaction(const SenderAccount: TAccount;
-      var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
+    function UpdateOpTransaction(const SenderAccount: TAccount; var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
     procedure SendPASC();
   public
     constructor Create(AOwner: TComponent); override;
@@ -67,7 +66,7 @@ begin
     Exit;
   end;
 
-  if Length(Model.SendPASC.SelectedAccounts) = 0 then
+  if Length(Model.Account.SelectedAccounts) = 0 then
   begin
     errors := 'No sender account';
     Exit;
@@ -75,9 +74,9 @@ begin
   else
   begin
 
-    for iAcc := Low(Model.SendPASC.SelectedAccounts) to High(Model.SendPASC.SelectedAccounts) do
+    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
     begin
-      sender_account := Model.SendPASC.SelectedAccounts[iAcc];
+      sender_account := Model.Account.SelectedAccounts[iAcc];
       iWallet := TWallet.Keys.IndexOfAccountKey(sender_account.accountInfo.accountKey);
       if (iWallet < 0) then
       begin
@@ -105,12 +104,11 @@ begin
     end;
   end;
 
-  Result := UpdateOpTransaction(Model.SendPASC.SelectedAccounts[0], dest_account, amount, errors);
+  Result := UpdateOpTransaction(Model.Account.SelectedAccounts[0], dest_account, amount, errors);
   UpdatePayload(sender_account, e);
 end;
 
-function TWIZSendPASCWizard.UpdateOpTransaction(const SenderAccount: TAccount;
-  var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
+function TWIZSendPASCWizard.UpdateOpTransaction(const SenderAccount: TAccount; var DestAccount: TAccount; var amount: int64; var errors: string): boolean;
 var
   c: cardinal;
 begin
@@ -119,13 +117,16 @@ begin
 
   DestAccount := Model.SendPASC.DestinationAccount;
 
-  if Length(Model.SendPASC.SelectedAccounts) = 1 then
-  begin
-    amount := Model.SendPASC.SingleAmountToSend;
-  end
-  else
-  begin
-    amount := 0; // ALL BALANCE
+  case Model.SendPASC.SendPASCMode of
+    akaAllBalance:
+    begin
+      amount := 0; // ALL BALANCE
+    end;
+
+    akaSpecifiedAmount:
+    begin
+      amount := Model.SendPASC.SingleAmountToSend;
+    end;
   end;
 
   if DestAccount.account = SenderAccount.account then
@@ -134,14 +135,17 @@ begin
     Exit;
   end;
 
-  if (Length(Model.SendPASC.SelectedAccounts) = 1) then
-  begin
-    if (SenderAccount.balance < (amount + Model.Fee.SingleOperationFee)) then
+  case Model.SendPASC.SendPASCMode of
+    akaSpecifiedAmount:
     begin
-      errors := 'Insufficient funds';
-      Exit;
+      if (SenderAccount.balance < (amount + Model.Fee.SingleOperationFee)) then
+      begin
+        errors := 'Insufficient funds';
+        Exit;
+      end;
     end;
   end;
+
   Result := True;
 end;
 
@@ -170,10 +174,10 @@ begin
     _signer_n_ops := 0;
     operationstxt := '';
     operation_to_string := '';
-    for iAcc := Low(Model.SendPASC.SelectedAccounts) to High(Model.SendPASC.SelectedAccounts) do
+    for iAcc := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
     begin
       op := nil;
-      account := Model.SendPASC.SelectedAccounts[iAcc];
+      account := Model.Account.SelectedAccounts[iAcc];
       if not UpdatePayload(account, errors) then
       begin
         raise Exception.Create('Error encoding payload of sender account ' +
@@ -195,27 +199,30 @@ begin
 
       if not UpdateOpTransaction(account, destAccount, _amount, errors) then
         raise Exception.Create(errors);
-      if Length(Model.SendPASC.SelectedAccounts) > 1 then
+
+      if account.balance > 0 then
       begin
-        if account.balance > 0 then
+        if account.balance > Model.Fee.SingleOperationFee then
         begin
-          if account.balance > Model.Fee.SingleOperationFee then
-          begin
-            _amount := account.balance - Model.Fee.SingleOperationFee;
-            _fee := Model.Fee.SingleOperationFee;
-          end
-          else
-          begin
-            _amount := account.balance;
-            _fee := 0;
+          case Model.SendPASC.SendPASCMode of
+            akaAllBalance:
+            begin
+              _amount := account.balance - Model.Fee.SingleOperationFee;
+              _fee := Model.Fee.SingleOperationFee;
+            end;
           end;
         end
         else
-          dooperation := False;
+        begin
+          _amount := account.balance;
+          _fee := 0;
+        end;
       end
       else
       begin
+        dooperation := False;
       end;
+
       if dooperation then
       begin
         op := TOpTransaction.CreateTransaction(
@@ -240,12 +247,12 @@ begin
     if (ops.OperationsCount = 0) then
       raise Exception.Create('No valid operation to execute');
 
-    if (Length(Model.SendPASC.SelectedAccounts) > 1) then
+    if (Length(Model.Account.SelectedAccounts) > 1) then
     begin
       auxs := 'Total amount that dest will receive: ' + TAccountComp.FormatMoney(
         _totalamount) + #10;
       if Application.MessageBox(
-        PChar('Execute ' + IntToStr(Length(Model.SendPASC.SelectedAccounts)) +
+        PChar('Execute ' + IntToStr(Length(Model.Account.SelectedAccounts)) +
         ' operations?' + #10 + 'Operation: ' + operationstxt + #10 +
         auxs + 'Total fee: ' + TAccountComp.FormatMoney(_totalfee) +
         #10 + #10 + 'Note: This operation will be transmitted to the network!'),
@@ -316,8 +323,7 @@ begin
   end;
 end;
 
-function TWIZSendPASCWizard.UpdatePayload(const SenderAccount: TAccount;
-  var errors: string): boolean;
+function TWIZSendPASCWizard.UpdatePayload(const SenderAccount: TAccount; var errors: string): boolean;
 var
   valid: boolean;
   payload_encrypted, payload_u: string;

+ 1 - 1
src/gui/wizards/operations/UWIZSendPASC_ConfirmSender.lfm

@@ -6,7 +6,7 @@ object WIZSendPASC_ConfirmSender: TWIZSendPASC_ConfirmSender
   Caption = 'WIZSendPASC_ConfirmSender'
   ClientHeight = 261
   ClientWidth = 429
-  LCLVersion = '1.8.1.0'
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gpSender: TGroupBox
     Left = 4

+ 40 - 24
src/gui/wizards/operations/UWIZSendPASC_ConfirmSender.pas

@@ -27,10 +27,11 @@ type
     gpSender: TGroupBox;
     paGrid: TPanel;
   private
-    FSendersGrid : TVisualGrid;
+    FSendersGrid: TVisualGrid;
   public
     procedure OnPresent; override;
     procedure OnNext; override;
+    function Validate(out message: ansistring): boolean; override;
   end;
 
 
@@ -39,56 +40,58 @@ implementation
 
 {$R *.lfm}
 
-uses UAccounts, UDataSources, UCommon, UCommon.UI, Generics.Collections;
+uses UAccounts, UCoreUtils, UDataSources, UCommon, UCommon.UI, Generics.Collections;
 
 type
 
   { TAccountSenderDataSource }
 
   TAccountSenderDataSource = class(TAccountsDataSourceBase)
-    private
-      FModel : TWIZOperationsModel.TSendPASCModel;
-    public
-      property Model : TWIZOperationsModel.TSendPASCModel read FModel write FModel;
-      procedure FetchAll(const AContainer : TList<TAccount>); override;
+  private
+    FModel: TWIZOperationsModel;
+  public
+    property Model: TWIZOperationsModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
   end;
 
 { TWIZSendPASC_ConfirmSender }
 
 procedure TWIZSendPASC_ConfirmSender.OnPresent;
 var
-  data : TAccountSenderDataSource;
+  Data: TAccountSenderDataSource;
 begin
   FSendersGrid := TVisualGrid.Create(Self);
-  FSendersGrid.CanSearch:= False;
+  FSendersGrid.CanSearch := False;
   FSendersGrid.SortMode := smMultiColumn;
   FSendersGrid.FetchDataInThread := False;
   FSendersGrid.AutoPageSize := True;
   FSendersGrid.SelectionType := stNone;
   FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  with FSendersGrid.AddColumn('Account') do begin
-    StretchedToFill := true;
+  with FSendersGrid.AddColumn('Account') do
+  begin
+    StretchedToFill := True;
     Binding := 'AccountNumber';
     SortBinding := 'AccountNumber';
     DisplayBinding := 'Account';
     Width := 100;
     HeaderFontStyles := [fsBold];
     DataFontStyles := [fsBold];
-    Filters:=SORTABLE_NUMERIC_FILTER;
+    Filters := SORTABLE_NUMERIC_FILTER;
   end;
-  with FSendersGrid.AddColumn('Balance') do begin
+  with FSendersGrid.AddColumn('Balance') do
+  begin
     Binding := 'BalanceDecimal';
     SortBinding := 'Balance';
     DisplayBinding := 'Balance';
     Width := 100;
-    HeaderAlignment:=taRightJustify;
-    DataAlignment:=taRightJustify;
+    HeaderAlignment := taRightJustify;
+    DataAlignment := taRightJustify;
     Renderer := TCellRenderers.PASC;
-    Filters:=SORTABLE_NUMERIC_FILTER;
+    Filters := SORTABLE_NUMERIC_FILTER;
   end;
-  data := TAccountSenderDataSource.Create(FSendersGrid);
-  data.Model := Model.SendPASC;
-  FSendersGrid.DataSource := data;
+  Data := TAccountSenderDataSource.Create(FSendersGrid);
+  Data.Model := Model;
+  FSendersGrid.DataSource := Data;
   paGrid.AddControlDockCenter(FSendersGrid);
 end;
 
@@ -97,17 +100,30 @@ begin
   UpdatePath(ptReplaceAllNext, [TWIZSendPASC_EnterRecipient, TWIZSendPASC_Confirmation]);
 end;
 
+function TWIZSendPASC_ConfirmSender.Validate(out message: ansistring): boolean;
+begin
+  Result := True;
+  // get signer accounts from selected accounts
+  Model.Signer.SignerCandidates := TCoreTool.GetSignerCandidates(Length(Model.Account.SelectedAccounts), Model.Account.SelectedAccounts);
+
+  if Length(Model.Signer.SignerCandidates) < 1 then
+  begin
+    Result := False;
+    message := 'no valid signer account was found.';
+  end;
+
+end;
+
 { TAccountSenderDataSource }
 
-procedure TAccountSenderDataSource.FetchAll(const AContainer : TList<TAccount>);
+procedure TAccountSenderDataSource.FetchAll(const AContainer: TList<TAccount>);
 var
-  i: Integer;
+  i: integer;
 begin
-  for i := Low(Model.SelectedAccounts) to High(Model.SelectedAccounts) do
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
   begin
-    AContainer.Add( Model.SelectedAccounts[i] );
+    AContainer.Add(Model.Account.SelectedAccounts[i]);
   end;
 end;
 
 end.
-

+ 43 - 39
src/gui/wizards/operations/UWIZSendPASC_Confirmation.pas

@@ -31,7 +31,7 @@ type
     lblSgnAcc: TLabel;
     paGrid: TPanel;
   private
-    FSendersGrid : TVisualGrid;
+    FSendersGrid: TVisualGrid;
   public
     procedure OnPresent; override;
   end;
@@ -48,97 +48,101 @@ type
   { TAccountSenderDataSource }
 
   TAccountSenderDataSource = class(TAccountsDataSourceBase)
-    private
-      FModel : TWIZOperationsModel;
-    protected
-      function GetColumns : TDataColumns; override;
-    public
-      property Model : TWIZOperationsModel read FModel write FModel;
-      procedure FetchAll(const AContainer : TList<TAccount>); override;
-      function GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant; override;
+  private
+    FModel: TWIZOperationsModel;
+  protected
+    function GetColumns: TDataColumns; override;
+  public
+    property Model: TWIZOperationsModel read FModel write FModel;
+    procedure FetchAll(const AContainer: TList<TAccount>); override;
+    function GetItemField(constref AItem: TAccount; const ABindingName: ansistring): variant; override;
   end;
 
 { TWIZSendPASC_Confirmation }
 
 procedure TWIZSendPASC_Confirmation.OnPresent;
 var
-  data : TAccountSenderDataSource;
+  Data: TAccountSenderDataSource;
 begin
   FSendersGrid := TVisualGrid.Create(Self);
-  FSendersGrid.CanSearch:= False;
+  FSendersGrid.CanSearch := False;
   FSendersGrid.SortMode := smMultiColumn;
   FSendersGrid.FetchDataInThread := False;
   FSendersGrid.AutoPageSize := True;
   FSendersGrid.SelectionType := stNone;
   FSendersGrid.Options := [vgoColAutoFill, vgoColSizing, vgoSortDirectionAllowNone, vgoAutoHidePaging];
-  with FSendersGrid.AddColumn('Sender') do begin
+  with FSendersGrid.AddColumn('Sender') do
+  begin
     Binding := 'SenderAccount';
     Filters := SORTABLE_NUMERIC_FILTER;
     Width := 100;
     HeaderFontStyles := [fsBold];
     DataFontStyles := [fsBold];
   end;
-  with FSendersGrid.AddColumn('Balance') do begin
+  with FSendersGrid.AddColumn('Balance') do
+  begin
     Filters := SORTABLE_NUMERIC_FILTER;
     Width := 100;
     Renderer := TCellRenderers.PASC;
   end;
-  with FSendersGrid.AddColumn('Amount To Send') do begin
+  with FSendersGrid.AddColumn('Amount To Send') do
+  begin
     Binding := 'AmountToSend';
     Filters := SORTABLE_TEXT_FILTER;
     Width := 100;
   end;
-  with FSendersGrid.AddColumn('Fee') do begin
+  with FSendersGrid.AddColumn('Fee') do
+  begin
     Filters := SORTABLE_TEXT_FILTER;
     Width := 100;
   end;
 
-   data := TAccountSenderDataSource.Create(FSendersGrid);
-   data.Model := Model;
-   FSendersGrid.DataSource := data;
-   paGrid.AddControlDockCenter(FSendersGrid);
-   lblSgnAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Model.Signer.SignerAccount.account);
-   lblDestAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Model.SendPASC.DestinationAccount.account);
+  Data := TAccountSenderDataSource.Create(FSendersGrid);
+  Data.Model := Model;
+  FSendersGrid.DataSource := Data;
+  paGrid.AddControlDockCenter(FSendersGrid);
+  lblSgnAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Model.Signer.SignerAccount.account);
+  lblDestAcc.Caption := TAccountComp.AccountNumberToAccountTxtNumber(Model.SendPASC.DestinationAccount.account);
 end;
 
 { TAccountSenderDataSource }
 
-function TAccountSenderDataSource.GetColumns : TDataColumns;
+function TAccountSenderDataSource.GetColumns: TDataColumns;
 begin
   Result := TDataColumns.Create(
     TDataColumn.From('SenderAccount'),
     TDataColumn.From('Balance'),
     TDataColumn.From('AmountToSend'),
     TDataColumn.From('Fee')
-  );
+    );
 end;
 
-function TAccountSenderDataSource.GetItemField(constref AItem: TAccount; const ABindingName : AnsiString) : Variant;
+function TAccountSenderDataSource.GetItemField(constref AItem: TAccount; const ABindingName: ansistring): variant;
 var
-  index : Integer;
+  index: integer;
 begin
-   if ABindingName = 'SenderAccount' then
-     Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account)
-   else if ABindingName = 'Balance' then
-     Result := TAccountComp.FormatMoney(AItem.Balance)
-   else if ABindingName = 'AmountToSend' then
-     Result := TAccountComp.FormatMoney(Model.SendPASC.SingleAmountToSend)
-     else if ABindingName = 'Fee' then
-     Result := TAccountComp.FormatMoney(Model.Fee.SingleOperationFee)
-   else raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
+  if ABindingName = 'SenderAccount' then
+    Result := TAccountComp.AccountNumberToAccountTxtNumber(AItem.account)
+  else if ABindingName = 'Balance' then
+    Result := TAccountComp.FormatMoney(AItem.Balance)
+  else if ABindingName = 'AmountToSend' then
+    Result := IIF(Model.SendPASC.SendPASCMode = akaAllBalance, 'ALL BALANCE', TAccountComp.FormatMoney(Model.SendPASC.SingleAmountToSend))
+  else if ABindingName = 'Fee' then
+    Result := TAccountComp.FormatMoney(Model.Fee.SingleOperationFee)
+  else
+    raise Exception.Create(Format('Field not found [%s]', [ABindingName]));
 end;
 
 
-procedure TAccountSenderDataSource.FetchAll(const AContainer : TList<TAccount>);
+procedure TAccountSenderDataSource.FetchAll(const AContainer: TList<TAccount>);
 var
-  i: Integer;
+  i: integer;
 begin
-  for i := Low(Model.SendPASC.SelectedAccounts) to High(Model.SendPASC.SelectedAccounts) do
+  for i := Low(Model.Account.SelectedAccounts) to High(Model.Account.SelectedAccounts) do
   begin
-    AContainer.Add( Model.SendPASC.SelectedAccounts[i] );
+    AContainer.Add(Model.Account.SelectedAccounts[i]);
   end;
 end;
 
 
 end.
-

+ 3 - 1
src/gui/wizards/operations/UWIZSendPASC_EnterQuantity.pas

@@ -59,11 +59,13 @@ begin
   begin
     edtAmt.Text := 'ALL BALANCE';
     edtAmt.Enabled := False;
+    Model.SendPASC.SendPASCMode := akaAllBalance;
   end
   else
   begin
     edtAmt.Text := TAccountComp.FormatMoney(0);
     edtAmt.Enabled := True;
+    Model.SendPASC.SendPASCMode := akaSpecifiedAmount;
   end;
 
   Model.Payload.HasPayload := IIF(chkAttachPayload.Checked, True, False);
@@ -77,7 +79,7 @@ end;
 
 procedure TWIZSendPASC_EnterQuantity.OnPresent;
 begin
-  edtAmt.Text := TAccountComp.FormatMoney(0);
+  UpdateUI();
 end;
 
 procedure TWIZSendPASC_EnterQuantity.chkAttachPayloadChange(Sender: TObject);

+ 1 - 0
src/gui/wizards/operations/UWIZSendPASC_EnterRecipient.lfm

@@ -7,6 +7,7 @@ object WIZSendPASC_EnterRecipient: TWIZSendPASC_EnterRecipient
   Caption = 'WIZSendPASC_EnterRecipient'
   ClientHeight = 253
   ClientWidth = 429
+  LCLVersion = '1.8.2.0'
   Visible = False
   object gbRecipient: TGroupBox
     Left = 16

+ 1 - 1
src/gui/wizards/operations/UWIZSendPASC_EnterRecipient.pas

@@ -154,7 +154,7 @@ begin
   end;
 
   AccountNumbersWithChecksum :=
-    TListTool<TAccount, string>.Transform(Model.SendPASC.SelectedAccounts, GetAccNoWithCheckSum);
+    TListTool<TAccount, string>.Transform(Model.Account.SelectedAccounts, GetAccNoWithCheckSum);
 
   if TArrayTool<string>.Contains(AccountNumbersWithChecksum, edtDestAcc.Text) then
   begin

+ 0 - 19
src/gui/wizards/operations/UWIZSendPASC_FeeOverride.lfm

@@ -1,19 +0,0 @@
-object WIZFeeOverride: TWIZFeeOverride
-  Left = 0
-  Height = 253
-  Top = 0
-  Width = 429
-  Caption = 'WIZFeeOverride'
-  ClientHeight = 253
-  ClientWidth = 429
-  LCLVersion = '1.8.1.0'
-  Visible = False
-  object gbTransactionFee: TGroupBox
-    Left = 14
-    Height = 232
-    Top = 8
-    Width = 400
-    Caption = 'Transaction Fee'
-    TabOrder = 0
-  end
-end

+ 2 - 0
src/gui/wizards/operations/UWIZTransferAccount.pas

@@ -7,6 +7,8 @@ unit UWIZTransferAccount;
   Distributed under the MIT software license, see the accompanying file LICENSE
   or visit http://www.opensource.org/licenses/mit-license.php.
 
+  Acknowledgements:
+    Herman Schoenfeld <[email protected]>: added grid-based layout
 }
 
 interface

+ 61 - 58
src/pascalcoin_wallet.lpi

@@ -33,7 +33,7 @@
         <PackageName Value="LCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="101">
+    <Units Count="97">
       <Unit0>
         <Filename Value="pascalcoin_wallet.dpr"/>
         <IsPartOfProject Value="True"/>
@@ -368,182 +368,185 @@
       <Unit68>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_EnterName.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_EnterName"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit68>
       <Unit69>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_GenerateOrImport.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_GenerateOrImport"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit69>
       <Unit70>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_ImportPrivKey.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_ImportPrivKey"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit70>
       <Unit71>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_ImportPubKey.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_ImportPubKey"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit71>
       <Unit72>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_SelectEncryption.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_SelectEncryption"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit72>
       <Unit73>
         <Filename Value="gui\wizards\wallet\UWIZAddKey_Start.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZAddKey_Start"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit73>
       <Unit74>
-        <Filename Value="gui\wizards\operations\UWIZChangeAccountPrivateKey.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit74>
       <Unit75>
-        <Filename Value="gui\wizards\operations\UWIZChangeAccountPrivateKey_Confirmation.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Confirmation.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZChangeAccountPrivateKey_Confirmation"/>
         <HasResources Value="True"/>
-        <ResourceBaseClass Value="Form"/>
       </Unit75>
       <Unit76>
-        <Filename Value="gui\wizards\operations\UWIZChangeAccountPrivateKey_Start.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_List.pas"/>
         <IsPartOfProject Value="True"/>
         <HasResources Value="True"/>
       </Unit76>
       <Unit77>
-        <Filename Value="gui\wizards\operations\UWIZChangeAccountPrivateKey_Transaction.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_PrivateSaleConfig.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZChangeAccountPrivateKey_Transaction"/>
         <HasResources Value="True"/>
-        <ResourceBaseClass Value="Form"/>
       </Unit77>
       <Unit78>
-        <Filename Value="gui\wizards\operations\UWIZChangeAccountPrivateKey_TransactionPayload.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Start.pas"/>
         <IsPartOfProject Value="True"/>
         <HasResources Value="True"/>
       </Unit78>
       <Unit79>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Transaction.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZEnlistAccountForSale_Transaction"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit79>
       <Unit80>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Confirmation.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_TransactionPayload.pas"/>
         <IsPartOfProject Value="True"/>
         <HasResources Value="True"/>
       </Unit80>
       <Unit81>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_List.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZFeeOverride.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZFeeOverride"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit81>
       <Unit82>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_PrivateSaleConfig.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZPayloadContentOverride.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZPayloadContentOverride"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit82>
       <Unit83>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Start.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZPayloadOverride.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZPayloadOverride"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit83>
       <Unit84>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_Transaction.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZPayloadPasswordOverride.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZEnlistAccountForSale_Transaction"/>
+        <ComponentName Value="WIZPayloadPasswordOverride"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
       </Unit84>
       <Unit85>
-        <Filename Value="gui\wizards\operations\UWIZEnlistAccountForSale_TransactionPayload.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSelectSignerOverride.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZSelectSignerOverride"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit85>
       <Unit86>
-        <Filename Value="gui\wizards\operations\UWIZFeeOverride.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSendPASC.pas"/>
         <IsPartOfProject Value="True"/>
-        <HasResources Value="True"/>
       </Unit86>
       <Unit87>
-        <Filename Value="gui\wizards\operations\UWIZPayloadContentOverride.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSendPASC_Confirmation.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZPayloadContentOverride"/>
+        <ComponentName Value="WIZSendPASC_Confirmation"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
       </Unit87>
       <Unit88>
-        <Filename Value="gui\wizards\operations\UWIZPayloadOverride.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSendPASC_ConfirmSender.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZSendPASC_ConfirmSender"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit88>
       <Unit89>
-        <Filename Value="gui\wizards\operations\UWIZPayloadPasswordOverride.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSendPASC_EnterQuantity.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZSendPASC_EnterQuantity"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit89>
       <Unit90>
-        <Filename Value="gui\wizards\operations\UWIZSelectSignerOverride.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZSendPASC_EnterRecipient.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZSelectSignerOverride"/>
         <HasResources Value="True"/>
-        <ResourceBaseClass Value="Form"/>
       </Unit90>
       <Unit91>
-        <Filename Value="gui\wizards\operations\UWIZSendPASC.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit91>
       <Unit92>
-        <Filename Value="gui\wizards\operations\UWIZSendPASC_Confirmation.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey_SelectOption.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZSendPASC_Confirmation"/>
+        <ComponentName Value="WIZChangeKey_SelectOption"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
       </Unit92>
       <Unit93>
-        <Filename Value="gui\wizards\operations\UWIZSendPASC_ConfirmSender.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey_EnterKey.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeKey_EnterKey"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit93>
       <Unit94>
-        <Filename Value="gui\wizards\operations\UWIZSendPASC_EnterQuantity.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey_SelectKey.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZSendPASC_EnterQuantity"/>
+        <ComponentName Value="WIZChangeKey_SelectKey"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
       </Unit94>
       <Unit95>
-        <Filename Value="gui\wizards\operations\UWIZSendPASC_EnterRecipient.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey_ConfirmAccount.pas"/>
         <IsPartOfProject Value="True"/>
+        <ComponentName Value="WIZChangeKey_ConfirmAccount"/>
         <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
       </Unit95>
       <Unit96>
-        <Filename Value="gui\wizards\operations\UWIZTransferAccount.pas"/>
-        <IsPartOfProject Value="True"/>
-      </Unit96>
-      <Unit97>
-        <Filename Value="gui\wizards\operations\UWIZTransferAccount_Confirmation.pas"/>
+        <Filename Value="gui\wizards\operations\UWIZChangeKey_Confirmation.pas"/>
         <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZTransferAccount_Confirmation"/>
+        <ComponentName Value="WIZChangeKey_Confirmation"/>
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
-      </Unit97>
-      <Unit98>
-        <Filename Value="gui\wizards\operations\UWIZTransferAccount_Start.pas"/>
-        <IsPartOfProject Value="True"/>
-        <HasResources Value="True"/>
-      </Unit98>
-      <Unit99>
-        <Filename Value="gui\wizards\operations\UWIZTransferAccount_Transaction.pas"/>
-        <IsPartOfProject Value="True"/>
-        <ComponentName Value="WIZTransferAccount_Transaction"/>
-        <HasResources Value="True"/>
-        <ResourceBaseClass Value="Form"/>
-      </Unit99>
-      <Unit100>
-        <Filename Value="gui\wizards\operations\UWIZTransferAccount_TransactionPayload.pas"/>
-        <IsPartOfProject Value="True"/>
-        <HasResources Value="True"/>
-      </Unit100>
+      </Unit96>
     </Units>
   </ProjectOptions>
   <CompilerOptions>