feat: approve token transactions on swap (#21076)
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
c861d95d69
commit
d5238ac82f
|
@ -591,11 +591,17 @@
|
|||
(def ^:const default-slippage 0.5)
|
||||
(def ^:const max-recommended-slippage 5)
|
||||
(def ^:const max-slippage-decimal-places 2)
|
||||
(def ^:const swap-default-provider
|
||||
(def ^:const swap-provider-paraswap
|
||||
{:name :paraswap
|
||||
:full-name "Paraswap"
|
||||
:color :blue
|
||||
:contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57"
|
||||
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
|
||||
(def ^:const swap-providers
|
||||
{:paraswap swap-provider-paraswap})
|
||||
|
||||
(def ^:const token-for-fees-symbol "ETH")
|
||||
|
||||
(def ^:const transaction-status-success "Success")
|
||||
(def ^:const transaction-status-pending "Pending")
|
||||
(def ^:const transaction-status-failed "Failed")
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
(ns status-im.contexts.wallet.common.utils
|
||||
(:require [clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[quo.foundations.resources :as resources]
|
||||
[status-im.common.qr-codes.view :as qr-codes]
|
||||
[status-im.constants :as constants]
|
||||
[utils.hex :as utils.hex]
|
||||
[utils.money :as money]
|
||||
[utils.number :as number]
|
||||
[utils.string]))
|
||||
|
@ -348,3 +350,121 @@
|
|||
[tokens]
|
||||
(let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)]
|
||||
(sort-by (juxt (comp - :balance) priority) tokens)))
|
||||
|
||||
(defn- transaction-data
|
||||
[{:keys [from-address to-address token-address route data eth-transfer?]}]
|
||||
(let [{:keys [amount-in gas-amount gas-fees]} route
|
||||
eip-1559-enabled? (:eip-1559-enabled gas-fees)
|
||||
{:keys [gas-price max-fee-per-gas-medium
|
||||
max-priority-fee-per-gas]} gas-fees]
|
||||
(cond-> {:From from-address
|
||||
:To (or token-address to-address)
|
||||
:Gas (money/to-hex gas-amount)
|
||||
:Value (when eth-transfer? amount-in)
|
||||
:Nonce nil
|
||||
:Input ""
|
||||
:Data (or data "0x")}
|
||||
eip-1559-enabled? (assoc
|
||||
:TxType "0x02"
|
||||
:MaxFeePerGas
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
max-fee-per-gas-medium))
|
||||
:MaxPriorityFeePerGas
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
max-priority-fee-per-gas)))
|
||||
(not eip-1559-enabled?) (assoc :TxType "0x00"
|
||||
:GasPrice
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
gas-price))))))
|
||||
|
||||
(defn approval-path
|
||||
[{:keys [route from-address to-address token-address]}]
|
||||
(let [{:keys [from]} route
|
||||
from-chain-id (:chain-id from)
|
||||
approval-amount-required (:approval-amount-required route)
|
||||
approval-amount-required-sanitized (-> approval-amount-required
|
||||
(utils.hex/normalize-hex)
|
||||
(native-module/hex-to-number))
|
||||
approval-contract-address (:approval-contract-address route)
|
||||
data (native-module/encode-function-call
|
||||
constants/contract-function-signature-erc20-approve
|
||||
[approval-contract-address
|
||||
approval-amount-required-sanitized])
|
||||
tx-data (transaction-data {:from-address from-address
|
||||
:to-address to-address
|
||||
:token-address token-address
|
||||
:route route
|
||||
:data data
|
||||
:eth-transfer? false})]
|
||||
{:BridgeName constants/bridge-name-transfer
|
||||
:ChainID from-chain-id
|
||||
:TransferTx tx-data}))
|
||||
|
||||
(defn transaction-path
|
||||
[{:keys [from-address to-address token-id token-address route data eth-transfer?]}]
|
||||
(let [{:keys [bridge-name amount-in bonder-fees from
|
||||
to]} route
|
||||
tx-data (transaction-data {:from-address from-address
|
||||
:to-address to-address
|
||||
:token-address token-address
|
||||
:route route
|
||||
:data data
|
||||
:eth-transfer? eth-transfer?})
|
||||
to-chain-id (:chain-id to)
|
||||
from-chain-id (:chain-id from)]
|
||||
(cond-> {:BridgeName bridge-name
|
||||
:ChainID from-chain-id}
|
||||
|
||||
(= bridge-name constants/bridge-name-erc-721-transfer)
|
||||
(assoc :ERC721TransferTx
|
||||
(assoc tx-data
|
||||
:Recipient to-address
|
||||
:TokenID token-id
|
||||
:ChainID to-chain-id))
|
||||
|
||||
(= bridge-name constants/bridge-name-erc-1155-transfer)
|
||||
(assoc :ERC1155TransferTx
|
||||
(assoc tx-data
|
||||
:Recipient to-address
|
||||
:TokenID token-id
|
||||
:ChainID to-chain-id
|
||||
:Amount amount-in))
|
||||
|
||||
(= bridge-name constants/bridge-name-transfer)
|
||||
(assoc :TransferTx tx-data)
|
||||
|
||||
(= bridge-name constants/bridge-name-hop)
|
||||
(assoc :HopTx
|
||||
(assoc tx-data
|
||||
:ChainID from-chain-id
|
||||
:ChainIDTo to-chain-id
|
||||
:Symbol token-id
|
||||
:Recipient to-address
|
||||
:Amount amount-in
|
||||
:BonderFee bonder-fees))
|
||||
|
||||
(not (or (= bridge-name constants/bridge-name-erc-721-transfer)
|
||||
(= bridge-name constants/bridge-name-transfer)
|
||||
(= bridge-name constants/bridge-name-hop)))
|
||||
(assoc :CbridgeTx
|
||||
(assoc tx-data
|
||||
:ChainID to-chain-id
|
||||
:Symbol token-id
|
||||
:Recipient to-address
|
||||
:Amount amount-in)))))
|
||||
|
||||
(defn multi-transaction-command
|
||||
[{:keys [from-address to-address from-asset to-asset amount-out multi-transaction-type]
|
||||
:or {multi-transaction-type constants/multi-transaction-type-unknown}}]
|
||||
{:fromAddress from-address
|
||||
:toAddress to-address
|
||||
:fromAsset from-asset
|
||||
:toAsset to-asset
|
||||
:fromAmount amount-out
|
||||
:type multi-transaction-type})
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[taoensso.timbre :as log]
|
||||
[utils.address :as address]
|
||||
[utils.hex :as utils.hex]
|
||||
[utils.money :as money]
|
||||
[utils.money :as utils.money]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
@ -582,124 +582,6 @@
|
|||
[{:ms 20
|
||||
:dispatch [:wallet/clean-up-transaction-flow]}]]]})))
|
||||
|
||||
(defn- transaction-data
|
||||
[{:keys [from-address to-address token-address route data eth-transfer?]}]
|
||||
(let [{:keys [amount-in gas-amount gas-fees]} route
|
||||
eip-1559-enabled? (:eip-1559-enabled gas-fees)
|
||||
{:keys [gas-price max-fee-per-gas-medium
|
||||
max-priority-fee-per-gas]} gas-fees]
|
||||
(cond-> {:From from-address
|
||||
:To (or token-address to-address)
|
||||
:Gas (money/to-hex gas-amount)
|
||||
:Value (when eth-transfer? amount-in)
|
||||
:Nonce nil
|
||||
:Input ""
|
||||
:Data (or data "0x")}
|
||||
eip-1559-enabled? (assoc
|
||||
:TxType "0x02"
|
||||
:MaxFeePerGas
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
max-fee-per-gas-medium))
|
||||
:MaxPriorityFeePerGas
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
max-priority-fee-per-gas)))
|
||||
(not eip-1559-enabled?) (assoc :TxType "0x00"
|
||||
:GasPrice
|
||||
(money/to-hex
|
||||
(money/->wei
|
||||
:gwei
|
||||
gas-price))))))
|
||||
|
||||
(defn- approval-path
|
||||
[{:keys [route from-address to-address token-address]}]
|
||||
(let [{:keys [from]} route
|
||||
from-chain-id (:chain-id from)
|
||||
approval-amount-required (:approval-amount-required route)
|
||||
approval-amount-required-sanitized (-> approval-amount-required
|
||||
(utils.hex/normalize-hex)
|
||||
(native-module/hex-to-number))
|
||||
approval-contract-address (:approval-contract-address route)
|
||||
data (native-module/encode-function-call
|
||||
constants/contract-function-signature-erc20-approve
|
||||
[approval-contract-address
|
||||
approval-amount-required-sanitized])
|
||||
tx-data (transaction-data {:from-address from-address
|
||||
:to-address to-address
|
||||
:token-address token-address
|
||||
:route route
|
||||
:data data
|
||||
:eth-transfer? false})]
|
||||
{:BridgeName constants/bridge-name-transfer
|
||||
:ChainID from-chain-id
|
||||
:TransferTx tx-data}))
|
||||
|
||||
(defn- transaction-path
|
||||
[{:keys [from-address to-address token-id token-address route data eth-transfer?]}]
|
||||
(let [{:keys [bridge-name amount-in bonder-fees from
|
||||
to]} route
|
||||
tx-data (transaction-data {:from-address from-address
|
||||
:to-address to-address
|
||||
:token-address token-address
|
||||
:route route
|
||||
:data data
|
||||
:eth-transfer? eth-transfer?})
|
||||
to-chain-id (:chain-id to)
|
||||
from-chain-id (:chain-id from)]
|
||||
(cond-> {:BridgeName bridge-name
|
||||
:ChainID from-chain-id}
|
||||
|
||||
(= bridge-name constants/bridge-name-erc-721-transfer)
|
||||
(assoc :ERC721TransferTx
|
||||
(assoc tx-data
|
||||
:Recipient to-address
|
||||
:TokenID token-id
|
||||
:ChainID to-chain-id))
|
||||
|
||||
(= bridge-name constants/bridge-name-erc-1155-transfer)
|
||||
(assoc :ERC1155TransferTx
|
||||
(assoc tx-data
|
||||
:Recipient to-address
|
||||
:TokenID token-id
|
||||
:ChainID to-chain-id
|
||||
:Amount amount-in))
|
||||
|
||||
(= bridge-name constants/bridge-name-transfer)
|
||||
(assoc :TransferTx tx-data)
|
||||
|
||||
(= bridge-name constants/bridge-name-hop)
|
||||
(assoc :HopTx
|
||||
(assoc tx-data
|
||||
:ChainID from-chain-id
|
||||
:ChainIDTo to-chain-id
|
||||
:Symbol token-id
|
||||
:Recipient to-address
|
||||
:Amount amount-in
|
||||
:BonderFee bonder-fees))
|
||||
|
||||
(not (or (= bridge-name constants/bridge-name-erc-721-transfer)
|
||||
(= bridge-name constants/bridge-name-transfer)
|
||||
(= bridge-name constants/bridge-name-hop)))
|
||||
(assoc :CbridgeTx
|
||||
(assoc tx-data
|
||||
:ChainID to-chain-id
|
||||
:Symbol token-id
|
||||
:Recipient to-address
|
||||
:Amount amount-in)))))
|
||||
|
||||
(defn- multi-transaction-command
|
||||
[{:keys [from-address to-address from-asset to-asset amount-out multi-transaction-type]
|
||||
:or {multi-transaction-type constants/multi-transaction-type-unknown}}]
|
||||
{:fromAddress from-address
|
||||
:toAddress to-address
|
||||
:fromAsset from-asset
|
||||
:toAsset to-asset
|
||||
:fromAmount amount-out
|
||||
:type multi-transaction-type})
|
||||
|
||||
(rf/reg-event-fx :wallet/send-transaction
|
||||
(fn [{:keys [db]} [sha3-pwd]]
|
||||
(let [routes (get-in db [:wallet :ui :send :route])
|
||||
|
@ -731,7 +613,7 @@
|
|||
(native-module/encode-transfer
|
||||
(address/normalized-hex to-address)
|
||||
(:amount-in route)))
|
||||
base-path (transaction-path
|
||||
base-path (utils/transaction-path
|
||||
{:to-address to-address
|
||||
:from-address from-address
|
||||
:route route
|
||||
|
@ -743,15 +625,15 @@
|
|||
:data data
|
||||
:eth-transfer? eth-transfer?})]
|
||||
(if approval-required?
|
||||
[(approval-path {:route route
|
||||
:token-address token-address
|
||||
:from-address from-address
|
||||
:to-address to-address})
|
||||
[(utils/approval-path {:route route
|
||||
:token-address token-address
|
||||
:from-address from-address
|
||||
:to-address to-address})
|
||||
base-path]
|
||||
[base-path]))))
|
||||
routes)
|
||||
request-params
|
||||
[(multi-transaction-command
|
||||
[(utils/multi-transaction-command
|
||||
{:from-address from-address
|
||||
:to-address to-address
|
||||
:from-asset token-id
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
(ns status-im.contexts.wallet.signals
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[status-im.constants :as constants]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.transforms :as transforms]))
|
||||
|
@ -8,9 +9,21 @@
|
|||
(rf/reg-event-fx
|
||||
:wallet/pending-transaction-status-changed-received
|
||||
(fn [{:keys [db]} [{:keys [message]}]]
|
||||
(let [details (transforms/json->clj message)
|
||||
tx-hash (:hash details)]
|
||||
{:db (update-in db [:wallet :transactions tx-hash] assoc :status :confirmed :blocks 1)})))
|
||||
(let [details (transforms/json->clj message)
|
||||
tx-hash (:hash details)
|
||||
tx-status (:status details)
|
||||
status (cond
|
||||
(= tx-status constants/transaction-status-success)
|
||||
:confirmed
|
||||
(= tx-status constants/transaction-status-pending)
|
||||
:pending
|
||||
(= tx-status constants/transaction-status-failed)
|
||||
:failed)
|
||||
swap-approval-transaction-id (get-in db [:wallet :ui :swap :approval-transaction-id])
|
||||
swap-approval-transaction? (= swap-approval-transaction-id tx-hash)]
|
||||
(cond-> {:db (update-in db [:wallet :transactions tx-hash] assoc :status status)}
|
||||
swap-approval-transaction?
|
||||
(assoc :fx [[:dispatch [:wallet.swap/approve-transaction-update status]]])))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/signal-received
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
(ns status-im.contexts.wallet.swap.events
|
||||
(:require [re-frame.core :as rf]
|
||||
(:require [native-module.core :as native-module]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.address :as address]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.number]))
|
||||
|
||||
(rf/reg-event-fx :wallet.swap/start
|
||||
|
@ -152,8 +156,123 @@
|
|||
:last-request-uuid
|
||||
:swap-proposal
|
||||
:error-response
|
||||
:loading-swap-proposal?)}))
|
||||
:loading-swap-proposal?
|
||||
:approval-transaction-id)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/clean-swap
|
||||
(fn [{:keys [db]}]
|
||||
{:db (update-in db [:wallet :ui] dissoc :swap)}))
|
||||
|
||||
(rf/reg-event-fx :wallet/swap-transaction
|
||||
(fn [{:keys [db]} [sha3-pwd]]
|
||||
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
||||
{:keys [asset-to-pay swap-proposal network
|
||||
approval-transaction-id]} (get-in db [:wallet :ui :swap])
|
||||
transactions (get-in db [:wallet :transactions])
|
||||
approval-transaction (when approval-transaction-id
|
||||
(get transactions approval-transaction-id))
|
||||
already-approved? (and approval-transaction
|
||||
(= (:status approval-transaction) :confirmed))
|
||||
approval-required? (and (:approval-required swap-proposal)
|
||||
(not already-approved?))
|
||||
multi-transaction-type constants/multi-transaction-type-swap
|
||||
swap-chain-id (:chain-id network)
|
||||
token-id (:symbol asset-to-pay)
|
||||
erc20-transfer? (and asset-to-pay (not= token-id "ETH"))
|
||||
eth-transfer? (and asset-to-pay (not erc20-transfer?))
|
||||
token-address (when erc20-transfer?
|
||||
(get-in asset-to-pay
|
||||
[:balances-per-chain swap-chain-id :address]))
|
||||
data (when erc20-transfer?
|
||||
(native-module/encode-transfer
|
||||
(address/normalized-hex wallet-address)
|
||||
(:amount-in swap-proposal)))
|
||||
transaction-paths (if approval-required?
|
||||
[(utils/approval-path {:route swap-proposal
|
||||
:token-address token-address
|
||||
:from-address wallet-address
|
||||
:to-address wallet-address})]
|
||||
[(utils/transaction-path
|
||||
{:to-address wallet-address
|
||||
:from-address wallet-address
|
||||
:route swap-proposal
|
||||
:token-address token-address
|
||||
:token-id token-id
|
||||
:data data
|
||||
:eth-transfer? eth-transfer?})])
|
||||
request-params [(utils/multi-transaction-command
|
||||
{:from-address wallet-address
|
||||
:to-address wallet-address
|
||||
:from-asset token-id
|
||||
:to-asset token-id
|
||||
:amount-out (if eth-transfer?
|
||||
(:amount-out swap-proposal)
|
||||
"0x0")
|
||||
:multi-transaction-type multi-transaction-type})
|
||||
transaction-paths
|
||||
sha3-pwd]]
|
||||
(log/info "multi transaction called")
|
||||
{:json-rpc/call [{:method "wallet_createMultiTransaction"
|
||||
:params request-params
|
||||
:on-success (fn [result]
|
||||
(when result
|
||||
(rf/dispatch [:wallet.swap/add-authorized-transaction
|
||||
{:transaction result
|
||||
:approval-transaction? approval-required?}])
|
||||
(rf/dispatch [:dismiss-modal
|
||||
:screen/wallet.swap-set-spending-cap])
|
||||
(rf/dispatch [:hide-bottom-sheet])))
|
||||
:on-error (fn [error]
|
||||
(log/error "failed swap transaction"
|
||||
{:event :wallet/swap-transaction
|
||||
:error error
|
||||
:params request-params})
|
||||
(rf/dispatch [:toasts/upsert
|
||||
{:id :swap-transaction-error
|
||||
:type :negative
|
||||
:text (:message error)}]))}]})))
|
||||
|
||||
(rf/reg-event-fx :wallet.swap/add-authorized-transaction
|
||||
(fn [{:keys [db]} [{:keys [transaction approval-transaction?]}]]
|
||||
(let [transaction-batch-id (:id transaction)
|
||||
transaction-hashes (:hashes transaction)
|
||||
transaction-ids (flatten (vals transaction-hashes))
|
||||
transaction-details (send-utils/map-multitransaction-by-ids transaction-batch-id
|
||||
transaction-hashes)]
|
||||
{:db (cond-> db
|
||||
:always (assoc-in [:wallet :transactions] transaction-details)
|
||||
:always (assoc-in [:wallet :ui :swap :transaction-ids] transaction-ids)
|
||||
approval-transaction? (assoc-in [:wallet :ui :swap :approval-transaction-id]
|
||||
(first transaction-ids)))})))
|
||||
|
||||
(rf/reg-event-fx :wallet.swap/approve-transaction-update
|
||||
(fn [{:keys [db]} [status]]
|
||||
(let [{:keys [amount asset-to-pay swap-proposal]} (get-in db [:wallet :ui :swap])
|
||||
provider-name (:bridge-name swap-proposal)
|
||||
token-symbol (:symbol asset-to-pay)
|
||||
current-viewing-account-address (get-in db
|
||||
[:wallet :current-viewing-account-address])
|
||||
account-name (get-in db
|
||||
[:wallet :accounts
|
||||
current-viewing-account-address :name])
|
||||
transaction-confirmed-or-failed? (#{:confirmed :failed} status)
|
||||
transaction-confirmed? (= status :confirmed)]
|
||||
(when transaction-confirmed-or-failed?
|
||||
(cond-> {:fx
|
||||
[[:dispatch
|
||||
[:toasts/upsert
|
||||
{:id :approve-transaction-update
|
||||
:type (if transaction-confirmed? :positive :negative)
|
||||
:text (if transaction-confirmed?
|
||||
(i18n/label :t/spending-cap-set
|
||||
{:amount amount
|
||||
:token-symbol token-symbol
|
||||
:provider-name provider-name
|
||||
:account-name account-name})
|
||||
(i18n/label :t/spending-cap-failed
|
||||
{:amount amount
|
||||
:token-symbol token-symbol
|
||||
:provider-name provider-name
|
||||
:account-name account-name}))}]]]}
|
||||
(not transaction-confirmed?)
|
||||
(assoc :db (update-in db [:wallet :ui :swap] dissoc :approval-transaction-id)))))))
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns status-im.contexts.wallet.swap.set-spending-cap.view
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.resources :as resources]
|
||||
[quo.theme :as quo.theme]
|
||||
|
@ -7,84 +8,123 @@
|
|||
[status-im.common.events-helper :as events-helper]
|
||||
[status-im.common.floating-button-page.view :as floating-button-page]
|
||||
[status-im.common.standard-authentication.core :as standard-auth]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.common.utils.external-links :as external-links]
|
||||
[status-im.contexts.wallet.swap.set-spending-cap.style :as style]
|
||||
[utils.address :as address-utils]
|
||||
[utils.hex :as hex]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.number :as number]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn- swap-title
|
||||
[{:keys [pay-token-symbol pay-amount account provider]}]
|
||||
[rn/view {:style style/content-container}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :set-spending-cap-of}
|
||||
(i18n/label :t/set-spending-cap-of)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:token pay-token-symbol
|
||||
:label (str pay-amount " " pay-token-symbol)
|
||||
:type :token}]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :for}
|
||||
(i18n/label :t/for)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:label (:full-name provider)
|
||||
:type :network
|
||||
:image-source (resources/get-network (:name provider))
|
||||
:customization-color (:color provider)}]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :on}
|
||||
(i18n/label :t/on)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:label (:name account)
|
||||
:type :account
|
||||
:emoji (:emoji account)
|
||||
:customization-color (:color account)}]]])
|
||||
[]
|
||||
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
amount-in (rf/sub [:wallet/swap-proposal-amount-in])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
provider (rf/sub [:wallet/swap-proposal-provider])
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
pay-token-decimals (:decimals asset-to-pay)
|
||||
pay-amount (when amount-in
|
||||
(number/convert-to-whole-number
|
||||
(native-module/hex-to-number
|
||||
(hex/normalize-hex
|
||||
amount-in))
|
||||
pay-token-decimals))]
|
||||
[rn/view {:style style/content-container}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :set-spending-cap-of}
|
||||
(i18n/label :t/set-spending-cap-of)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:token pay-token-symbol
|
||||
:label (str pay-amount " " pay-token-symbol)
|
||||
:type :token}]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :for}
|
||||
(i18n/label :t/for)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:label (:full-name provider)
|
||||
:type :network
|
||||
:image-source (resources/get-network (:name provider))
|
||||
:customization-color (:color provider)}]
|
||||
[quo/text
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style style/title-container
|
||||
:accessibility-label :on}
|
||||
(i18n/label :t/on)]]
|
||||
[rn/view {:style style/title-line-with-margin-top}
|
||||
[quo/summary-tag
|
||||
{:label (:name account)
|
||||
:type :account
|
||||
:emoji (:emoji account)
|
||||
:customization-color (:color account)}]]]))
|
||||
|
||||
(defn- spending-cap-section
|
||||
[{:keys [theme amount token-symbol]}]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :spending-cap-label}
|
||||
(i18n/label :t/spending-cap)]
|
||||
[quo/approval-info
|
||||
{:type :spending-cap
|
||||
:unlimited-icon? false
|
||||
:label (str amount " " token-symbol)
|
||||
:avatar-props {:token token-symbol}}]])
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
amount-in (rf/sub [:wallet/swap-proposal-amount-in])
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
pay-token-decimals (:decimals asset-to-pay)
|
||||
pay-amount (when amount-in
|
||||
(number/convert-to-whole-number
|
||||
(native-module/hex-to-number
|
||||
(hex/normalize-hex
|
||||
amount-in))
|
||||
pay-token-decimals))]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :spending-cap-label}
|
||||
(i18n/label :t/spending-cap)]
|
||||
[quo/approval-info
|
||||
{:type :spending-cap
|
||||
:unlimited-icon? false
|
||||
:label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:token pay-token-symbol}}]]))
|
||||
|
||||
(defn- account-section
|
||||
[{:keys [theme account pay-token-symbol pay-token-amount]}]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :account-label}
|
||||
(i18n/label :t/account)]
|
||||
[quo/approval-info
|
||||
{:type :account
|
||||
:unlimited-icon? false
|
||||
:label (:name account)
|
||||
:description (address-utils/get-short-wallet-address (:address account))
|
||||
:tag-label (str pay-token-amount " " pay-token-symbol)
|
||||
:avatar-props {:emoji (:emoji account)
|
||||
:customization-color (:color account)}}]])
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
amount-in (rf/sub [:wallet/swap-proposal-amount-in])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
pay-token-decimals (:decimals asset-to-pay)
|
||||
pay-amount (when amount-in
|
||||
(number/convert-to-whole-number
|
||||
(native-module/hex-to-number
|
||||
(hex/normalize-hex
|
||||
amount-in))
|
||||
pay-token-decimals))]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :account-label}
|
||||
(i18n/label :t/account)]
|
||||
[quo/approval-info
|
||||
{:type :account
|
||||
:unlimited-icon? false
|
||||
:label (:name account)
|
||||
:description (address-utils/get-short-wallet-address (:address account))
|
||||
:tag-label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:emoji (:emoji account)
|
||||
:customization-color (:color account)}}]]))
|
||||
|
||||
(defn- on-option-press
|
||||
[{:keys [chain-id contract-address]}]
|
||||
|
@ -103,42 +143,52 @@
|
|||
:right-icon :i/external}]]])}]))
|
||||
|
||||
(defn- token-section
|
||||
[{:keys [theme token-address token-symbol network-chain-id]}]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :token-label}
|
||||
(i18n/label :t/token)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address token-address})
|
||||
:unlimited-icon? false
|
||||
:label token-symbol
|
||||
:description (address-utils/get-short-wallet-address token-address)
|
||||
:avatar-props {:token token-symbol}}]])
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
network (rf/sub [:wallet/swap-network])
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
network-chain-id (:chain-id network)
|
||||
pay-token-address (get-in asset-to-pay [:balances-per-chain network-chain-id :address])]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :token-label}
|
||||
(i18n/label :t/token)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address pay-token-address})
|
||||
:unlimited-icon? false
|
||||
:label pay-token-symbol
|
||||
:description (address-utils/get-short-wallet-address pay-token-address)
|
||||
:avatar-props {:token pay-token-symbol}}]]))
|
||||
|
||||
(defn- spender-contract-section
|
||||
[{:keys [theme provider network-chain-id]}]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :spender-contract-label}
|
||||
(i18n/label :t/spender-contract)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address (:contract-address provider)})
|
||||
:unlimited-icon? false
|
||||
:label (:full-name provider)
|
||||
:description (address-utils/get-short-wallet-address (:contract-address provider))
|
||||
:avatar-props {:image (resources/get-network (:name provider))}}]])
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
network (rf/sub [:wallet/swap-network])
|
||||
provider (rf/sub [:wallet/swap-proposal-provider])
|
||||
network-chain-id (:chain-id network)]
|
||||
[rn/view {:style style/summary-section-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/section-label theme)
|
||||
:accessibility-label :spender-contract-label}
|
||||
(i18n/label :t/spender-contract)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address (:contract-address provider)})
|
||||
:unlimited-icon? false
|
||||
:label (:full-name provider)
|
||||
:description (address-utils/get-short-wallet-address (:contract-address provider))
|
||||
:avatar-props {:image (resources/get-network (:name provider))}}]]))
|
||||
|
||||
(defn- data-item
|
||||
[{:keys [network-image title subtitle size loading?]}]
|
||||
|
@ -154,56 +204,52 @@
|
|||
:size size}])
|
||||
|
||||
(defn- transaction-details
|
||||
[{:keys [estimated-time-min max-fees network loading-fees?]}]
|
||||
[rn/view {:style style/details-container}
|
||||
[:<>
|
||||
[data-item
|
||||
{:title (i18n/label :t/network)
|
||||
:subtitle (:full-name network)
|
||||
:network-image (:source network)}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/est-time)
|
||||
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time-min)})}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading-fees?
|
||||
:size :small}]]])
|
||||
[]
|
||||
(let [network (rf/sub [:wallet/swap-network])
|
||||
max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
constants/token-for-fees-symbol])
|
||||
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
estimated-time (rf/sub [:wallet/swap-proposal-estimated-time])]
|
||||
[rn/view {:style style/details-container}
|
||||
[:<>
|
||||
[data-item
|
||||
{:title (i18n/label :t/network)
|
||||
:subtitle (:full-name network)
|
||||
:network-image (:source network)}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/est-time)
|
||||
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading-fees?
|
||||
:size :small}]]]))
|
||||
|
||||
(defn footer
|
||||
[{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}]
|
||||
(let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])
|
||||
on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))]
|
||||
[rn/view {:style {:margin-bottom -10}}
|
||||
[transaction-details
|
||||
{:estimated-time-min estimated-time-min
|
||||
:max-fees fee-formatted
|
||||
:network network
|
||||
:loading-fees? loading-fees?
|
||||
:theme theme}]
|
||||
[standard-auth/slide-button
|
||||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-swap)
|
||||
:container-style {:z-index 2}
|
||||
:customization-color account-color
|
||||
:disabled? loading-fees?
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]]))
|
||||
(defn- slide-button
|
||||
[]
|
||||
(let [loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
on-auth-success (rn/use-callback #(rf/dispatch
|
||||
[:wallet/swap-transaction
|
||||
(security/safe-unmask-data %)]))]
|
||||
[standard-auth/slide-button
|
||||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-swap)
|
||||
:container-style {:z-index 2}
|
||||
:customization-color (:color account)
|
||||
:disabled? loading-fees?
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]))
|
||||
|
||||
(defn- footer
|
||||
[]
|
||||
[rn/view {:style {:margin-bottom -10}}
|
||||
[transaction-details]
|
||||
[slide-button]])
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [theme (quo.theme/use-theme)
|
||||
swap-transaction-data (rf/sub [:wallet/swap])
|
||||
{:keys [asset-to-pay network pay-amount
|
||||
providers swap-proposal
|
||||
loading-fees?]} swap-transaction-data
|
||||
estimated-time-min (:estimated-time swap-proposal)
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
pay-token-address (:address asset-to-pay)
|
||||
native-currency-symbol (get-in swap-proposal [:from :native-currency-symbol])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
account-color (:color account)
|
||||
provider (first providers)]
|
||||
(let [account (rf/sub [:wallet/current-viewing-account])]
|
||||
[rn/view {:style style/container}
|
||||
[floating-button-page/view
|
||||
{:footer-container-padding 0
|
||||
|
@ -213,37 +259,12 @@
|
|||
:margin-top 8
|
||||
:background :blur
|
||||
:accessibility-label :top-bar}]
|
||||
:footer [footer
|
||||
{:estimated-time-min estimated-time-min
|
||||
:native-currency-symbol native-currency-symbol
|
||||
:network network
|
||||
:account-color account-color
|
||||
:provider provider
|
||||
:loading-fees? loading-fees?
|
||||
:theme theme}]
|
||||
:footer [footer]
|
||||
:gradient-cover? true
|
||||
:customization-color account-color}
|
||||
:customization-color (:color account)}
|
||||
[:<>
|
||||
[swap-title
|
||||
{:pay-token-symbol pay-token-symbol
|
||||
:pay-amount pay-amount
|
||||
:account account
|
||||
:provider provider}]
|
||||
[spending-cap-section
|
||||
{:token-symbol pay-token-symbol
|
||||
:amount pay-amount
|
||||
:theme theme}]
|
||||
[account-section
|
||||
{:account account
|
||||
:pay-token-symbol pay-token-symbol
|
||||
:pay-token-amount pay-amount
|
||||
:theme theme}]
|
||||
[token-section
|
||||
{:token-symbol pay-token-symbol
|
||||
:token-address pay-token-address
|
||||
:network-chain-id (:chain-id network)
|
||||
:theme theme}]
|
||||
[spender-contract-section
|
||||
{:provider provider
|
||||
:network-chain-id (:chain-id network)
|
||||
:theme theme}]]]]))
|
||||
[swap-title]
|
||||
[spending-cap-section]
|
||||
[account-section]
|
||||
[token-section]
|
||||
[spender-contract-section]]]]))
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
approval-required (rf/sub [:wallet/swap-proposal-approval-required])
|
||||
approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])
|
||||
pay-input-num-value (controlled-input/numeric-value input-state)
|
||||
pay-input-amount (controlled-input/input-value input-state)
|
||||
pay-token-symbol (:symbol asset-to-pay)
|
||||
|
@ -141,7 +142,11 @@
|
|||
{:number available-crypto-limit
|
||||
:token-symbol pay-token-symbol})
|
||||
:networks [{:source (:source network)}]}
|
||||
:approval-label-props {:status :approve
|
||||
:approval-label-props {:status (case approval-transaction-status
|
||||
:pending :approving
|
||||
:confirmed :approved
|
||||
:finalised :approved
|
||||
:approve)
|
||||
:token-value approval-amount-required-num
|
||||
:button-props {:on-press on-approve-press}
|
||||
:customization-color account-color
|
||||
|
@ -199,16 +204,18 @@
|
|||
|
||||
(defn- action-button
|
||||
[{:keys [on-press]}]
|
||||
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])]
|
||||
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])
|
||||
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])]
|
||||
[quo/bottom-actions
|
||||
{:actions :one-action
|
||||
:button-one-label (i18n/label :t/review-swap)
|
||||
:button-one-props {:disabled? (or (not swap-proposal)
|
||||
approval-required?
|
||||
(and approval-required?
|
||||
(not= approval-transaction-status :confirmed))
|
||||
loading-swap-proposal?
|
||||
loading-fees?)
|
||||
:customization-color account-color
|
||||
|
@ -262,7 +269,7 @@
|
|||
:on-max-press #(set-pay-input-state pay-token-balance-selected-chain)
|
||||
:input-focused? pay-input-focused?
|
||||
:on-token-press #(js/alert "Token Pressed")
|
||||
:on-approve-press #(js/alert "Approve Pressed")
|
||||
:on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])
|
||||
:on-input-focus #(set-pay-input-focused? true)}]
|
||||
[swap-order-button {:on-press #(js/alert "Swap Order Pressed")}]
|
||||
[receive-token-input
|
||||
|
|
|
@ -80,6 +80,18 @@
|
|||
:<- [:wallet/swap]
|
||||
:-> :loading-fees?)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-approval-transaction-id
|
||||
:<- [:wallet/swap]
|
||||
:-> :approval-transaction-id)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-approval-transaction-status
|
||||
:<- [:wallet/transactions]
|
||||
:<- [:wallet/swap-approval-transaction-id]
|
||||
(fn [[transactions approval-transaction-id]]
|
||||
(get-in transactions [approval-transaction-id :status])))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal
|
||||
:<- [:wallet/swap]
|
||||
|
@ -95,6 +107,19 @@
|
|||
:<- [:wallet/swap-proposal]
|
||||
:-> :amount-out)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal-amount-in
|
||||
:<- [:wallet/swap-proposal]
|
||||
:-> :amount-in)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal-provider
|
||||
:<- [:wallet/swap-proposal]
|
||||
(fn [swap-proposal]
|
||||
(let [bridge-name (:bridge-name swap-proposal)
|
||||
provider-key (keyword (string/lower-case bridge-name))]
|
||||
(get constants/swap-providers provider-key))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal-approval-required
|
||||
:<- [:wallet/swap-proposal]
|
||||
|
@ -105,6 +130,11 @@
|
|||
:<- [:wallet/swap-proposal]
|
||||
:-> :approval-amount-required)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal-estimated-time
|
||||
:<- [:wallet/swap-proposal]
|
||||
:-> :estimated-time)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
:<- [:wallet/current-viewing-account]
|
||||
|
|
|
@ -2313,6 +2313,8 @@
|
|||
"specify-symbol": "Specify a symbol",
|
||||
"spender-contract": "Spender contract",
|
||||
"spending-cap": "Spending cap",
|
||||
"spending-cap-failed": "Spending cap failed: {{amount}} {{token-symbol}} for {{provider-name}} in {{account-name}}",
|
||||
"spending-cap-set": "Spending cap set: {{amount}} {{token-symbol}} for {{provider-name}} in {{account-name}}",
|
||||
"start-chat": "Start chat",
|
||||
"start-conversation": "Start conversation",
|
||||
"start-group-chat": "Start group chat",
|
||||
|
|
Loading…
Reference in New Issue