Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [2.9.4](https://github.com/Superhero-com/superhero-wallet/compare/v2.9.3...v2.9.4) (2026-03-26)


### Features

* **aeternity:** add advance transaction settings ([8455b71](https://github.com/Superhero-com/superhero-wallet/commit/8455b71db749f5d6ad2dc891fc5588c6eb2b8e7d))


### Bug Fixes

* **aeternity:** set custom fee correctly in multiple recipients flow ([7b8eede](https://github.com/Superhero-com/superhero-wallet/commit/7b8eedeab9e7bb50e198696df25fe5aa2b31e97b))
* do not remove pending transaction after 10 minutes ([b75ef88](https://github.com/Superhero-com/superhero-wallet/commit/b75ef88c3e1e86aa0d44f7d5626d1c852b8c911d))
* **infinite-scroll:** only render over actual items ([f34adcd](https://github.com/Superhero-com/superhero-wallet/commit/f34adcdc1bb2d9f71307b027e92a87a8d92b8ea1))
* remove unused locale ([4d0d4b0](https://github.com/Superhero-com/superhero-wallet/commit/4d0d4b0b44c1295e605501a87a8bf4882f933acf))


### Maintenance

* adjust BrowserActions margins ([29cd26a](https://github.com/Superhero-com/superhero-wallet/commit/29cd26aaa866b9188b299067d23f5a81554685f0))
* avoid transaction being removed from pool rapidly ([7018de7](https://github.com/Superhero-com/superhero-wallet/commit/7018de79c3e2503f5a69fff4d2fe66922786aac7))

### [2.9.3](https://github.com/Superhero-com/superhero-wallet/compare/v2.9.2...v2.9.3) (2026-03-16)


Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.superhero.cordova"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 20903
versionName "2.9.3"
versionCode 20904
versionName "2.9.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters 'arm64-v8a'
Expand Down
4 changes: 2 additions & 2 deletions ios/App/App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.9.3;
MARKETING_VERSION = 2.9.4;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.superhero.cordova;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -390,7 +390,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.9.3;
MARKETING_VERSION = 2.9.4;
PRODUCT_BUNDLE_IDENTIFIER = com.superhero.cordova;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "superhero-wallet",
"version": "2.9.3",
"version": "2.9.4",
"description": "Superhero wallet",
"author": "Superhero",
"license": "ISC",
Expand Down
2 changes: 2 additions & 0 deletions src/composables/aeSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export function useAeSdk() {
}],
id: APP_NAME,
type: IS_EXTENSION || IS_OFFSCREEN_TAB ? WALLET_TYPE.extension : WALLET_TYPE.window,
// TODO: the ttl is set to 0 to avoid the timeout error when blockchain experiencing issues
ttl: 0,
onConnection(aeppId, params, origin) {
aeppInfo[aeppId] = { ...params, origin };
},
Expand Down
44 changes: 20 additions & 24 deletions src/composables/latestTransactionList.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { computed, ref, watch } from 'vue';
import { isEqual, remove } from 'lodash-es';
import { isEqual } from 'lodash-es';
import type {
AccountAddress,
IAccount,
ICommonTransaction,
ITransaction,
} from '@/types';
import { STORAGE_KEYS, TRANSACTION_CERTAINLY_MINED_TIME } from '@/constants';
import { STORAGE_KEYS } from '@/constants';
import {
pipe,
removeDuplicatedTransactions,
Expand Down Expand Up @@ -66,13 +66,18 @@ export function useLatestTransactionList() {
const { balances } = useBalances();
const { tokenBalances } = useFungibleTokens();

function removeAccountPendingTransaction(address: AccountAddress, hash: string) {
accountsTransactionsPending.value[address] = remove(
accountsTransactionsPending.value[address],
(transaction) => hash === transaction.hash,
);
function removeAccountPendingTransactionByHash(address: AccountAddress, hash: string) {
accountsTransactionsPending.value[address] = (
accountsTransactionsPending.value[address] || []
).filter((transaction) => hash !== transaction.hash);
}

function removeAccountPendingTransactionByNonce(address: AccountAddress, nonce?: number) {
if (!nonce) return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falsy check on nonce skips valid zero value

Low Severity

removeAccountPendingTransactionByNonce uses if (!nonce) return; which treats nonce 0 as falsy, skipping removal. Similarly, in submit(), effectiveTransferData.nonce || await fetchCurrentAccountNonce() uses || instead of ??, causing nonce 0 to be discarded in favor of a fetched value. Both locations conflate "not provided" with "zero." Using nonce == null and ?? respectively would be more precise.

Additional Locations (1)
Fix in Cursor Fix in Web

accountsTransactionsPending.value[address] = (
accountsTransactionsPending.value[address] || []
).filter((transaction) => nonce !== transaction.tx?.nonce);
}
async function loadAccountLatestTransactions({ address, protocol }: IAccount) {
const adapter = ProtocolAdapterFactory.getAdapter(protocol);
const currentNetworkName = activeNetwork.value.name;
Expand All @@ -97,7 +102,12 @@ export function useLatestTransactionList() {
];

if (accountsTransactionsPending.value[address]?.length) {
regularTransactions.forEach(({ hash }) => removeAccountPendingTransaction(address, hash));
regularTransactions.forEach(
({ hash }) => removeAccountPendingTransactionByHash(address, hash),
);
regularTransactions.forEach(
(transaction) => removeAccountPendingTransactionByNonce(address, transaction.tx?.nonce),
);
}
}

Expand Down Expand Up @@ -134,7 +144,7 @@ export function useLatestTransactionList() {
.waitTransactionMined(transaction.hash);
loadAccountLatestTransactions(account);
} finally {
removeAccountPendingTransaction(address, transaction.hash);
removeAccountPendingTransactionByHash(address, transaction.hash);
}
}
}
Expand All @@ -145,23 +155,9 @@ export function useLatestTransactionList() {
loadAllLatestTransactions();

/**
* Remove old pending transactions that we can consider as already mined.
* This prevents situation where user creates transaction and closes the app/extension
* immediately so the `waitTransactionMined` couldn't work properly.
* Refresh account transactions lists if any of the transactions is pending.
*/
setInterval(() => {
Object.entries(accountsTransactionsPending.value)
.forEach(([accountAddress, transactionList]) => {
transactionList.forEach(({ hash, microTime }) => {
if (Date.now() - (microTime || 0) > TRANSACTION_CERTAINLY_MINED_TIME) {
removeAccountPendingTransaction(accountAddress, hash);
}
});
});

/**
* Refresh account transactions lists if any of the transactions is pending.
*/
Object.entries(accountsTransactionsLatest.value)
.forEach(([accountAddress, transactionList]) => {
if (transactionList.some(({ pending }) => !!pending)) {
Expand Down
3 changes: 0 additions & 3 deletions src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,9 +547,6 @@ export const ASSET_TYPES = {
token: 'token',
} as const;

/** 10 minutes is the time we are 100% sure the transaction was mined */
export const TRANSACTION_CERTAINLY_MINED_TIME = 600000;

export const POLLING_INTERVAL_TRANSACTIONS = 15000;

// toBase64Url(JSON.stringify({ alg: 'EdDSA', typ: 'JWT' }))
Expand Down
35 changes: 33 additions & 2 deletions src/popup/components/AccordionItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<template>
<div class="accordion-item">
<div
class="accordion-item"
:class="`variant-${variant}`"
>
<a
class="accordion-label"
data-cy="accordion-item-label"
Expand All @@ -24,16 +27,28 @@
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import { defineComponent, PropType, ref } from 'vue';

import ChevronDownIcon from '../../icons/chevron-down.svg?vue-component';

export const ACCORDION_ITEM_VARIANT = [
'default',
'muted',
] as const;

export type AccordionItemVariant = typeof ACCORDION_ITEM_VARIANT[number];

export default defineComponent({
components: {
ChevronDownIcon,
},
props: {
label: { type: String, required: true },
variant: {
type: String as PropType<AccordionItemVariant>,
validator: (value: AccordionItemVariant) => ACCORDION_ITEM_VARIANT.includes(value),
default: ACCORDION_ITEM_VARIANT[0],
},
},
setup() {
const isVisible = ref(false);
Expand Down Expand Up @@ -81,5 +96,21 @@ export default defineComponent({
color: $color-success;
}
}

&.variant {
&-muted {
.accordion-label {
.accordion-label-text {
color: rgba($color-white, 0.75);
}

&:hover {
.accordion-label-text {
color: $color-white;
}
}
}
}
}
}
</style>
8 changes: 7 additions & 1 deletion src/popup/components/InfiniteScroll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
<div class="vs-spacer" :style="{ height: bottomPadding }" />
</div>
<template v-else>
<slot />
<div
v-for="(item, i) in items || []"
:key="getKey(item, i)"
class="vs-row"
>
<slot :item="item" :index="i" />
</div>
</template>
</div>
</template>
Expand Down
6 changes: 6 additions & 0 deletions src/popup/components/Modals/BrowserActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,9 @@ export default defineComponent({
},
});
</script>

<style lang="scss" scoped>
.info {
margin-bottom: 16px;
}
</style>
30 changes: 24 additions & 6 deletions src/popup/components/TransferQRCodeGenerator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import BigNumber from 'bignumber.js';
import {
defineComponent,
onMounted,
PropType,
ref,
watch,
} from 'vue';
import {
encode,
Expand Down Expand Up @@ -66,6 +66,7 @@ export default defineComponent({
},
setup(props) {
const fragments = ref();
let updateRequestId = 0;

const { nodeNetworkId, getAeSdk } = useAeSdk();
const { activeAccount, isActiveAccountAirGap } = useAccounts();
Expand All @@ -76,7 +77,11 @@ export default defineComponent({
copy(getURFromFragments(fragments.value));
}

onMounted(async () => {
async function generateQrFragments() {
updateRequestId += 1;
const requestId = updateRequestId;
fragments.value = undefined;

const aeSdk = await getAeSdk();
const {
amount: amountRaw,
Expand All @@ -88,7 +93,7 @@ export default defineComponent({
|| !recipients?.length
|| !selectedAsset
|| !isActiveAccountAirGap.value) {
return null;
return;
}

const amount = (selectedAsset.contractId === AE_CONTRACT_ID)
Expand All @@ -101,15 +106,28 @@ export default defineComponent({
recipientId: recipients[0],
amount: new BigNumber(amount).toFixed().toString(),
payload: encode(new TextEncoder().encode(props.transferData.payload), Encoding.Bytearray),
nonce: props.transferData.nonce,
fee: props.transferData.fee ? aeToAettos(props.transferData.fee) : undefined,
});

fragments.value = await generateTransactionURDataFragments(
const nextFragments = await generateTransactionURDataFragments(
activeAccount.value?.publicKey!,
txRaw,
nodeNetworkId.value!,
);
return null;
});

if (requestId === updateRequestId) {
fragments.value = nextFragments;
}
}

watch(
() => props.transferData,
() => {
generateQrFragments();
},
{ deep: true, immediate: true },
);

return {
fragments,
Expand Down
8 changes: 5 additions & 3 deletions src/popup/components/TransferSend/TransferReviewBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
v-else
class="details-item receiving-addresses"
data-cy="review-recipient"
:class="{ 'multiple-addresses': transferData.addresses?.length! > 1 }"
:expandable="transferData.addresses?.length! > 1"
:class="{ 'multiple-addresses': (transferData.addresses?.length || 0) > 1 }"
:expandable="(transferData.addresses?.length || 0) > 1"
:label="`${$t('pages.send.show')} ${transferData.addresses?.length} ${$t('pages.send.recipients')}`"
:expanded-label="transferData.addresses?.length! > 1
:expanded-label="(transferData.addresses?.length || 0) > 1
? `${$t('pages.send.hide')} ${transferData.addresses?.length} ${$t('pages.send.recipients')}`
: $t('pages.send.recipient')"
>
Expand Down Expand Up @@ -115,6 +115,8 @@
:payload="transferData.payload"
/>

<slot name="bottom" />

<Loader v-if="loading" />
</div>
</template>
Expand Down
5 changes: 5 additions & 0 deletions src/popup/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@
"estimatedGasUsed": "Estimated gas used",
"maxFee": "Maximum transaction fee",
"advancedDetails": "Advanced transaction details",
"advancedFeeInvalid": "Enter a valid fee greater than 0.",
"advancedFeeTooLow": "Fee must be at least the estimated minimum fee.",
"advancedNonceInvalid": "Enter a valid nonce greater than 0.",
"advancedNonceLowWarning": "Setting a nonce lower than the current account nonce usually leads to rejection unless a transaction with that nonce is still pending in the mempool.",
"advancedNonceReplacementWarning": "A pending transaction with this nonce may be replaced by this new transaction if you continue.",
"rawData": "Raw",
"decoded": "Decoded",
"decodingDataFailed": "Transaction data could not be decoded.",
Expand Down
5 changes: 5 additions & 0 deletions src/popup/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@
"estimatedGasUsed": "预估 Gas 用量",
"maxFee": "最大交易费用",
"advancedDetails": "高级交易详情",
"advancedFeeInvalid": "请输入大于 0 的有效手续费。",
"advancedFeeTooLow": "手续费不能低于预估的最低手续费。",
"advancedNonceInvalid": "请输入大于 0 的有效 nonce。",
"advancedNonceLowWarning": "如果 nonce 低于当前账户 nonce,且内存池中没有使用该 nonce 的待处理交易,这笔交易通常会被拒绝。",
"advancedNonceReplacementWarning": "如果内存池中已有使用该 nonce 的待处理交易,继续发送可能会让这笔新交易替换掉原来的待处理交易。",
"rawData": "原始",
"decoded": "已解码",
"decodingDataFailed": "无法解码交易数据。",
Expand Down
Loading
Loading