fix(wallet): show decoded program account data

This commit is contained in:
Ricardo Guilherme Schmidt
2026-07-18 19:53:02 -03:00
parent 5c151728ad
commit 2977399cb0
7 changed files with 103 additions and 2 deletions
+15
View File
@@ -95,6 +95,19 @@ QJsonObject enumFields(const QJsonValue& value, const QString& variant)
return value.toObject().value(variant).toObject();
}
QString decodedDataText(const QJsonValue& value)
{
if (value.isObject()) {
return QString::fromUtf8(
QJsonDocument(value.toObject()).toJson(QJsonDocument::Indented)).trimmed();
}
if (value.isArray()) {
return QString::fromUtf8(
QJsonDocument(value.toArray()).toJson(QJsonDocument::Indented)).trimmed();
}
return {};
}
WalletAccountRead accountRead(const WalletAccount& account)
{
WalletAccountRead read;
@@ -444,6 +457,8 @@ void AmmUiBackend::applyWalletPortfolio(quint64 generation)
presentation.address = account.id;
presentation.programName = program.programName;
presentation.accountType = account.typeName;
if (account.status == QStringLiteral("decoded"))
presentation.decodedData = decodedDataText(account.value);
if (program.programId == m_tokenProgramId
&& account.typeName == QStringLiteral("TokenHolding")) {
const QJsonObject fungible = enumFields(
+2
View File
@@ -757,6 +757,7 @@ Item {
required property string section
required property string programName
required property string accountType
required property string decodedData
required property string visibility
required property bool canBePrimary
required property bool isPrimary
@@ -786,6 +787,7 @@ Item {
section: accountWrapper.section
programName: accountWrapper.programName
accountType: accountWrapper.accountType
decodedData: accountWrapper.decodedData
visibility: accountWrapper.visibility
canBePrimary: accountWrapper.canBePrimary
isPrimary: accountWrapper.isPrimary
@@ -16,6 +16,7 @@ ItemDelegate {
required property string section
required property string programName
required property string accountType
required property string decodedData
required property string visibility
required property bool canBePrimary
required property bool isPrimary
@@ -87,14 +88,51 @@ ItemDelegate {
}
Label {
visible: root.programName.length > 0
objectName: "walletProgramName"
visible: root.section === "advanced" && root.programName.length > 0
Layout.fillWidth: true
text: qsTr("%1 program · wallet controlled").arg(root.programName)
text: qsTr("Program: %1").arg(root.programName)
color: "#a1a1aa"
font.pixelSize: 11
elide: Text.ElideRight
}
ColumnLayout {
visible: root.section === "advanced" && root.decodedData.length > 0
Layout.fillWidth: true
spacing: 4
Label {
objectName: "walletDecodedDataLabel"
text: qsTr("Decoded data")
color: "#a1a1aa"
font.pixelSize: 11
}
Rectangle {
objectName: "walletDecodedDataBox"
Layout.fillWidth: true
implicitHeight: decodedDataText.implicitHeight + 16
color: "#18181b"
radius: 6
border.width: 1
border.color: "#3f3f46"
Text {
id: decodedDataText
objectName: "walletDecodedData"
anchors.fill: parent
anchors.margins: 8
text: root.decodedData
color: "#d4d4d8"
font.family: "monospace"
font.pixelSize: 10
textFormat: Text.PlainText
wrapMode: Text.WrapAnywhere
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 4
@@ -59,6 +59,8 @@ QVariant WalletAccountModel::data(const QModelIndex& index, int role) const
return account.definitionId;
case AliasRole:
return account.alias;
case DecodedDataRole:
return account.decodedData;
default:
return {};
}
@@ -84,6 +86,7 @@ QHash<int, QByteArray> WalletAccountModel::roleNames() const
{ DefinitionIdRole, "definitionId" },
{ AliasRole, "alias" },
{ DisplayAddressRole, "displayAddress" },
{ DecodedDataRole, "decodedData" },
};
}
@@ -154,6 +157,7 @@ bool WalletAccountModel::applyPresentations(
entry.programName = presentation.programName;
entry.accountType = presentation.accountType;
entry.definitionId = presentation.definitionId;
entry.decodedData = presentation.decodedData;
entry.semanticName = presentation.semanticName;
entry.section = sectionFor(entry, presentation.hiddenFromAccounts);
entry.canBePrimary = entry.kind == QStringLiteral("user")
@@ -175,6 +179,7 @@ bool WalletAccountModel::applyPresentations(
&& entry.programName == current.programName
&& entry.accountType == current.accountType
&& entry.definitionId == current.definitionId
&& entry.decodedData == current.decodedData
&& entry.canBePrimary == current.canBePrimary
&& entry.isPrimary == current.isPrimary) {
continue;
@@ -193,6 +198,7 @@ bool WalletAccountModel::applyPresentations(
SectionRole,
ProgramNameRole,
AccountTypeRole,
DecodedDataRole,
CanBePrimaryRole,
IsPrimaryRole,
DefinitionIdRole,
@@ -14,6 +14,7 @@ struct WalletAccountPresentation {
QString accountType;
QString definitionId;
bool hiddenFromAccounts = false;
QString decodedData;
};
class WalletAccountModel final : public QAbstractListModel {
@@ -39,6 +40,7 @@ public:
DefinitionIdRole,
AliasRole,
DisplayAddressRole,
DecodedDataRole,
};
Q_ENUM(Role)
@@ -79,6 +81,7 @@ private:
QString programName;
QString accountType;
QString definitionId;
QString decodedData;
bool canBePrimary = false;
bool isPrimary = false;
};
@@ -604,6 +604,8 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles()
QCOMPARE(model.data(model.index(1), WalletAccountModel::AddressRole).toString(), ACCOUNT_B);
QCOMPARE(model.roleNames().value(WalletAccountModel::DisplayAddressRole),
QByteArray("displayAddress"));
QCOMPARE(model.roleNames().value(WalletAccountModel::DecodedDataRole),
QByteArray("decodedData"));
QCOMPARE(model.data(model.index(1), WalletAccountModel::DisplayAddressRole).toString(),
walletAccountIdToBase58(ACCOUNT_B));
QCOMPARE(model.data(model.index(1), WalletAccountModel::BalanceRole).toString(),
@@ -623,6 +625,7 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles()
QStringLiteral("UserAccount"),
{},
false,
QStringLiteral("{\"public_key\":\"Public/test\"}"),
},
{
walletAccountIdToBase58(ACCOUNT_C),
@@ -640,6 +643,8 @@ void LogosWalletProviderTest::exposesStableAccountModelRoles()
QStringLiteral("hidden"));
QCOMPARE(model.data(model.index(2), WalletAccountModel::NameRole).toString(),
QStringLiteral("TEST holding"));
QCOMPARE(model.data(model.index(0), WalletAccountModel::DecodedDataRole).toString(),
QStringLiteral("{\"public_key\":\"Public/test\"}"));
model.setAlias(ACCOUNT_C, QStringLiteral("Reserve"));
QCOMPARE(model.data(model.index(2), WalletAccountModel::NameRole).toString(),
QStringLiteral("Reserve"));
@@ -180,6 +180,7 @@ Item {
section: account.section || "accounts",
programName: account.programName || "",
accountType: account.accountType || "",
decodedData: account.decodedData || "",
visibility: account.visibility || (account.isPublic === false ? "private" : "public"),
canBePrimary: account.canBePrimary === undefined ? true : account.canBePrimary,
isPrimary: account.isPrimary === true
@@ -622,6 +623,37 @@ Item {
compare(fixture.control.selectedAddress, userAddress)
}
function test_advancedShowsProgramAndDecodedData() {
const decodedData = "{\n \"name\": \"Test token\"\n}"
const fixture = createControl({ isWalletOpen: true }, [
{
name: "Token definition",
address: "c".repeat(64),
balance: "0",
isPublic: true,
kind: "token_definition",
section: "advanced",
programName: "Token",
accountType: "TokenDefinition",
decodedData: decodedData,
canBePrimary: false
}
])
mouseClick(findChild(fixture.control, "walletAccountButton"))
mouseClick(findChild(fixture.control, "walletAccountsButton"))
mouseClick(findChild(fixture.control, "walletAdvancedAccountsButton"))
const list = findChild(fixture.control, "walletAccountList")
tryVerify(function() { return list.itemAtIndex(0) !== null })
const program = findChild(list.itemAtIndex(0), "walletProgramName")
const decoded = findChild(list.itemAtIndex(0), "walletDecodedData")
verify(program && decoded, "Advanced details exist")
tryCompare(program, "visible", true)
compare(program.text, "Program: Token")
tryCompare(decoded, "visible", true)
compare(decoded.text, decodedData)
}
function test_onlyProgramRecordsLeavesPrimaryEmpty() {
const fixture = createControl({ isWalletOpen: true }, [{
name: "Token definition",