fix(MintTokens): Network chosen locks in the network used for all future tokens minted for that community

- Mint Owner token / TokenMaster token form: Changed text description for network field.
- Mint asset / collectible: Lock network. It should be the same network than the owner token one.
-  Mint asset / collectible: Additional simplification / clean-up of description field.
- Added network model helper file.

Fixes #11989
This commit is contained in:
Noelia 2023-09-12 11:26:25 +02:00 committed by Noelia
parent 4f2f9646ec
commit 0a930fc9b1
9 changed files with 180 additions and 143 deletions

View File

@ -11,6 +11,10 @@ SplitView {
id: root
Logs { id: logs }
readonly property string ethereumName : "Ethereum Mainnet"
readonly property string optimismName : "Optimism"
readonly property string arbitrumName : "Arbitrum"
SplitView {
orientation: Qt.Vertical
@ -41,7 +45,18 @@ SplitView {
multiSelection: multiSelectionCheckBox.checked
onToggleNetwork: logs.logEvent("onToggleNetwork: " + network.chainName)
onToggleNetwork: {
logs.logEvent("onToggleNetwork: " + network.chainName)
if(network.chainName === root.ethereumName)
ethRadioBtn.checked = true
else if(network.chainName === root.optimismName)
optRadioBtn.checked = true
else if(network.chainName === root.arbitrumName)
arbRadioBtn.checked = true
}
}
}
}
@ -80,37 +95,21 @@ SplitView {
RadioButton {
id: ethRadioBtn
text: "Ethereum Mainnet"
text: root.ethereumName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.ethNet)
}
RadioButton {
text: "Optimism"
id: optRadioBtn
text: root.optimismName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.optimismNet)
}
RadioButton {
text: "Arbitrum"
id: arbRadioBtn
text: root.arbitrumName
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.arbitrumNet)
}
RadioButton {
text: "Hermez"
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.hermezNet)
}
RadioButton {
text: "Testnet"
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.testnetNet)
}
RadioButton {
text: "Custom"
onCheckedChanged: if(checked) networkFilter.setChain(NetworksModel.customNet)
}
RadioButton {
text: "Undefined"
onCheckedChanged: if(checked) networkFilter.setChain()
}
RadioButton {
text: "Not existing network id"
onCheckedChanged: if(checked) networkFilter.setChain(77)
}
}
}
}

View File

@ -23,9 +23,9 @@ ListModel {
infiniteSupply: false,
transferable: true,
remoteSelfDestruct: false,
chainId: 2,
chainName: "Optimism",
chainIcon: ModelsData.networks.optimism,
chainId: 3,
chainName: "Arbitrum",
chainIcon: ModelsData.networks.arbitrum,
accountName: "Another account - generated",
tokenOwnersModel: root.emptyModel
},
@ -43,9 +43,9 @@ ListModel {
infiniteSupply: true,
transferable: false,
remoteSelfDestruct: true,
chainId: 2,
chainName: "Optimism",
chainIcon: ModelsData.networks.optimism,
chainId: 3,
chainName: "Arbitrum",
chainIcon: ModelsData.networks.arbitrum,
accountName: "Another account - generated",
tokenOwnersModel: root.tokenOwnersModel
},

View File

@ -12,6 +12,7 @@ import AppLayouts.Communities.helpers 1.0
import AppLayouts.Communities.layouts 1.0
import AppLayouts.Communities.popups 1.0
import AppLayouts.Communities.views 1.0
import AppLayouts.Wallet.helpers 1.0
import shared.controls 1.0
@ -271,15 +272,37 @@ StackView {
SettingsPage {
id: newTokenPage
readonly property int ownerTokenChainId: SQUtils.ModelUtils.get(root.tokensModel, "privilegesLevel", Constants.TokenPrivilegesLevel.Owner).chainId ?? 0
readonly property var chainModel: NetworkModelHelpers.getLayerNetworkModelByChainId(root.layer1Networks,
root.layer2Networks,
ownerTokenChainId) ?? root.layer2Networks
readonly property int chainIndex: NetworkModelHelpers.getChainIndexByChainId(root.layer1Networks,
root.layer2Networks,
ownerTokenChainId)
readonly property string chainName: NetworkModelHelpers.getChainName(chainModel, chainIndex)
readonly property string chainIcon: NetworkModelHelpers.getChainIconUrl(chainModel, chainIndex)
property TokenObject asset: TokenObject{
type: Constants.TokenType.ERC20
multiplierIndex: 18
// Minted tokens will use ALWAYS the same chain where the owner token was deployed.
chainId: newTokenPage.ownerTokenChainId
chainName: newTokenPage.chainName
chainIcon: newTokenPage.chainIcon
}
property TokenObject collectible: TokenObject {
type: Constants.TokenType.ERC721
// Minted tokens will use ALWAYS the same chain where the owner token was deployed.
chainId: newTokenPage.ownerTokenChainId
chainName: newTokenPage.chainName
chainIcon: newTokenPage.chainIcon
}
property bool isAssetView: false
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
property string referenceName: ""
@ -343,8 +366,6 @@ StackView {
viewWidth: root.viewWidth
layer1Networks: root.layer1Networks
layer2Networks: root.layer2Networks
enabledNetworks: root.enabledNetworks
allNetworks: root.allNetworks
accounts: root.accounts
tokensModel: root.tokensModel
tokensModelWallet: root.tokensModelWallet

View File

@ -38,8 +38,6 @@ StatusScrollView {
// Network related properties:
property var layer1Networks
property var layer2Networks
property var enabledNetworks
property var allNetworks
// Account expected roles: address, name, color, emoji, walletType
property var accounts
@ -77,10 +75,6 @@ StatusScrollView {
contentWidth: mainLayout.width
contentHeight: mainLayout.height
Component.onCompleted: {
networkSelector.setChain(root.token.chainId)
}
ColumnLayout {
id: mainLayout
@ -122,8 +116,8 @@ StatusScrollView {
validationMode: root.validationMode
minLengthValidator.errorMessage: qsTr("Please name your token name (use A-Z and 0-9, hyphens and underscores only)")
regexValidator.errorMessage: d.hasEmoji(text) ?
qsTr("Your token name is too cool (use A-Z and 0-9, hyphens and underscores only)") :
qsTr("Your token name contains invalid characters (use A-Z and 0-9, hyphens and underscores only)")
qsTr("Your token name is too cool (use A-Z and 0-9, hyphens and underscores only)") :
qsTr("Your token name contains invalid characters (use A-Z and 0-9, hyphens and underscores only)")
extraValidator.validate: function (value) {
// If minting failed, we can retry same deployment, so same name allowed
const allowRepeatedName = root.token.deployState === Constants.ContractTransactionStatus.Failed
@ -145,7 +139,7 @@ StatusScrollView {
label: qsTr("Description")
text: root.token.description
charLimit: 280
placeholderText: root.isAssetView ? qsTr("Describe your asset") : qsTr("Describe your collectible")
placeholderText: root.isAssetView ? qsTr("Describe your asset (will be shown in hodlers wallets)") : qsTr("Describe your collectible (will be shown in hodlers wallets)")
input.multiline: true
input.verticalAlignment: Qt.AlignTop
input.placeholder.verticalAlignment: Qt.AlignTop
@ -169,7 +163,7 @@ StatusScrollView {
validationMode: root.validationMode
minLengthValidator.errorMessage: qsTr("Please enter your token symbol (use A-Z only)")
regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your token symbol is too cool (use A-Z only)") :
qsTr("Your token symbol contains invalid characters (use A-Z only)")
qsTr("Your token symbol contains invalid characters (use A-Z only)")
regexValidator.regularExpression: Constants.regularExpressions.capitalOnly
extraValidator.validate: function (value) {
// If minting failed, we can retry same deployment, so same symbol allowed
@ -180,10 +174,11 @@ StatusScrollView {
// Otherwise, no repeated names allowed:
return (!SQUtils.ModelUtils.contains(root.tokensModel, "symbol", symbolInput.text) &&
!SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text))
!SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text))
}
extraValidator.errorMessage: SQUtils.ModelUtils.contains(root.tokensModelWallet, "symbol", symbolInput.text) ?
qsTr("This token symbol is already in use") : qsTr("You have used this token symbol before")
qsTr("This token symbol is already in use") :
qsTr("You have used this token symbol before")
onTextChanged: {
const cursorPos = input.edit.cursorPosition
@ -194,11 +189,53 @@ StatusScrollView {
}
}
CustomNetworkFilterRowComponent {
id: networkSelector
StatusBaseText {
text: qsTr("Network")
color: Theme.palette.directColor1
font.pixelSize: Theme.primaryTextFontSize
}
label: qsTr("Select network")
description: qsTr("The network on which this token will be minted")
Rectangle {
Layout.preferredHeight: 44
Layout.fillWidth: true
radius: 8
color: "transparent"
border.color: Theme.palette.directColor7
RowLayout {
id: networkRow
anchors.verticalCenter: parent.verticalCenter
spacing: 16
StatusSmartIdenticon {
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.current.padding
asset.height: 24
asset.width: asset.height
asset.isImage: true
asset.name: Style.svg(token.chainIcon)
active: true
visible: active
}
StatusBaseText {
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
Layout.rightMargin: Style.current.padding
font.pixelSize: 13
font.weight: Font.Medium
elide: Text.ElideRight
lineHeight: 24
lineHeightMode: Text.FixedHeight
verticalAlignment: Text.AlignVCenter
text: token.chainName
color: Theme.palette.baseColor1
visible: !!text
}
}
}
CustomSwitchRowComponent {
@ -221,12 +258,12 @@ StatusScrollView {
visible: !unlimitedSupplyChecker.checked
label: qsTr("Total finite supply")
text: SQUtils.AmountsArithmetic.toNumber(root.token.supply,
root.token.multiplierIndex)
root.token.multiplierIndex)
placeholderText: qsTr("e.g. 300")
minLengthValidator.errorMessage: qsTr("Please enter a total finite supply")
regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your total finite supply is too cool (use 0-9 only)") :
qsTr("Your total finite supply contains invalid characters (use 0-9 only)")
qsTr("Your total finite supply contains invalid characters (use 0-9 only)")
regexValidator.regularExpression: Constants.regularExpressions.numerical
extraValidator.validate: function (value) { return parseInt(value) > 0 && parseInt(value) <= 999999999 }
extraValidator.errorMessage: qsTr("Enter a number between 1 and 999,999,999")
@ -274,7 +311,7 @@ StatusScrollView {
validationMode: StatusInput.ValidationMode.Always
minLengthValidator.errorMessage: qsTr("Please enter how many decimals your token should have")
regexValidator.errorMessage: d.hasEmoji(text) ? qsTr("Your decimal amount is too cool (use 0-9 only)") :
qsTr("Your decimal amount contains invalid characters (use 0-9 only)")
qsTr("Your decimal amount contains invalid characters (use 0-9 only)")
regexValidator.regularExpression: Constants.regularExpressions.numerical
extraValidator.validate: function (value) { return parseInt(value) > 0 && parseInt(value) <= 10 }
extraValidator.errorMessage: qsTr("Enter a number between 1 and 10")
@ -308,8 +345,8 @@ StatusScrollView {
function onAccountAddressChanged() {
const idx = SQUtils.ModelUtils.indexOf(
feesBox.accountsSelector.model, "address",
root.token.accountAddress)
feesBox.accountsSelector.model, "address",
root.token.accountAddress)
feesBox.accountsSelector.currentIndex = idx
}
@ -414,41 +451,4 @@ StatusScrollView {
id: switch_
}
}
component CustomNetworkFilterRowComponent: RowLayout {
id: networkComponent
property string label
property string description
function setChain(chainId) { netFilter.setChain(chainId) }
Layout.fillWidth: true
Layout.topMargin: Style.current.padding
spacing: 32
CustomLabelDescriptionComponent {
label: networkComponent.label
description: networkComponent.description
}
NetworkFilter {
id: netFilter
Layout.preferredWidth: 160
allNetworks: root.allNetworks
layer1Networks: root.layer1Networks
layer2Networks: root.layer2Networks
enabledNetworks: root.enabledNetworks
multiSelection: false
onToggleNetwork: (network) => {
root.token.chainId = network.chainId
root.token.chainName = network.chainName
root.token.chainIcon = network.iconUrl
}
}
}
}

View File

@ -141,9 +141,7 @@ StatusScrollView {
bottomPadding: Style.current.padding
}
// TO BE REMOVED: It will be removed with the new fees panel
CustomLabelDescriptionComponent {
label: qsTr("Select account")
description: qsTr("This account will be where you receive your Owner token and will also be the account that pays the token minting gas fees.")
}
@ -198,7 +196,7 @@ StatusScrollView {
id: networkSelector
label: qsTr("Select network")
description: qsTr("The network on which these tokens will be minted.")
description: qsTr("The network you select will be where all your communitys tokens reside. Once set, this setting cant be changed and tokens cant move to other networks.")
}
FeesBox {
@ -217,30 +215,6 @@ StatusScrollView {
showAccountsSelector: false
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Style.current.halfPadding
StatusIcon {
Layout.preferredWidth: d.iconSize
Layout.preferredHeight: d.iconSize
Layout.alignment: Qt.AlignTop
color: Theme.palette.baseColor1
icon: "info"
}
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: Style.current.primaryTextFontSize
color: Theme.palette.baseColor1
lineHeight: 1.2
text: qsTr("Make sure youre happy with the blockchain network selected before minting these tokens as they cant be moved to a different network later.")
}
}
StatusButton {
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
@ -308,8 +282,14 @@ StatusScrollView {
layer1Networks: root.layer1Networks
layer2Networks: root.layer2Networks
enabledNetworks: root.enabledNetworks
multiSelection: false
control.topPadding: 10
control.background: Rectangle {
height: 44
radius: 8
color: "transparent"
border.color: Theme.palette.directColor7
}
onToggleNetwork: (network) => {
// Set Owner Token network properties:

View File

@ -169,8 +169,8 @@ StatusScrollView {
delegate: StatusListItem {
height: 64
width: mainLayout.width
title: model.name
subTitle: model.symbol
title: model.name ?? ""
subTitle: model.symbol ?? ""
asset.name: model.image ? model.image : ""
asset.isImage: true
components: [

View File

@ -7,6 +7,8 @@ import StatusQ.Core.Utils 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import AppLayouts.Wallet.helpers 1.0
import utils 1.0
import "../views"
@ -29,27 +31,12 @@ StatusComboBox {
function setChain(chainId) {
if(!multiSelection && !!d.currentModel && d.currentModel.count > 0) {
// Find given chain id:
var chainIdExists = false
if(chainId) {
if(!!root.layer1Networks && ModelUtils.contains(root.layer1Networks, "chainId", chainId)) {
d.currentModel = root.layer1Networks
chainIdExists = true
} else if(!!root.layer2Networks && ModelUtils.contains(root.layer2Networks, "chainId", chainId)) {
d.currentModel = root.layer2Networks
chainIdExists = true
}
}
// Set chain:
if(chainIdExists) {
d.currentIndex = ModelUtils.indexOf(d.currentModel, "chainId", chainId)
}
else {
// Default value if not specified
d.currentModel = root.layer2Networks
d.currentIndex = 0
}
d.currentModel = NetworkModelHelpers.getLayerNetworkModelByChainId(root.layer1Networks,
root.layer2Networks,
chainId) ?? root.layer2Networks
d.currentIndex = NetworkModelHelpers.getChainIndexByChainId(root.layer1Networks,
root.layer2Networks,
chainId)
// Notify change:
root.toggleNetwork(ModelUtils.get(d.currentModel, d.currentIndex))
@ -59,8 +46,8 @@ StatusComboBox {
QtObject {
id: d
readonly property string selectedChainName: ModelUtils.get(d.currentModel, d.currentIndex, "chainName") ?? ""
readonly property string selectedIconUrl: ModelUtils.get(d.currentModel, d.currentIndex, "iconUrl") ?? ""
readonly property string selectedChainName: NetworkModelHelpers.getChainName(d.currentModel, d.currentIndex)
readonly property string selectedIconUrl: NetworkModelHelpers.getChainIconUrl(d.currentModel, d.currentIndex)
readonly property bool allSelected: (!!root.enabledNetworks && !!root.allNetworks) ? root.enabledNetworks.count === root.allNetworks.count :
false
readonly property bool noneSelected: (!!root.enabledNetworks) ? root.enabledNetworks.count === 0 : false

View File

@ -0,0 +1,49 @@
pragma Singleton
import QtQml 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Internal 0.1 as Internal
import AppLayouts.Communities.controls 1.0
QtObject {
// Given a specific network model and an index inside the model, it gets the chain name.
function getChainName(model, index) {
return ModelUtils.get(model, index, "chainName") ?? ""
}
// Given a specific network model and an index inside the model, it gets the chain icon url.
function getChainIconUrl(model, index) {
return ModelUtils.get(model, index, "iconUrl") ?? ""
}
// Given a layer1 network model and layer2 network model, it looks for the provided chainId and returns
// the layer network model that contains the specific chain. If not found, returns undefined.
function getLayerNetworkModelByChainId(layer1NetworksModel, layer2NetworksModel, chainId) {
if(chainId) {
if(!!layer1NetworksModel && ModelUtils.contains(layer1NetworksModel, "chainId", chainId))
return layer1NetworksModel
else if(!!layer2NetworksModel && ModelUtils.contains(layer2NetworksModel, "chainId", chainId))
return layer2NetworksModel
}
// Default value if chainId is not part of any provided layer network model
return undefined
}
// Given a layer1 network model and layer2 network model, it looks for the provided chainId and returns
// the index of the the specific chain. If not found, returns 0 value.
function getChainIndexByChainId(layer1NetworksModel, layer2NetworksModel, chainId) {
const currentModel = getLayerNetworkModelByChainId(layer1NetworksModel, layer2NetworksModel, chainId)
if(!!currentModel)
return ModelUtils.indexOf(currentModel, "chainId", chainId)
// Default value if no model specified
return 0
}
}

View File

@ -0,0 +1 @@
singleton NetworkModelHelpers 1.0 NetworkModelHelpers.qml