diff --git a/cell/src/main/java/org/ton/java/tlb/types/ActionPhase.java b/cell/src/main/java/org/ton/java/tlb/types/ActionPhase.java index 239a53db..e9b62558 100644 --- a/cell/src/main/java/org/ton/java/tlb/types/ActionPhase.java +++ b/cell/src/main/java/org/ton/java/tlb/types/ActionPhase.java @@ -1,13 +1,12 @@ package org.ton.java.tlb.types; +import java.math.BigInteger; import lombok.Builder; import lombok.Data; import org.ton.java.cell.Cell; import org.ton.java.cell.CellBuilder; import org.ton.java.cell.CellSlice; -import java.math.BigInteger; - /** *
  * tr_phase_action$_
@@ -75,8 +74,8 @@ public static ActionPhase deserialize(CellSlice cs) {
                 .valid(cs.loadBit())
                 .noFunds(cs.loadBit())
                 .statusChange(AccStatusChange.deserialize(cs))
-                .totalFwdFees(cs.loadBit() ? cs.loadCoins() : null)
-                .totalActionFees(cs.loadBit() ? cs.loadCoins() : null)
+                .totalFwdFees(cs.loadBit() ? cs.loadCoins() : BigInteger.ZERO)
+                .totalActionFees(cs.loadBit() ? cs.loadCoins() : BigInteger.ZERO)
                 .resultCode(cs.loadUint(32).longValue())
                 .resultArg(cs.loadBit() ? cs.loadUint(32).longValue() : 0)
                 .totalActions(cs.loadUint(16).longValue())
diff --git a/cell/src/main/java/org/ton/java/tlb/types/CommonMsg.java b/cell/src/main/java/org/ton/java/tlb/types/CommonMsg.java
deleted file mode 100644
index 7b2b8523..00000000
--- a/cell/src/main/java/org/ton/java/tlb/types/CommonMsg.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.ton.java.tlb.types;
-
-
-public class CommonMsg {
-
-}
diff --git a/cell/src/main/java/org/ton/java/tlb/types/Transaction.java b/cell/src/main/java/org/ton/java/tlb/types/Transaction.java
index 412238a7..a44aef96 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/Transaction.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/Transaction.java
@@ -1,5 +1,6 @@
 package org.ton.java.tlb.types;
 
+import static java.util.Objects.isNull;
 import static java.util.Objects.nonNull;
 
 import java.math.BigInteger;
@@ -9,6 +10,7 @@
 import org.ton.java.cell.CellBuilder;
 import org.ton.java.cell.CellSlice;
 import org.ton.java.cell.TonHashMapE;
+import org.ton.java.utils.Utils;
 
 /**
  *
@@ -179,4 +181,190 @@ public static AccountStates deserializeAccountState(byte state) {
     }
     return null;
   }
+
+  public TransactionFees getTransactionFees() {
+    Transaction tx = this;
+
+    BigInteger totalFees = tx.getTotalFees().getCoins();
+    BigInteger totalForwardFees = getForwardFees(tx.getDescription());
+    BigInteger computeFees = getComputeFees(tx.getDescription());
+
+    BigInteger inForwardFees = BigInteger.ZERO;
+    BigInteger valueIn = BigInteger.ZERO;
+    BigInteger valueOut = BigInteger.ZERO;
+    BigInteger op = null;
+    long exitCode = getExitCode(tx.getDescription());
+    long actionCode = getActionCode(tx.getDescription());
+    long totalActions = getTotalActions(tx.getDescription());
+
+    Message inMsg = tx.getInOut().getIn();
+    Cell body = inMsg.getBody();
+
+    if (nonNull(body) && body.getBits().getUsedBits() >= 32) {
+      op = CellSlice.beginParse(body).preloadInt(32);
+    } else {
+      op = BigInteger.ONE.negate();
+    }
+
+    if (inMsg.getInfo() instanceof InternalMessageInfo) {
+      valueIn = ((InternalMessageInfo) inMsg.getInfo()).getValue().getCoins();
+      inForwardFees = ((InternalMessageInfo) inMsg.getInfo()).getFwdFee();
+    }
+
+    for (Message outMsg : tx.getInOut().getOutMessages()) {
+      InternalMessageInfo intMsgInfo = (InternalMessageInfo) outMsg.getInfo();
+      valueOut = valueOut.add(intMsgInfo.getValue().getCoins());
+    }
+
+    return TransactionFees.builder()
+        .op(
+            (isNull(op))
+                ? "N/A"
+                : (op.compareTo(BigInteger.ONE.negate()) != 0) ? op.toString(16) : "no body")
+        .valueIn(valueIn)
+        .valueOut(valueOut)
+        .totalFees(totalFees)
+        .outForwardFee(totalForwardFees)
+        .computeFee(computeFees)
+        .inForwardFee(inForwardFees)
+        .exitCode(exitCode)
+        .actionCode(actionCode)
+        .totalActions(totalActions)
+        .build();
+  }
+
+  private BigInteger getComputeFees(TransactionDescription txDesc) {
+    if (txDesc instanceof TransactionDescriptionOrdinary) {
+      ComputePhase computePhase = ((TransactionDescriptionOrdinary) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getGasFees();
+      }
+    } else if (txDesc instanceof TransactionDescriptionSplitPrepare) {
+      ComputePhase computePhase = ((TransactionDescriptionSplitPrepare) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getGasFees();
+      }
+    } else if (txDesc instanceof TransactionDescriptionTickTock) {
+      ComputePhase computePhase = ((TransactionDescriptionTickTock) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getGasFees();
+      }
+    } else if (txDesc instanceof TransactionDescriptionMergeInstall) {
+      ComputePhase computePhase = ((TransactionDescriptionMergeInstall) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getGasFees();
+      }
+    } else {
+      return BigInteger.ZERO;
+    }
+    return BigInteger.ZERO;
+  }
+
+  private BigInteger getForwardFees(TransactionDescription txDesc) {
+    if (txDesc instanceof TransactionDescriptionOrdinary) {
+      ActionPhase actionPhase = ((TransactionDescriptionOrdinary) txDesc).getActionPhase();
+      return actionPhase.getTotalFwdFees();
+    } else if (txDesc instanceof TransactionDescriptionSplitPrepare) {
+      ActionPhase actionPhase = ((TransactionDescriptionSplitPrepare) txDesc).getActionPhase();
+      return actionPhase.getTotalFwdFees();
+    } else if (txDesc instanceof TransactionDescriptionTickTock) {
+      ActionPhase actionPhase = ((TransactionDescriptionTickTock) txDesc).getActionPhase();
+      return actionPhase.getTotalFwdFees();
+    } else if (txDesc instanceof TransactionDescriptionMergeInstall) {
+      ActionPhase actionPhase = ((TransactionDescriptionMergeInstall) txDesc).getActionPhase();
+      return actionPhase.getTotalFwdFees();
+    } else {
+      return BigInteger.ZERO;
+    }
+  }
+
+  private long getTotalActions(TransactionDescription txDesc) {
+    if (txDesc instanceof TransactionDescriptionOrdinary) {
+      ActionPhase actionPhase = ((TransactionDescriptionOrdinary) txDesc).getActionPhase();
+      return actionPhase.getTotalActions();
+    } else if (txDesc instanceof TransactionDescriptionSplitPrepare) {
+      ActionPhase actionPhase = ((TransactionDescriptionSplitPrepare) txDesc).getActionPhase();
+      return actionPhase.getTotalActions();
+    } else if (txDesc instanceof TransactionDescriptionTickTock) {
+      ActionPhase actionPhase = ((TransactionDescriptionTickTock) txDesc).getActionPhase();
+      return actionPhase.getTotalActions();
+    } else if (txDesc instanceof TransactionDescriptionMergeInstall) {
+      ActionPhase actionPhase = ((TransactionDescriptionMergeInstall) txDesc).getActionPhase();
+      return actionPhase.getTotalActions();
+    } else {
+      return -1;
+    }
+  }
+
+  private long getActionCode(TransactionDescription txDesc) {
+    if (txDesc instanceof TransactionDescriptionOrdinary) {
+      ActionPhase actionPhase = ((TransactionDescriptionOrdinary) txDesc).getActionPhase();
+      return actionPhase.getResultCode();
+    } else if (txDesc instanceof TransactionDescriptionSplitPrepare) {
+      ActionPhase actionPhase = ((TransactionDescriptionSplitPrepare) txDesc).getActionPhase();
+      return actionPhase.getResultCode();
+    } else if (txDesc instanceof TransactionDescriptionTickTock) {
+      ActionPhase actionPhase = ((TransactionDescriptionTickTock) txDesc).getActionPhase();
+      return actionPhase.getResultCode();
+    } else if (txDesc instanceof TransactionDescriptionMergeInstall) {
+      ActionPhase actionPhase = ((TransactionDescriptionMergeInstall) txDesc).getActionPhase();
+      return actionPhase.getResultCode();
+    } else {
+      return -1;
+    }
+  }
+
+  private long getExitCode(TransactionDescription txDesc) {
+    if (txDesc instanceof TransactionDescriptionOrdinary) {
+      ComputePhase computePhase = ((TransactionDescriptionOrdinary) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getDetails().getExitCode();
+      }
+    } else if (txDesc instanceof TransactionDescriptionSplitPrepare) {
+      ComputePhase computePhase = ((TransactionDescriptionSplitPrepare) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getDetails().getExitCode();
+      }
+    } else if (txDesc instanceof TransactionDescriptionTickTock) {
+      ComputePhase computePhase = ((TransactionDescriptionTickTock) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getDetails().getExitCode();
+      }
+    } else if (txDesc instanceof TransactionDescriptionMergeInstall) {
+      ComputePhase computePhase = ((TransactionDescriptionMergeInstall) txDesc).getComputePhase();
+      if (computePhase instanceof ComputePhaseVM) {
+        return ((ComputePhaseVM) computePhase).getDetails().getExitCode();
+      }
+    } else {
+      return -1;
+    }
+    return -1;
+  }
+
+  public void printTransactionFees(boolean withHeader) {
+    TransactionFees txFees = getTransactionFees();
+    String header =
+        "| op       | valueIn        | valueOut       | totalFees    | inForwardFee | outForwardFee | outActions | computeFee    | exitCode | actionCode |";
+    if (withHeader) {
+      System.out.println(
+          "_________________________________________________________________________________________________________________________________________________");
+      System.out.println(header);
+      System.out.println(
+          "-------------------------------------------------------------------------------------------------------------------------------------------------");
+    }
+    String str =
+        String.format(
+            "| %-9s| %-15s| %-15s| %-13s| %-13s| %-14s| %-11s| %-14s| %-9s| %-11s|",
+            txFees.getOp(),
+            Utils.formatNanoValue(txFees.getValueIn()),
+            Utils.formatNanoValue(txFees.getValueOut()),
+            Utils.formatNanoValue(txFees.getTotalFees()),
+            Utils.formatNanoValue(txFees.getInForwardFee().toString()),
+            Utils.formatNanoValue(txFees.getOutForwardFee()),
+            txFees.getTotalActions(),
+            Utils.formatNanoValue(txFees.getComputeFee()),
+            txFees.getExitCode(),
+            txFees.getActionCode());
+    System.out.println(str);
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescription.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescription.java
index bdd8f8cc..01cd1c31 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescription.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescription.java
@@ -1,64 +1,105 @@
 package org.ton.java.tlb.types;
 
-import lombok.Builder;
-import lombok.Data;
 import org.ton.java.cell.Cell;
-import org.ton.java.cell.CellBuilder;
 import org.ton.java.cell.CellSlice;
 
-@Builder
-@Data
-public class TransactionDescription {
-    Object description; // `tlb:"."`
+/**
+ *
+ *
+ * 
+ * trans_ord$0000 credit_first:Bool
+ *   storage_ph:(Maybe TrStoragePhase)
+ *   credit_ph:(Maybe TrCreditPhase)
+ *   compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
+ *   aborted:Bool bounce:(Maybe TrBouncePhase)
+ *   destroyed:Bool
+ *   = TransactionDescr;
+ *
+ * trans_storage$0001 storage_ph:TrStoragePhase
+ *   = TransactionDescr;
+ *
+ * trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase
+ *   compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
+ *   aborted:Bool destroyed:Bool = TransactionDescr;
+ * //
+ * split_merge_info$_ cur_shard_pfx_len:(## 6)
+ *   acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
+ *   = SplitMergeInfo;
+ * trans_split_prepare$0100 split_info:SplitMergeInfo
+ *   storage_ph:(Maybe TrStoragePhase)
+ *   compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
+ *   aborted:Bool destroyed:Bool
+ *   = TransactionDescr;
+ * trans_split_install$0101 split_info:SplitMergeInfo
+ *   prepare_transaction:^Transaction
+ *   installed:Bool = TransactionDescr;
+ *
+ * trans_merge_prepare$0110 split_info:SplitMergeInfo
+ *   storage_ph:TrStoragePhase aborted:Bool
+ *   = TransactionDescr;
+ *
+ * trans_merge_install$0111 split_info:SplitMergeInfo
+ *   prepare_transaction:^Transaction
+ *   storage_ph:(Maybe TrStoragePhase)
+ *   credit_ph:(Maybe TrCreditPhase)
+ *   compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
+ *   aborted:Bool destroyed:Bool
+ *   = TransactionDescr;
+ *   
+ */ +public interface TransactionDescription { - public Cell toCell() { - CellBuilder c = CellBuilder.beginCell(); + Cell toCell(); - if (description instanceof TransactionDescriptionStorage) { - c.storeUint(0b0001, 3); - c.storeSlice(CellSlice.beginParse(((TransactionDescriptionStorage) description).toCell())); - } else if (description instanceof TransactionDescriptionOrdinary) { - c.storeUint(0b000, 3); - c.storeSlice(CellSlice.beginParse(((TransactionDescriptionOrdinary) description).toCell())); - } - return c.endCell(); - } + // { + // CellBuilder c = CellBuilder.beginCell(); + // + // if (description instanceof TransactionDescriptionStorage) { + // c.storeUint(0b0001, 3); + // c.storeSlice(CellSlice.beginParse(((TransactionDescriptionStorage) + // description).toCell())); + // } else if (description instanceof TransactionDescriptionOrdinary) { + // c.storeUint(0b000, 3); + // c.storeSlice(CellSlice.beginParse(((TransactionDescriptionOrdinary) + // description).toCell())); + // } + // return c.endCell(); + // } - public static TransactionDescription deserialize(CellSlice cs) { - int pfx = cs.preloadUint(3).intValue(); - switch (pfx) { - case 0b000: { - boolean isStorage = cs.preloadBit(); - if (isStorage) { - TransactionDescriptionStorage desc = TransactionDescriptionStorage.deserialize(cs); - return TransactionDescription.builder().description(desc).build(); - } - TransactionDescriptionOrdinary descOrdinary = TransactionDescriptionOrdinary.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descOrdinary).build(); - } - case 0b001: { - TransactionDescriptionTickTock descTickTock = TransactionDescriptionTickTock.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descTickTock).build(); - } - case 0b010: { - boolean isInstall = cs.preloadBit(); - if (isInstall) { - TransactionDescriptionSplitInstall descSplit = TransactionDescriptionSplitInstall.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descSplit).build(); - } - TransactionDescriptionSplitPrepare descSplitPrepare = TransactionDescriptionSplitPrepare.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descSplitPrepare).build(); - } - case 0b011: { - boolean isInstall = cs.preloadBit(); - if (isInstall) { - TransactionDescriptionMergeInstall descMerge = TransactionDescriptionMergeInstall.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descMerge).build(); - } - TransactionDescriptionMergePrepare descMergePrepare = TransactionDescriptionMergePrepare.deserialize(cs); // skipped was true - return TransactionDescription.builder().description(descMergePrepare).build(); - } + static TransactionDescription deserialize(CellSlice cs) { + int pfx = cs.preloadUint(3).intValue(); + switch (pfx) { + case 0b000: + { + boolean isStorage = cs.preloadBit(); + if (isStorage) { + return TransactionDescriptionStorage.deserialize(cs); + } + return TransactionDescriptionOrdinary.deserialize(cs); + } + case 0b001: + { + return TransactionDescriptionTickTock.deserialize(cs); + } + case 0b010: + { + boolean isInstall = cs.preloadBit(); + if (isInstall) { + return TransactionDescriptionSplitInstall.deserialize(cs); + } + return TransactionDescriptionSplitPrepare.deserialize(cs); + } + case 0b011: + { + boolean isInstall = cs.preloadBit(); + if (isInstall) { + return TransactionDescriptionMergeInstall.deserialize(cs); + } + return TransactionDescriptionMergePrepare.deserialize(cs); } - throw new Error("unknown transaction description type (must be in range [0..3], found 0x" + Integer.toBinaryString(pfx)); } + throw new Error( + "unknown transaction description type (must be in range [0..3], found 0x" + + Integer.toBinaryString(pfx)); + } } diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergeInstall.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergeInstall.java index 9d5464bb..83f4ed1c 100644 --- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergeInstall.java +++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergeInstall.java @@ -7,6 +7,8 @@ import org.ton.java.cell.CellSlice; /** + * + * *
  * trans_merge_install$0111
  *   split_info:SplitMergeInfo
@@ -21,49 +23,52 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionMergeInstall {
-    int magic;
-    SplitMergeInfo splitInfo;
-    Transaction prepareTransaction;
-    StoragePhase storagePhase;
-    CreditPhase creditPhase;
-    ComputePhase computePhase;
-    ActionPhase actionPhase;
-    boolean aborted;
-    boolean destroyed;
+public class TransactionDescriptionMergeInstall implements TransactionDescription {
+  int magic;
+  SplitMergeInfo splitInfo;
+  Transaction prepareTransaction;
+  StoragePhase storagePhase;
+  CreditPhase creditPhase;
+  ComputePhase computePhase;
+  ActionPhase actionPhase;
+  boolean aborted;
+  boolean destroyed;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b0111, 4)
-                .storeCell(splitInfo.toCell())
-                .storeRef(prepareTransaction.toCell())
-                .storeCellMaybe(storagePhase.toCell())
-                .storeCellMaybe(creditPhase.toCell())
-                .storeCell(computePhase.toCell())
-                .storeRefMaybe(actionPhase.toCell())
-                .storeBit(aborted)
-                .storeBit(destroyed)
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell()
+        .storeUint(0b0111, 4)
+        .storeCell(splitInfo.toCell())
+        .storeRef(prepareTransaction.toCell())
+        .storeCellMaybe(storagePhase.toCell())
+        .storeCellMaybe(creditPhase.toCell())
+        .storeCell(computePhase.toCell())
+        .storeRefMaybe(actionPhase.toCell())
+        .storeBit(aborted)
+        .storeBit(destroyed)
+        .endCell();
+  }
 
-    public static TransactionDescriptionMergeInstall deserialize(CellSlice cs) {
-        long magic = cs.loadUint(4).intValue();
-        assert (magic == 0b0111) : "TransactionDescriptionMergeInstall: magic not equal to 0b0111, found 0x" + Long.toHexString(magic);
+  public static TransactionDescriptionMergeInstall deserialize(CellSlice cs) {
+    long magic = cs.loadUint(4).intValue();
+    assert (magic == 0b0111)
+        : "TransactionDescriptionMergeInstall: magic not equal to 0b0111, found 0x"
+            + Long.toHexString(magic);
 
-        return TransactionDescriptionMergeInstall.builder()
-                .magic(0b0111)
-                .splitInfo(SplitMergeInfo.deserialize(cs))
-                .prepareTransaction(Transaction.deserialize(CellSlice.beginParse(cs.loadRef())))
-                .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
-                .creditPhase(cs.loadBit() ? CreditPhase.deserialize(cs) : null)
-                .computePhase(ComputePhase.deserialize(cs))
-                .actionPhase(cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
-                .aborted(cs.loadBit())
-                .destroyed(cs.loadBit())
-                .build();
-    }
+    return TransactionDescriptionMergeInstall.builder()
+        .magic(0b0111)
+        .splitInfo(SplitMergeInfo.deserialize(cs))
+        .prepareTransaction(Transaction.deserialize(CellSlice.beginParse(cs.loadRef())))
+        .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
+        .creditPhase(cs.loadBit() ? CreditPhase.deserialize(cs) : null)
+        .computePhase(ComputePhase.deserialize(cs))
+        .actionPhase(
+            cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
+        .aborted(cs.loadBit())
+        .destroyed(cs.loadBit())
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergePrepare.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergePrepare.java
index 386132d7..e02b049d 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergePrepare.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionMergePrepare.java
@@ -7,46 +7,46 @@
 import org.ton.java.cell.CellSlice;
 
 /**
+ *
+ *
  * 
- * trans_split_prepare$0100
- *   split_info:SplitMergeInfo
- *   storage_ph:(Maybe TrStoragePhase)
- *   compute_ph:TrComputePhase
- *   action:(Maybe ^TrActionPhase)
- *   aborted:Bool destroyed:Bool
+ * trans_merge_prepare$0110 split_info:SplitMergeInfo
+ *   storage_ph:TrStoragePhase aborted:Bool
  *   = TransactionDescr;
  *   
*/ @Builder @Data -public class TransactionDescriptionMergePrepare { - int magic; - SplitMergeInfo splitInfo; - StoragePhase storagePhase; - boolean aborted; +public class TransactionDescriptionMergePrepare implements TransactionDescription { + int magic; + SplitMergeInfo splitInfo; + StoragePhase storagePhase; + boolean aborted; - private String getMagic() { - return Long.toBinaryString(magic); - } + private String getMagic() { + return Long.toBinaryString(magic); + } - public Cell toCell() { - return CellBuilder.beginCell() - .storeUint(0b0100, 4) - .storeCell(splitInfo.toCell()) - .storeCellMaybe(storagePhase.toCell()) - .storeBit(aborted) - .endCell(); - } + public Cell toCell() { + return CellBuilder.beginCell() + .storeUint(0b0100, 4) + .storeCell(splitInfo.toCell()) + .storeCellMaybe(storagePhase.toCell()) + .storeBit(aborted) + .endCell(); + } - public static TransactionDescriptionMergePrepare deserialize(CellSlice cs) { - long magic = cs.loadUint(4).intValue(); - assert (magic == 0b0110) : "TransactionDescriptionMergePrepare: magic not equal to 0b0110, found 0x" + Long.toHexString(magic); + public static TransactionDescriptionMergePrepare deserialize(CellSlice cs) { + long magic = cs.loadUint(4).intValue(); + assert (magic == 0b0110) + : "TransactionDescriptionMergePrepare: magic not equal to 0b0110, found 0x" + + Long.toHexString(magic); - return TransactionDescriptionMergePrepare.builder() - .magic(0b0110) - .splitInfo(SplitMergeInfo.deserialize(cs)) - .storagePhase(StoragePhase.deserialize(cs)) - .aborted(cs.loadBit()) - .build(); - } + return TransactionDescriptionMergePrepare.builder() + .magic(0b0110) + .splitInfo(SplitMergeInfo.deserialize(cs)) + .storagePhase(StoragePhase.deserialize(cs)) + .aborted(cs.loadBit()) + .build(); + } } diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionOrdinary.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionOrdinary.java index 413f826f..76b9eb1c 100644 --- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionOrdinary.java +++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionOrdinary.java @@ -7,6 +7,8 @@ import org.ton.java.cell.CellSlice; /** + * + * *
  * trans_ord$0000
  *   credit_first:Bool
@@ -22,49 +24,52 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionOrdinary {
-    int magic;
-    boolean creditFirst;
-    StoragePhase storagePhase;
-    CreditPhase creditPhase;
-    ComputePhase computePhase;
-    ActionPhase actionPhase;
-    boolean aborted;
-    BouncePhase bouncePhase;
-    boolean destroyed;
+public class TransactionDescriptionOrdinary implements TransactionDescription {
+  int magic;
+  boolean creditFirst;
+  StoragePhase storagePhase;
+  CreditPhase creditPhase;
+  ComputePhase computePhase;
+  ActionPhase actionPhase;
+  boolean aborted;
+  BouncePhase bouncePhase;
+  boolean destroyed;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b0000, 4)
-                .storeBit(creditFirst)
-                .storeCellMaybe(storagePhase.toCell())
-                .storeCellMaybe(creditPhase.toCell())
-                .storeCell(computePhase.toCell())
-                .storeRefMaybe(actionPhase.toCell())
-                .storeBit(aborted)
-                .storeCellMaybe(bouncePhase.toCell())
-                .storeBit(destroyed)
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell()
+        .storeUint(0b0000, 4)
+        .storeBit(creditFirst)
+        .storeCellMaybe(storagePhase.toCell())
+        .storeCellMaybe(creditPhase.toCell())
+        .storeCell(computePhase.toCell())
+        .storeRefMaybe(actionPhase.toCell())
+        .storeBit(aborted)
+        .storeCellMaybe(bouncePhase.toCell())
+        .storeBit(destroyed)
+        .endCell();
+  }
 
-    public static TransactionDescriptionOrdinary deserialize(CellSlice cs) {
-        long magic = cs.loadUint(4).intValue();
-        assert (magic == 0b0000) : "TransactionDescriptionOrdinary: magic not equal to 0b0000, found 0x" + Long.toHexString(magic);
+  public static TransactionDescriptionOrdinary deserialize(CellSlice cs) {
+    long magic = cs.loadUint(4).intValue();
+    assert (magic == 0b0000)
+        : "TransactionDescriptionOrdinary: magic not equal to 0b0000, found 0x"
+            + Long.toHexString(magic);
 
-        return TransactionDescriptionOrdinary.builder()
-                .magic(0b0000)
-                .creditFirst(cs.loadBit())
-                .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
-                .creditPhase(cs.loadBit() ? CreditPhase.deserialize(cs) : null)
-                .computePhase(ComputePhase.deserialize(cs))
-                .actionPhase(cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
-                .aborted(cs.loadBit())
-                .bouncePhase(cs.loadBit() ? BouncePhase.deserialize(cs) : null)
-                .destroyed(cs.loadBit())
-                .build();
-    }
+    return TransactionDescriptionOrdinary.builder()
+        .magic(0b0000)
+        .creditFirst(cs.loadBit())
+        .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
+        .creditPhase(cs.loadBit() ? CreditPhase.deserialize(cs) : null)
+        .computePhase(ComputePhase.deserialize(cs))
+        .actionPhase(
+            cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
+        .aborted(cs.loadBit())
+        .bouncePhase(cs.loadBit() ? BouncePhase.deserialize(cs) : null)
+        .destroyed(cs.loadBit())
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitInstall.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitInstall.java
index 1f8acebb..596f9462 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitInstall.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitInstall.java
@@ -7,6 +7,8 @@
 import org.ton.java.cell.CellSlice;
 
 /**
+ *
+ *
  * 
  * trans_split_install$0101
  *   split_info:SplitMergeInfo
@@ -16,34 +18,36 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionSplitInstall {
-    int magic;
-    SplitMergeInfo splitInfo;
-    Transaction prepareTransaction;
-    boolean installed;
+public class TransactionDescriptionSplitInstall implements TransactionDescription {
+  int magic;
+  SplitMergeInfo splitInfo;
+  Transaction prepareTransaction;
+  boolean installed;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b0101, 4)
-                .storeCell(splitInfo.toCell())
-                .storeRef(prepareTransaction.toCell())
-                .storeBit(installed)
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell()
+        .storeUint(0b0101, 4)
+        .storeCell(splitInfo.toCell())
+        .storeRef(prepareTransaction.toCell())
+        .storeBit(installed)
+        .endCell();
+  }
 
-    public static TransactionDescriptionSplitInstall deserialize(CellSlice cs) {
-        long magic = cs.loadUint(4).intValue();
-        assert (magic == 0b0101) : "TransactionDescriptionSplitInstall: magic not equal to 0b0101, found 0x" + Long.toHexString(magic);
+  public static TransactionDescriptionSplitInstall deserialize(CellSlice cs) {
+    long magic = cs.loadUint(4).intValue();
+    assert (magic == 0b0101)
+        : "TransactionDescriptionSplitInstall: magic not equal to 0b0101, found 0x"
+            + Long.toHexString(magic);
 
-        return TransactionDescriptionSplitInstall.builder()
-                .magic(0b0101)
-                .splitInfo(SplitMergeInfo.deserialize(cs))
-                .prepareTransaction(Transaction.deserialize(CellSlice.beginParse(cs.loadRef())))
-                .installed(cs.loadBit())
-                .build();
-    }
+    return TransactionDescriptionSplitInstall.builder()
+        .magic(0b0101)
+        .splitInfo(SplitMergeInfo.deserialize(cs))
+        .prepareTransaction(Transaction.deserialize(CellSlice.beginParse(cs.loadRef())))
+        .installed(cs.loadBit())
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitPrepare.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitPrepare.java
index 7fadc26c..719892cd 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitPrepare.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionSplitPrepare.java
@@ -7,6 +7,8 @@
 import org.ton.java.cell.CellSlice;
 
 /**
+ *
+ *
  * 
  * trans_split_prepare$0100
  *   split_info:SplitMergeInfo
@@ -19,42 +21,45 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionSplitPrepare {
-    int magic;
-    SplitMergeInfo splitInfo;
-    StoragePhase storagePhase;
-    ComputePhase computePhase;
-    ActionPhase actionPhase;
-    boolean aborted;
-    boolean destroyed;
+public class TransactionDescriptionSplitPrepare implements TransactionDescription {
+  int magic;
+  SplitMergeInfo splitInfo;
+  StoragePhase storagePhase;
+  ComputePhase computePhase;
+  ActionPhase actionPhase;
+  boolean aborted;
+  boolean destroyed;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b0100, 4)
-                .storeCell(splitInfo.toCell())
-                .storeCellMaybe(storagePhase.toCell())
-                .storeCell(computePhase.toCell())
-                .storeRefMaybe(actionPhase.toCell())
-                .storeBit(aborted)
-                .storeBit(destroyed)
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell()
+        .storeUint(0b0100, 4)
+        .storeCell(splitInfo.toCell())
+        .storeCellMaybe(storagePhase.toCell())
+        .storeCell(computePhase.toCell())
+        .storeRefMaybe(actionPhase.toCell())
+        .storeBit(aborted)
+        .storeBit(destroyed)
+        .endCell();
+  }
 
-    public static TransactionDescriptionSplitPrepare deserialize(CellSlice cs) {
-        long magic = cs.loadUint(4).intValue();
-        assert (magic == 0b0100) : "TransactionDescriptionSplitPrepare: magic not equal to 0b0100, found 0x" + Long.toHexString(magic);
-        return TransactionDescriptionSplitPrepare.builder()
-                .magic(0b0100)
-                .splitInfo(SplitMergeInfo.deserialize(cs))
-                .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
-                .computePhase(ComputePhase.deserialize(cs))
-                .actionPhase(cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
-                .aborted(cs.loadBit())
-                .destroyed(cs.loadBit())
-                .build();
-    }
+  public static TransactionDescriptionSplitPrepare deserialize(CellSlice cs) {
+    long magic = cs.loadUint(4).intValue();
+    assert (magic == 0b0100)
+        : "TransactionDescriptionSplitPrepare: magic not equal to 0b0100, found 0x"
+            + Long.toHexString(magic);
+    return TransactionDescriptionSplitPrepare.builder()
+        .magic(0b0100)
+        .splitInfo(SplitMergeInfo.deserialize(cs))
+        .storagePhase(cs.loadBit() ? StoragePhase.deserialize(cs) : null)
+        .computePhase(ComputePhase.deserialize(cs))
+        .actionPhase(
+            cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
+        .aborted(cs.loadBit())
+        .destroyed(cs.loadBit())
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionStorage.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionStorage.java
index 036c69b7..8c2b34d3 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionStorage.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionStorage.java
@@ -7,6 +7,8 @@
 import org.ton.java.cell.CellSlice;
 
 /**
+ *
+ *
  * 
  * trans_storage$0001
  *    storage_ph:TrStoragePhase
@@ -15,28 +17,27 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionStorage {
-    int magic;
-    StoragePhase storagePhase;
+public class TransactionDescriptionStorage implements TransactionDescription {
+  int magic;
+  StoragePhase storagePhase;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b0001, 4)
-                .storeCell(storagePhase.toCell())
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell().storeUint(0b0001, 4).storeCell(storagePhase.toCell()).endCell();
+  }
 
-    public static TransactionDescriptionStorage deserialize(CellSlice cs) {
-        long magic = cs.loadUint(4).intValue();
-        assert (magic == 0b0001) : "TransactionDescriptionStorage: magic not equal to 0b0001, found 0x" + Long.toHexString(magic);
+  public static TransactionDescriptionStorage deserialize(CellSlice cs) {
+    long magic = cs.loadUint(4).intValue();
+    assert (magic == 0b0001)
+        : "TransactionDescriptionStorage: magic not equal to 0b0001, found 0x"
+            + Long.toHexString(magic);
 
-        return TransactionDescriptionStorage.builder()
-                .magic(0b0001)
-                .storagePhase(StoragePhase.deserialize(cs))
-                .build();
-    }
+    return TransactionDescriptionStorage.builder()
+        .magic(0b0001)
+        .storagePhase(StoragePhase.deserialize(cs))
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionTickTock.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionTickTock.java
index 5680011d..f28358f0 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionTickTock.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionDescriptionTickTock.java
@@ -7,6 +7,8 @@
 import org.ton.java.cell.CellSlice;
 
 /**
+ *
+ *
  * 
  * trans_tick_tock$001
  *   is_tock:Bool
@@ -19,43 +21,46 @@
  */
 @Builder
 @Data
-public class TransactionDescriptionTickTock {
-    int magic;
-    boolean isTock;
-    StoragePhase storagePhase;
-    ComputePhase computePhase;
-    ActionPhase actionPhase;
-    boolean aborted;
-    boolean destroyed;
+public class TransactionDescriptionTickTock implements TransactionDescription {
+  int magic;
+  boolean isTock;
+  StoragePhase storagePhase;
+  ComputePhase computePhase;
+  ActionPhase actionPhase;
+  boolean aborted;
+  boolean destroyed;
 
-    private String getMagic() {
-        return Long.toBinaryString(magic);
-    }
+  private String getMagic() {
+    return Long.toBinaryString(magic);
+  }
 
-    public Cell toCell() {
-        return CellBuilder.beginCell()
-                .storeUint(0b001, 3)
-                .storeBit(isTock)
-                .storeCell(storagePhase.toCell())
-                .storeCell(computePhase.toCell())
-                .storeRefMaybe(actionPhase.toCell())
-                .storeBit(aborted)
-                .storeBit(destroyed)
-                .endCell();
-    }
+  public Cell toCell() {
+    return CellBuilder.beginCell()
+        .storeUint(0b001, 3)
+        .storeBit(isTock)
+        .storeCell(storagePhase.toCell())
+        .storeCell(computePhase.toCell())
+        .storeRefMaybe(actionPhase.toCell())
+        .storeBit(aborted)
+        .storeBit(destroyed)
+        .endCell();
+  }
 
-    public static TransactionDescriptionTickTock deserialize(CellSlice cs) {
-        long magic = cs.loadUint(3).intValue();
-        assert (magic == 0b001) : "TransactionDescriptionTickTock: magic not equal to 0b001, found 0x" + Long.toHexString(magic);
+  public static TransactionDescriptionTickTock deserialize(CellSlice cs) {
+    long magic = cs.loadUint(3).intValue();
+    assert (magic == 0b001)
+        : "TransactionDescriptionTickTock: magic not equal to 0b001, found 0x"
+            + Long.toHexString(magic);
 
-        return TransactionDescriptionTickTock.builder()
-                .magic(0b001)
-                .isTock(cs.loadBit())
-                .storagePhase(StoragePhase.deserialize(cs))
-                .computePhase(ComputePhase.deserialize(cs))
-                .actionPhase(cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
-                .aborted(cs.loadBit())
-                .destroyed(cs.loadBit())
-                .build();
-    }
+    return TransactionDescriptionTickTock.builder()
+        .magic(0b001)
+        .isTock(cs.loadBit())
+        .storagePhase(StoragePhase.deserialize(cs))
+        .computePhase(ComputePhase.deserialize(cs))
+        .actionPhase(
+            cs.loadBit() ? ActionPhase.deserialize(CellSlice.beginParse(cs.loadRef())) : null)
+        .aborted(cs.loadBit())
+        .destroyed(cs.loadBit())
+        .build();
+  }
 }
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionFees.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionFees.java
new file mode 100644
index 00000000..b4c4e40e
--- /dev/null
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionFees.java
@@ -0,0 +1,20 @@
+package org.ton.java.tlb.types;
+
+import java.math.BigInteger;
+import lombok.Builder;
+import lombok.Data;
+
+@Builder
+@Data
+public class TransactionFees {
+  String op;
+  BigInteger totalFees;
+  BigInteger computeFee;
+  BigInteger inForwardFee;
+  BigInteger outForwardFee;
+  BigInteger valueIn;
+  BigInteger valueOut;
+  long exitCode;
+  long actionCode;
+  long totalActions;
+}
diff --git a/cell/src/main/java/org/ton/java/tlb/types/TransactionIO.java b/cell/src/main/java/org/ton/java/tlb/types/TransactionIO.java
index 314f26c4..0fbdc4b9 100644
--- a/cell/src/main/java/org/ton/java/tlb/types/TransactionIO.java
+++ b/cell/src/main/java/org/ton/java/tlb/types/TransactionIO.java
@@ -1,13 +1,17 @@
 package org.ton.java.tlb.types;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 import lombok.Builder;
 import lombok.Data;
 import org.ton.java.cell.Cell;
 import org.ton.java.cell.CellBuilder;
-import org.ton.java.cell.CellSlice;
 import org.ton.java.cell.TonHashMapE;
 
 /**
+ *
+ *
  * 
  * ^[
  *   in_msg:(Maybe ^(Message Any))
@@ -18,22 +22,23 @@
 @Builder
 @Data
 public class TransactionIO {
-    Message in;
-    TonHashMapE out;
+  Message in;
+  TonHashMapE out;
 
-    public Cell toCell() {
+  public Cell toCell() {
 
-        Cell dictCell = out.serialize(
-                k -> CellBuilder.beginCell().storeUint((Long) k, 15).endCell().getBits(),
-                v -> CellBuilder.beginCell().storeRef((Cell) v).endCell()
-        );
-        return CellBuilder.beginCell()
-                .storeRefMaybe(in.toCell())
-                .storeDict(dictCell)
-                .endCell();
-    }
+    Cell dictCell =
+        out.serialize(
+            k -> CellBuilder.beginCell().storeUint((Long) k, 15).endCell().getBits(),
+            v -> CellBuilder.beginCell().storeRef((Cell) v).endCell());
+    return CellBuilder.beginCell().storeRefMaybe(in.toCell()).storeDict(dictCell).endCell();
+  }
 
-    public static TransactionIO deserialize(CellSlice cs) {
-        return null;
+  public List getOutMessages() {
+    List msgs = new ArrayList<>();
+    for (Map.Entry entry : out.elements.entrySet()) {
+      msgs.add((Message) entry.getValue());
     }
+    return msgs;
+  }
 }
diff --git a/devenv/src/main/resources/archetype-resources/src/test/java/DevEnvTest.java b/devenv/src/main/resources/archetype-resources/src/test/java/DevEnvTest.java
index df0a4912..36f0da1f 100644
--- a/devenv/src/main/resources/archetype-resources/src/test/java/DevEnvTest.java
+++ b/devenv/src/main/resources/archetype-resources/src/test/java/DevEnvTest.java
@@ -1,36 +1,110 @@
-import java.io.IOException;
+import com.iwebpp.crypto.TweetNaclFast;
+import java.util.Collections;
 import lombok.extern.slf4j.Slf4j;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.ton.java.address.Address;
 import org.ton.java.cell.Cell;
+import org.ton.java.emulator.EmulateTransactionResult;
+import org.ton.java.emulator.tx.TxEmulator;
+import org.ton.java.emulator.tx.TxEmulatorConfig;
+import org.ton.java.emulator.tx.TxVerbosityLevel;
+import org.ton.java.smartcontract.*;
 import org.ton.java.smartcontract.SmartContractCompiler;
+import org.ton.java.smartcontract.types.Destination;
+import org.ton.java.smartcontract.types.WalletV5Config;
+import org.ton.java.smartcontract.wallet.v5.WalletV5;
+import org.ton.java.tlb.types.Message;
+import org.ton.java.tlb.types.StateInit;
 import org.ton.java.tonlib.Tonlib;
-import org.ton.java.tonlib.types.*;
+import org.ton.java.tonlib.types.BlockIdExt;
+import org.ton.java.utils.Utils;
 
 @Slf4j
 @RunWith(JUnit4.class)
 public class DevEnvTest {
 
-  static Cell codeCell;
-
-  @BeforeClass
-  public static void setUpBeforeClass() throws IOException {
-    log.info("Compiling main.fc...");
+  @Test
+  public void testCompileContract() {
     SmartContractCompiler smcFunc =
-        SmartContractCompiler.builder().contractAsResource("/main.fc").build();
-    codeCell = smcFunc.compileToCell();
+            SmartContractCompiler.builder().contractAsResource("/simple.fc").build();
+    Cell codeCell = smcFunc.compileToCell();
     log.info("codeCell {}", codeCell.print());
   }
 
-  @Test
-  public void testCompileContract() throws IOException {}
-
   @Test
   public void testTonlibGetLastBlock() {
     Tonlib tonlib = Tonlib.builder().testnet(true).build();
     BlockIdExt block = tonlib.getLast().getLast();
     log.info("block {}", block);
   }
+
+  @Test
+  public void testTxEmulatorWalletV5ExternalMsgSimplified() {
+
+    TxEmulator txEmulator =
+            TxEmulator.builder()
+                    .configType(TxEmulatorConfig.TESTNET)
+                    .verbosityLevel(TxVerbosityLevel.UNLIMITED)
+                    .build();
+
+    SmartContractCompiler smcFunc =
+            SmartContractCompiler.builder().contractAsResource("/new-wallet-v5.fc").build();
+
+    Cell codeCell = smcFunc.compileToCell();
+
+    byte[] publicKey =
+            Utils.hexToSignedBytes("82A0B2543D06FEC0AAC952E9EC738BE56AB1B6027FC0C1AA817AE14B4D1ED2FB");
+    byte[] secretKey =
+            Utils.hexToSignedBytes("F182111193F30D79D517F2339A1BA7C25FDF6C52142F0F2C1D960A1F1D65E1E4");
+    TweetNaclFast.Signature.KeyPair keyPair = TweetNaclFast.Signature.keyPair_fromSeed(secretKey);
+
+    WalletV5 walletV5 =
+            WalletV5.builder()
+                    .keyPair(keyPair)
+                    .isSigAuthAllowed(false)
+                    .initialSeqno(0)
+                    .walletId(42)
+                    .build();
+
+    Cell dataCell = walletV5.createDataCell();
+
+    log.info("codeCellHex {}", codeCell.toHex());
+    log.info("dataCellHex {}", dataCell.toHex());
+
+    StateInit walletV5StateInit = StateInit.builder().code(codeCell).data(dataCell).build();
+
+    Address address = walletV5StateInit.getAddress();
+    log.info("addressRaw {}", address.toRaw());
+    log.info("addressBounceable {}", address.toBounceable());
+
+    String rawDummyDestinationAddress =
+            "0:258e549638a6980ae5d3c76382afd3f4f32e34482dafc3751e3358589c8de00d";
+
+    WalletV5Config walletV5Config =
+            WalletV5Config.builder()
+                    .seqno(0)
+                    .walletId(42)
+                    .body(
+                            walletV5
+                                    .createBulkTransfer(
+                                            Collections.singletonList(
+                                                    Destination.builder()
+                                                            .bounce(false)
+                                                            .address(rawDummyDestinationAddress)
+                                                            .amount(Utils.toNano(1))
+                                                            .build()))
+                                    .toCell())
+                    .build();
+
+    Message extMsg = walletV5.prepareExternalMsg(walletV5Config);
+
+    EmulateTransactionResult result =
+            txEmulator.emulateTransaction(
+                    codeCell, dataCell, Utils.toNano(2), extMsg.toCell().toBase64());
+
+    log.info("result sendExternalMessage[1]: {}", result);
+    result.getTransaction().printTransactionFees(true);
+  }
 }
diff --git a/devenv/src/main/resources/archetype-resources/src/test/resources/new-wallet-v5.fc b/devenv/src/main/resources/archetype-resources/src/test/resources/new-wallet-v5.fc
new file mode 100644
index 00000000..a9407462
--- /dev/null
+++ b/devenv/src/main/resources/archetype-resources/src/test/resources/new-wallet-v5.fc
@@ -0,0 +1,301 @@
+#pragma version =0.4.4;
+
+#include "stdlib.fc";
+
+const int error::signature_disabled = 132;
+const int error::invalid_seqno = 133;
+const int error::invalid_wallet_id = 134;
+const int error::invalid_signature = 135;
+const int error::expired = 136;
+const int error::external_send_message_must_have_ignore_errors_send_mode = 137;
+const int error::invalid_message_operation = 138;
+const int error::add_extension = 139;
+const int error::remove_extension = 140;
+const int error::unsupported_action = 141;
+const int error::disable_signature_when_extensions_is_empty = 142;
+const int error::this_signature_mode_already_set = 143;
+const int error::remove_last_extension_when_signature_disabled = 144;
+const int error::extension_wrong_workchain = 145;
+const int error::only_extension_can_change_signature_mode = 146;
+const int error::invalid_c5 = 147;
+
+const int size::bool = 1;
+const int size::seqno = 32;
+const int size::wallet_id = 32;
+const int size::public_key = 256;
+const int size::valid_until = 32;
+const int size::message_flags = 4;
+const int size::signature = 512;
+const int size::message_operation_prefix = 32;
+const int size::address_hash_size = 256;
+const int size::query_id = 64;
+
+const int prefix::signed_external = 0x7369676E;
+const int prefix::signed_internal = 0x73696E74;
+const int prefix::extension_action = 0x6578746E;
+
+(slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{02} SDBEGINSQ";
+(slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{03} SDBEGINSQ";
+(slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{04} SDBEGINSQ";
+
+;;; returns the number of trailing zeroes in slice s.
+int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0";
+
+;;; returns the last 0 ≤ l ≤ 1023 bits of s.
+slice get_last_bits(slice s, int l) asm "SDCUTLAST";
+;;; returns all but the last 0 ≤ l ≤ 1023 bits of s.
+slice remove_last_bits(slice s, int l) asm "SDSKIPLAST";
+
+;; `action_send_msg` has 0x0ec3c86d prefix
+;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380
+slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS";
+
+;;; put raw list of OutActions to C5 register.
+;;; OutList TLB-schema - https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L378
+;;; C5 register - https://docs.ton.org/tvm.pdf, page 11
+() set_c5_actions(cell action_list) impure asm "c5 POP";
+
+;;; transforms an ordinary or exotic cell into a Slice, as if it were an ordinary cell. A flag is returned indicating whether c is exotic. If that be the case, its type can later be deserialized from the first eight bits of s.
+(slice, int) begin_parse_raw(cell c) asm "XCTOS";
+
+cell verify_c5_actions(cell c5, int is_external) inline {
+    ;; XCTOS doesn't automatically load exotic cells (unlike CTOS `begin_parse`).
+    ;; we use it in `verify_c5_actions` because during action phase processing exotic cells in c5 won't be unfolded too.
+    ;; exotic cell starts with 0x02, 0x03 or 0x04 so it will not pass action_send_msg prefix check
+    (slice cs, _) = c5.begin_parse_raw();
+
+    int count = 0;
+
+    while (~ cs.slice_empty?()) {
+        ;; only `action_send_msg` is allowed; `action_set_code`, `action_reserve_currency` or `action_change_library` are not.
+        cs = cs.enforce_and_remove_action_send_msg_prefix();
+
+        throw_unless(error::invalid_c5, cs.slice_bits() == 8); ;; send_mode uint8
+        throw_unless(error::invalid_c5, cs.slice_refs() == 2); ;; next-action-ref and MessageRelaxed ref
+
+        ;; enforce that send_mode has +2 bit (ignore errors) set for external message.
+        ;; if such send_mode is not set and sending fails at the action phase (for example due to insufficient balance) then the seqno will not be increased and the external message will be processed again and again.
+
+        ;; action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction;
+        ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380
+        ;; load 7 bits and make sure that they end with 1
+        throw_if(error::external_send_message_must_have_ignore_errors_send_mode, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0));
+
+        (cs, _) = cs.preload_ref().begin_parse_raw();
+        count += 1;
+    }
+    throw_unless(error::invalid_c5, count <= 255);
+    throw_unless(error::invalid_c5, cs.slice_refs() == 0);
+
+    return c5;
+}
+
+() process_actions(slice cs, int is_external, int is_extension) impure inline_ref {
+    cell c5_actions = cs~load_maybe_ref();
+    ifnot (cell_null?(c5_actions)) {
+        ;; Simply set the C5 register with all pre-computed actions after verification:
+        set_c5_actions(c5_actions.verify_c5_actions(is_external));
+    }
+    if (cs~load_int(1) == 0) { ;; has_other_actions
+        return ();
+    }
+
+    ;; Loop extended actions
+    while (true) {
+        int is_add_extension = cs~check_and_remove_add_extension_prefix();
+        int is_remove_extension = is_add_extension ? 0 : cs~check_and_remove_remove_extension_prefix();
+        ;; Add/remove extensions
+        if (is_add_extension | is_remove_extension) {
+            (int address_wc, int address_hash) = parse_std_addr(cs~load_msg_addr());
+            (int my_address_wc, _) = parse_std_addr(my_address());
+
+            throw_unless(error::extension_wrong_workchain, my_address_wc == address_wc); ;; the extension must be in the same workchain as the wallet.
+
+            slice data_slice = get_data().begin_parse();
+            slice data_slice_before_extensions = data_slice~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key);
+            cell extensions = data_slice.preload_dict();
+
+            ;; Add extension
+            if (is_add_extension) {
+                (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(-1, 1));
+                throw_unless(error::add_extension, is_success);
+            } else { ;; Remove extension
+                (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash);
+                throw_unless(error::remove_extension, is_success);
+                int is_signature_allowed = data_slice_before_extensions.preload_int(size::bool);
+                throw_if(error::remove_last_extension_when_signature_disabled, null?(extensions) & (~ is_signature_allowed));
+            }
+
+            set_data(begin_cell()
+                    .store_slice(data_slice_before_extensions)
+                    .store_dict(extensions)
+                    .end_cell());
+
+        } elseif (cs~check_and_remove_set_signature_allowed_prefix()) { ;; allow/disallow signature
+            throw_unless(error::only_extension_can_change_signature_mode, is_extension);
+            int allow_signature = cs~load_int(1);
+            slice data_slice = get_data().begin_parse();
+            int is_signature_allowed = data_slice~load_int(size::bool);
+            throw_if(error::this_signature_mode_already_set, is_signature_allowed == allow_signature);
+            is_signature_allowed = allow_signature;
+
+            slice data_tail = data_slice; ;; seqno, wallet_id, public_key, extensions
+            ifnot (allow_signature) { ;; disallow
+                int is_extensions_not_empty = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key).preload_int(1);
+                throw_unless(error::disable_signature_when_extensions_is_empty, is_extensions_not_empty);
+            }
+
+            set_data(begin_cell()
+                    .store_int(is_signature_allowed, size::bool)
+                    .store_slice(data_tail) ;; seqno, wallet_id, public_key, extensions
+                    .end_cell());
+        } else {
+            throw(error::unsupported_action);
+        }
+        ifnot (cs.slice_refs()) {
+            return ();
+        }
+        cs = cs.preload_ref().begin_parse();
+    }
+}
+
+;; ------------------------------------------------------------------------------------------------
+
+() process_signed_request(slice in_msg_body, int is_external) impure inline {
+    slice signature = in_msg_body.get_last_bits(size::signature);
+    slice signed_slice = in_msg_body.remove_last_bits(size::signature);
+
+    slice cs = signed_slice.skip_bits(size::message_operation_prefix); ;; skip signed_internal or signed_external prefix
+    (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno));
+
+    slice data_slice = get_data().begin_parse();
+    int is_signature_allowed = data_slice~load_int(size::bool);
+    int stored_seqno = data_slice~load_uint(size::seqno);
+    slice data_tail = data_slice; ;; wallet_id, public_key, extensions
+    int stored_wallet_id = data_slice~load_uint(size::wallet_id);
+    int public_key = data_slice~load_uint(size::public_key);
+    int is_extensions_not_empty = data_slice.preload_int(1);
+
+    int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key);
+    ifnot (is_signature_valid) {
+        if (is_external) {
+            throw(error::invalid_signature);
+        } else {
+            return ();
+        }
+    }
+    ;; In case the wallet application has initially, by mistake, deployed a contract with the wrong bit (signature is forbidden and extensions are empty) - we allow such a contract to work.
+    throw_if(error::signature_disabled, (~ is_signature_allowed) & is_extensions_not_empty);
+    throw_unless(error::invalid_seqno, seqno == stored_seqno);
+    throw_unless(error::invalid_wallet_id, wallet_id == stored_wallet_id);
+    throw_if(error::expired, valid_until <= now());
+
+    if (is_external) {
+        accept_message();
+    }
+
+    stored_seqno = stored_seqno + 1;
+    set_data(begin_cell()
+            .store_int(true, size::bool) ;; is_signature_allowed
+            .store_uint(stored_seqno, size::seqno)
+            .store_slice(data_tail) ;; wallet_id, public_key, extensions
+            .end_cell());
+
+    if (is_external) {
+        ;; For external messages we commit seqno changes, so that even if an exception occurs further on, the reply-protection will still work.
+        commit();
+    }
+
+    process_actions(cs, is_external, false);
+}
+
+() recv_external(slice in_msg_body) impure inline {
+    throw_unless(error::invalid_message_operation, in_msg_body.preload_uint(size::message_operation_prefix) == prefix::signed_external);
+    process_signed_request(in_msg_body, true);
+}
+
+;; ------------------------------------------------------------------------------------------------
+
+() recv_internal(cell in_msg_full, slice in_msg_body) impure inline {
+    if (in_msg_body.slice_bits() < size::message_operation_prefix) {
+        return (); ;; just receive Toncoins
+    }
+    int op = in_msg_body.preload_uint(size::message_operation_prefix);
+    if ((op != prefix::extension_action) & (op != prefix::signed_internal)) {
+        return (); ;; just receive Toncoins
+    }
+
+    ;; bounced messages has 0xffffffff prefix and skipped by op check
+
+    if (op == prefix::extension_action) {
+        in_msg_body~skip_bits(size::message_operation_prefix);
+
+        slice in_msg_full_slice = in_msg_full.begin_parse();
+        in_msg_full_slice~skip_bits(size::message_flags);
+        ;; Authenticate extension by its address.
+        (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr());
+        (int my_address_wc, _) = parse_std_addr(my_address());
+
+        if (my_address_wc != sender_address_wc) {
+            return ();
+        }
+
+        cell extensions = get_data().begin_parse()
+                .skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key)
+                .preload_dict();
+
+        ;; Note that some random contract may have deposited funds with this prefix,
+        ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same).
+        (_, int extension_found) = extensions.udict_get?(size::address_hash_size, sender_address_hash);
+        ifnot (extension_found) {
+            return ();
+        }
+
+        in_msg_body~skip_bits(size::query_id); ;; skip query_id
+
+        process_actions(in_msg_body, false, true);
+        return ();
+
+    }
+
+    ;; Before signature checking we handle errors silently (return), after signature checking we throw exceptions.
+
+    ;; Check to make sure that there are enough bits for reading before signature check
+    if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + size::signature) {
+        return ();
+    }
+    process_signed_request(in_msg_body, false);
+}
+
+;; ------------------------------------------------------------------------------------------------
+;; Get methods
+
+int is_signature_allowed() method_id {
+    return get_data().begin_parse()
+            .preload_int(size::bool);
+}
+
+int seqno() method_id {
+    return get_data().begin_parse()
+            .skip_bits(size::bool)
+            .preload_uint(size::seqno);
+}
+
+int get_subwallet_id() method_id {
+    return get_data().begin_parse()
+            .skip_bits(size::bool + size::seqno)
+            .preload_uint(size::wallet_id);
+}
+
+int get_public_key() method_id {
+    return get_data().begin_parse()
+            .skip_bits(size::bool + size::seqno + size::wallet_id)
+            .preload_uint(size::public_key);
+}
+
+;; Returns raw dictionary (or null if empty) where keys are address hashes. Workchains of extensions are same with wallet smart contract workchain.
+cell get_extensions() method_id {
+    return get_data().begin_parse()
+            .skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key)
+            .preload_dict();
+}
\ No newline at end of file
diff --git a/devenv/src/main/resources/archetype-resources/src/test/resources/main.fc b/devenv/src/main/resources/archetype-resources/src/test/resources/simple.fc
similarity index 93%
rename from devenv/src/main/resources/archetype-resources/src/test/resources/main.fc
rename to devenv/src/main/resources/archetype-resources/src/test/resources/simple.fc
index 5ceb4aa4..69ac8e68 100644
--- a/devenv/src/main/resources/archetype-resources/src/test/resources/main.fc
+++ b/devenv/src/main/resources/archetype-resources/src/test/resources/simple.fc
@@ -8,6 +8,7 @@
 
 () recv_external(slice in_msg_body) impure {
     ~dump(41);
+
     var msg_seqno = in_msg_body~load_uint(32);
     var ds = get_data().begin_parse();
     var stored_seqno = ds~load_uint(32);
@@ -16,12 +17,13 @@
 
     accept_message();
 
+    ;; replay protection
     set_data(begin_cell()
             .store_uint(stored_seqno + 1, 32)
             .end_cell());
 }
 
 int get_id() method_id {
-    ;;     ~dump(42);
+    ~dump(42);
     return 42;
 }
\ No newline at end of file
diff --git a/emulator/src/main/java/org/ton/java/emulator/EmulateTransactionResult.java b/emulator/src/main/java/org/ton/java/emulator/EmulateTransactionResult.java
index 394ade37..dc235d50 100644
--- a/emulator/src/main/java/org/ton/java/emulator/EmulateTransactionResult.java
+++ b/emulator/src/main/java/org/ton/java/emulator/EmulateTransactionResult.java
@@ -5,9 +5,7 @@
 import org.apache.commons.lang3.StringUtils;
 import org.ton.java.cell.Cell;
 import org.ton.java.cell.CellSlice;
-import org.ton.java.tlb.types.OutList;
-import org.ton.java.tlb.types.ShardAccount;
-import org.ton.java.tlb.types.Transaction;
+import org.ton.java.tlb.types.*;
 
 @Builder
 @Data
@@ -22,7 +20,7 @@ public class EmulateTransactionResult implements Serializable {
   String actions; // Base64 encoded compute phase actions boc (OutList n)"
   double elapsed_time;
 
-  ShardAccount getNewShardAccount() {
+  public ShardAccount getNewShardAccount() {
     if (StringUtils.isNotEmpty(shard_account)) {
       return ShardAccount.deserialize(CellSlice.beginParse(Cell.fromBocBase64(shard_account)));
     } else {
@@ -30,7 +28,7 @@ ShardAccount getNewShardAccount() {
     }
   }
 
-  Transaction getTransaction() {
+  public Transaction getTransaction() {
     if (StringUtils.isNotEmpty(transaction)) {
       return Transaction.deserialize(CellSlice.beginParse(Cell.fromBocBase64(transaction)));
     } else {
@@ -38,7 +36,7 @@ Transaction getTransaction() {
     }
   }
 
-  OutList getActions() {
+  public OutList getActions() {
     if (StringUtils.isNotEmpty(actions)) {
       return OutList.deserialize(CellSlice.beginParse(Cell.fromBocBase64(actions)));
     } else {
diff --git a/emulator/src/test/java/org/ton/java/emulator/TestTxEmulator.java b/emulator/src/test/java/org/ton/java/emulator/TestTxEmulator.java
index 1e89c73a..de30ce27 100644
--- a/emulator/src/test/java/org/ton/java/emulator/TestTxEmulator.java
+++ b/emulator/src/test/java/org/ton/java/emulator/TestTxEmulator.java
@@ -56,7 +56,7 @@ public static void setUpBeforeClass() {
 
     tonlib = Tonlib.builder().testnet(true).ignoreCache(false).build();
 
-    config = tonlib.getConfigAll(128);
+    //    config = tonlib.getConfigAll(128);
 
     txEmulator =
         TxEmulator.builder()
@@ -254,6 +254,7 @@ public void testTxEmulatorEmulateTxWithEmptyAccount() {
         txEmulator.emulateTransaction(shardAccountBocBase64, internalMsgBocBase64);
     log.info("result {}", result);
     assertThat(result.isSuccess()).isTrue();
+    result.getTransaction().printTransactionFees(true);
   }
 
   @Test
@@ -296,10 +297,11 @@ public void testTxEmulatorEmulateTxWithAccount() {
     log.info("new shardAccount {}", result.getNewShardAccount());
     log.info("new transaction {}", result.getTransaction());
     log.info("new actions {}", result.getActions());
+    result.getTransaction().printTransactionFees(true);
   }
 
   @Test
-  public void testTxEmulatorWalletV5ExternalMsg() throws IOException, URISyntaxException {
+  public void testTxEmulatorWalletV5ExternalMsg() {
 
     SmartContractCompiler smcFunc =
         SmartContractCompiler.builder()
@@ -403,8 +405,7 @@ public void testTxEmulatorWalletV5ExternalMsg() throws IOException, URISyntaxExc
     TransactionDescription txDesc = result.getTransaction().getDescription();
     log.info("txDesc {}", txDesc);
 
-    TransactionDescriptionOrdinary txDescOrd =
-        (TransactionDescriptionOrdinary) txDesc.getDescription();
+    TransactionDescriptionOrdinary txDescOrd = (TransactionDescriptionOrdinary) txDesc;
 
     ComputePhaseVM computePhase = (ComputePhaseVM) txDescOrd.getComputePhase();
     assertThat(computePhase.isSuccess()).isTrue();
@@ -443,8 +444,9 @@ public void testTxEmulatorWalletV5ExternalMsg() throws IOException, URISyntaxExc
 
     txDesc = result.getTransaction().getDescription();
     log.info("txDesc {}", txDesc);
+    result.getTransaction().printTransactionFees(true);
 
-    txDescOrd = (TransactionDescriptionOrdinary) txDesc.getDescription();
+    txDescOrd = (TransactionDescriptionOrdinary) txDesc;
 
     computePhase = (ComputePhaseVM) txDescOrd.getComputePhase();
     assertThat(computePhase.isSuccess()).isTrue();
@@ -461,7 +463,72 @@ public void testTxEmulatorWalletV5ExternalMsg() throws IOException, URISyntaxExc
   }
 
   @Test
-  public void testTxEmulatorWalletV5InternalMsg() throws IOException, URISyntaxException {
+  public void testTxEmulatorWalletV5ExternalMsgSimplified() {
+
+    SmartContractCompiler smcFunc =
+        SmartContractCompiler.builder()
+            .contractAsResource("/contracts/wallets/new-wallet-v5.fc")
+            .build();
+
+    Cell codeCell = smcFunc.compileToCell();
+
+    byte[] publicKey =
+        Utils.hexToSignedBytes("82A0B2543D06FEC0AAC952E9EC738BE56AB1B6027FC0C1AA817AE14B4D1ED2FB");
+    byte[] secretKey =
+        Utils.hexToSignedBytes("F182111193F30D79D517F2339A1BA7C25FDF6C52142F0F2C1D960A1F1D65E1E4");
+    TweetNaclFast.Signature.KeyPair keyPair = TweetNaclFast.Signature.keyPair_fromSeed(secretKey);
+
+    WalletV5 walletV5 =
+        WalletV5.builder()
+            .keyPair(keyPair)
+            .isSigAuthAllowed(false)
+            .initialSeqno(0)
+            .walletId(42)
+            .build();
+
+    Cell dataCell = walletV5.createDataCell();
+
+    log.info("codeCellHex {}", codeCell.toHex());
+    log.info("dataCellHex {}", dataCell.toHex());
+
+    StateInit walletV5StateInit = StateInit.builder().code(codeCell).data(dataCell).build();
+
+    Address address = walletV5StateInit.getAddress();
+    log.info("addressRaw {}", address.toRaw());
+    log.info("addressBounceable {}", address.toBounceable());
+
+    String rawDummyDestinationAddress =
+        "0:258e549638a6980ae5d3c76382afd3f4f32e34482dafc3751e3358589c8de00d";
+
+    WalletV5Config walletV5Config =
+        WalletV5Config.builder()
+            .seqno(0)
+            .walletId(42)
+            .body(
+                walletV5
+                    .createBulkTransfer(
+                        Collections.singletonList(
+                            Destination.builder()
+                                .bounce(false)
+                                .address(rawDummyDestinationAddress)
+                                .amount(Utils.toNano(1))
+                                .build()))
+                    .toCell())
+            .build();
+
+    Message extMsg = walletV5.prepareExternalMsg(walletV5Config);
+
+    EmulateTransactionResult result =
+        txEmulator.emulateTransaction(
+            codeCell, dataCell, Utils.toNano(2), extMsg.toCell().toBase64());
+
+    log.info("result sendExternalMessage[1]: {}", result);
+    //    log.info("txFees: {}", result.getTransaction().getTransactionFees());
+    result.getTransaction().printTransactionFees(true);
+  }
+
+  @Test
+  public void testTxEmulatorWalletV5InternalMsg() throws URISyntaxException {
 
     URL resource = SmartContractCompiler.class.getResource("/contracts/wallets/new-wallet-v5.fc");
     String contractAbsolutePath = Paths.get(resource.toURI()).toFile().getAbsolutePath();
@@ -567,8 +634,7 @@ public void testTxEmulatorWalletV5InternalMsg() throws IOException, URISyntaxExc
     TransactionDescription txDesc = result.getTransaction().getDescription();
     log.info("txDesc {}", txDesc);
 
-    TransactionDescriptionOrdinary txDescOrd =
-        (TransactionDescriptionOrdinary) txDesc.getDescription();
+    TransactionDescriptionOrdinary txDescOrd = (TransactionDescriptionOrdinary) txDesc;
 
     ComputePhaseVM computePhase = (ComputePhaseVM) txDescOrd.getComputePhase();
     assertThat(computePhase.isSuccess()).isTrue();
@@ -613,7 +679,9 @@ public void testTxEmulatorWalletV5InternalMsg() throws IOException, URISyntaxExc
     txDesc = result.getTransaction().getDescription();
     log.info("txDesc {}", txDesc);
 
-    txDescOrd = (TransactionDescriptionOrdinary) txDesc.getDescription();
+    result.getTransaction().printTransactionFees(true);
+
+    txDescOrd = (TransactionDescriptionOrdinary) txDesc;
 
     computePhase = (ComputePhaseVM) txDescOrd.getComputePhase();
     assertThat(computePhase.isSuccess()).isTrue();
@@ -642,7 +710,6 @@ private static Cell getLibs() {
       Cell lib = Cell.fromBocBase64(cellLibBoc);
       log.info("cell lib {}", lib.toHex());
       x.elements.put(1L, lib);
-      x.elements.put(2L, lib); // 2nd because of the bug in hashmap/e
     }
 
     Cell dictLibs =
diff --git a/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java b/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java
index c9786c1e..21bf2f82 100644
--- a/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java
+++ b/smartcontract/src/main/java/org/ton/java/smartcontract/SmartContractCompiler.java
@@ -65,7 +65,6 @@ public String compile() {
         || outputFiftAsmFile.contains("error: undefined function")) {
       throw new Error("Compile error: " + outputFiftAsmFile);
     }
-    //    outputFiftAsmFile = StringUtils.replace(outputFiftAsmFile, "2 boc+>B", "0 boc+>B");
     outputFiftAsmFile =
         "\"\"\"\"TonUtil.fif\"\"\"\" include \"\"\"\"Asm.fif\"\"\"\" include PROGRAM{ "
             + outputFiftAsmFile