Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#21483] fix: update collectible amounts after sending #21867

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 50 additions & 18 deletions src/status_im/contexts/wallet/collectible/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
(let [current-collectible-idx (get-in db [:wallet :accounts account :current-collectible-idx] 0)
collectibles-filter nil
data-type (collectible-data-types :header)
fetch-criteria {:fetch-type (fetch-type :fetch-if-not-cached)
fetch-criteria {:fetch-type (fetch-type :fetch-if-cache-old)
:max-cache-age-seconds max-cache-age-seconds}
chain-ids (chain/chain-ids db)
request-params [request-id
Expand Down Expand Up @@ -171,16 +171,19 @@

(rf/reg-event-fx
:wallet/collectible-ownership-update-finished
(fn [{:keys [db]} [{:keys [accounts chainId]}]]
(fn [{:keys [db]} [{:keys [accounts chainId message]}]]
(let [address (first accounts)
pending-chain-ids (get-in db [:wallet :ui :collectibles :updating address])
updated-chain-ids (disj pending-chain-ids chainId)
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))]
all-chain-updated? (and (some? pending-chain-ids) (empty? updated-chain-ids))
collectible-id (data-store/rpc->collectible-id message)]
{:db (cond-> db
(some? pending-chain-ids)
(assoc-in [:wallet :ui :collectibles :updating address] updated-chain-ids))
:fx [(when all-chain-updated?
[:dispatch [:wallet/request-collectibles-for-account address]])]})))
[:dispatch [:wallet/request-collectibles-for-account address]])
(when collectible-id
[:dispatch [:wallet/get-collectibles-by-unique-id-async collectible-id]])]})))

(defn- update-collectibles-in-account
[existing-collectibles updated-collectibles]
Expand Down Expand Up @@ -282,28 +285,33 @@
[{{collectible-id :id :as collectible} :collectible
aspect-ratio :aspect-ratio
gradient-color :gradient-color}]]
{:db (assoc-in db
[:wallet :ui :collectible]
{:details collectible
:aspect-ratio aspect-ratio
:gradient-color gradient-color})
:fx [[:dispatch [:wallet/get-collectibles-by-unique-id-async collectible-id]]
;; We delay the navigation because we need re-frame to update the DB on time.
;; By doing it, we skip a blink while visiting the collectible detail page.
[:dispatch-later
{:ms 17
:dispatch [:navigate-to :screen/wallet.collectible]}]]}))

(rf/reg-event-fx
:wallet/get-collectibles-by-unique-id-async
(fn [_ [collectible-id]]
(let [request-id 0
collectible-id-converted (cske/transform-keys transforms/->PascalCaseKeyword collectible-id)
data-type (collectible-data-types :details)
request-params [request-id [collectible-id-converted] data-type]]
{:db (assoc-in db
[:wallet :ui :collectible]
{:details collectible
:aspect-ratio aspect-ratio
:gradient-color gradient-color})
:fx [[:json-rpc/call
{:fx [[:json-rpc/call
[{:method "wallet_getCollectiblesByUniqueIDAsync"
:params request-params
:on-error (fn [error]
(log/error "failed to request collectible"
{:event :wallet/navigate-to-collectible-details
:error error
:params request-params}))}]]
;; We delay the navigation because we need re-frame to update the DB on time.
;; By doing it, we skip a blink while visiting the collectible detail page.
[:dispatch-later
{:ms 17
:dispatch [:navigate-to :screen/wallet.collectible]}]]})))
:params request-params}))}]]]})))

(defn- keep-not-empty-value
[old-value new-value]
Expand All @@ -318,19 +326,43 @@
(transforms/json->clj message))
{[collectible] :collectibles} response
known-collectible-info (get-in db [:wallet :ui :collectible :details])
current-account-address (get-in db [:wallet :current-viewing-account-address])
total-owned (collectible-utils/collectible-balance collectible
current-account-address)
merged-collectible (as-> known-collectible-info c
(merge-with keep-not-empty-value
c
collectible)
(update c
:ownership
collectible-utils/remove-duplicates-in-ownership))]
collectible-utils/remove-duplicates-in-ownership))
updated-collectible (assoc merged-collectible
:total-owned
total-owned)]
(if collectible
{:db (assoc-in db [:wallet :ui :collectible :details] merged-collectible)}
{:db (assoc-in db [:wallet :ui :collectible :details] updated-collectible)
:fx [[:dispatch [:wallet/update-collectibles-data updated-collectible]]]}
(log/error "failed to get collectible details"
{:event :wallet/get-collectible-details-done
:response response})))))

(rf/reg-event-fx
:wallet/update-collectibles-data
(fn [{:keys [db]} [collectible]]
(let [collectibles-by-address (collectible-utils/group-collectibles-by-ownership-address collectible)]
{:db (update-in db
[:wallet :accounts]
#(reduce-kv
(fn [accounts address updated-collectibles]
(if (contains? accounts address)
(update-in accounts
[address :collectibles]
update-collectibles-in-account
[updated-collectibles])
accounts))
%
collectibles-by-address))})))

(rf/reg-event-fx
:wallet/clear-collectible-details
(fn [{:keys [db]}]
Expand Down
24 changes: 20 additions & 4 deletions src/status_im/contexts/wallet/collectible/utils.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@
[taoensso.timbre :as log]
[utils.number :as utils.number]))

(defn- total-collectible-balance
([ownership]
(reduce (fn [total {:keys [balance]}]
(+ total (or (js/parseInt balance) 0)))
0
ownership)))

(defn collectible-balance
([{:keys [ownership]} address]
(->> ownership
(some #(when (= address (:address %))
(:balance %)))
utils.number/parse-int)))
(let [balance (if address
(some #(when (= address (:address %))
(:balance %))
ownership)
(total-collectible-balance ownership))]
(utils.number/parse-int balance))))

(def supported-collectible-types
#{"image/jpeg"
Expand Down Expand Up @@ -79,3 +88,10 @@
acc)))
{})
vals))

(defn group-collectibles-by-ownership-address
[collectible]
(->> (:ownership collectible)
(map (fn [{:keys [address]}]
[address collectible]))
(into {})))
43 changes: 43 additions & 0 deletions src/status_im/contexts/wallet/collectible/utils_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,46 @@
:token-id token-id
:test-networks-enabled? true})
"https://testnets.opensea.io/assets/optimism-sepolia/0xC/0xT"))))

(deftest collectible-balance-test
(testing "Returns balance for a specific address"
(let [collectible {:ownership [{:address "0x123" :balance "10"}
{:address "0x456" :balance "20"}]}]
(is (= 10 (utils/collectible-balance collectible "0x123")))
(is (= 20 (utils/collectible-balance collectible "0x456")))
(is (= 0 (utils/collectible-balance collectible "0x789")))))

(testing "Returns total balance when no address is provided"
(let [collectible {:ownership [{:address "0x123" :balance "10"}
{:address "0x456" :balance "20"}]}]
(is (= 30 (utils/collectible-balance collectible nil)))))

(testing "Returns 0 when ownership is empty"
(let [collectible {:ownership []}]
(is (= 0 (utils/collectible-balance collectible nil)))))

(testing "Handles nil balance values correctly"
(let [collectible {:ownership [{:address "0x123" :balance nil}
{:address "0x456" :balance nil}]}]
(is (= 0 (utils/collectible-balance collectible nil))))))

(deftest group-collectibles-by-ownership-address-test
(testing "Groups collectibles by ownership address correctly"
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
:ownership [{:address "0x123" :balance 10}
{:address "0x456" :balance 20}]}
expected {"0x123" collectible
"0x456" collectible}]
(is (= expected (utils/group-collectibles-by-ownership-address collectible)))))

(testing "Returns an empty map when there is no ownership data"
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
:ownership []}]
(is (= {} (utils/group-collectibles-by-ownership-address collectible)))))

(testing "Handles duplicate ownership addresses"
(let [collectible {:id {:contract-id {:chain-id 10 :address "0xContract"} :token-id "1"}
:ownership [{:address "0x123" :balance 10}
{:address "0x123" :balance 5}]}
expected {"0x123" collectible}]
(is (= expected (utils/group-collectibles-by-ownership-address collectible))))))
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
[status-im.contexts.wallet.collectible.utils :as utils]
[status-im.contexts.wallet.common.collectibles-tab.style :as style]
[status-im.contexts.wallet.common.empty-tab.view :as empty-tab]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.i18n :as i18n]))

(defn- loading-collectible-item
[_ index]
Expand Down Expand Up @@ -82,9 +81,9 @@
;; 1. If possible, move `collectibles-data` calculation to a subscription
;; 2. Optimization: do not recalculate all the collectibles, process only the new ones
(let [collectibles-data (map
(fn [{:keys [ownership] :as collectible}]
(let [total-owned (rf/sub [:wallet/total-owned-collectible ownership
current-account-address])]
(fn [collectible]
(let [total-owned (utils/collectible-balance collectible
current-account-address)]
(assoc collectible
:total-owned total-owned
:on-long-press on-collectible-long-press
Expand Down
14 changes: 14 additions & 0 deletions src/status_im/contexts/wallet/data_store.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,17 @@
selected-keypair-uid (get-in db [:wallet :ui :create-account :selected-keypair-uid])
keypair (get keypairs selected-keypair-uid)]
(boolean (seq (:keycards keypair)))))

(defn- transform-collectible
[{:keys [added updated removed]}]
(let [entry (first (remove nil? (concat added updated removed)))]
(when entry
{:contract-id {:chain-id (:chainID (:contractID entry))
:address (:address (:contractID entry))}
:token-id (str (:tokenID entry))})))

(defn rpc->collectible-id
[collectible]
(-> collectible
transforms/json->clj
transform-collectible))
10 changes: 10 additions & 0 deletions src/status_im/contexts/wallet/data_store_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,13 @@
(testing "returns false when db does not contain wallet data"
(let [db {}]
(is (false? (sut/selected-keypair-keycard? db))))))

(deftest rpc->collectible-id-test
(testing "Transforms JSON with `updated` key into collectible ID"
(let
[json
"{\"updated\": [{\"contractID\": {\"chainID\": 222, \"address\": \"0x123456\"}, \"tokenID\": 77}]}"
expected {:contract-id {:chain-id 222
:address "0x123456"}
:token-id "77"}]
(is (= expected (sut/rpc->collectible-id json))))))