feat: approve token transactions on swap (#21076)

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2024-09-04 13:53:45 -03:00 committed by GitHub
parent c861d95d69
commit d5238ac82f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 519 additions and 319 deletions

View File

@ -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")

View File

@ -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})

View File

@ -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

View File

@ -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

View File

@ -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)))))))

View File

@ -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]]]]))

View File

@ -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

View File

@ -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]

View File

@ -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",