chore(Communities): Refactor amounts handling for displaying, minting, airdropping and burning

Closes: #11491
This commit is contained in:
Michał Cieślak 2023-08-10 14:23:59 +02:00 committed by Michał
parent f3526d6e01
commit 1c50ec17a8
29 changed files with 404 additions and 176 deletions

View File

@ -21,14 +21,14 @@ method computeAirdropFee*(self: AccessInterface, communityId: string, tokensJson
method selfDestructCollectibles*(self: AccessInterface, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.base.} =
raise newException(ValueError, "No implementation available")
method burnTokens*(self: AccessInterface, communityId: string, contractUniqueKey: string, amount: float64) {.base.} =
method burnTokens*(self: AccessInterface, communityId: string, contractUniqueKey: string, amount: string) {.base.} =
raise newException(ValueError, "No implementation available")
method deployCollectibles*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: float64, infiniteSupply: bool, transferable: bool,
method deployCollectibles*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool,
selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.base.} =
raise newException(ValueError, "No implementation available")
method deployAssets*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: float64, infiniteSupply: bool, decimals: int,
method deployAssets*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int,
chainId: int, imageCropInfoJson: string) {.base.} =
raise newException(ValueError, "No implementation available")
@ -48,7 +48,7 @@ method computeDeployFee*(self: AccessInterface, chainId: int, accountAddress: st
method computeSelfDestructFee*(self: AccessInterface, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.base.} =
raise newException(ValueError, "No implementation available")
method computeBurnFee*(self: AccessInterface, contractUniqueKey: string, amount: float64) {.base.} =
method computeBurnFee*(self: AccessInterface, contractUniqueKey: string, amount: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onDeployFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode) {.base.} =

View File

@ -2,6 +2,7 @@ import NimQml, Tables, strformat, sequtils, stint
import token_item
import token_owners_item
import token_owners_model
import ../../../../../../app_service/service/community/dto/community
import ../../../../../../app_service/service/community_tokens/dto/community_token
import ../../../../../../app_service/service/community_tokens/community_collectible_owner
import ../../../../../../app_service/common/utils
@ -32,6 +33,7 @@ type
BurnState
RemotelyDestructState
PrivilegesLevel
MultiplierIndex
QtObject:
type TokenModel* = ref object of QAbstractListModel
@ -181,7 +183,8 @@ QtObject:
ModelRole.Decimals.int:"decimals",
ModelRole.BurnState.int:"burnState",
ModelRole.RemotelyDestructState.int:"remotelyDestructState",
ModelRole.PrivilegesLevel.int:"privilegesLevel"
ModelRole.PrivilegesLevel.int:"privilegesLevel",
ModelRole.MultiplierIndex.int:"multiplierIndex"
}.toTable
method data(self: TokenModel, index: QModelIndex, role: int): QVariant =
@ -206,7 +209,7 @@ QtObject:
result = newQVariant(item.tokenDto.description)
of ModelRole.Supply:
# we need to present maxSupply - destructedAmount
result = newQVariant(supplyByType(item.tokenDto.supply - item.destructedAmount, item.tokenDto.tokenType))
result = newQVariant((item.tokenDto.supply - item.destructedAmount).toString(10))
of ModelRole.InfiniteSupply:
result = newQVariant(item.tokenDto.infiniteSupply)
of ModelRole.Transferable:
@ -230,7 +233,7 @@ QtObject:
of ModelRole.AccountAddress:
result = newQVariant(item.tokenDto.deployer)
of ModelRole.RemainingSupply:
result = newQVariant(supplyByType(item.remainingSupply, item.tokenDto.tokenType))
result = newQVariant(item.remainingSupply.toString(10))
of ModelRole.Decimals:
result = newQVariant(item.tokenDto.decimals)
of ModelRole.BurnState:
@ -240,9 +243,11 @@ QtObject:
result = newQVariant(destructStatus)
of ModelRole.PrivilegesLevel:
result = newQVariant(item.tokenDto.privilegesLevel.int)
of ModelRole.MultiplierIndex:
result = newQVariant(if item.tokenDto.tokenType == TokenType.ERC20: 18 else: 0)
proc `$`*(self: TokenModel): string =
for i in 0 ..< self.items.len:
result &= fmt"""TokenModel:
[{i}]:({$self.items[i]})
"""
"""

View File

@ -93,29 +93,17 @@ proc authenticate(self: Module) =
else:
self.controller.authenticateUser()
# for collectibles conversion is: "1" -> Uint256(1)
# for assets amount is converted to basic units (wei-like): "1.5" -> Uint256(1500000000000000000)
proc convertAmountByTokenType(self: Module, tokenType: TokenType, amount: float64): Uint256 =
const decimals = 18
case tokenType
of TokenType.ERC721:
return stint.parse($amount.int, Uint256)
of TokenType.ERC20:
return conversion.eth2Wei(amount, decimals)
else:
error "Converting amount - unknown token type", tokenType=tokenType
proc getTokenAndAmountList(self: Module, communityId: string, tokensJsonString: string): seq[CommunityTokenAndAmount] =
try:
let tokensJson = tokensJsonString.parseJson
for token in tokensJson:
let contractUniqueKey = token["contractUniqueKey"].getStr
let tokenDto = self.controller.findContractByUniqueId(contractUniqueKey)
let amountStr = token["amount"].getFloat
let amountStr = token["amount"].getStr
if tokenDto.tokenType == TokenType.Unknown:
error "Can't find token for community", contractUniqueKey=contractUniqueKey
return @[]
result.add(CommunityTokenAndAmount(communityToken: tokenDto, amount: self.convertAmountByTokenType(tokenDto.tokenType, amountStr)))
result.add(CommunityTokenAndAmount(communityToken: tokenDto, amount: amountStr.parse(Uint256)))
except Exception as e:
error "Error getTokenAndAmountList", msg = e.msg
@ -148,16 +136,16 @@ method selfDestructCollectibles*(self: Module, communityId: string, collectibles
self.tempContractAction = ContractAction.SelfDestruct
self.authenticate()
method burnTokens*(self: Module, communityId: string, contractUniqueKey: string, amount: float64) =
method burnTokens*(self: Module, communityId: string, contractUniqueKey: string, amount: string) =
let tokenDto = self.controller.findContractByUniqueId(contractUniqueKey)
self.tempCommunityId = communityId
self.tempContractUniqueKey = contractUniqueKey
self.tempAmount = self.convertAmountByTokenType(tokenDto.tokenType, amount)
self.tempAmount = amount.parse(Uint256)
self.tempContractAction = ContractAction.Burn
self.authenticate()
method deployCollectibles*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string,
supply: float64, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) =
supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) =
let ownerToken = self.controller.getOwnerToken(communityId)
let masterToken = self.controller.getTokenMasterToken(communityId)
@ -170,7 +158,7 @@ method deployCollectibles*(self: Module, communityId: string, fromAddress: strin
self.tempChainId = chainId
self.tempDeploymentParams.name = name
self.tempDeploymentParams.symbol = symbol
self.tempDeploymentParams.supply = self.convertAmountByTokenType(TokenType.ERC721, supply)
self.tempDeploymentParams.supply = supply.parse(Uint256)
self.tempDeploymentParams.infiniteSupply = infiniteSupply
self.tempDeploymentParams.transferable = transferable
self.tempDeploymentParams.remoteSelfDestruct = selfDestruct
@ -207,14 +195,14 @@ method deployOwnerToken*(self: Module, communityId: string, fromAddress: string,
self.tempContractAction = ContractAction.DeployOwnerToken
self.authenticate()
method deployAssets*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: float64, infiniteSupply: bool, decimals: int,
method deployAssets*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int,
chainId: int, imageCropInfoJson: string) =
self.tempAddressFrom = fromAddress
self.tempCommunityId = communityId
self.tempChainId = chainId
self.tempDeploymentParams.name = name
self.tempDeploymentParams.symbol = symbol
self.tempDeploymentParams.supply = self.convertAmountByTokenType(TokenType.ERC20, supply)
self.tempDeploymentParams.supply = supply.parse(Uint256)
self.tempDeploymentParams.infiniteSupply = infiniteSupply
self.tempDeploymentParams.decimals = decimals
self.tempDeploymentParams.tokenUri = utl.changeCommunityKeyCompression(communityId) & "/"
@ -269,9 +257,9 @@ method computeSelfDestructFee*(self: Module, collectiblesToBurnJsonString: strin
let walletAndAmountList = self.getWalletAndAmountListFromJson(collectiblesToBurnJsonString)
self.controller.computeSelfDestructFee(walletAndAmountList, contractUniqueKey)
method computeBurnFee*(self: Module, contractUniqueKey: string, amount: float64) =
method computeBurnFee*(self: Module, contractUniqueKey: string, amount: string) =
let tokenDto = self.controller.findContractByUniqueId(contractUniqueKey)
self.controller.computeBurnFee(contractUniqueKey, self.convertAmountByTokenType(tokenDto.tokenType, amount))
self.controller.computeBurnFee(contractUniqueKey, amount.parse(Uint256))
proc createUrl(self: Module, chainId: int, transactionHash: string): string =
let network = self.controller.getNetwork(chainId)

View File

@ -21,10 +21,10 @@ QtObject:
result.QObject.setup
result.communityTokensModule = communityTokensModule
proc deployCollectible*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: float, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.slot.} =
proc deployCollectible*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, imageCropInfoJson: string) {.slot.} =
self.communityTokensModule.deployCollectibles(communityId, fromAddress, name, symbol, description, supply, infiniteSupply, transferable, selfDestruct, chainId, imageCropInfoJson)
proc deployAssets*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: float, infiniteSupply: bool, decimals: int, chainId: int, imageCropInfoJson: string) {.slot.} =
proc deployAssets*(self: View, communityId: string, fromAddress: string, name: string, symbol: string, description: string, supply: string, infiniteSupply: bool, decimals: int, chainId: int, imageCropInfoJson: string) {.slot.} =
self.communityTokensModule.deployAssets(communityId, fromAddress, name, symbol, description, supply, infiniteSupply, decimals, chainId, imageCropInfoJson)
proc deployOwnerToken*(self:View, communityId: string, fromAddress: string, ownerName: string, ownerSymbol: string, ownerDescription: string, masterName: string, masterSymbol: string, masterDescription: string, chainId: int, imageCropInfoJson: string) {.slot.} =
@ -42,7 +42,7 @@ QtObject:
proc selfDestructCollectibles*(self: View, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.slot.} =
self.communityTokensModule.selfDestructCollectibles(communityId, collectiblesToBurnJsonString, contractUniqueKey)
proc burnTokens*(self: View, communityId: string, contractUniqueKey: string, amount: float) {.slot.} =
proc burnTokens*(self: View, communityId: string, contractUniqueKey: string, amount: string) {.slot.} =
self.communityTokensModule.burnTokens(communityId, contractUniqueKey, amount)
proc deployFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int) {.signal.}
@ -56,7 +56,7 @@ QtObject:
proc computeSelfDestructFee*(self: View, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.slot.} =
self.communityTokensModule.computeSelfDestructFee(collectiblesToBurnJsonString, contractUniqueKey)
proc computeBurnFee*(self: View, contractUniqueKey: string, amount: float) {.slot.} =
proc computeBurnFee*(self: View, contractUniqueKey: string, amount: string) {.slot.} =
self.communityTokensModule.computeBurnFee(contractUniqueKey, amount)
proc updateDeployFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int) =

View File

@ -45,6 +45,7 @@ proc toUInt256*(flt: float): UInt256 =
proc toUInt64*(flt: float): StUInt[64] =
toStUInt(flt, StUInt[64])
# This method may introduce distortions and should be avoided if possible.
proc eth2Wei*(eth: float, decimals: int = 18): UInt256 =
let weiValue = eth * parseFloat(alignLeft("1", decimals + 1, '0'))
weiValue.toUInt256

View File

@ -89,14 +89,3 @@ proc toCommunityTokenDto*(jsonObj: JsonNode): CommunityTokenDto =
proc parseCommunityTokens*(response: RpcResponse[JsonNode]): seq[CommunityTokenDto] =
result = map(response.result.getElems(),
proc(x: JsonNode): CommunityTokenDto = x.toCommunityTokenDto())
proc supplyByType*(supply: Uint256, tokenType: TokenType): float64 =
try:
var eths: string
if tokenType == TokenType.ERC20:
eths = wei2Eth(supply, 18)
else:
eths = supply.toString(10)
return parseFloat(eths)
except Exception as e:
error "Error parsing supply by type ", msg=e.msg, supply=supply, tokenType=tokenType

View File

@ -104,6 +104,8 @@ SplitView {
assetsModel: AssetsModel {}
collectiblesModel: ListModel {}
accountsModel: ListModel {}
CollectiblesModel {
id: collectiblesModel
}
@ -118,6 +120,10 @@ SplitView {
name: "supply"
expression: ((model.index + 1) * 115).toString()
},
ExpressionRole {
name: "multiplierIndex"
expression: 0
},
ExpressionRole {
name: "infiniteSupply"
expression: !(model.index % 4)
@ -158,7 +164,12 @@ SplitView {
proxyRoles: [
ExpressionRole {
name: "supply"
expression: ((model.index + 1) * 258).toString()
expression: ((model.index + 1) * 584).toString()
+ "0".repeat(18)
},
ExpressionRole {
name: "multiplierIndex"
expression: 18
},
ExpressionRole {
name: "infiniteSupply"

View File

@ -66,7 +66,11 @@ SplitView {
proxyRoles: [
ExpressionRole {
name: "supply"
expression: model.index === 1 ? model.index : (model.index + 1) * 115
expression: (model.index === 1 ? 1 : (model.index + 1) * 115).toString()
},
ExpressionRole {
name: "multiplierIndex"
expression: 0
},
ExpressionRole {
name: "infiniteSupply"
@ -99,7 +103,12 @@ SplitView {
proxyRoles: [
ExpressionRole {
name: "supply"
expression: (model.index + 1) * 584
expression: ((model.index + 1) * 584).toString()
+ "0".repeat(18)
},
ExpressionRole {
name: "multiplierIndex"
expression: 18
},
ExpressionRole {
name: "infiniteSupply"

View File

@ -13,20 +13,30 @@ Item {
{
name: "Optimism",
icon: Style.svg(ModelsData.networks.optimism),
amount: 300,
amount: "300",
multiplierIndex: 0,
infiniteAmount: false
},
{
name: "Arbitrum",
icon: Style.svg(ModelsData.networks.arbitrum),
amount: 400,
amount: "400000",
multiplierIndex: 3,
infiniteAmount: false
},
{
name: "Hermez",
icon: Style.svg(ModelsData.networks.hermez),
amount: 0,
amount: "0",
multiplierIndex: 0,
infiniteAmount: true
},
{
name: "Ethereum",
icon: Style.svg(ModelsData.networks.ethereum),
amount: "12" + "0".repeat(18),
multiplierIndex: 18,
infiniteAmount: false
}
]
@ -78,6 +88,10 @@ Item {
Layout.alignment: Qt.AlignHCenter
text: `current amount: ${comboBox.currentAmount}`
}
Label {
Layout.alignment: Qt.AlignHCenter
text: `current multiplier index: ${comboBox.currentMultiplierIndex}`
}
Label {
Layout.alignment: Qt.AlignHCenter
text: `current amount infinite: ${comboBox.currentInfiniteAmount}`

View File

@ -2,6 +2,8 @@ import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.settings 1.0
import Models 1.0
import Storybook 1.0
import utils 1.0
@ -20,20 +22,30 @@ SplitView {
{
name: "Optimism",
icon: Style.svg(ModelsData.networks.optimism),
amount: 300,
amount: "300",
multiplierIndex: 0,
infiniteAmount: false
},
{
name: "Arbitrum",
icon: Style.svg(ModelsData.networks.arbitrum),
amount: 400,
amount: "400000",
multiplierIndex: 3,
infiniteAmount: false
},
{
name: "Hermez",
icon: Style.svg(ModelsData.networks.hermez),
amount: 500,
amount: "0",
multiplierIndex: 0,
infiniteAmount: true
},
{
name: "Ethereum",
icon: Style.svg(ModelsData.networks.ethereum),
amount: "12" + "0".repeat(18),
multiplierIndex: 18,
infiniteAmount: false
}
]
@ -119,8 +131,17 @@ SplitView {
text: "∞"
}
}
Label {
text: "amount: " + tokenPanel.amount
}
}
}
Settings {
property alias networksModelCheckBoxChecked:
networksModelCheckBox.checked
}
}
// category: Panels

View File

@ -40,13 +40,14 @@ QtObject {
console.assert(!isNaN(number) && Number.isInteger(multiplier)
&& multiplier >= 0)
const amount = new Big.Big(number).times(10 ** multiplier)
console.assert(amount.eq(amount.round()))
// TODO: restore assert when permissions handled as bigints
// console.assert(amount.eq(amount.round()))
return amount
}
/*!
\qmlmethod AmountsArithmetic::toNumber(amount, multiplier = 0)
\brief Converts an amount to a java script number.
\brief Converts an amount (in form of amount object or string) to a java script number.
This operation may result in loss of precision. Because of that it should
be used only to display a value in the user interface, but requires
@ -57,10 +58,16 @@ QtObject {
\qml
console.log(AmountsArithmetic.toNumber(
AmountsArithmetic.fromString("123456789123456789123"))) // 123456789123456800000
console.log(AmountsArithmetic.toNumber("123456789123456789123")) // 123456789123456800000
\endqml
*/
function toNumber(amount, multiplier = 0) {
console.assert(Number.isInteger(multiplier) && multiplier >= 0)
if (typeof amount === "string")
amount = fromString(amount)
console.assert(amount instanceof Big.Big)
return amount.div(10 ** multiplier).toNumber()
}
@ -84,7 +91,8 @@ QtObject {
function fromString(numStr) {
console.assert(typeof numStr === "string")
const amount = new Big.Big(numStr)
console.assert(amount.eq(amount.round()))
// TODO: restore assert when permissions handled as bigints
//console.assert(amount.eq(amount.round()))
return amount
}
@ -105,6 +113,16 @@ QtObject {
return amount.times(multiplier)
}
/*!
\qmlmethod AmountsArithmetic::div(divident, divisor)
\brief Returns a Big number whose value is the value of divident divided by divisor.
*/
function div(divident, divisor) {
console.assert(divident instanceof Big.Big)
console.assert(divisor instanceof Big.Big)
return divident.div(divisor)
}
/*!
\qmlmethod AmountsArithmetic::cmp(amount1, amount2)
\brief Compares two amounts.

View File

@ -5,6 +5,7 @@ import QtQml 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import SortFilterProxyModel 0.2
@ -13,6 +14,7 @@ StatusComboBox {
readonly property string currentName: control.currentText
readonly property alias currentAmount: instantiator.amount
readonly property alias currentMultiplierIndex: instantiator.multiplierIndex
readonly property alias currentInfiniteAmount: instantiator.infiniteAmount
readonly property alias currentIcon: instantiator.icon
@ -49,6 +51,10 @@ StatusComboBox {
readonly property int iconSize: 32
readonly property string infinitySymbol: "∞"
function amountText(amount, multiplierIndex) {
return SQUtils.AmountsArithmetic.toNumber(amount, multiplierIndex)
}
}
component CustomText: StatusBaseText {
@ -94,7 +100,8 @@ StatusComboBox {
id: instantiator
property string icon
property int amount
property string amount
property int multiplierIndex
property bool infiniteAmount
model: SortFilterProxyModel {
@ -109,6 +116,7 @@ StatusComboBox {
readonly property list<Binding> bindings: [
Bind { property: "icon"; value: model.icon },
Bind { property: "amount"; value: model.amount },
Bind { property: "multiplierIndex"; value: model.multiplierIndex },
Bind { property: "infiniteAmount"; value: model.infiniteAmount }
]
}
@ -118,10 +126,17 @@ StatusComboBox {
title: root.control.displayText
iconSource: instantiator.icon
amount: !d.oneItem
? (instantiator.infiniteAmount ? d.infinitySymbol
: instantiator.amount)
: ""
amount: {
if (d.oneItem || !instantiator.amount)
return ""
if (instantiator.infiniteAmount)
return d.infinitySymbol
return d.amountText(instantiator.amount,
instantiator.multiplierIndex)
}
cursorShape: d.oneItem ? Qt.ArrowCursor : Qt.PointingHandCursor
onClicked: {
@ -135,7 +150,9 @@ StatusComboBox {
delegate: DelegateItem {
title: model.name
iconSource: model.icon
amount: model.infiniteAmount ? d.infinitySymbol : model.amount
amount: model.infiniteAmount
? d.infinitySymbol
: d.amountText(model.amount, model.multiplierIndex)
width: root.width
height: root.height

View File

@ -1,12 +1,13 @@
import QtQuick 2.13
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import utils 1.0
@ -98,12 +99,20 @@ StatusListView {
showSubItemsIcon: !!model.subItems && model.subItems.count > 0
selected: root.checkedKeys.includes(model.key)
amount: {
if(!model.infiniteSupply && !!model.supply && model.supply == 1)
if (model.supply === undefined
|| model.multiplierIndex === undefined)
return ""
if (model.infiniteSupply)
return "∞"
if (model.supply === "1")
return qsTr("Max. 1")
if(root.showTokenAmount)
return !!model.infiniteSupply ? "∞" : model.supply ?? ""
if (root.showTokenAmount)
return SQUtils.AmountsArithmetic.toNumber(model.supply,
model.multiplierIndex)
return ""
}

View File

@ -1,5 +1,5 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
@ -21,6 +21,7 @@ ColumnLayout {
property alias tokenImage: item.iconSource
property alias amountText: amountInput.text
property alias amount: amountInput.amount
property alias multiplierIndex: amountInput.multiplierIndex
property alias tokenCategoryText: tokenLabel.text
property alias networkLabelText: d.networkLabelText
property alias addOrUpdateButtonEnabled: addOrUpdateButton.enabled
@ -33,8 +34,9 @@ ColumnLayout {
signal updateClicked
signal removeClicked
function setAmount(amount) {
amountInput.setAmount(amount)
function setAmount(amount, multiplierIndex = 0) {
console.assert(typeof amount === "string")
amountInput.setAmount(amount, multiplierIndex)
}
QtObject {
@ -86,7 +88,10 @@ ColumnLayout {
spacing: 10
property alias currentAmount: inlineNetworksComboBox.currentAmount
property alias currentInfiniteAmount: inlineNetworksComboBox.currentInfiniteAmount
property alias currentMultiplierIndex:
inlineNetworksComboBox.currentMultiplierIndex
property alias currentInfiniteAmount:
inlineNetworksComboBox.currentInfiniteAmount
CustomText {
id: networkLabel
@ -124,10 +129,14 @@ ColumnLayout {
!networksComboBoxLoader.item.currentInfiniteAmount
maximumAmount: !!networksComboBoxLoader.item
? networksComboBoxLoader.item.currentAmount : 0
? networksComboBoxLoader.item.currentAmount : "0"
multiplierIndex: !!networksComboBoxLoader.item
? networksComboBoxLoader.item.currentMultiplierIndex : 0
onKeyPressed: {
if(!addOrUpdateButton.enabled) return
if(!addOrUpdateButton.enabled)
return
if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
addOrUpdateButton.clicked()
@ -145,7 +154,8 @@ ColumnLayout {
StatusButton {
id: addOrUpdateButton
text: (root.mode === HoldingTypes.Mode.Add) ? qsTr("Add") : qsTr("Update")
text: root.mode === HoldingTypes.Mode.Add ? qsTr("Add")
: qsTr("Update")
Layout.preferredHeight: d.defaultHeight
Layout.topMargin: d.defaultSpacing
Layout.fillWidth: true

View File

@ -73,6 +73,9 @@ QtObject {
}
function setHoldingsTextFormat(type, name, amount) {
if (typeof amount === "string")
amount = AmountsArithmetic.toNumber(AmountsArithmetic.fromString(amount))
switch (type) {
case HoldingTypes.Type.Asset:
return `${LocaleUtils.numberToLocaleString(amount)} ${name}`

View File

@ -22,8 +22,9 @@ QtObject {
property string symbol
property string description
property bool infiniteSupply: true
property int supply: 1
property int remainingTokens: supply
property string supply: "1"
property string remainingTokens: supply
property int multiplierIndex: 0
// Artwork related properties:
property url artworkSource

View File

@ -65,7 +65,7 @@ StackView {
readonly property bool isAdminOnly: root.isAdmin && !root.isPrivilegedTokenOwnerProfile
signal selectToken(string key, int amount, int type)
signal selectToken(string key, string amount, int type)
signal addAddresses(var addresses)
}

View File

@ -66,16 +66,16 @@ StackView {
signal mintOwnerToken(var ownerToken, var tMasterToken)
signal deployFeesRequested(int chainId, string accountAddress, int tokenType)
signal burnFeesRequested(string tokenKey, int amount, string accountAddress)
signal burnFeesRequested(string tokenKey, string amount, string accountAddress)
signal remotelyDestructFeesRequest(var remotelyDestructTokensList, // [key , amount]
string tokenKey,
string accountAddress)
signal remotelyDestructCollectibles(var remotelyDestructTokensList, // [key , amount]
string tokenKey,
string accountAddress)
signal signBurnTransactionOpened(string tokenKey, int amount, string accountAddress)
signal burnToken(string tokenKey, int amount, string accountAddress)
signal airdropToken(string tokenKey, int type, var addresses)
signal signBurnTransactionOpened(string tokenKey, string amount, string accountAddress)
signal burnToken(string tokenKey, string amount, string accountAddress)
signal airdropToken(string tokenKey, string amount, int type, var addresses)
signal deleteToken(string tokenKey)
function setFeeLoading() {
@ -281,6 +281,7 @@ StackView {
property TokenObject asset: TokenObject{
type: Constants.TokenType.ERC20
multiplierIndex: 18
}
property TokenObject collectible: TokenObject {
@ -520,11 +521,15 @@ StackView {
token: TokenObject {}
onGeneralAirdropRequested: {
root.airdropToken(view.airdropKey, view.token.type, []) // tokenKey instead when backend airdrop ready to use key instead of symbol
root.airdropToken(view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, []) // tokenKey instead when backend airdrop ready to use key instead of symbol
}
onAirdropRequested: {
root.airdropToken(view.airdropKey, view.token.type, [address]) // tokenKey instead when backend airdrop ready to use key instead of symbol
root.airdropToken(view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, [address]) // tokenKey instead when backend airdrop ready to use key instead of symbol
}
onRemoteDestructRequested: {
@ -609,15 +614,17 @@ StackView {
remotelyDestructVisible: token.remotelyDestruct
burnVisible: !token.infiniteSupply
onAirdropClicked: root.airdropToken(view.airdropKey, // tokenKey instead when backend airdrop ready to use key instead of symbol
view.token.type, [])
onAirdropClicked: root.airdropToken(
view.airdropKey,
"1" + "0".repeat(view.token.multiplierIndex),
view.token.type, [])
onRemotelyDestructClicked: remotelyDestructPopup.open()
onBurnClicked: burnTokensPopup.open()
// helper properties to pass data through popups
property var remotelyDestructTokensList
property int burnAmount
property string burnAmount
property string accountAddress
RemotelyDestructPopup {
@ -703,6 +710,7 @@ StackView {
communityName: root.communityName
tokenName: footer.token.name
remainingTokens: footer.token.remainingTokens
multiplierIndex: footer.token.multiplierIndex
tokenSource: footer.token.artworkSource
chainName: footer.token.chainName
@ -786,6 +794,7 @@ StackView {
token.burnState: model.burnState
token.remotelyDestructState: model.remotelyDestructState
token.accountAddress: model.accountAddress
token.multiplierIndex: model.multiplierIndex
}
onCountChanged: {

View File

@ -177,7 +177,11 @@ Control {
id: totalbox
label: qsTr("Total")
value: token.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(token.supply)
value: token.infiniteSupply
? d.infiniteSymbol
: LocaleUtils.numberToLocaleString(
StatusQUtils.AmountsArithmetic.toNumber(token.supply,
token.multiplierIndex))
isLoading: !token.infiniteSupply &&
((!root.isAssetPanel && token.remotelyDestructState === Constants.ContractTransactionStatus.InProgress) ||
(d.burnState === Constants.ContractTransactionStatus.InProgress))
@ -189,7 +193,11 @@ Control {
readonly property int remainingTokens: root.preview ? token.supply : token.remainingTokens
label: qsTr("Remaining")
value: token.infiniteSupply ? d.infiniteSymbol : LocaleUtils.numberToLocaleString(remainingTokens)
value: token.infiniteSupply
? d.infiniteSymbol
: LocaleUtils.numberToLocaleString(
StatusQUtils.AmountsArithmetic.toNumber(token.remainingTokens,
token.multiplierIndex))
isLoading: !token.infiniteSupply && (d.burnState === Constants.ContractTransactionStatus.InProgress)
}

View File

@ -25,7 +25,8 @@ StatusDialog {
property string communityName
property bool isAsset // If asset isAsset = true; if collectible --> isAsset = false
property string tokenName
property int remainingTokens
property string remainingTokens
property int multiplierIndex
property url tokenSource
property string chainName
@ -38,13 +39,20 @@ StatusDialog {
// Account expected roles: address, name, color, emoji, walletType
property var accounts
signal burnClicked(int burnAmount, string accountAddress)
signal burnClicked(string burnAmount, string accountAddress)
signal cancelClicked
signal burnFeesRequested(int burnAmount, string accountAddress)
signal burnFeesRequested(string burnAmount, string accountAddress)
QtObject {
id: d
readonly property real remainingTokensFloat:
SQUtils.AmountsArithmetic.toNumber(
root.remainingTokens, root.multiplierIndex)
readonly property string remainingTokensDisplayText:
LocaleUtils.numberToLocaleString(remainingTokensFloat)
property string accountAddress
property alias amountToBurn: amountToBurnInput.text
readonly property bool isFeeError: root.feeErrorText !== ""
@ -75,7 +83,7 @@ StatusDialog {
StatusBaseText {
Layout.fillWidth: true
text: qsTr("How many of %1s remaining %n %2 tokens would you like to burn?", "", root.remainingTokens).arg(root.communityName).arg(root.tokenName)
text: qsTr("How many of %1s remaining %n %2 tokens would you like to burn?", "", d.remainingTokensFloat).arg(root.communityName).arg(root.tokenName)
wrapMode: Text.WordWrap
lineHeight: 1.2
font.pixelSize: Style.current.primaryTextFontSize
@ -107,7 +115,19 @@ StatusDialog {
validationMode: StatusInput.ValidationMode.OnlyWhenDirty
validators: [
StatusValidator {
validate: (value) => { return (parseInt(value) > 0 && parseInt(value) <= root.remainingTokens) }
validate: (value) => {
const intAmount = parseInt(value)
if (!intAmount)
return false
const current = SQUtils.AmountsArithmetic.fromNumber(
intAmount, root.multiplierIndex)
const remaining = SQUtils.AmountsArithmetic.fromString(
root.remainingTokens)
return SQUtils.AmountsArithmetic.cmp(current, remaining) <= 0
}
errorMessage: qsTr("Exceeds available remaining")
},
StatusValidator {
@ -127,7 +147,7 @@ StatusDialog {
Layout.alignment: Qt.AlignTop
text: qsTr("All available remaining (%1)").arg(root.remainingTokens)
text: qsTr("All available remaining (%1)").arg(d.remainingTokensDisplayText)
font.pixelSize: Style.current.primaryTextFontSize
ButtonGroup.group: radioGroup
}
@ -173,10 +193,18 @@ StatusDialog {
interval: 500
onTriggered: {
if(specificAmountButton.checked)
root.burnFeesRequested(parseInt(amountToBurnInput.text), d.accountAddress)
else
if(specificAmountButton.checked) {
if (!amountToBurnInput.text)
return
root.burnFeesRequested(
SQUtils.AmountsArithmetic.fromNumber(
parseInt(amountToBurnInput.text),
root.multiplierIndex),
d.accountAddress)
} else {
root.burnFeesRequested(root.remainingTokens, d.accountAddress)
}
}
}
@ -193,7 +221,7 @@ StatusDialog {
header: StatusDialogHeader {
headline.title: qsTr("Burn %1 tokens").arg(root.tokenName)
headline.subtitle: qsTr("%n %1 remaining in smart contract", "", root.remainingTokens).arg(root.tokenName)
headline.subtitle: qsTr("%n %1 remaining in smart contract", "", d.remainingTokensFloat).arg(root.tokenName)
leftComponent: Rectangle {
height: 40
width: height
@ -236,10 +264,15 @@ StatusDialog {
text: qsTr("Burn tokens")
type: StatusBaseButton.Type.Danger
onClicked: {
if(specificAmountButton.checked)
root.burnClicked(parseInt(amountToBurnInput.text), d.accountAddress)
else
if(specificAmountButton.checked) {
root.burnClicked(
SQUtils.AmountsArithmetic.fromNumber(
parseInt(amountToBurnInput.text),
root.multiplierIndex),
d.accountAddress)
} else {
root.burnClicked(root.remainingTokens, d.accountAddress)
}
}
}
}

View File

@ -35,20 +35,21 @@ StatusDropdown {
property var usedEnsNames: []
property string assetKey: ""
property real assetAmount: 0
property string assetAmount: "0"
property int assetMultiplierIndex: 0
property string collectibleKey: ""
property real collectibleAmount: 1
property string collectibleAmount: "1"
property string ensDomainName: ""
property bool showTokenAmount: true
signal addAsset(string key, real amount)
signal addCollectible(string key, real amount)
signal addAsset(string key, string amount)
signal addCollectible(string key, string amount)
signal addEns(string domain)
signal updateAsset(string key, real amount)
signal updateCollectible(string key, real amount)
signal updateAsset(string key, string amount)
signal updateCollectible(string key, string amount)
signal updateEns(string domain)
signal removeClicked
@ -94,8 +95,10 @@ StatusDropdown {
]
readonly property var tabsModel: [qsTr("Assets"), qsTr("Collectibles"), qsTr("ENS")]
readonly property var tabsModelNoEns: [qsTr("Assets"), qsTr("Collectibles")]
readonly property bool assetsReady: root.assetAmount > 0 && root.assetKey
readonly property bool collectiblesReady: root.collectibleAmount > 0 && root.collectibleKey
readonly property bool assetsReady: root.assetAmount !== "0" && root.assetKey
readonly property bool collectiblesReady: root.collectibleAmount !== "0" && root.collectibleKey
readonly property bool ensReady: d.ensDomainNameValid
property int extendedDropdownType: ExtendedDropdownContent.Type.Assets
@ -139,8 +142,8 @@ StatusDropdown {
function setDefaultAmounts() {
d.assetAmountText = ""
d.collectibleAmountText = ""
root.assetAmount = 0
root.collectibleAmount = 1
root.assetAmount = "0"
root.collectibleAmount = "1"
}
function forceLayout() {
@ -388,7 +391,8 @@ StatusDropdown {
TokenPanel {
id: assetPanel
readonly property real effectiveAmount: amountValid ? amount : 0
readonly property string effectiveAmount: amountValid ? amount : "0"
property bool completed: false
tokenName: PermissionsHelpers.getTokenNameByKey(root.assetsModel, root.assetKey)
tokenShortName: PermissionsHelpers.getTokenShortNameByKey(root.assetsModel, root.assetKey)
@ -415,9 +419,10 @@ StatusDropdown {
return
append({
name:chainName,
name: chainName,
icon: chainIcon,
amount: asset.supply,
multiplierIndex: asset.multiplierIndex,
infiniteAmount: asset.infiniteSupply
})
@ -425,7 +430,12 @@ StatusDropdown {
}
}
onEffectiveAmountChanged: root.assetAmount = effectiveAmount
onEffectiveAmountChanged: {
if (completed)
root.assetAmount = effectiveAmount
}
onMultiplierIndexChanged: root.assetMultiplierIndex = multiplierIndex
onAmountTextChanged: d.assetAmountText = amountText
onAddClicked: root.addAsset(root.assetKey, root.assetAmount)
onUpdateClicked: root.updateAsset(root.assetKey, root.assetAmount)
@ -438,8 +448,11 @@ StatusDropdown {
}
Component.onCompleted: {
if (d.assetAmountText.length === 0 && root.assetAmount)
assetPanel.setAmount(root.assetAmount)
completed = true
if (d.assetAmountText.length === 0 && root.assetAmount !== "0")
assetPanel.setAmount(root.assetAmount,
root.assetMultiplierIndex)
}
}
}
@ -450,7 +463,8 @@ StatusDropdown {
TokenPanel {
id: collectiblePanel
readonly property real effectiveAmount: amountValid ? amount : 0
readonly property string effectiveAmount: amountValid ? amount : "0"
property bool completed: false
tokenName: PermissionsHelpers.getTokenNameByKey(root.collectiblesModel, root.collectibleKey)
tokenShortName: ""
@ -482,6 +496,7 @@ StatusDropdown {
name:chainName,
icon: chainIcon,
amount: collectible.supply,
multiplierIndex: collectible.multiplierIndex,
infiniteAmount: collectible.infiniteSupply
})
@ -489,13 +504,19 @@ StatusDropdown {
}
}
onEffectiveAmountChanged: root.collectibleAmount = effectiveAmount
onEffectiveAmountChanged: {
if (completed)
root.collectibleAmount = effectiveAmount
}
onAmountTextChanged: d.collectibleAmountText = amountText
onAddClicked: root.addCollectible(root.collectibleKey, root.collectibleAmount)
onUpdateClicked: root.updateCollectible(root.collectibleKey, root.collectibleAmount)
onRemoveClicked: root.removeClicked()
Component.onCompleted: {
completed = true
if (d.collectibleAmountText.length === 0 && root.collectibleAmount)
collectiblePanel.setAmount(root.collectibleAmount)
}

View File

@ -403,8 +403,8 @@ StatusSectionLayout {
onAirdropToken: {
root.goTo(Constants.CommunitySettingsSections.Airdrops)
// Force a token selection to be airdroped with default amount 1
airdropPanel.selectToken(tokenKey, 1, type)
// Force a token selection to be airdroped with given amount
airdropPanel.selectToken(tokenKey, amount, type)
// Set given addresses as recipients
airdropPanel.addAddresses(addresses)

View File

@ -87,11 +87,9 @@ StatusScrollView {
function selectToken(key, amount, type) {
if(selectedHoldingsModel)
selectedHoldingsModel.clear()
var tokenModel = null
if(type === Constants.TokenType.ERC20)
tokenModel = root.assetsModel
else if (type === Constants.TokenType.ERC721)
tokenModel = root.collectiblesModel
const tokenModel = type === Constants.TokenType.ERC20 ?
root.assetsModel : root.collectiblesModel
const modelItem = PermissionsHelpers.getTokenByKey(
tokenModel, key)
@ -178,22 +176,23 @@ StatusScrollView {
onTotalRevisionChanged: Qt.callLater(() => d.resetFees())
function prepareEntry(key, amount, type) {
let tokenModel = null
if(type === Constants.TokenType.ERC20)
tokenModel = root.assetsModel
else if (type === Constants.TokenType.ERC721)
tokenModel = root.collectiblesModel
const tokenModel = type === Constants.TokenType.ERC20
? root.assetsModel : root.collectiblesModel
const modelItem = PermissionsHelpers.getTokenByKey(
tokenModel, key)
const multiplierIndex = modelItem.multiplierIndex
const amountNumber = AmountsArithmetic.toNumber(
amount, multiplierIndex)
const amountLocalized = LocaleUtils.numberToLocaleString(
amountNumber, -1)
return {
key, amount, type,
tokenText: amount + " " + modelItem.name,
tokenText: amountLocalized + " " + modelItem.name,
tokenImage: modelItem.iconSource,
networkText: modelItem.chainName,
networkImage: Style.svg(modelItem.chainIcon),
supply: modelItem.supply,
multiplierIndex: modelItem.multiplierIndex,
infiniteSupply: modelItem.infiniteSupply,
contractUniqueKey: modelItem.contractUniqueKey,
accountName: modelItem.accountName,
@ -278,7 +277,13 @@ StatusScrollView {
if (!item || item.infiniteSupply)
continue
min = Math.min(item.supply / item.amount, min)
const dividient = AmountsArithmetic.fromString(item.supply)
const divisor = AmountsArithmetic.fromString(item.amount)
const quotient = AmountsArithmetic.toNumber(
AmountsArithmetic.div(dividient, divisor))
min = Math.min(quotient, min)
}
infinity = min === Number.MAX_SAFE_INTEGER
@ -286,12 +291,24 @@ StatusScrollView {
}
delegate: QtObject {
readonly property int supply: model.supply
readonly property real amount: model.amount
readonly property string supply: model.supply
readonly property string amount: model.amount
readonly property bool infiniteSupply: model.infiniteSupply
readonly property bool valid:
infiniteSupply || amount * airdropRecipientsSelector.count <= supply
readonly property bool valid: {
if (infiniteSupply)
return true
const recipientsCount = airdropRecipientsSelector.count
const demand = AmountsArithmetic.times(
AmountsArithmetic.fromString(amount),
recipientsCount)
const available = AmountsArithmetic.fromString(supply)
return AmountsArithmetic.cmp(demand, available) <= 0
}
onSupplyChanged: recipientsCountInstantiator.findRecipientsCount()
onAmountChanged: recipientsCountInstantiator.findRecipientsCount()
@ -426,6 +443,7 @@ StatusScrollView {
case HoldingTypes.Type.Asset:
dropdown.assetKey = modelItem.key
dropdown.assetAmount = modelItem.amount
dropdown.assetMultiplierIndex = modelItem.multiplierIndex
dropdown.setActiveTab(HoldingTypes.Type.Asset)
break
case HoldingTypes.Type.Collectible:

View File

@ -254,7 +254,13 @@ StatusScrollView {
visible: !unlimitedSupplyChecker.checked
label: qsTr("Total finite supply")
text: root.isAssetView ? asset.supply : collectible.supply
text: {
const token = root.isAssetView ? root.asset : root.collectible
return SQUtils.AmountsArithmetic.toNumber(token.supply,
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)") :
@ -264,14 +270,13 @@ StatusScrollView {
extraValidator.errorMessage: qsTr("Enter a number between 1 and 999,999,999")
onTextChanged: {
const amount = parseInt(text)
if (Number.isNaN(amount) || Object.values(errors).length)
const supplyNumber = parseInt(text)
if (Number.isNaN(supplyNumber) || Object.values(errors).length)
return
if(root.isAssetView)
asset.supply = amount
else
collectible.supply = amount
const token = root.isAssetView ? root.asset : root.collectible
token.supply = SQUtils.AmountsArithmetic.fromNumber(
supplyNumber, token.multiplierIndex).toFixed(0)
}
}

View File

@ -49,7 +49,7 @@ StatusScrollView {
symbol: PermissionsHelpers.communityNameToSymbol(true, root.communityName)
transferable: true
remotelyDestruct: false
supply: 1
supply: "1"
infiniteSupply: false
description: qsTr("This is the %1 Owner token. The hodler of this collectible has ultimate control over %1 Community token administration.").arg(root.communityName)
}

View File

@ -172,7 +172,7 @@ StatusScrollView {
onPermissionTypeChanged: Qt.callLater(() => d.loadInitValues())
contentWidth: mainLayout.width
contentHeight: mainLayout.height
SequenceColumnLayout {
id: mainLayout
@ -242,7 +242,7 @@ StatusScrollView {
const key = item.key
d.dirtyValues.selectedHoldingsModel.append(
{ type, key, amount })
{ type, key, amount: parseFloat(amount) })
}
function prepareUpdateIndex(key) {
@ -270,6 +270,7 @@ StatusScrollView {
onAddAsset: {
const modelItem = PermissionsHelpers.getTokenByKey(
root.assetsModel, key)
addItem(HoldingTypes.Type.Asset, modelItem, amount)
dropdown.close()
}
@ -277,6 +278,7 @@ StatusScrollView {
onAddCollectible: {
const modelItem = PermissionsHelpers.getTokenByKey(
root.collectiblesModel, key)
addItem(HoldingTypes.Type.Collectible, modelItem, amount)
dropdown.close()
}
@ -292,7 +294,7 @@ StatusScrollView {
const modelItem = PermissionsHelpers.getTokenByKey(root.assetsModel, key)
d.dirtyValues.selectedHoldingsModel.set(
itemIndex, { type: HoldingTypes.Type.Asset, key, amount })
itemIndex, { type: HoldingTypes.Type.Asset, key, amount: parseFloat(amount) })
dropdown.close()
}
@ -303,7 +305,7 @@ StatusScrollView {
d.dirtyValues.selectedHoldingsModel.set(
itemIndex,
{ type: HoldingTypes.Type.Collectible, key, amount })
{ type: HoldingTypes.Type.Collectible, key, amount: parseFloat(amount) })
dropdown.close()
}

View File

@ -161,7 +161,7 @@ StatusScrollView {
id: assetsList
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Layout.preferredHeight: contentHeight
visible: count > 0
model: assetsModel

View File

@ -3,6 +3,7 @@ import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import utils 1.0
@ -13,11 +14,13 @@ Input {
property var locale: Qt.locale()
readonly property alias amount: d.amount
property alias multiplierIndex: d.multiplierIndex
readonly property bool valid: validationError.length === 0
property bool allowDecimals: true
property bool validateMaximumAmount: false
property real maximumAmount: 0
property string maximumAmount: "0"
validationErrorTopMargin: 8
fontPixelSize: 13
@ -27,18 +30,27 @@ Input {
textField.rightPadding: labelText.implicitWidth + labelText.anchors.rightMargin
+ textField.leftPadding
function setAmount(amount) {
root.text = LocaleUtils.numberToLocaleString(amount, -1, root.locale)
function setAmount(amount, multiplierIndex = 0) {
console.assert(typeof amount === "string")
d.multiplierIndex = multiplierIndex
const amountNumber = SQUtils.AmountsArithmetic.toNumber(
amount, multiplierIndex)
root.text = LocaleUtils.numberToLocaleString(amountNumber, -1,
root.locale)
}
onTextChanged: d.validate()
onValidateMaximumAmountChanged: d.validate()
onMaximumAmountChanged: d.validate()
onMultiplierIndexChanged: d.validate()
QtObject {
id: d
property real amount: 0
property string amount: "0"
property int multiplierIndex: 0
function getEffectiveDigitsCount(str) {
const digits = LocaleUtils.getLocalizedDigitsCount(text, root.locale)
@ -50,7 +62,7 @@ Input {
root.text = root.text.replace(root.locale.decimalPoint, "")
if(root.text.length === 0) {
d.amount = 0
d.amount = "0"
root.validationError = ""
return
}
@ -60,16 +72,38 @@ Input {
return
}
const amount = LocaleUtils.numberFromLocaleString(root.text, root.locale)
if (isNaN(amount)) {
d.amount = 0
const amountNumber = LocaleUtils.numberFromLocaleString(root.text, root.locale)
if (isNaN(amountNumber)) {
d.amount = "0"
root.validationError = qsTr("Invalid amount format")
} else if (root.validateMaximumAmount && amount > root.maximumAmount) {
root.validationError = qsTr("Amount exceeds balance")
} else {
d.amount = amount
root.validationError = ""
return
}
const amount = SQUtils.AmountsArithmetic.fromNumber(
amountNumber, d.multiplierIndex)
if (root.validateMaximumAmount) {
const maximumAmount = SQUtils.AmountsArithmetic.fromString(
root.maximumAmount)
const maxExceeded = SQUtils.AmountsArithmetic.cmp(
amount, maximumAmount) === 1
if (SQUtils.AmountsArithmetic.cmp(amount, maximumAmount) === 1) {
root.validationError = qsTr("Amount exceeds balance")
return
}
}
// Fallback to handle float amounts for permissions
// As a target amount should be always integer number
if (!Number.isInteger(amountNumber) && d.multiplierIndex === 0) {
d.amount = amount.toString()
} else {
d.amount = amount.toFixed(0)
}
root.validationError = ""
}
}

View File

@ -26,10 +26,10 @@ QtObject {
// Minting tokens:
function deployCollectible(communityId, collectibleItem)
{
if (collectibleItem.key !== "") {
{
if (collectibleItem.key !== "")
deleteToken(communityId, collectibleItem.key)
}
const jsonArtworkFile = Utils.getImageAndCropInfoJson(collectibleItem.artworkSource, collectibleItem.artworkCropRect)
communityTokensModuleInst.deployCollectible(communityId, collectibleItem.accountAddress, collectibleItem.name,
collectibleItem.symbol, collectibleItem.description, collectibleItem.supply,
@ -39,9 +39,9 @@ QtObject {
function deployAsset(communityId, assetItem)
{
if (assetItem.key !== "") {
if (assetItem.key !== "")
deleteToken(communityId, assetItem.key)
}
const jsonArtworkFile = Utils.getImageAndCropInfoJson(assetItem.artworkSource, assetItem.artworkCropRect)
communityTokensModuleInst.deployAssets(communityId, assetItem.accountAddress, assetItem.name,
assetItem.symbol, assetItem.description, assetItem.supply,
@ -120,11 +120,13 @@ QtObject {
// Burn:
function computeBurnFee(tokenKey, amount, accountAddress) {
console.assert(typeof amount === "string")
// TODO: Backend. It should include the account address in the calculation.
communityTokensModuleInst.computeBurnFee(tokenKey, amount/*, accountAddress*/)
}
function burnToken(communityId, tokenKey, burnAmount, accountAddress) {
console.assert(typeof burnAmount === "string")
// TODO: Backend. It should include the account address in the burn action.
communityTokensModuleInst.burnTokens(communityId, tokenKey, burnAmount/*, accountAddress*/)
}