chore(Communities): Refactor amounts handling for displaying, minting, airdropping and burning
Closes: #11491
This commit is contained in:
parent
f3526d6e01
commit
1c50ec17a8
|
@ -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.} =
|
||||
|
|
|
@ -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]})
|
||||
"""
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ""
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 %1’s remaining %n %2 tokens would you like to burn?", "", root.remainingTokens).arg(root.communityName).arg(root.tokenName)
|
||||
text: qsTr("How many of %1’s 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ StatusScrollView {
|
|||
id: assetsList
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: childrenRect.height
|
||||
Layout.preferredHeight: contentHeight
|
||||
|
||||
visible: count > 0
|
||||
model: assetsModel
|
||||
|
|
|
@ -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 = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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*/)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue