La création manuelle du DU a été implémentée, et sera disponible dès le genesis de la nouvelle ĞDev après le 1er reset (vers début septembre).
En attendant, cette fonctionnalité est déjà disponible sur l’image duniter/duniter-v2s:debug-latest
, vous pouvez donc déjà la tester avec une blockchain locale
La Merge Request : feat(runtime): create UDs manually with new call universalDividend.claim_uds (!83) · Merge requests · nodes / rust / Duniter v2S · GitLab
Résumé de l’implémentation
Pour simplifier et optimiser l’implémentation, je « compte » le nombre de DU créés en incrémentant un nombre entier à chaque nouveau DU, c’est le currentUdIndex
. Cela permet également de découpler totalement l’implémentation de toute notion de temps, elle restera donc fonctionnelle même si on fait varier le temps entre deux DU ou entre deux réévaluations.
Chaque identité a un nouveau champ data: ItdyData
, qui permet de stocker des données supplémentaires, pas utilisées par la pallet identity, mais nécessaire pour d’autres pallets.
Pour le moment, le type ItdyData
contient un seul champ firstEligibleUd
, qui correspond à l’index du premier DU réclamable, mais on pourra ajouter d’autres champs à l’avenir si besoin.
Du coup, pour savoir combien de DU il faut créer lorsqu’un utilisateur réclame ses DU, il suffit de faire la soustraction currentUdIndex - firstEligibleUd
.
Mais en réalité, on veut connaître ce nombre pour chaque montant du DU (entre chaque réévaluation).
Pour ce faire, le storage de la pallet universal dividend contient un nouvel item pastReevals
, qui contient un tableau de tuple (UdIndex, Balance)
.
L’algo qui calcule le montant total à verser lors d’une réclamation des DU est ici : pallets/universal-dividend/src/compute_claim_uds.rs · master · nodes / rust / Duniter v2S · GitLab
Guide pour les développeurs des Clients/Wallets
Définitions
Solde réel : Le solde réel est la somme free + reserved
de l’objet system.account(address).data
.
Solde potentiel : L’addition du solde réel et de la somme des DU non-réclamés.
Solde transférable : La quantité maximale de monnaie qui peut être envoyée sur un autre compte sans prise en compte des frais, le solde transférable vaut donc : « solde potentiel - solde réservé ».
Recommandations
Je vous recommande de faire en sorte que la notion de création manuelle du DU soit totalement masquée à l’utilisateur, car ce n’est en place que pour des raisons d’optimisations, il n’y a aucune nécessité ni aucun intérêt d’embêter l’utilisateur avec ça.
L’idée est d’afficher à l’utilisateur son solde potentiel, et de réclamer automatiquement ses DU lors d’un virement s’ils sont nécessaires pour que le virement réussisse ou si les DU n’ont pas été réclamés depuis longtemps.
Cela fonctionne, car les batchs sont séquentiels, il suffit donc de remplacer le call
balance.transferKeepAlive(dest, amount)
par
utility.batchAll([universalDividend.claimUds(), balance.transferKeepAlive(dest, amount)])
Le coût d’exécution de universalDividend.claimUds()
est faible quel que soit le nombre de DUs à verser, donc l’utilisateur attentif verra juste que la transaction semble consommer un peu plus de frais/quotas que d’habitude.
Exemple
Alice à 100 Ğ1 (free balance), 20 Ğ1 bloquées dans un proxy (reserved), et 3 DU non-réclamés, le DU courant vaut 10 Ğ1.
Le solde potentiel d’Alice est 150 Ğ1, son solde transférable est 130 Ğ1.
Si Alice veut envoyer 110 Ğ1 à Bob, elle peut le faire directement en un seul extrinsic contenant le call :
utility.batchAll([universalDividend.claimUds(), balance.transferKeepAlive(Bob, 11000)])
La blockchain va alors prélever les frais (disons 1 Ğ1), puis exécuter claimUds
, ce qui va incrémenter la free balance d’Alice de 30 Ğ1, puis exécuter transferKeepAlive
, ce qui va prendre 110 Ğ1 de la free balance d’Alice pour les ajouter à la free balance de Bob.
Après la transaction, le nouveau solde potentiel d’Alice est 39 Ğ1, dont 19 transférables.
Afficher le solde potentiel
Je vous recommande d’afficher à l’utilisateur le solde potentiel, et seulement si l’utilisateur souhaite le détail (par un clic sur le solde ou une petite flèche à côté), afficher à côté ou en dessous le solde transférable.
Pour calculer le solde potentiel d’un compte, vous avez trois possibilités :
- Le calculer côté client à partir des données du storage, je détaille plus bas comment faire, c’est la méthode recommandée si vous n’êtes pas dans un environnement web.
- Demander directement le solde potentiel d’un compte à l’extension g1/g1-connect/G1-compagnon, on à pas encore entériné le nom, mais avec @ManUtopiK et @1000i100 on va coder une extension web pour duniter-v2s, qui gérera les comptes Ğ1 (comme l’extension polkadotjs) et qui proposera également d’autres fonctionnalités à terme, dont la possibilité de demander directement à l’extension certaines informations sur un compte, dont son solde potentiel.
- Passer par un indexer qui le calculera pour vous. Je déconseille cette solution, car ça demande de truster l’indexeur sur une information critique qu’est le solde.
Calculer le solde potentiel
Pour ce faire, il vous faut requêter cinq éléments de storage :
system.account(address)
identity.identityIndexOf(address)
identity.identities(idtyIndex)
universalDividend.currentUdIndex()
universalDividend.pastReevals()
Par exemple en javascript ça donnerait quelque chose comme ça :
const api = await ApiPromise.create(...);
const { data: balance } = await api.query.system.account(address);
const idtyIndex = await api.query.identity.identityIndexOf(address);
const { data: idtyData } = await api.query.identity.identies(idtyIndex);
const currentUdIndex = await api.query.universalDividend.currentUdIndex();
const pastReevals = await api.query.universalDividend.pastReevals();
J’ai pas testé ce code, le but est juste pour vous guider par l’exemple, pas de coder l’implémentation à votre place
À partir de ces données, vous pouvez alors calculer le solde des DU non-réclamés, voici l’algo utilisé en Rust (code simplifié) :
fn compute_claim_uds(
mut current_ud_index: UdIndex,
first_ud_index: UdIndex,
past_reevals: impl DoubleEndedIterator<Item = (UdIndex, Balance)>,
) -> Balance {
let mut total_amount = Zero::zero();
// Iterate reevaluations in reverse order (recent -> old)
for (ud_index, ud_amount) in past_reevals.rev() {
if ud_index <= first_ud_index {
let count = current_ud_index - first_ud_index;
total_amount += Balance::from(count) * ud_amount;
break;
} else {
let count = current_ud_index - ud_index;
total_amount += Balance::from(count) * ud_amount;
current_ud_index = ud_index;
}
}
total_amount
}
Je laisse les fans d’algorithmique traduire cet algo en js/python/dart, il vous suffit de lire le résumé de l’implémentation au début de ce post pour comprendre cet algo
Enfin, il suffit de sommer le tout :
let newUdsAmount = computeClaimUds(currentUdIndex, idtyData.firstEligibleUd, pastReevals);
let transferableBalance = balance.free + newUdsAmount;
let potentialBalance = balance.reserved + transferableBalance;