fix(Update fees periodically): Update airdrop flows to use fees subscriber
This commit is contained in:
parent
f2b3ba1ae7
commit
f9e7265447
|
@ -104,8 +104,6 @@ SplitView {
|
|||
assetsModel: AssetsModel {}
|
||||
collectiblesModel: ListModel {}
|
||||
|
||||
accountsModel: ListModel {}
|
||||
|
||||
CollectiblesModel {
|
||||
id: collectiblesModel
|
||||
}
|
||||
|
|
|
@ -99,32 +99,35 @@ SplitView {
|
|||
|
||||
interval: 2000
|
||||
|
||||
property var response
|
||||
property var feesPerContract: []
|
||||
|
||||
function createAmount(amount, symbol, decimals) {
|
||||
return {
|
||||
amount, symbol,
|
||||
displayDecimals: decimals, stripTrailingZeroes: false
|
||||
}
|
||||
}
|
||||
|
||||
function requestMockedFees(contractKeysAndAmounts) {
|
||||
const fees = []
|
||||
if (!loader.item)
|
||||
return
|
||||
|
||||
function createAmount(amount, symbol, decimals) {
|
||||
return {
|
||||
amount, symbol,
|
||||
displayDecimals: decimals, stripTrailingZeroes: false
|
||||
}
|
||||
}
|
||||
const view = loader.item
|
||||
view.feesAvailable = false
|
||||
view.totalFeeText = ""
|
||||
view.feeErrorText = ""
|
||||
view.feesPerSelectedContract = []
|
||||
|
||||
const fees = []
|
||||
|
||||
contractKeysAndAmounts.forEach(entry => {
|
||||
fees.push({
|
||||
ethFee: createAmount(0.0002120115, "ETH", 4),
|
||||
fiatFee: createAmount(123.15, "USD", 2),
|
||||
errorCode: 0,
|
||||
contractUniqueKey: entry.contractUniqueKey
|
||||
contractUniqueKey: entry.contractUniqueKey,
|
||||
feeText: "0.0002120115 ETH (123.15 USD)"
|
||||
})
|
||||
})
|
||||
|
||||
response = {
|
||||
fees, errorCode: feesErrorsButtonGroup.checkedButton.code,
|
||||
totalEthFee: createAmount(0.0002120115 * fees.length, "ETH", 4),
|
||||
totalFiatFee: createAmount(123.15 * fees.length, "USD", 2)
|
||||
}
|
||||
feesPerContract = fees
|
||||
|
||||
restart()
|
||||
}
|
||||
|
@ -134,7 +137,10 @@ SplitView {
|
|||
return
|
||||
|
||||
const view = loader.item
|
||||
view.airdropFees = response
|
||||
view.totalFeeText = createAmount(0.0002120115 * feesPerContract.length, "ETH", 4) + "(" ,createAmount(123.15 * feesPerContract.length, "USD", 2),"USD)"
|
||||
view.feeErrorText = feesErrorsButtonGroup.checkedButton.code ? feesErrorsButtonGroup.checkedButton.text : ""
|
||||
view.feesAvailable = true
|
||||
view.feesPerSelectedContract = feesCalculationTimer.feesPerContract
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,6 +258,14 @@ SplitView {
|
|||
assetsModel: AssetsModel {}
|
||||
collectiblesModel: CollectiblesModel {}
|
||||
membersModel: members
|
||||
totalFeeText: ""
|
||||
feeErrorText: ""
|
||||
feesPerSelectedContract: []
|
||||
feesAvailable: false
|
||||
|
||||
onShowingFeesChanged: {
|
||||
feesCalculationTimer.requestMockedFees(loader.item.selectedContractKeysAndAmounts)
|
||||
}
|
||||
|
||||
accountsModel: ListModel {
|
||||
ListElement {
|
||||
|
@ -283,15 +297,6 @@ SplitView {
|
|||
"membersPubKeys", "feeAccountAddress"],
|
||||
arguments)
|
||||
}
|
||||
|
||||
onAirdropFeesRequested: {
|
||||
logs.logEvent("EditAirdropView::airdropFeesRequested",
|
||||
["contractKeysAndAmounts", "addresses",
|
||||
"feeAccountAddress"],
|
||||
arguments)
|
||||
|
||||
feesCalculationTimer.requestMockedFees(contractKeysAndAmounts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -317,6 +322,12 @@ SplitView {
|
|||
id: feesErrorsButtonGroup
|
||||
|
||||
buttons: feesErrorsRow.children
|
||||
onCheckedButtonChanged: {
|
||||
if(!loader.item)
|
||||
return
|
||||
|
||||
feesCalculationTimer.requestMockedFees(loader.item.selectedContractKeysAndAmounts)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
|
||||
import utils 1.0
|
||||
/*!
|
||||
\qmltype AirdropFeesSubscriber
|
||||
\inherits QtObject
|
||||
\brief Helper object that holds the request data and the fee response when available
|
||||
*/
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
required property string communityId
|
||||
required property var contractKeysAndAmounts
|
||||
required property var addressesToAirdrop
|
||||
required property string feeAccountAddress
|
||||
required property bool enabled
|
||||
|
||||
// JS object specifing fees for the airdrop operation, should be set to
|
||||
// provide response to airdropFeesRequested signal.
|
||||
//
|
||||
// The expected structure is as follows:
|
||||
// {
|
||||
// fees: [{
|
||||
// ethFee: {CurrencyAmount JSON},
|
||||
// fiatFee: {CurrencyAmount JSON},
|
||||
// contractUniqueKey: string,
|
||||
// errorCode: ComputeFeeErrorCode (int)
|
||||
// }],
|
||||
// totalEthFee: {CurrencyAmount JSON},
|
||||
// totalFiatFee: {CurrencyAmount JSON},
|
||||
// errorCode: ComputeFeeErrorCode (int)
|
||||
// }
|
||||
property var airdropFeesResponse: null
|
||||
|
||||
readonly property string feesError: {
|
||||
if (!airdropFeesResponse) return ""
|
||||
|
||||
if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Success) return ""
|
||||
|
||||
if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Balance)
|
||||
return qsTr("Your account does not have enough ETH to pay the gas fee for this airdrop. Try adding some ETH to your account.")
|
||||
|
||||
if (airdropFeesResponse.errorCode === Constants.ComputeFeeErrorCode.Infura)
|
||||
return qsTr("Infura error")
|
||||
|
||||
return qsTr("Unknown error")
|
||||
}
|
||||
readonly property string totalFee: {
|
||||
if (!airdropFeesResponse || !Object.values(airdropFeesResponse.totalEthFee).length || !Object.values(airdropFeesResponse.totalFiatFee).length) return ""
|
||||
|
||||
if (airdropFeesResponse.errorCode !== Constants.ComputeFeeErrorCode.Success && airdropFeesResponse.errorCode !== Constants.ComputeFeeErrorCode.Balance)
|
||||
return ""
|
||||
|
||||
return `${LocaleUtils.currencyAmountToLocaleString(airdropFeesResponse.totalEthFee)} (${LocaleUtils.currencyAmountToLocaleString(airdropFeesResponse.totalFiatFee)})`
|
||||
}
|
||||
|
||||
readonly property var feesPerContract: {
|
||||
if (!airdropFeesResponse || !Object.values(airdropFeesResponse.fees).length || totalFee == "") return []
|
||||
|
||||
return airdropFeesResponse.fees.map(fee => {
|
||||
return {
|
||||
contractUniqueKey: fee.contractUniqueKey,
|
||||
feeText: `${LocaleUtils.currencyAmountToLocaleString(fee.ethFee)} (${LocaleUtils.currencyAmountToLocaleString(fee.fiatFee)})`
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import StatusQ.Controls 0.1
|
|||
|
||||
import AppLayouts.Communities.layouts 1.0
|
||||
import AppLayouts.Communities.views 1.0
|
||||
import AppLayouts.Communities.helpers 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
@ -19,6 +20,7 @@ StackView {
|
|||
required property bool isTokenMasterOwner
|
||||
required property bool isAdmin
|
||||
readonly property bool isPrivilegedTokenOwnerProfile: root.isOwner || root.isTokenMasterOwner
|
||||
readonly property alias airdropFeesSubscriber: d.aidropFeeSubscriber
|
||||
|
||||
// Owner and TMaster token related properties:
|
||||
readonly property bool arePrivilegedTokensDeployed: root.isOwnerTokenDeployed && root.isTMasterTokenDeployed
|
||||
|
@ -32,11 +34,6 @@ StackView {
|
|||
required property var membersModel
|
||||
required property var accountsModel
|
||||
|
||||
// JS object specifing fees for the airdrop operation, should be set to
|
||||
// provide response to airdropFeesRequested signal.
|
||||
// Refer EditAirdropView::airdropFees for details.
|
||||
property var airdropFees: null
|
||||
|
||||
property int viewWidth: 560 // by design
|
||||
property string previousPageName: depth > 1 ? qsTr("Airdrops") : ""
|
||||
|
||||
|
@ -64,7 +61,7 @@ StackView {
|
|||
id: d
|
||||
|
||||
readonly property bool isAdminOnly: root.isAdmin && !root.isPrivilegedTokenOwnerProfile
|
||||
|
||||
property AirdropFeesSubscriber aidropFeeSubscriber: null
|
||||
signal selectToken(string key, string amount, int type)
|
||||
signal addAddresses(var addresses)
|
||||
}
|
||||
|
@ -80,7 +77,6 @@ StackView {
|
|||
|
||||
text: qsTr("New Airdrop")
|
||||
enabled: !d.isAdminOnly && root.arePrivilegedTokensDeployed
|
||||
|
||||
onClicked: root.push(newAirdropView, StackView.Immediate)
|
||||
}
|
||||
]
|
||||
|
@ -121,10 +117,10 @@ StackView {
|
|||
collectiblesModel: root.collectiblesModel
|
||||
membersModel: root.membersModel
|
||||
accountsModel: root.accountsModel
|
||||
|
||||
Binding on airdropFees {
|
||||
value: root.airdropFees
|
||||
}
|
||||
totalFeeText: feesSubscriber.totalFee
|
||||
feeErrorText: feesSubscriber.feesError
|
||||
feesPerSelectedContract: feesSubscriber.feesPerContract
|
||||
feesAvailable: !!feesSubscriber.airdropFeesResponse
|
||||
|
||||
onAirdropClicked: {
|
||||
root.airdropClicked(airdropTokens, addresses, feeAccountAddress)
|
||||
|
@ -136,7 +132,16 @@ StackView {
|
|||
Component.onCompleted: {
|
||||
d.selectToken.connect(view.selectToken)
|
||||
d.addAddresses.connect(view.addAddresses)
|
||||
airdropFeesRequested.connect(root.airdropFeesRequested)
|
||||
d.aidropFeeSubscriber = feesSubscriber
|
||||
}
|
||||
|
||||
AirdropFeesSubscriber {
|
||||
id: feesSubscriber
|
||||
enabled: view.visible && view.showingFees
|
||||
communityId: view.communityDetails.id
|
||||
contractKeysAndAmounts: view.selectedContractKeysAndAmounts
|
||||
addressesToAirdrop: view.selectedAddressesToAirdrop
|
||||
feeAccountAddress: view.selectedFeeAccount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,26 +35,40 @@ StatusScrollView {
|
|||
// A model containing accounts from which the fee can be paid:
|
||||
required property var accountsModel
|
||||
|
||||
// JS object specifing fees for the airdrop operation, should be set to
|
||||
// provide response to airdropFeesRequested signal.
|
||||
//
|
||||
// The expected structure is as follows:
|
||||
// {
|
||||
// fees: [{
|
||||
// ethFee: {CurrencyAmount JSON},
|
||||
// fiatFee: {CurrencyAmount JSON},
|
||||
// contractUniqueKey: string,
|
||||
// errorCode: ComputeFeeErrorCode (int)
|
||||
// }],
|
||||
// totalEthFee: {CurrencyAmount JSON},
|
||||
// totalFiatFee: {CurrencyAmount JSON},
|
||||
// errorCode: ComputeFeeErrorCode (int)
|
||||
// }
|
||||
property var airdropFees: null
|
||||
// Text to display as total fee
|
||||
required property string totalFeeText
|
||||
// Text to display in case of error
|
||||
required property string feeErrorText
|
||||
// Array containing the fees for each token
|
||||
// [{contractUniqueKey: string, feeText: string}]
|
||||
required property var feesPerSelectedContract
|
||||
// Bool property indicating whether the fees are available
|
||||
required property bool feesAvailable
|
||||
|
||||
property int viewWidth: 560 // by design
|
||||
|
||||
readonly property var selectedHoldingsModel: ListModel {}
|
||||
// Array containing the contract keys and amounts of the tokens to be airdropped
|
||||
readonly property alias selectedContractKeysAndAmounts: d.selectedContractKeysAndAmounts
|
||||
// Array containing the addresses to which the tokens will be airdropped
|
||||
readonly property alias selectedAddressesToAirdrop: d.selectedAddressesToAirdrop
|
||||
// The address of the account from which the fee will be paid
|
||||
readonly property alias selectedFeeAccount: d.selectedFeeAccount
|
||||
// Bool property indicating whether the fees are shown
|
||||
readonly property bool showingFees: d.showFees
|
||||
|
||||
onFeesPerSelectedContractChanged: {
|
||||
feesModel.clear()
|
||||
feesPerSelectedContract.forEach(entry => {
|
||||
feesModel.append({
|
||||
contractUniqueKey: entry.contractUniqueKey,
|
||||
title: qsTr("Airdrop %1 on %2")
|
||||
.arg(ModelUtils.getByKey(root.selectedHoldingsModel, "contractUniqueKey", entry.contractUniqueKey, "symbol"))
|
||||
.arg(ModelUtils.getByKey(root.selectedHoldingsModel, "contractUniqueKey", entry.contractUniqueKey, "networkText")),
|
||||
feeText: entry.feeText
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ModelChangeTracker {
|
||||
id: holdingsModelTracker
|
||||
|
@ -80,20 +94,12 @@ StatusScrollView {
|
|||
|
||||
signal airdropClicked(var airdropTokens, var addresses, string feeAccountAddress)
|
||||
|
||||
signal airdropFeesRequested(var contractKeysAndAmounts, var addresses, string feeAccountAddress)
|
||||
|
||||
signal navigateToMintTokenSettings(bool isAssetType)
|
||||
|
||||
function selectToken(key, amount, type) {
|
||||
if(selectedHoldingsModel)
|
||||
selectedHoldingsModel.clear()
|
||||
|
||||
const tokenModel = type === Constants.TokenType.ERC20 ?
|
||||
root.assetsModel : root.collectiblesModel
|
||||
|
||||
const modelItem = PermissionsHelpers.getTokenByKey(
|
||||
tokenModel, key)
|
||||
|
||||
const entry = d.prepareEntry(key, amount, type)
|
||||
entry.valid = true
|
||||
selectedHoldingsModel.append(entry)
|
||||
|
@ -103,51 +109,6 @@ StatusScrollView {
|
|||
addresses.addAddresses(_addresses)
|
||||
}
|
||||
|
||||
onAirdropFeesChanged: {
|
||||
if (root.airdropFees === null)
|
||||
return
|
||||
|
||||
const fees = root.airdropFees.fees
|
||||
const errorCode = root.airdropFees.errorCode
|
||||
|
||||
function buildFeeString(ethFee, fiatFee) {
|
||||
return `${LocaleUtils.currencyAmountToLocaleString(ethFee)} (${LocaleUtils.currencyAmountToLocaleString(fiatFee)})`
|
||||
}
|
||||
|
||||
d.feesError = ""
|
||||
d.totalFee = ""
|
||||
|
||||
if (errorCode === Constants.ComputeFeeErrorCode.Infura) {
|
||||
d.feesError = qsTr("Infura error")
|
||||
return
|
||||
}
|
||||
|
||||
if (errorCode === Constants.ComputeFeeErrorCode.Success ||
|
||||
errorCode === Constants.ComputeFeeErrorCode.Balance) {
|
||||
fees.forEach(fee => {
|
||||
const idx = ModelUtils.indexOf(
|
||||
feesModel, "contractUniqueKey",
|
||||
fee.contractUniqueKey)
|
||||
|
||||
feesModel.set(idx, {
|
||||
feeText: buildFeeString(fee.ethFee, fee.fiatFee)
|
||||
})
|
||||
})
|
||||
|
||||
d.totalFee = buildFeeString(
|
||||
root.airdropFees.totalEthFee,
|
||||
root.airdropFees.totalFiatFee)
|
||||
|
||||
if (errorCode === Constants.ComputeFeeErrorCode.Balance) {
|
||||
d.feesError = qsTr("Your account does not have enough ETH to pay the gas fee for this airdrop. Try adding some ETH to your account.")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
d.feesError = qsTr("Unknown error")
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
|
@ -155,25 +116,32 @@ StatusScrollView {
|
|||
readonly property int dropdownHorizontalOffset: 4
|
||||
readonly property int dropdownVerticalOffset: 1
|
||||
|
||||
property string feesError
|
||||
property string totalFee
|
||||
|
||||
readonly property bool isFeeLoading:
|
||||
root.airdropFees === null ||
|
||||
(root.airdropFees.errorCode !== Constants.ComputeFeeErrorCode.Success &&
|
||||
root.airdropFees.errorCode !== Constants.ComputeFeeErrorCode.Balance)
|
||||
|
||||
readonly property bool showFees: root.selectedHoldingsModel.count > 0
|
||||
&& airdropRecipientsSelector.valid
|
||||
&& airdropRecipientsSelector.count > 0
|
||||
|
||||
readonly property int totalRevision: holdingsModelTracker.revision
|
||||
+ addressesModelTracker.revision
|
||||
+ membersModelTracker.revision
|
||||
+ feesBox.accountsSelector.currentIndex
|
||||
+ (d.showFees ? 1 : 0)
|
||||
readonly property var selectedContractKeysAndAmounts: {
|
||||
//Depedencies:
|
||||
root.selectedHoldingsModel
|
||||
holdingsModelTracker.revision
|
||||
|
||||
onTotalRevisionChanged: Qt.callLater(() => d.resetFees())
|
||||
return ModelUtils.modelToArray(
|
||||
root.selectedHoldingsModel,
|
||||
["contractUniqueKey", "amount"])
|
||||
}
|
||||
|
||||
readonly property var selectedAddressesToAirdrop: {
|
||||
//Dependecies:
|
||||
addresses
|
||||
addressesModelTracker.revision
|
||||
|
||||
return ModelUtils.modelToArray(
|
||||
addresses, ["address"]).map(e => e.address)
|
||||
.concat([...selectedKeysFilter.keys])
|
||||
}
|
||||
|
||||
readonly property string selectedFeeAccount: ModelUtils.get(root.accountsModel,
|
||||
feesBox.accountIndex).address
|
||||
|
||||
function prepareEntry(key, amount, type) {
|
||||
const tokenModel = type === Constants.TokenType.ERC20
|
||||
|
@ -199,61 +167,6 @@ StatusScrollView {
|
|||
symbol: modelItem.symbol
|
||||
}
|
||||
}
|
||||
|
||||
function rebuildFeesModel() {
|
||||
feesModel.clear()
|
||||
|
||||
const airdropTokens = ModelUtils.modelToArray(
|
||||
root.selectedHoldingsModel,
|
||||
["contractUniqueKey", "accountName",
|
||||
"symbol", "amount", "tokenText",
|
||||
"networkText"])
|
||||
|
||||
airdropTokens.forEach(entry => {
|
||||
feesModel.append({
|
||||
contractUniqueKey: entry.contractUniqueKey,
|
||||
title: qsTr("Airdrop %1 on %2")
|
||||
.arg(entry.symbol)
|
||||
.arg(entry.networkText),
|
||||
feeText: ""
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function requestFees() {
|
||||
const airdropTokens = ModelUtils.modelToArray(
|
||||
root.selectedHoldingsModel,
|
||||
["contractUniqueKey", "amount"])
|
||||
|
||||
const contractKeysAndAmounts = airdropTokens.map(item => ({
|
||||
amount: item.amount,
|
||||
contractUniqueKey: item.contractUniqueKey
|
||||
}))
|
||||
const addressesArray = ModelUtils.modelToArray(
|
||||
addresses, ["address"]).map(e => e.address)
|
||||
|
||||
const airdropAddresses = [...selectedKeysFilter.keys]
|
||||
|
||||
const accountItem = ModelUtils.get(root.accountsModel,
|
||||
feesBox.accountIndex)
|
||||
|
||||
airdropFeesRequested(contractKeysAndAmounts, addressesArray.concat(airdropAddresses),
|
||||
accountItem.address)
|
||||
}
|
||||
|
||||
function resetFees() {
|
||||
root.airdropFees = null
|
||||
d.feesError = ""
|
||||
d.totalFee = ""
|
||||
|
||||
if (!d.showFees) {
|
||||
feesModel.clear()
|
||||
return
|
||||
}
|
||||
|
||||
d.rebuildFeesModel()
|
||||
d.requestFees()
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
|
@ -622,10 +535,10 @@ StatusScrollView {
|
|||
model: feesModel
|
||||
accountsSelector.model: root.accountsModel
|
||||
|
||||
totalFeeText: d.totalFee
|
||||
totalFeeText: root.totalFeeText
|
||||
placeholderText: qsTr("Add valid “What” and “To” values to see fees")
|
||||
|
||||
accountErrorText: d.feesError
|
||||
accountErrorText: root.feeErrorText
|
||||
}
|
||||
|
||||
WarningPanel {
|
||||
|
@ -646,7 +559,7 @@ StatusScrollView {
|
|||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.current.bigPadding
|
||||
text: qsTr("Create airdrop")
|
||||
enabled: root.isFullyFilled && !d.isFeeLoading && d.feesError === ""
|
||||
enabled: root.isFullyFilled && root.feesAvailable && root.feeErrorText === ""
|
||||
|
||||
onClicked: {
|
||||
const accountItem = ModelUtils.get(root.accountsModel,
|
||||
|
@ -666,8 +579,8 @@ StatusScrollView {
|
|||
|
||||
model: feesModel
|
||||
|
||||
totalFeeText: d.totalFee
|
||||
errorText: d.feesError
|
||||
totalFeeText: root.totalFeeText
|
||||
errorText: root.feeErrorText
|
||||
|
||||
onOpened: {
|
||||
const title1 = qsTr("Sign transaction - Airdrop %n token(s)", "",
|
||||
|
@ -676,8 +589,6 @@ StatusScrollView {
|
|||
addresses.count + airdropRecipientsSelector.membersModel.count)
|
||||
|
||||
title = `${title1} ${title2}`
|
||||
|
||||
d.resetFees()
|
||||
}
|
||||
|
||||
onSignTransactionClicked: {
|
||||
|
|
Loading…
Reference in New Issue