<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paul</title>
    <description>The latest articles on DEV Community by Paul (@fraggedreality).</description>
    <link>https://dev.to/fraggedreality</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F321200%2F28bb7841-28d5-44b6-8f67-0bd7c58762d4.jpg</url>
      <title>DEV Community: Paul</title>
      <link>https://dev.to/fraggedreality</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fraggedreality"/>
    <language>en</language>
    <item>
      <title>Logging steps in fca00c:Asteroids with a proxy contract</title>
      <dc:creator>Paul</dc:creator>
      <pubDate>Thu, 30 Mar 2023 13:34:27 +0000</pubDate>
      <link>https://dev.to/fraggedreality/logging-steps-in-fca00casteroids-with-a-proxy-contract-bpm</link>
      <guid>https://dev.to/fraggedreality/logging-steps-in-fca00casteroids-with-a-proxy-contract-bpm</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/fraggedreality/implementing-a-proxy-contract-for-the-game-engine-to-log-solution-steps-in-fca00c-24pl"&gt;previous post&lt;/a&gt; we discussed how to implement a proxy contract for the &lt;code&gt;game_engine&lt;/code&gt; to log solution-steps in &lt;a href="https://fastcheapandoutofcontrol.com/game/asteroids/play" rel="noopener noreferrer"&gt;fca00c:Asteroids&lt;/a&gt;. Now let's add a bit of flesh to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;We have learned that in &lt;a href="https://soroban.stellar.org" rel="noopener noreferrer"&gt;soroban&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;any client generated from a &lt;code&gt;WASM&lt;/code&gt; can be used to interact with any other contract as long as the functions exist on both&lt;/li&gt;
&lt;li&gt;when building a contract without &lt;code&gt;debug-assertions&lt;/code&gt; the proxy still can be used for logging

&lt;ul&gt;
&lt;li&gt;logging-capabilities are a bit limited due to the absence of &lt;code&gt;std&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the code can be found &lt;a href="https://github.com/hanseartic/fca00c-asteroids/tree/logging_engine" rel="noopener noreferrer"&gt;on github&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Adding actual logging to the existing &lt;code&gt;logging_engine&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's continue where we left off: &lt;a href="https://github.com/hanseartic/fca00c-asteroids/tree/proxy_engine" rel="noopener noreferrer"&gt;https://github.com/hanseartic/fca00c-asteroids/tree/proxy_engine&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=logging_engine&amp;amp;repo=620754220&amp;amp;machine=basicLinux32gb&amp;amp;location=WestEurope" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/codespaces/badge.svg" alt="Open in GitHub Codespaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First thing we'd need to do is to record all the steps that are invoked on the &lt;code&gt;game_engine&lt;/code&gt;. Ideally we'd only record actions that change the state of the game and skip the read-only operations.&lt;/p&gt;

&lt;p&gt;For that we need to persist the actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;log_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ActionItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="nf"&gt;.push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="nf"&gt;.storage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ACTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and call this function in every function that changes the game's state, e.g. like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;     pub fn p_turn(env: Env, direction: game_engine::Direction) -&amp;gt; Result&amp;lt;(), game_engine::Error&amp;gt; {
&lt;span class="gd"&gt;-        Ok(Self::get_engine(&amp;amp;env).p_turn(&amp;amp;direction))
&lt;/span&gt;&lt;span class="gi"&gt;+        if let Err(Ok(e)) = Self::get_engine(&amp;amp;env).try_p_turn(&amp;amp;direction) {
+            return Err(e);
+        }
+        Self::log_action(&amp;amp;env, &amp;amp;ActionItem(Action::Turn, direction as u32));
+        Ok(())
&lt;/span&gt;     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets add this to all functions (see complete &lt;a href="https://github.com/hanseartic/fca00c-asteroids/commit/8a11ed8" rel="noopener noreferrer"&gt;diff&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Printing the recorded actions in test
&lt;/h2&gt;

&lt;p&gt;To see the recorded actions we need to print them in the test-case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -40,9 +40,8 @@&lt;/span&gt; fn fca00c_fast() {
     let env = Env::default();
     let proxy_engine_id = env.register_contract_wasm(None, logging_contract::WASM);
     let engine_id = env.register_contract_wasm(None, GameEngineWASM);
&lt;span class="gd"&gt;-    let engine = GameEngine::new(&amp;amp;env, &amp;amp;proxy_engine_id);
-
-    logging_contract::Client::new(&amp;amp;env, &amp;amp;proxy_engine_id).wrap(&amp;amp;engine_id);
&lt;/span&gt;&lt;span class="gi"&gt;+    let engine = logging_contract::Client::new(&amp;amp;env, &amp;amp;proxy_engine_id);
+    engine.wrap(&amp;amp;engine_id);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;     // DON'T CHANGE THE FOLLOWING INIT() PARAMETERS
     // Once you've submitted your contract on the FCA00C site, we will invoke
&lt;span class="p"&gt;Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y
@@ -79,6 +78,12 @@&lt;/span&gt; fn fca00c_fast() {
     let logs = env.logger().all();
     println!("{}", logs.join("\n"));
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    for action in engine.actions() {
+        if let Ok(a) = action {
+            println!("{:?}", a);
+        }
+    }
+
&lt;/span&gt;     let points = engine.p_points();
&lt;span class="err"&gt;
&lt;/span&gt;     println!("Points: {}", points);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;🛈 as we might be adding more function to the log-contract it makes sense to use the &lt;code&gt;logging_engine::Client&lt;/code&gt; for all invocations. The other way was just there to show the client does not actually matter as long as it implements a function present in the contract&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;and run the test again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ make &lt;span class="nb"&gt;test
&lt;/span&gt;cargo &lt;span class="nb"&gt;test &lt;/span&gt;fca00c_fast &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--nocapture&lt;/span&gt;
   Compiling soroban-asteroids-solution v0.0.0 &lt;span class="o"&gt;(&lt;/span&gt;/home/paul/Code/fca00c - logging proxy/contracts/solution&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;4.94s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;target/debug/deps/soroban_asteroids_solution-5b0a3423b755223b&lt;span class="o"&gt;)&lt;/span&gt;

running 1 &lt;span class="nb"&gt;test
&lt;/span&gt;invoker account is not configured
invoker account is not configured
🗒️ logger engine taking notes
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Shoot, 1&lt;span class="o"&gt;)&lt;/span&gt;
Points: 1
&lt;span class="nb"&gt;test test&lt;/span&gt;::fca00c_fast ... ok

&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 1 filtered out&lt;span class="p"&gt;;&lt;/span&gt; finished &lt;span class="k"&gt;in &lt;/span&gt;0.56s
❯
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;👀 notice &lt;code&gt;ActionItem(Shoot, 1)&lt;/code&gt; in the logs - that's us shooting and hitting one asteroid&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the output
&lt;/h2&gt;

&lt;p&gt;We can rely on the debugging capabilities of &lt;code&gt;println!&lt;/code&gt; macro or implement a slightly more fine-grained logging directly in the &lt;code&gt;logging_engine&lt;/code&gt;. Let's for example add up all consecutive &lt;code&gt;p_move&lt;/code&gt; calls and only record the last of multiple consecutive &lt;code&gt;p_turn&lt;/code&gt; commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/logging_engine/src/engine.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/logging_engine/src/engine.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -30,7 +30,26 @@&lt;/span&gt; impl LoggingEngine {
     }
     fn log_action(env: &amp;amp;Env, action: &amp;amp;ActionItem) {
         let mut actions = Self::actions(env.clone());
&lt;span class="gd"&gt;-        actions.push_back(*action);
&lt;/span&gt;&lt;span class="gi"&gt;+        let add_action: ActionItem;
+
+        if let Some(Ok(last_action_item)) = actions.last() {
+            match last_action_item {
+                ActionItem(Action::Turn, _) if action.0 == Action::Turn =&amp;gt; {
+                    actions.pop_back();
+                    add_action = action.clone();
+                }
+                ActionItem(Action::Move, _) if action.0 == Action::Move =&amp;gt; {
+                    actions.pop_back();
+                    add_action = ActionItem(last_action_item.0, last_action_item.1 + (action.1 as u32));
+
+                }
+                _ =&amp;gt; add_action = *action,
+            }
+        } else {
+            add_action = *action;
+        }
+
+        actions.push_back(add_action);
&lt;/span&gt;         env.storage().set(&amp;amp;ACTIONS, &amp;amp;actions);
     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;🔔 don't forget to compile!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now let's add some more calls to the &lt;code&gt;game_engine&lt;/code&gt; in our solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/solution/src/lib.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/solution/src/lib.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,6 +1,6 @@&lt;/span&gt;
 #![no_std]
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-use engine::Client as GameEngine;
&lt;/span&gt;&lt;span class="gi"&gt;+use engine::{Client as GameEngine, Direction};
&lt;/span&gt; use soroban_sdk::{contractimpl, BytesN, Env};
&lt;span class="err"&gt;
&lt;/span&gt; pub struct Solution;
&lt;span class="p"&gt;@@ -19,6 +19,12 @@&lt;/span&gt; impl Solution {
         // YOUR CODE START
&lt;span class="err"&gt;
&lt;/span&gt;         engine.p_shoot();
&lt;span class="gi"&gt;+        engine.p_move(&amp;amp;None);
+        engine.p_move(&amp;amp;Some(2));
+        engine.p_turn(&amp;amp;Direction::UpLeft);
+        engine.p_turn(&amp;amp;Direction::Left);
+        engine.p_move(&amp;amp;Some(1));
+        engine.p_turn(&amp;amp;Direction::Down);
&lt;/span&gt;         // YOUR CODE END
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;we would expect consecutive moves to be summed up as well as only the last of multiple consecutive turns to be logged&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's take a look at the test-results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗒️ logger engine taking notes
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Shoot, 1&lt;span class="o"&gt;)&lt;/span&gt;
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Move, 3&lt;span class="o"&gt;)&lt;/span&gt;
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Turn, 6&lt;span class="o"&gt;)&lt;/span&gt;
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Move, 1&lt;span class="o"&gt;)&lt;/span&gt;
ActionItem&lt;span class="o"&gt;(&lt;/span&gt;Turn, 4&lt;span class="o"&gt;)&lt;/span&gt;
Points: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking good!&lt;/p&gt;

&lt;p&gt;Check the diff of what we did &lt;a href="https://github.com/hanseartic/fca00c-asteroids/commit/c52d27a" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize the logs
&lt;/h2&gt;

&lt;p&gt;Let's add a bit of color to the logs, shall we?&lt;br&gt;
So let's have two options, one to be easily parseable (if you want to play back to steps) and one to be playful and nice to the human eye.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/logging_engine/src/types.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/logging_engine/src/types.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,4 +1,4 @@&lt;/span&gt;
&lt;span class="gd"&gt;-use soroban_sdk::{contracterror, contracttype};
&lt;/span&gt;&lt;span class="gi"&gt;+use soroban_sdk::{contracterror, contracttype, Env};
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; #[contracterror]
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
&lt;span class="p"&gt;@@ -21,3 +21,15 @@&lt;/span&gt; pub enum Action {
 #[contracttype]
 #[derive(Copy, Clone)]
 pub struct ActionItem(pub Action, pub u32);
&lt;span class="gi"&gt;+
+#[contracttype]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum LogLevel {
+    Quiet,
+    Human,
+    Machine,
+}
+
+pub trait LogFormat {
+    fn log_format(&amp;amp;self, env: &amp;amp;Env, level: &amp;amp;LogLevel);
+}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and define a trait how we expect logs to be called.&lt;/p&gt;

&lt;p&gt;Now let's implement this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/logging_engine/src/engine.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/logging_engine/src/engine.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,5 +1,5 @@&lt;/span&gt;
 use soroban_sdk::{contractimpl, log, panic_with_error, vec, BytesN, Env, Map, Vec};
&lt;span class="gd"&gt;-use crate::types::{Action, ActionItem, ProxyError};
&lt;/span&gt;&lt;span class="gi"&gt;+use crate::types::{Action, ActionItem, LogFormat, LogLevel, ProxyError};
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; mod game_engine {
     soroban_sdk::contractimport!(file = "../game_engine.wasm");
&lt;span class="p"&gt;@@ -53,6 +53,42 @@&lt;/span&gt; impl LoggingEngine {
         env.storage().set(&amp;amp;ACTIONS, &amp;amp;actions);
     }
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    pub fn get_logs(env: Env, level: LogLevel) {
+        log!(&amp;amp;env, "\n");
+        if LogLevel::Quiet == level {
+            log!(&amp;amp;env, "🤷 `LogLevel::Quiet` won't output any logs.");
+            return;
+        }
+        log!(&amp;amp;env, "📼 here are the recorded steps:\n");
+
+        log!(&amp;amp;env, "\n==&amp;lt; MEANING &amp;gt;==\n");
+        match level {
+            LogLevel::Human =&amp;gt; {
+                log!(&amp;amp;env, "🧰: upgrade");
+                log!(&amp;amp;env, "🚶: move");
+                log!(&amp;amp;env, "⇔⇕: turning");
+                log!(&amp;amp;env, "🔫🎆[🎆[🎆]]: hit(s)");
+                log!(&amp;amp;env, "⛽: harvest");
+            }
+            LogLevel::Machine =&amp;gt; {
+                log!(&amp;amp;env, "0: upgrade");
+                log!(&amp;amp;env, "1..3: hit");
+                log!(&amp;amp;env, "4: harvest");
+                log!(&amp;amp;env, "8..15: turning");
+                log!(&amp;amp;env, "16..: move");
+            }
+            _ =&amp;gt; (),
+        };
+
+        log!(&amp;amp;env, "\n===&amp;lt; STEPS &amp;gt;===\n");
+        for a_i in Self::actions(env.clone()) {
+            if let Ok(a) = a_i {
+                a.log_format(&amp;amp;env, &amp;amp;level);
+            }
+        }
+        log!(&amp;amp;env, "\n===============\n");
+    }
+
&lt;/span&gt;     /// wrapping interface implemention
     pub fn init(
         env: Env,
&lt;span class="p"&gt;@@ -134,3 +170,52 @@&lt;/span&gt; impl LoggingEngine {
         Self::get_engine(&amp;amp;env).get_map()
     }
 }
&lt;span class="gi"&gt;+
+impl LogFormat for ActionItem {
+    fn log_format(&amp;amp;self, env: &amp;amp;Env, level: &amp;amp;LogLevel) {
+        match &amp;amp;self.0 {
+            Action::Harvest =&amp;gt; match level {
+                LogLevel::Human =&amp;gt; log!(&amp;amp;env, "⛽"),
+                LogLevel::Machine =&amp;gt; log!(&amp;amp;env, "4"),
+                _ =&amp;gt; (),
+            },
+            Action::Upgrade =&amp;gt; match level {
+                LogLevel::Human =&amp;gt; log!(&amp;amp;env, "🧰"),
+                LogLevel::Machine =&amp;gt; log!(&amp;amp;env, "0"),
+                _ =&amp;gt; (),
+            },
+            Action::Shoot =&amp;gt; match level {
+                LogLevel::Human =&amp;gt; {
+                    match self.1 {
+                        3 =&amp;gt; log!(&amp;amp;env, "🔫🎆🎆🎆"),
+                        2 =&amp;gt; log!(&amp;amp;env, "🔫🎆🎆"),
+                        1 =&amp;gt; log!(&amp;amp;env, "🔫🎆"),
+                        _ =&amp;gt; log!(&amp;amp;env, "🔫"),
+                    };
+                }
+                LogLevel::Machine =&amp;gt; log!(&amp;amp;env, "{}", self.1 as u32),
+                _ =&amp;gt; (),
+            },
+            Action::Move =&amp;gt; match level {
+                LogLevel::Human =&amp;gt; log!(&amp;amp;env, "🚶 {}", self.1),
+                LogLevel::Machine =&amp;gt; log!(&amp;amp;env, "{}", self.1 as u32 + 15),
+                _ =&amp;gt; (),
+            },
+            Action::Turn =&amp;gt; match level {
+                LogLevel::Human =&amp;gt; match self.1 {
+                    0 =&amp;gt; log!(&amp;amp;env, "⇧"),
+                    1 =&amp;gt; log!(&amp;amp;env, "⇗"),
+                    2 =&amp;gt; log!(&amp;amp;env, "⇒"),
+                    3 =&amp;gt; log!(&amp;amp;env, "⇘"),
+                    4 =&amp;gt; log!(&amp;amp;env, "⇩"),
+                    5 =&amp;gt; log!(&amp;amp;env, "⇙"),
+                    6 =&amp;gt; log!(&amp;amp;env, "⇦"),
+                    7 =&amp;gt; log!(&amp;amp;env, "⇖"),
+                    _ =&amp;gt; (),
+                },
+                LogLevel::Machine =&amp;gt; log!(&amp;amp;env, "{}", (self.1 as u32) + 8),
+                _ =&amp;gt; (),
+            },
+        };
+    }
+}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've just added a way to present the logs in different variations. Let's check back with the tests.&lt;/p&gt;

&lt;p&gt;But first we of course need to a) compile the &lt;code&gt;logging_engine&lt;/code&gt; and b) add a call to the newly added log-function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -75,15 +75,13 @@&lt;/span&gt; fn fca00c_fast() {
&lt;span class="err"&gt;
&lt;/span&gt;     solution.solve(&amp;amp;proxy_engine_id);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    engine.get_logs(&amp;amp;logging_contract::LogLevel::Human);
+    engine.get_logs(&amp;amp;logging_contract::LogLevel::Machine);
+    engine.get_logs(&amp;amp;logging_contract::LogLevel::Quiet);
+
&lt;/span&gt;     let logs = env.logger().all();
     println!("{}", logs.join("\n"));
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    for action in engine.actions() {
-        if let Ok(a) = action {
-            println!("{:?}", a);
-        }
-    }
-
&lt;/span&gt;     let points = engine.p_points();
&lt;span class="err"&gt;
&lt;/span&gt;     println!("Points: {}", points);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🗒️ logger engine taking notes


📼 here are the recorded steps:


&lt;span class="o"&gt;==&lt;/span&gt;&amp;lt; MEANING &lt;span class="o"&gt;&amp;gt;==&lt;/span&gt;

🧰: upgrade
🚶: move
⇔⇕: turning
🔫🎆[🎆[🎆]]: hit&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;
⛽: harvest

&lt;span class="o"&gt;===&lt;/span&gt;&amp;lt; STEPS &lt;span class="o"&gt;&amp;gt;===&lt;/span&gt;

🔫🎆
🚶 U32&lt;span class="o"&gt;(&lt;/span&gt;3&lt;span class="o"&gt;)&lt;/span&gt;
⇦
🚶 U32&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
⇩

&lt;span class="o"&gt;===============&lt;/span&gt;



📼 here are the recorded steps:


&lt;span class="o"&gt;==&lt;/span&gt;&amp;lt; MEANING &lt;span class="o"&gt;&amp;gt;==&lt;/span&gt;

0: upgrade
1..3: hit
4: harvest
8..15: turning
16..: move

&lt;span class="o"&gt;===&lt;/span&gt;&amp;lt; STEPS &lt;span class="o"&gt;&amp;gt;===&lt;/span&gt;

U32&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;
U32&lt;span class="o"&gt;(&lt;/span&gt;18&lt;span class="o"&gt;)&lt;/span&gt;
U32&lt;span class="o"&gt;(&lt;/span&gt;14&lt;span class="o"&gt;)&lt;/span&gt;
U32&lt;span class="o"&gt;(&lt;/span&gt;16&lt;span class="o"&gt;)&lt;/span&gt;
U32&lt;span class="o"&gt;(&lt;/span&gt;12&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;===============&lt;/span&gt;



🤷 &lt;span class="sb"&gt;`&lt;/span&gt;LogLevel::Quiet&lt;span class="sb"&gt;`&lt;/span&gt; won&lt;span class="s1"&gt;'t output any logs.
Points: 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Congratulations! We've added nice looking, customizable logging to the challenge without bloating our solution!&lt;/p&gt;

&lt;p&gt;Now you just need to solve the actual &lt;a href="https://fastcheapandoutofcontrol.com/game/asteroids/play" rel="noopener noreferrer"&gt;fca00c:Asteroids&lt;/a&gt; challenge - the all-time leaderboard is still open for submissions!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawbacks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As the &lt;code&gt;u32&lt;/code&gt; type is always rendered as &lt;code&gt;U32(&amp;lt;VALUE&amp;gt;)&lt;/code&gt; numbers can't nicely be logged from soroban (yet).&lt;/li&gt;
&lt;li&gt;To proxy another contract we'd need to implement the complete interface again. There is no generic proxying solution in soroban.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>soroban</category>
      <category>sorobanathon</category>
      <category>stellar</category>
      <category>fca00c</category>
    </item>
    <item>
      <title>Implementing a proxy contract for the game_engine to log solution-steps in fca00c</title>
      <dc:creator>Paul</dc:creator>
      <pubDate>Wed, 29 Mar 2023 22:08:26 +0000</pubDate>
      <link>https://dev.to/fraggedreality/implementing-a-proxy-contract-for-the-game-engine-to-log-solution-steps-in-fca00c-24pl</link>
      <guid>https://dev.to/fraggedreality/implementing-a-proxy-contract-for-the-game-engine-to-log-solution-steps-in-fca00c-24pl</guid>
      <description>&lt;p&gt;To provide a hands on gamified approach to tinker with &lt;a href="//soroban.stellar.org/"&gt;&lt;strong&gt;soroban&lt;/strong&gt;&lt;/a&gt;, the latest series of &lt;a href="//quest.stellar.org"&gt;Stellar Quest&lt;/a&gt; (&lt;a href="https://fastcheapandoutofcontrol.com/game/asteroids/play" rel="noopener noreferrer"&gt;fca00c: Asteroids&lt;/a&gt;) was all about submitting a compiled WASM to compete in three categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;earliest working submission&lt;/li&gt;
&lt;li&gt;smallest working submission&lt;/li&gt;
&lt;li&gt;most efficient (CPU cycles) working submission&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimizing the contract
&lt;/h2&gt;

&lt;p&gt;Although soroban already offers pretty resource-oriented logging capabilites (e.g. strips everything related to logging from &lt;a href="https://soroban.stellar.org/docs/how-to-guides/logging#cargotoml-profile" rel="noopener noreferrer"&gt;release builds&lt;/a&gt;), all the logic around making logs readable would still stay in the contract and bloat the file size.&lt;/p&gt;

&lt;p&gt;So our approach here is to keep everything log-related out of the solution (that was supposed to be submitted) and use a proxy between the solution and the &lt;a href="https://github.com/stellar/fca00c-asteroids/blob/main/contracts/game_engine.wasm" rel="noopener noreferrer"&gt;game-engine&lt;/a&gt; provided. To achieve that we can make use of a proxy that logs all the commands before invoking them on the actual engine.&lt;/p&gt;

&lt;p&gt;A few things need to be considered here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;release builds (as described/configured per &lt;a href="https://soroban.stellar.org/docs/how-to-guides/logging#with-logs" rel="noopener noreferrer"&gt;soroban docs&lt;/a&gt;) strip the &lt;code&gt;log!&lt;/code&gt; macro related code&lt;/li&gt;
&lt;li&gt;we want the proxy contract to &lt;em&gt;always&lt;/em&gt; log - even if used by a &lt;em&gt;release&lt;/em&gt; &lt;strong&gt;solution&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;as the proxy is another soroban contract, the same &lt;a href="https://soroban.stellar.org/docs/how-to-guides/logging#using-the-log-macro" rel="noopener noreferrer"&gt;limitations&lt;/a&gt; regarding logging/formatting apply due to &lt;code&gt;std&lt;/code&gt; not being available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For sake of context let's just add the contract next to the solution contract but keep it out of the workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ tree contracts/ &lt;span class="nt"&gt;-L&lt;/span&gt; 2
contracts/
├── _game_engine
│   ├── engine.rs
│   ├── lib.rs
│   ├── map.rs
│   ├── storage.rs
│   └── types.rs
├── game_engine.wasm
├── logging_engine
│   ├── Cargo.lock
│   ├── Cargo.toml
│   └── src
└── solution
    ├── Cargo.toml
    └── src

5 directories, 9 files
❯ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That way we can individually configure the contract to always build with &lt;code&gt;debug-assertions = true&lt;/code&gt; but we don't need to deal with configuring this as a workspace package with profile-overrides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the engine contract
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/codespaces/new?hide_repo_select=true&amp;amp;ref=proxy_engine&amp;amp;repo=620754220&amp;amp;machine=basicLinux32gb&amp;amp;location=WestEurope" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/codespaces/badge.svg" alt="Open in GitHub Codespaces"&gt;&lt;/a&gt;&lt;br&gt;
First of all the original &lt;code&gt;game_engine&lt;/code&gt; contract-interface needs to be implemented so that our &lt;code&gt;logging_engine&lt;/code&gt; provides all the expected functions defined by the &lt;code&gt;game_engine::Contract&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By using &lt;a href="https://github.com/hanseartic/fca00c-asteroids/blob/4629981/contracts/logging_engine/src/engine.rs#L9" rel="noopener noreferrer"&gt;&lt;code&gt;impl Contract for LoggingEngine&lt;/code&gt;&lt;/a&gt; the complete &lt;code&gt;game_engine&lt;/code&gt; interface is enforced to be implemented.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;contracts/logging_engine/src/engine.rs:&lt;/code&gt;&lt;br&gt;

  show file listing
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;soroban_sdk&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;contractimpl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;game_engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;soroban_sdk&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;contractimport!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"../game_engine.wasm"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;LoggingEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nd"&gt;#[contractimpl]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Contract&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;LoggingEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;move_step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;laser_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;view_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fuel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;asteroid_reward&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;asteroid_density&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pod_density&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_turn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Direction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;times&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_shoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_harvest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_upgrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_pos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Direction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_points&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;p_fuel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;game_engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MapElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"needs implementation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This can only be an intermediary step to make sure every function was implemented. Unfortunately only a single &lt;code&gt;#[contractimpl]&lt;/code&gt; is possible per soroban contract.&lt;/p&gt;

&lt;p&gt;As we want to augment the logging contract with at least one more function (we need to tell the proxy &lt;strong&gt;what&lt;/strong&gt; to actually proxy, right?) we need to remove to &lt;code&gt;impl Contract for&lt;/code&gt; because we can't add functions to an implementation that are not defined in the trait.&lt;/p&gt;

&lt;p&gt;So let's compile the contract to make sure everything is in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ &lt;span class="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-A unused"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  cargo build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-unknown-unknown &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--release&lt;/span&gt; 
    Finished release &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.12s

❯
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;we are passing the &lt;code&gt;RUSTFLAGS&lt;/code&gt; here once just to prevent ⚠️warnings⚠️ for all the unused variables&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎉 Great - it builds 🛠️&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Proxying the &lt;code&gt;game_engine::Client&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now let's add the actual proxying to the implementation.&lt;/p&gt;

&lt;p&gt;First we need a way to call the proxied client. So let's add another function (&lt;code&gt;wrap&lt;/code&gt;) that accepts the client-id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -1,13 +1,27 @@&lt;/span&gt;
&lt;span class="gd"&gt;-use soroban_sdk::{contractimpl, Env, Map};
&lt;/span&gt;&lt;span class="gi"&gt;+use soroban_sdk::{contractimpl, log, BytesN, Env, Map};
&lt;/span&gt; mod game_engine {
     soroban_sdk::contractimport!(file = "../game_engine.wasm");
 }
&lt;span class="gd"&gt;-use game_engine::Contract;
&lt;/span&gt;&lt;span class="gi"&gt;+
+const ENGINE_ID: &amp;amp;str = "engine";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; pub struct LoggingEngine;
 #[contractimpl]
&lt;span class="gd"&gt;-impl Contract for LoggingEngine {
-    fn init(
&lt;/span&gt;&lt;span class="gi"&gt;+impl LoggingEngine {
+    pub fn wrap(env: Env, engine_id: BytesN&amp;lt;32&amp;gt;) {
+        env.storage().set(&amp;amp;ENGINE_ID, &amp;amp;engine_id);
+        log!(&amp;amp;env, "🗒️ logger engine taking notes");
+    }
+
+    fn engine_id(env: Env) -&amp;gt; BytesN&amp;lt;32&amp;gt; {
+        env.storage().get(&amp;amp;ENGINE_ID).unwrap().unwrap()
+    }
+    fn get_engine(env: &amp;amp;Env) -&amp;gt; game_engine::Client {
+        game_engine::Client::new(&amp;amp;env, &amp;amp;Self::engine_id(env.clone()))
+    }
+
+    /// wrapping interface implemention
+    pub fn init(
&lt;/span&gt;         env: Env,
         move_step: u32,
         laser_range: u32,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally we'd add all the proxy-calls (see &lt;a href="https://github.com/stellar/fca00c-asteroids/commit/77dc35fc76979113aa6c88c3907f0d3844617ae3" rel="noopener noreferrer"&gt;here&lt;/a&gt; for the full diff) and build again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ cargo build &lt;span class="nt"&gt;--target&lt;/span&gt; wasm32-unknown-unknown &lt;span class="nt"&gt;--release&lt;/span&gt; 
   Compiling stellar-xdr v0.0.14
   Compiling static_assertions v1.1.0
   Compiling soroban-env-common v0.0.14
   Compiling soroban-env-guest v0.0.14
   Compiling soroban-sdk v0.6.0
   Compiling logging-engine v0.0.0 &lt;span class="o"&gt;(&lt;/span&gt;/home/paul/Code/fca00c - logging proxy/contracts/logging_engine&lt;span class="o"&gt;)&lt;/span&gt;
    Finished release &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;7.91s
❯

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;🎊 no ⚠️warning⚠️ anymore&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Now let's put this thing together
&lt;/h2&gt;

&lt;p&gt;Finally we want to see if/how this can be invoked in our test. &lt;/p&gt;

&lt;p&gt;It's pretty easy: As both contracts implement the same interface we can even use the original &lt;code&gt;game_engine::Client&lt;/code&gt; to invoke functions in the proxy (&lt;code&gt;logging_engine&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To prove this is working let's just shoot the first asteroid (which is fortunately just ahead of the starting position):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/solution/src/lib.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/solution/src/lib.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -18,6 +18,7 @@&lt;/span&gt; impl Solution {
&lt;span class="err"&gt;
&lt;/span&gt;         // YOUR CODE START
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+        engine.p_shoot();
&lt;/span&gt;         // YOUR CODE END
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and do this &lt;strong&gt;through&lt;/strong&gt; the &lt;code&gt;logging_engine&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- a/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/contracts/solution/src/test.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -15,7 +15,7 @@&lt;/span&gt;
 /// cost will decrease as well.
 use std::println;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-use soroban_sdk::Env;
&lt;/span&gt;&lt;span class="gi"&gt;+use soroban_sdk::{testutils::Logger, Env};
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; use crate::{
     engine::{Client as GameEngine, WASM as GameEngineWASM},
&lt;span class="p"&gt;@@ -24,14 +24,25 @@&lt;/span&gt; use crate::{
&lt;span class="err"&gt;
&lt;/span&gt; extern crate std;
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+mod logging_contract {
+    use crate::engine::{Direction, Error, MapElement, Point};
+
+    soroban_sdk::contractimport!(
+        file = "../logging_engine/target/wasm32-unknown-unknown/release/logging_engine.wasm"
+    );
+}
+
&lt;/span&gt; /// ESPECIALLY LEAVE THESE TESTS ALONE
 #[test]
 fn fca00c_fast() {
     // Here we install and register the GameEngine contract in a default Soroban
     // environment, and build a client that can be used to invoke the contract.
     let env = Env::default();
&lt;span class="gi"&gt;+    let proxy_engine_id = env.register_contract_wasm(None, logging_contract::WASM);
&lt;/span&gt;     let engine_id = env.register_contract_wasm(None, GameEngineWASM);
&lt;span class="gd"&gt;-    let engine = GameEngine::new(&amp;amp;env, &amp;amp;engine_id);
&lt;/span&gt;&lt;span class="gi"&gt;+    let engine = GameEngine::new(&amp;amp;env, &amp;amp;proxy_engine_id);
+
+    logging_contract::Client::new(&amp;amp;env, &amp;amp;proxy_engine_id).wrap(&amp;amp;engine_id);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;     // DON'T CHANGE THE FOLLOWING INIT() PARAMETERS
     // Once you've submitted your contract on the FCA00C site, we will invoke
&lt;span class="p"&gt;@@ -63,12 +74,15 @@&lt;/span&gt; fn fca00c_fast() {
     // We reset the budget so you have the best chance to not hit a TrapMemLimitExceeded or TrapCpuLimitExceeded error
     env.budget().reset();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    solution.solve(&amp;amp;engine_id);
&lt;/span&gt;&lt;span class="gi"&gt;+    solution.solve(&amp;amp;proxy_engine_id);
+
+    let logs = env.logger().all();
+    println!("{}", logs.join("\n"));
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;     let points = engine.p_points();
&lt;span class="err"&gt;
&lt;/span&gt;     println!("Points: {}", points);
&lt;span class="gd"&gt;-    assert!(points &amp;gt;= 100);
&lt;/span&gt;&lt;span class="gi"&gt;+    assert!(points &amp;gt;= 1);
&lt;/span&gt; }
&lt;span class="err"&gt;
&lt;/span&gt; #[test]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;❗note how the original &lt;code&gt;GameClient&lt;/code&gt; is initialized just with the &lt;code&gt;logging_engine&lt;/code&gt;'s id❗&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now &lt;strong&gt;run the tests&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ make &lt;span class="nb"&gt;test
&lt;/span&gt;cargo &lt;span class="nb"&gt;test &lt;/span&gt;fca00c_fast &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--nocapture&lt;/span&gt;
   Compiling soroban-asteroids-solution v0.0.0 &lt;span class="o"&gt;(&lt;/span&gt;/home/paul/Code/fca00c - logging proxy/contracts/solution&lt;span class="o"&gt;)&lt;/span&gt;
    Finished &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;5.05s
     Running unittests src/lib.rs &lt;span class="o"&gt;(&lt;/span&gt;target/debug/deps/soroban_asteroids_solution-5b0a3423b755223b&lt;span class="o"&gt;)&lt;/span&gt;

running 1 &lt;span class="nb"&gt;test
&lt;/span&gt;invoker account is not configured
invoker account is not configured
🗒️ logger engine taking notes
Points: 1
&lt;span class="nb"&gt;test test&lt;/span&gt;::fca00c_fast ... ok

&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 1 filtered out&lt;span class="p"&gt;;&lt;/span&gt; finished &lt;span class="k"&gt;in &lt;/span&gt;0.44s

❯
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🏁 Great, we've made a proxy contract that allows us to keep the solution clean and does not need us to tinker with the original &lt;code&gt;game_engine&lt;/code&gt; either 👏!&lt;/p&gt;

</description>
      <category>sorobanathon</category>
      <category>soroban</category>
      <category>stellar</category>
      <category>fca00c</category>
    </item>
  </channel>
</rss>
