feat(@desktop/communities): Add burning collectibles functionality

Burn collectibles.
Displaying burning fee.
Displaying remaining supply.

Issue #10816
This commit is contained in:
Michal Iskierko 2023-06-06 14:54:35 +02:00 committed by Michał Iskierko
parent c693e4e273
commit d57485c6e9
18 changed files with 361 additions and 44 deletions

View File

@ -48,6 +48,9 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_COMPUTE_SELF_DESTRUCT_FEE) do(e:Args):
let args = ComputeFeeArgs(e)
self.communityTokensModule.onSelfDestructFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode)
self.events.on(SIGNAL_COMPUTE_BURN_FEE) do(e:Args):
let args = ComputeFeeArgs(e)
self.communityTokensModule.onBurnFeeComputed(args.ethCurrency, args.fiatCurrency, args.errorCode)
self.events.on(SIGNAL_COMPUTE_AIRDROP_FEE) do(e:Args):
let args = AirdropFeesArgs(e)
self.communityTokensModule.onAirdropFeesComputed(args)
@ -60,6 +63,9 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_REMOTE_DESTRUCT_STATUS) do(e: Args):
let args = RemoteDestructArgs(e)
self.communityTokensModule.onRemoteDestructStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status)
self.events.on(SIGNAL_BURN_STATUS) do(e: Args):
let args = RemoteDestructArgs(e)
self.communityTokensModule.onBurnStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status)
self.events.on(SIGNAL_AIRDROP_STATUS) do(e: Args):
let args = AirdropArgs(e)
self.communityTokensModule.onAirdropStateChanged(args.communityToken.communityId, args.communityToken.name, args.communityToken.chainId, args.transactionHash, args.status)
@ -76,6 +82,9 @@ proc computeAirdropCollectiblesFee*(self: Controller, collectiblesAndAmounts: se
proc selfDestructCollectibles*(self: Controller, communityId: string, password: string, walletAndAmounts: seq[WalletAndAmount], contractUniqueKey: string) =
self.communityTokensService.selfDestructCollectibles(communityId, password, walletAndAmounts, contractUniqueKey)
proc burnCollectibles*(self: Controller, communityId: string, password: string, contractUniqueKey: string, amount: int) =
self.communityTokensService.burnCollectibles(communityId, password, contractUniqueKey, amount)
proc authenticateUser*(self: Controller, keyUid = "") =
let data = SharedKeycarModuleAuthenticationArgs(uniqueIdentifier: UNIQUE_DEPLOY_COLLECTIBLES_COMMUNITY_TOKENS_MODULE_IDENTIFIER, keyUid: keyUid)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER, data)
@ -92,5 +101,8 @@ proc computeSelfDestructFee*(self: Controller, walletAndAmountList: seq[WalletAn
proc findContractByUniqueId*(self: Controller, contractUniqueKey: string): CommunityTokenDto =
return self.communityTokensService.findContractByUniqueId(contractUniqueKey)
proc computeBurnFee*(self: Controller, contractUniqueKey: string, amount: int) =
self.communityTokensService.computeBurnFee(contractUniqueKey, amount)
proc getNetwork*(self:Controller, chainId: int): NetworkDto =
self.networksService.getNetwork(chainId)

View File

@ -19,6 +19,9 @@ method computeAirdropCollectiblesFee*(self: AccessInterface, communityId: string
method selfDestructCollectibles*(self: AccessInterface, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.base.} =
raise newException(ValueError, "No implementation available")
method burnCollectibles*(self: AccessInterface, communityId: string, contractUniqueKey: string, amount: int) {.base.} =
raise newException(ValueError, "No implementation available")
method deployCollectible*(self: AccessInterface, communityId: string, address: string, name: string, symbol: string, description: string, supply: int, infiniteSupply: bool, transferable: bool,
selfDestruct: bool, chainId: int, image: string) {.base.} =
raise newException(ValueError, "No implementation available")
@ -35,6 +38,9 @@ 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: int) {.base.} =
raise newException(ValueError, "No implementation available")
method onDeployFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode) {.base.} =
raise newException(ValueError, "No implementation available")
@ -44,11 +50,17 @@ method onSelfDestructFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAm
method onAirdropFeesComputed*(self: AccessInterface, args: AirdropFeesArgs) {.base.} =
raise newException(ValueError, "No implementation available")
method onBurnFeeComputed*(self: AccessInterface, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) {.base.} =
raise newException(ValueError, "No implementation available")
method onRemoteDestructStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} =
raise newException(ValueError, "No implementation available")
method onBurnStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} =
raise newException(ValueError, "No implementation available")
method onAirdropStateChanged*(self: AccessInterface, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -14,19 +14,22 @@ type
chainName*: string
chainIcon*: string
accountName*: string
remainingSupply*: int
tokenOwnersModel*: token_owners_model.TokenOwnersModel
proc initTokenItem*(
tokenDto: CommunityTokenDto,
network: NetworkDto,
tokenOwners: seq[CollectibleOwner],
accountName: string
accountName: string,
remainingSupply: int
): TokenItem =
result.tokenDto = tokenDto
if network != nil:
result.chainName = network.chainName
result.chainIcon = network.iconURL
result.accountName = accountName
result.remainingSupply = remainingSupply
result.tokenOwnersModel = newTokenOwnersModel()
result.tokenOwnersModel.setItems(tokenOwners.map(proc(owner: CollectibleOwner): TokenOwnersItem =
# TODO find member with the address - later when airdrop to member will be added
@ -38,6 +41,7 @@ proc `$`*(self: TokenItem): string =
tokenDto: {self.tokenDto},
chainName: {self.chainName},
chainIcon: {self.chainIcon},
remainingSupply: {self.remainingSupply},
tokenOwnersModel: {self.tokenOwnersModel}
]"""

View File

@ -25,6 +25,7 @@ type
ChainIcon
TokenOwnersModel
AccountName
RemainingSupply
QtObject:
type TokenModel* = ref object of QAbstractListModel
@ -50,6 +51,26 @@ QtObject:
self.dataChanged(index, index, @[ModelRole.DeployState.int])
return
proc updateSupply*(self: TokenModel, chainId: int, contractAddress: string, supply: int) =
for i in 0 ..< self.items.len:
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)):
if self.items[i].tokenDto.supply != supply:
self.items[i].tokenDto.supply = supply
let index = self.createIndex(i, 0, nil)
defer: index.delete
self.dataChanged(index, index, @[ModelRole.Supply.int])
return
proc updateRemainingSupply*(self: TokenModel, chainId: int, contractAddress: string, remainingSupply: int) =
for i in 0 ..< self.items.len:
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)):
if self.items[i].remainingSupply != remainingSupply:
self.items[i].remainingSupply = remainingSupply
let index = self.createIndex(i, 0, nil)
defer: index.delete
self.dataChanged(index, index, @[ModelRole.RemainingSupply.int])
return
proc setCommunityTokenOwners*(self: TokenModel, chainId: int, contractAddress: string, owners: seq[CollectibleOwner]) =
for i in 0 ..< self.items.len:
if((self.items[i].tokenDto.address == contractAddress) and (self.items[i].tokenDto.chainId == chainId)):
@ -108,6 +129,7 @@ QtObject:
ModelRole.ChainIcon.int:"chainIcon",
ModelRole.TokenOwnersModel.int:"tokenOwnersModel",
ModelRole.AccountName.int:"accountName",
ModelRole.RemainingSupply.int:"remainingSupply",
}.toTable
method data(self: TokenModel, index: QModelIndex, role: int): QVariant =
@ -152,6 +174,8 @@ QtObject:
result = newQVariant(item.tokenOwnersModel)
of ModelRole.AccountName:
result = newQVariant(item.accountName)
of ModelRole.RemainingSupply:
result = newQVariant(item.remainingSupply)
proc `$`*(self: TokenModel): string =
for i in 0 ..< self.items.len:

View File

@ -19,6 +19,7 @@ type
Deploy = 1
Airdrop = 2
SelfDestruct = 3
Burn = 4
type
Module* = ref object of io_interface.AccessInterface
@ -37,6 +38,7 @@ type
tempWalletAddresses: seq[string]
tempContractAction: ContractAction
tempContractUniqueKey: string
tempAmount: int
proc newCommunityTokensModule*(
parent: parent_interface.AccessInterface,
@ -121,6 +123,13 @@ method selfDestructCollectibles*(self: Module, communityId: string, collectibles
self.tempContractAction = ContractAction.SelfDestruct
self.authenticate()
method burnCollectibles*(self: Module, communityId: string, contractUniqueKey: string, amount: int) =
self.tempCommunityId = communityId
self.tempContractUniqueKey = contractUniqueKey
self.tempAmount = amount
self.tempContractAction = ContractAction.Burn
self.authenticate()
method deployCollectible*(self: Module, communityId: string, fromAddress: string, name: string, symbol: string, description: string,
supply: int, infiniteSupply: bool, transferable: bool, selfDestruct: bool, chainId: int, image: string) =
self.tempAddressFrom = fromAddress
@ -150,6 +159,8 @@ method onUserAuthenticated*(self: Module, password: string) =
self.controller.airdropCollectibles(self.tempCommunityId, password, self.tempTokenAndAmountList, self.tempWalletAddresses)
elif self.tempContractAction == ContractAction.SelfDestruct:
self.controller.selfDestructCollectibles(self.tempCommunityId, password, self.tempWalletAndAmountList, self.tempContractUniqueKey)
elif self.tempContractAction == ContractAction.Burn:
self.controller.burnCollectibles(self.tempCommunityId, password, self.tempContractUniqueKey, self.tempAmount)
method onDeployFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode) =
self.view.updateDeployFee(ethCurrency, fiatCurrency, errorCode.int)
@ -160,6 +171,9 @@ method onSelfDestructFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fia
method onAirdropFeesComputed*(self: Module, args: AirdropFeesArgs) =
self.view.updateAirdropFees(%args)
method onBurnFeeComputed*(self: Module, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: ComputeFeeErrorCode) =
self.view.updateBurnFee(ethCurrency, fiatCurrency, errorCode.int)
method computeDeployFee*(self: Module, chainId: int, accountAddress: string) =
self.controller.computeDeployFee(chainId, accountAddress)
@ -167,17 +181,30 @@ method computeSelfDestructFee*(self: Module, collectiblesToBurnJsonString: strin
let walletAndAmountList = self.getWalletAndAmountListFromJson(collectiblesToBurnJsonString)
self.controller.computeSelfDestructFee(walletAndAmountList, contractUniqueKey)
method onCommunityTokenDeployStateChanged*(self: Module, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) =
method computeBurnFee*(self: Module, contractUniqueKey: string, amount: int) =
self.controller.computeBurnFee(contractUniqueKey, amount)
proc createUrl(self: Module, chainId: int, transactionHash: string): string =
let network = self.controller.getNetwork(chainId)
let url = if network != nil: network.blockExplorerURL & "/tx/" & transactionHash else: ""
result = if network != nil: network.blockExplorerURL & "/tx/" & transactionHash else: ""
proc getChainName(self: Module, chainId: int): string =
let network = self.controller.getNetwork(chainId)
result = if network != nil: network.chainName else: ""
method onCommunityTokenDeployStateChanged*(self: Module, communityId: string, chainId: int, transactionHash: string, deployState: DeployState) =
let url = self.createUrl(chainId, transactionHash)
self.view.emitDeploymentStateChanged(communityId, deployState.int, url)
method onRemoteDestructStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) =
let network = self.controller.getNetwork(chainId)
let url = if network != nil: network.blockExplorerURL & "/tx/" & transactionHash else: ""
let url = self.createUrl(chainId, transactionHash)
self.view.emitRemoteDestructStateChanged(communityId, tokenName, status.int, url)
method onBurnStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) =
let url = self.createUrl(chainId, transactionHash)
self.view.emitBurnStateChanged(communityId, tokenName, status.int, url)
method onAirdropStateChanged*(self: Module, communityId: string, tokenName: string, chainId: int, transactionHash: string, status: ContractTransactionStatus) =
let network = self.controller.getNetwork(chainId)
let url = if network != nil: network.blockExplorerURL & "/tx/" & transactionHash else: ""
self.view.emitAirdropStateChanged(communityId, tokenName, status.int, url)
let url = self.createUrl(chainId, transactionHash)
let chainName = self.getChainName(chainId)
self.view.emitAirdropStateChanged(communityId, tokenName, chainName, status.int, url)

View File

@ -31,9 +31,13 @@ QtObject:
proc selfDestructCollectibles*(self: View, communityId: string, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.slot.} =
self.communityTokensModule.selfDestructCollectibles(communityId, collectiblesToBurnJsonString, contractUniqueKey)
proc burnCollectibles*(self: View, communityId: string, contractUniqueKey: string, amount: int) {.slot.} =
self.communityTokensModule.burnCollectibles(communityId, contractUniqueKey, amount)
proc deployFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int) {.signal.}
proc selfDestructFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int) {.signal.}
proc airdropFeesUpdated*(self: View, json: string) {.signal.}
proc burnFeeUpdated*(self: View, ethCurrency: QVariant, fiatCurrency: QVariant, errorCode: int) {.signal.}
proc computeDeployFee*(self: View, chainId: int, accountAddress: string) {.slot.} =
self.communityTokensModule.computeDeployFee(chainId, accountAddress)
@ -41,9 +45,15 @@ QtObject:
proc computeSelfDestructFee*(self: View, collectiblesToBurnJsonString: string, contractUniqueKey: string) {.slot.} =
self.communityTokensModule.computeSelfDestructFee(collectiblesToBurnJsonString, contractUniqueKey)
proc computeBurnFee*(self: View, contractUniqueKey: string, amount: int) {.slot.} =
self.communityTokensModule.computeBurnFee(contractUniqueKey, amount)
proc updateDeployFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int) =
self.deployFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode)
proc updateBurnFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int) =
self.burnFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode)
proc updateSelfDestructFee*(self: View, ethCurrency: CurrencyAmount, fiatCurrency: CurrencyAmount, errorCode: int) =
self.selfDestructFeeUpdated(newQVariant(ethCurrency), newQVariant(fiatCurrency), errorCode)
@ -58,6 +68,10 @@ QtObject:
proc emitRemoteDestructStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) =
self.remoteDestructStateChanged(communityId, tokenName, status, url)
proc airdropStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) {.signal.}
proc emitAirdropStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) =
self.airdropStateChanged(communityId, tokenName, status, url)
proc burnStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) {.signal.}
proc emitBurnStateChanged*(self: View, communityId: string, tokenName: string, status: int, url: string) =
self.burnStateChanged(communityId, tokenName, status, url)
proc airdropStateChanged*(self: View, communityId: string, tokenName: string, chainName: string, status: int, url: string) {.signal.}
proc emitAirdropStateChanged*(self: View, communityId: string, tokenName: string, chainName: string, status: int, url: string) =
self.airdropStateChanged(communityId, tokenName, chainName, status, url)

View File

@ -57,6 +57,7 @@ type
# Forward declaration
proc setActiveSection*(self: Controller, sectionId: string, skipSavingInSettings: bool = false)
proc getRemainingSupply*(self: Controller, chainId: int, contractAddress: string): int
proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter,
@ -343,6 +344,18 @@ proc init*(self: Controller) =
let args = CommunityTokenDeployedStatusArgs(e)
self.delegate.onCommunityTokenDeployStateChanged(args.communityId, args.chainId, args.contractAddress, args.deployState)
self.events.on(SIGNAL_BURN_STATUS) do(e: Args):
let args = RemoteDestructArgs(e)
let communityToken = args.communityToken
self.delegate.onCommunityTokenSupplyChanged(communityToken.communityId, communityToken.chainId,
communityToken.address, communityToken.supply, self.getRemainingSupply(communityToken.chainId, communityToken.address))
self.events.on(SIGNAL_AIRDROP_STATUS) do(e: Args):
let args = AirdropArgs(e)
let communityToken = args.communityToken
self.delegate.onCommunityTokenSupplyChanged(communityToken.communityId, communityToken.chainId,
communityToken.address, communityToken.supply, self.getRemainingSupply(communityToken.chainId, communityToken.address))
self.events.on(SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED) do(e: Args):
let args = CommunityTokenOwnersArgs(e)
self.delegate.onCommunityTokenOwnersFetched(args.communityId, args.chainId, args.contractAddress, args.owners)
@ -477,6 +490,9 @@ proc getCommunityTokenOwners*(self: Controller, communityId: string, chainId: in
proc getCommunityTokenOwnerName*(self: Controller, chainId: int, contractAddress: string): string =
return self.communityTokensService.contractOwnerName(chainId, contractAddress)
proc getRemainingSupply*(self: Controller, chainId: int, contractAddress: string): int =
return self.communityTokensService.getRemainingSupply(chainId, contractAddress)
proc getNetwork*(self:Controller, chainId: int): NetworkDto =
self.networksService.getNetwork(chainId)

View File

@ -300,6 +300,9 @@ method onCommunityTokenOwnersFetched*(self: AccessInterface, communityId: string
method onCommunityTokenDeployStateChanged*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, deployState: DeployState) {.base.} =
raise newException(ValueError, "No implementation available")
method onCommunityTokenSupplyChanged*(self: AccessInterface, communityId: string, chainId: int, contractAddress: string, supply: int, remainingSupply: int) {.base.} =
raise newException(ValueError, "No implementation available")
method onAcceptRequestToJoinFailed*(self: AccessInterface, communityId: string, memberKey: string, requestId: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -238,7 +238,8 @@ proc createTokenItem[T](self: Module[T], tokenDto: CommunityTokenDto) : TokenIte
let network = self.controller.getNetwork(tokenDto.chainId)
let tokenOwners = self.controller.getCommunityTokenOwners(tokenDto.communityId, tokenDto.chainId, tokenDto.address)
let ownerAddressName = self.controller.getCommunityTokenOwnerName(tokenDto.chainId, tokenDto.address)
result = initTokenItem(tokenDto, network, tokenOwners, ownerAddressName)
let remainingSupply = if tokenDto.infiniteSupply: 0 else: self.controller.getRemainingSupply(tokenDto.chainId, tokenDto.address)
result = initTokenItem(tokenDto, network, tokenOwners, ownerAddressName, remainingSupply)
proc createChannelGroupItem[T](self: Module[T], channelGroup: ChannelGroupDto): SectionItem =
let isCommunity = channelGroup.channelGroupType == ChannelGroupType.Community
@ -1008,6 +1009,12 @@ method onCommunityTokenDeployStateChanged*[T](self: Module[T], communityId: stri
if item.id != "":
item.updateCommunityTokenDeployState(chainId, contractAddress, deployState)
method onCommunityTokenSupplyChanged*[T](self: Module[T], communityId: string, chainId: int, contractAddress: string, supply: int, remainingSupply: int) =
let item = self.view.model().getItemById(communityId)
if item.id != "":
item.updateCommunityTokenSupply(chainId, contractAddress, supply)
item.updateCommunityRemainingSupply(chainId, contractAddress, remainingSupply)
method onAcceptRequestToJoinLoading*[T](self: Module[T], communityId: string, memberKey: string) =
let item = self.view.model().getItemById(communityId)
if item.id != "":

View File

@ -328,6 +328,12 @@ proc appendCommunityToken*(self: SectionItem, item: TokenItem) {.inline.} =
proc updateCommunityTokenDeployState*(self: SectionItem, chainId: int, contractAddress: string, deployState: DeployState) {.inline.} =
self.communityTokensModel.updateDeployState(chainId, contractAddress, deployState)
proc updateCommunityTokenSupply*(self: SectionItem, chainId: int, contractAddress: string, supply: int) {.inline.} =
self.communityTokensModel.updateSupply(chainId, contractAddress, supply)
proc updateCommunityRemainingSupply*(self: SectionItem, chainId: int, contractAddress: string, remainingSupply: int) {.inline.} =
self.communityTokensModel.updateRemainingSupply(chainId, contractAddress, remainingSupply)
proc setCommunityTokenOwners*(self: SectionItem, chainId: int, contractAddress: string, owners: seq[CollectibleOwner]) {.inline.} =
self.communityTokensModel.setCommunityTokenOwners(chainId, contractAddress, owners)

View File

@ -40,18 +40,42 @@ const asyncGetDeployFeesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.
})
type
AsyncGetBurnFees = ref object of QObjectTaskArg
AsyncGetRemoteBurnFees = ref object of QObjectTaskArg
chainId: int
contractAddress: string
tokenIds: seq[UInt256]
const asyncGetRemoteBurnFeesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncGetRemoteBurnFees](argEncoded)
try:
var gasTable: Table[ContractTuple, int] # gas per contract
var feeTable: Table[int, SuggestedFeesDto] # fees for chain
let fee = eth.suggestedFees(arg.chainId).result.toSuggestedFeesDto()
let burnGas = community_tokens.estimateRemoteBurn(arg.chainId, arg.contractAddress, arg.tokenIds).result.getInt
feeTable[arg.chainId] = fee
gasTable[(arg.chainId, arg.contractAddress)] = burnGas
arg.finish(%* {
"feeTable": tableToJsonArray(feeTable),
"gasTable": tableToJsonArray(gasTable),
"error": "" })
except Exception as e:
arg.finish(%* {
"error": e.msg,
})
type
AsyncGetBurnFees = ref object of QObjectTaskArg
chainId: int
contractAddress: string
amount: int
const asyncGetBurnFeesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncGetBurnFees](argEncoded)
try:
var gasTable: Table[ContractTuple, int] # gas per contract
var feeTable: Table[int, SuggestedFeesDto] # fees for chain
let fee = eth.suggestedFees(arg.chainId).result.toSuggestedFeesDto()
let burnGas = community_tokens.estimateRemoteBurn(arg.chainId, arg.contractAddress, arg.tokenIds).result.getInt
let burnGas = community_tokens.estimateBurn(arg.chainId, arg.contractAddress, arg.amount).result.getInt
feeTable[arg.chainId] = fee
gasTable[(arg.chainId, arg.contractAddress)] = burnGas
arg.finish(%* {

View File

@ -149,9 +149,11 @@ const SIGNAL_COMMUNITY_TOKEN_DEPLOY_STATUS* = "communityTokenDeployStatus"
const SIGNAL_COMMUNITY_TOKEN_DEPLOYED* = "communityTokenDeployed"
const SIGNAL_COMPUTE_DEPLOY_FEE* = "computeDeployFee"
const SIGNAL_COMPUTE_SELF_DESTRUCT_FEE* = "computeSelfDestructFee"
const SIGNAL_COMPUTE_BURN_FEE* = "computeBurnFee"
const SIGNAL_COMPUTE_AIRDROP_FEE* = "computeAirdropFee"
const SIGNAL_COMMUNITY_TOKEN_OWNERS_FETCHED* = "communityTokenOwnersFetched"
const SIGNAL_REMOTE_DESTRUCT_STATUS* = "communityTokenRemoteDestructStatus"
const SIGNAL_BURN_STATUS* = "communityTokenBurnStatus"
const SIGNAL_AIRDROP_STATUS* = "airdropStatus"
QtObject:
@ -215,9 +217,9 @@ QtObject:
let deployState = if receivedData.success: DeployState.Deployed else: DeployState.Failed
let tokenDto = toCommunityTokenDto(parseJson(receivedData.data))
if not receivedData.success:
error "Collectible contract not deployed", address=tokenDto.address
error "Collectible contract not deployed", chainId=tokenDto.chainId, address=tokenDto.address
try:
discard updateCommunityTokenState(tokenDto.address, deployState) #update db state
discard updateCommunityTokenState(tokenDto.chainId, tokenDto.address, deployState) #update db state
except RpcException:
error "Error updating collectibles contract state", message = getCurrentExceptionMsg()
let data = CommunityTokenDeployedStatusArgs(communityId: tokenDto.communityId, contractAddress: tokenDto.address,
@ -248,9 +250,21 @@ QtObject:
self.tempTokenOwnersToFetch = tokenDto
self.tokenOwners1SecTimer.start()
self.events.on(PendingTransactionTypeDto.CollectibleBurn.event) do(e: Args):
let receivedData = TransactionMinedArgs(e)
let tokenDto = toCommunityTokenDto(parseJson(receivedData.data))
let transactionStatus = if receivedData.success: ContractTransactionStatus.Completed else: ContractTransactionStatus.Failed
if receivedData.success:
try:
discard updateCommunityTokenSupply(tokenDto.chainId, tokenDto.address, tokenDto.supply) #update db state
except RpcException:
error "Error updating collectibles supply", message = getCurrentExceptionMsg()
let data = RemoteDestructArgs(communityToken: tokenDto, transactionHash: receivedData.transactionHash, status: transactionStatus)
self.events.emit(SIGNAL_BURN_STATUS, data)
proc buildTransactionDataDto(self: Service, addressFrom: string, chainId: int, contractAddress: string): TransactionDataDto =
let gasUnits = self.tempGasTable.getOrDefault((chainId, contractAddress), 0)
let suggestedFees = self.tempFeeTable[chainId]
let suggestedFees = self.tempFeeTable.getOrDefault(chainId, nil)
if suggestedFees == nil:
error "Can't find suggested fees for chainId", chainId=chainId
return
@ -340,6 +354,13 @@ QtObject:
except RpcException:
error "Error getting contract owner name", message = getCurrentExceptionMsg()
proc getRemainingSupply*(self: Service, chainId: int, contractAddress: string): int =
try:
let response = tokens_backend.remainingSupply(chainId, contractAddress)
return response.result.getInt()
except RpcException:
error "Error getting remaining supply", message = getCurrentExceptionMsg()
proc airdropCollectibles*(self: Service, communityId: string, password: string, collectiblesAndAmounts: seq[CommunityTokenAndAmount], walletAddresses: seq[string]) =
try:
for collectibleAndAmount in collectiblesAndAmounts:
@ -472,8 +493,8 @@ QtObject:
if len(tokenIds) == 0:
warn "token list is empty"
return
let arg = AsyncGetBurnFees(
tptr: cast[ByteAddress](asyncGetBurnFeesTask),
let arg = AsyncGetRemoteBurnFees(
tptr: cast[ByteAddress](asyncGetRemoteBurnFeesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onSelfDestructFees",
chainId: contract.chainId,
@ -500,6 +521,49 @@ QtObject:
errorCode = ComputeFeeErrorCode.Infura
return errorCode
proc burnCollectibles*(self: Service, communityId: string, password: string, contractUniqueKey: string, amount: int) =
try:
var contract = self.findContractByUniqueId(contractUniqueKey)
let addressFrom = self.contractOwner(contract.chainId, contract.address)
let txData = self.buildTransactionDataDto(addressFrom, contract.chainId, contract.address)
debug "Burn collectibles ", chainId=contract.chainId, address=contract.address, amount=amount
let response = tokens_backend.burn(contract.chainId, contract.address, %txData, password, amount)
let transactionHash = response.result.getStr()
debug "Burn transaction hash ", transactionHash=transactionHash
var data = RemoteDestructArgs(communityToken: contract, transactionHash: transactionHash, status: ContractTransactionStatus.InProgress)
self.events.emit(SIGNAL_BURN_STATUS, data)
contract.supply = contract.supply - amount # save with changed supply
# observe transaction state
self.transactionService.watchTransaction(
transactionHash,
addressFrom,
contract.address,
$PendingTransactionTypeDto.CollectibleBurn,
$contract.toJsonNode(),
contract.chainId,
)
except Exception as e:
error "Burn error", msg = e.msg
proc computeBurnFee*(self: Service, contractUniqueKey: string, amount: int) =
try:
let contract = self.findContractByUniqueId(contractUniqueKey)
self.tempAccountAddress = self.contractOwner(contract.chainId, contract.address)
self.tempChainId = contract.chainId
let arg = AsyncGetBurnFees(
tptr: cast[ByteAddress](asyncGetBurnFeesTask),
vptr: cast[ByteAddress](self.vptr),
slot: "onBurnFees",
chainId: contract.chainId,
contractAddress: contract.address,
amount: amount
)
self.threadpool.start(arg)
except Exception as e:
error "Error loading fees", msg = e.msg
proc createComputeFeeArgsWithError(self:Service, errorMessage: string): ComputeFeeArgs =
let errorCode = self.getErrorCodeFromMessage(errorMessage)
let (ethCurrency, fiatCurrency) = self.create0CurrencyAmounts()
@ -581,6 +645,9 @@ QtObject:
proc onSelfDestructFees*(self:Service, response: string) {.slot.} =
self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_SELF_DESTRUCT_FEE)
proc onBurnFees*(self:Service, response: string) {.slot.} =
self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_BURN_FEE)
proc onDeployFees*(self:Service, response: string) {.slot.} =
self.parseFeeResponseAndEmitSignal(response, SIGNAL_COMPUTE_DEPLOY_FEE)

View File

@ -19,6 +19,7 @@ type
CollectibleDeployment = "CollectibleDeployment"
CollectibleAirdrop = "CollectibleAirdrop"
CollectibleRemoteSelfDestruct = "CollectibleRemoteSelfDestruct"
CollectibleBurn = "CollectibleBurn"
proc event*(self:PendingTransactionTypeDto):string =
result = "transaction:" & $self

View File

@ -22,10 +22,14 @@ proc addCommunityToken*(token: CommunityTokenDto): RpcResponse[JsonNode] {.raise
let payload = %* [token.toJsonNode()]
return core.callPrivateRPC("wakuext_addCommunityToken", payload)
proc updateCommunityTokenState*(contractAddress: string, deployState: DeployState): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [contractAddress, deployState.int]
proc updateCommunityTokenState*(chainId: int, contractAddress: string, deployState: DeployState): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress, deployState.int]
return core.callPrivateRPC("wakuext_updateCommunityTokenState", payload)
proc updateCommunityTokenSupply*(chainId: int, contractAddress: string, supply: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress, supply]
return core.callPrivateRPC("wakuext_updateCommunityTokenSupply", payload)
proc mintTo*(chainId: int, contractAddress: string, txData: JsonNode, password: string, walletAddresses: seq[string], amount: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress, txData, utils.hashPassword(password), walletAddresses, amount]
return core.callPrivateRPC("collectibles_mintTo", payload)
@ -42,10 +46,22 @@ proc estimateRemoteBurn*(chainId: int, contractAddress: string, tokenIds: seq[UI
let payload = %* [chainId, contractAddress, tokenIds.map(x => x.toString(10))]
return core.callPrivateRPC("collectibles_estimateRemoteBurn", payload)
proc burn*(chainId: int, contractAddress: string, txData: JsonNode, password: string, amount: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress, txData, utils.hashPassword(password), stint.parse($amount, Uint256).toString(10)]
return core.callPrivateRPC("collectibles_burn", payload)
proc estimateBurn*(chainId: int, contractAddress: string, amount: int): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress, stint.parse($amount, Uint256).toString(10)]
return core.callPrivateRPC("collectibles_estimateBurn", payload)
proc contractOwner*(chainId: int, contractAddress: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress]
return core.callPrivateRPC("collectibles_contractOwner", payload)
proc remainingSupply*(chainId: int, contractAddress: string): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %* [chainId, contractAddress]
return core.callPrivateRPC("collectibles_remainingSupply", payload)
proc deployCollectiblesEstimate*(): RpcResponse[JsonNode] {.raises: [Exception].} =
let payload = %*[]
return core.callPrivateRPC("collectibles_deployCollectiblesEstimate", payload)

View File

@ -43,13 +43,19 @@ SettingsPageLayout {
signal mintCollectible(var collectibleItem)
signal mintAsset(var assetItem)
signal signMintTransactionOpened(int chainId, string accountAddress)
signal signRemoteDestructTransactionOpened(var remotelyDestructTokensList, // [key , amount]
string tokenKey)
signal remotelyDestructCollectibles(var remotelyDestructTokensList, // [key , amount]
string tokenKey)
signal signBurnTransactionOpened(int chainId)
signal signRemoteDestructTransactionOpened(var selfDestructTokensList, // [key , amount]
string tokenKey)
signal remotelyDestructCollectibles(var selfDestructTokensList, // [key , amount]
string tokenKey)
signal signBurnTransactionOpened(string tokenKey, int amount)
signal burnToken(string tokenKey, int amount)
signal airdropToken(string tokenKey, int type, var addresses)
signal airdropToken(string tokenKey)
signal deleteToken(string tokenKey)
function setFeeLoading() {
@ -413,6 +419,7 @@ SettingsPageLayout {
remotelyDestructPopup.close()
alertPopup.close()
signTransactionPopup.close()
burnTokensPopup.close()
}
airdropEnabled: !deployStateFailed
@ -485,7 +492,7 @@ SettingsPageLayout {
onOpened: {
root.setFeeLoading()
signTransactionPopup.isRemotelyDestructTransaction ? root.signRemoteDestructTransactionOpened(d.remotelyDestructTokensList, d.tokenKey) :
root.signBurnTransactionOpened(d.chainId)
root.signBurnTransactionOpened(d.tokenKey, d.burnAmount)
}
onCancelClicked: close()
onSignTransactionClicked: signTransaction()
@ -662,7 +669,7 @@ SettingsPageLayout {
BindCollectible { property: "description"; value: model.description },
BindCollectible { property: "supply"; value: model.supply },
BindCollectible { property: "infiniteSupply"; value: model.infiniteSupply },
BindCollectible { property: "remainingTokens"; value: model.remainingTokens },
BindCollectible { property: "remainingTokens"; value: model.remainingSupply },
BindCollectible { property: "chainId"; value: model.chainId },
BindCollectible { property: "chainName"; value: model.chainName },
BindCollectible { property: "chainIcon"; value: model.chainIcon },
@ -684,7 +691,7 @@ SettingsPageLayout {
BindAsset { property: "description"; value: model.description },
BindAsset { property: "supply"; value: model.supply },
BindAsset { property: "infiniteSupply"; value: model.infiniteSupply },
BindAsset { property: "remainingTokens"; value: model.remainingTokens },
BindAsset { property: "remainingTokens"; value: model.remainingSupply },
BindAsset { property: "chainId"; value: model.chainId },
BindAsset { property: "chainName"; value: model.chainName },
BindAsset { property: "chainIcon"; value: model.chainIcon },

View File

@ -317,8 +317,8 @@ StatusSectionLayout {
remotelyDestructTokensList,
tokenKey)
}
onSignBurnTransactionOpened: communityTokensStore.computeBurnFee(chainId)
onBurnToken: communityTokensStore.burnToken(tokenKey, amount)
onSignBurnTransactionOpened: communityTokensStore.computeBurnFee(tokenKey, amount)
onBurnToken: communityTokensStore.burnToken(root.community.id, tokenKey, amount)
onAirdropToken: root.goTo(Constants.CommunitySettingsSections.Airdrops)
onDeleteToken: communityTokensStore.deleteToken(root.community.id, tokenKey)
@ -332,6 +332,10 @@ StatusSectionLayout {
mintPanel.setFeesInfo(ethCurrency, fiatCurrency, errorCode)
}
function onBurnFeeUpdated(ethCurrency, fiatCurrency, errorCode) {
mintPanel.setFeesInfo(ethCurrency, fiatCurrency, errorCode)
}
function onRemoteDestructStateChanged(communityId, tokenName, status, url) {
if (root.community.id !== communityId) {
return
@ -364,6 +368,70 @@ StatusSectionLayout {
url)
}
function onAirdropStateChanged(communityId, tokenName, chainName, status, url) {
if (root.community.id !== communityId) {
return
}
let title = ""
let loading = false
let type = Constants.ephemeralNotificationType.normal
switch (status) {
case Constants.ContractTransactionStatus.InProgress:
title = qsTr("Airdrop on %1 in progress...").arg(chainName)
loading = true
break
case Constants.ContractTransactionStatus.Completed:
title = qsTr("Airdrop on %1 in complete").arg(chainName)
type = Constants.ephemeralNotificationType.success
break
case Constants.ContractTransactionStatus.Failed:
title = qsTr("Airdrop on %1 failed").arg(chainName)
break
default:
console.warn("Unknown airdrop state: "+status)
return
}
Global.displayToastMessage(title,
qsTr("View on etherscan"),
"",
loading,
type,
url)
}
function onBurnStateChanged(communityId, tokenName, status, url) {
if (root.community.id !== communityId) {
return
}
let title = ""
let loading = false
let type = Constants.ephemeralNotificationType.normal
switch (status) {
case Constants.ContractTransactionStatus.InProgress:
title = qsTr("%1 being burned...").arg(tokenName)
loading = true
break
case Constants.ContractTransactionStatus.Completed:
title = qsTr("%1 burning is complete").arg(tokenName)
type = Constants.ephemeralNotificationType.success
break
case Constants.ContractTransactionStatus.Failed:
title = qsTr("%1 burning is failed").arg(tokenName)
break
default:
console.warn("Unknown burning state: "+status)
return
}
Global.displayToastMessage(title,
qsTr("View on etherscan"),
"",
loading,
type,
url)
}
function onDeploymentStateChanged(communityId, status, url) {
if (root.community.id !== communityId) {
return

View File

@ -86,7 +86,7 @@ StatusScrollView {
components: [
StatusBaseText {
anchors.verticalCenter: parent.verticalCenter
text: d.getSubtitle(model.deployState, model.remainingTokens, model.supply, false)
text: d.getSubtitle(model.deployState, model.remainingSupply, model.supply, false)
color: (model.deployState === Constants.ContractTransactionStatus.Failed) ? Theme.palette.dangerColor1 : Theme.palette.baseColor1
font.pixelSize: 13
},
@ -139,7 +139,7 @@ StatusScrollView {
height: collectiblesGrid.cellHeight
width: collectiblesGrid.cellWidth
title: model.name ? model.name : "..."
subTitle: d.getSubtitle(model.deployState, model.remainingTokens, model.supply, true)
subTitle: d.getSubtitle(model.deployState, model.remainingSupply, model.supply, true)
subTitleColor: (model.deployState === Constants.ContractTransactionStatus.Failed) ? Theme.palette.dangerColor1 : Theme.palette.baseColor1
fallbackImageUrl: model.image ? model.image : ""
backgroundColor: "transparent"

View File

@ -16,12 +16,12 @@ QtObject {
signal deployFeeUpdated(var ethCurrency, var fiatCurrency, int error)
signal selfDestructFeeUpdated(var ethCurrency, var fiatCurrency, int error)
signal airdropFeeUpdated(var airdropFees)
signal burnFeeUpdated(var ethCurrency, var fiatCurrency, int error)
signal deploymentStateChanged(string communityId, int status, string url)
signal burnFeeUpdated(string value) // TO BE REMOVED
signal remoteDestructStateChanged(string communityId, string tokenName, int status, string url)
signal burnStateChanged(string communityId, string tokenName, int status, string url)
signal airdropStateChanged(string communityId, string tokenName, string chainName, int status, string url)
// Minting tokens:
function deployCollectible(communityId, collectibleItem)
@ -73,6 +73,18 @@ QtObject {
function onRemoteDestructStateChanged(communityId, tokenName, status, url) {
root.remoteDestructStateChanged(communityId, tokenName, status, url)
}
function onAirdropStateChanged(communityId, tokenName, chainName, status, url) {
root.airdropStateChanged(communityId, tokenName, chainName, status, url)
}
function onBurnStateChanged(communityId, tokenName, status, url) {
root.burnStateChanged(communityId, tokenName, status, url)
}
function onBurnFeeUpdated(ethCurrency, fiatCurrency, errorCode) {
root.burnFeeUpdated(ethCurrency, fiatCurrency, errorCode)
}
}
function computeDeployFee(chainId, accountAddress) {
@ -88,15 +100,12 @@ QtObject {
}
// Burn:
function computeBurnFee(chainId) {
// TODO BACKEND
root.burnFeeUpdated("0,0010 ETH")
console.warn("TODO: Compute burn fee backend")
function computeBurnFee(tokenKey, amount) {
communityTokensModuleInst.computeBurnFee(tokenKey, amount)
}
function burnToken(tokenKey, burnAmount) {
// TODO BACKEND
console.warn("TODO: Burn collectible backend")
function burnToken(communityId, tokenKey, burnAmount) {
communityTokensModuleInst.burnCollectibles(communityId, tokenKey, burnAmount)
}
// Airdrop tokens: