fix: the first wallet's first address is active as soon as it is added (closes #512)
The popup picked an active address only when it opened, so after the first wallet was created nothing was active until a reopen: Home had no headline, and Send and Receive refused. Each of the three import paths on AddWallet now makes the new wallet's first address active when no address is. The background already answered sites with that address through its own fallback, so nothing is broadcast. The address scan stand-in in the popup test boot now answers in the real scan's shape, which the phrase and xprv imports read. Model: opus-5-5
This commit is contained in:
@@ -1464,8 +1464,10 @@ view would leave a wallet one click from deletion.
|
|||||||
- **Transitions**:
|
- **Transitions**:
|
||||||
- "Import" with a valid entry and a matching password of at least 12
|
- "Import" with a valid entry and a matching password of at least 12
|
||||||
characters → creates the wallet, clears the navigation stack, and →
|
characters → creates the wallet, clears the navigation stack, and →
|
||||||
**Home**. The phrase and xprv modes then scan for further used addresses
|
**Home**. When no address is active yet, as on the first wallet, the new
|
||||||
and report the count as a flash message.
|
wallet's first address becomes the active one. The phrase and xprv modes
|
||||||
|
then scan for further used addresses and report the count as a flash
|
||||||
|
message.
|
||||||
- "Import" with a missing, short or mismatched password → full-sentence
|
- "Import" with a missing, short or mismatched password → full-sentence
|
||||||
error on the error line, no screen change
|
error on the error line, no screen change
|
||||||
- "Import" with an invalid entry or a duplicate wallet or address → flash
|
- "Import" with an invalid entry or a duplicate wallet or address → flash
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ async function importMnemonic(ctx) {
|
|||||||
};
|
};
|
||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = firstAddress;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
@@ -248,6 +251,9 @@ async function importPrivateKey(ctx) {
|
|||||||
addresses: [{ address: addr, balance: "0.0000", tokenBalances: [] }],
|
addresses: [{ address: addr, balance: "0.0000", tokenBalances: [] }],
|
||||||
});
|
});
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = addr;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
@@ -304,6 +310,9 @@ async function importXprvKey(ctx) {
|
|||||||
};
|
};
|
||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = firstAddress;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Adding the first wallet left no address active until the popup was reopened,
|
||||||
|
// so Send and Receive on Home only said "No active address selected."
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/512). Each test boots a fresh
|
||||||
|
// install, adds the first wallet through one of the three import tabs, and
|
||||||
|
// opens Receive from Home in the same popup.
|
||||||
|
|
||||||
|
const { HDNodeWallet, Mnemonic, Wallet } = require("ethers");
|
||||||
|
|
||||||
|
const { bootPopup, cleanupPopup } = require("./support/popupBoot");
|
||||||
|
|
||||||
|
const PHRASE =
|
||||||
|
"abandon abandon abandon abandon abandon abandon " +
|
||||||
|
"abandon abandon abandon abandon abandon about";
|
||||||
|
const PRIVATE_KEY = "0x" + "11".repeat(32);
|
||||||
|
// The master key of PHRASE, so its first address is the phrase's.
|
||||||
|
const XPRV = HDNodeWallet.fromSeed(
|
||||||
|
Mnemonic.fromPhrase(PHRASE).computeSeed(),
|
||||||
|
).extendedKey;
|
||||||
|
const PASSWORD = "correct horse battery staple";
|
||||||
|
|
||||||
|
const PHRASE_ADDRESS = HDNodeWallet.fromPhrase(PHRASE).address;
|
||||||
|
|
||||||
|
const TABS = [
|
||||||
|
{
|
||||||
|
tab: "tab-mnemonic",
|
||||||
|
field: "wallet-mnemonic",
|
||||||
|
secret: PHRASE,
|
||||||
|
address: PHRASE_ADDRESS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: "tab-privkey",
|
||||||
|
field: "import-private-key",
|
||||||
|
secret: PRIVATE_KEY,
|
||||||
|
address: new Wallet(PRIVATE_KEY).address,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: "tab-xprv",
|
||||||
|
field: "import-xprv-key",
|
||||||
|
secret: XPRV,
|
||||||
|
address: PHRASE_ADDRESS,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("the first wallet's first address is active right away", () => {
|
||||||
|
test.each(TABS)(
|
||||||
|
"added from $tab, Receive opens on it",
|
||||||
|
async ({ tab, field, secret, address }) => {
|
||||||
|
const page = await bootPopup(undefined);
|
||||||
|
expect(page.pageErrors).toEqual([]);
|
||||||
|
await page.click("btn-welcome-add");
|
||||||
|
await page.click(tab);
|
||||||
|
page.node(field).value = secret;
|
||||||
|
page.node("add-wallet-password").value = PASSWORD;
|
||||||
|
page.node("add-wallet-password-confirm").value = PASSWORD;
|
||||||
|
await page.click("btn-add-wallet-confirm");
|
||||||
|
expect(page.visibleViews()).toEqual(["main"]);
|
||||||
|
|
||||||
|
await page.click("btn-main-receive");
|
||||||
|
expect(page.visibleViews()).toEqual(["receive"]);
|
||||||
|
expect(page.node("receive-address-block").dataset.full).toBe(
|
||||||
|
address,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -258,12 +258,17 @@ async function bootPopup(stored, options) {
|
|||||||
formatAddressTotal: () => "",
|
formatAddressTotal: () => "",
|
||||||
getAddressValue: () => ({ usd: null, partial: false }),
|
getAddressValue: () => ({ usd: null, partial: false }),
|
||||||
}));
|
}));
|
||||||
|
// scanForAddresses() answers in the real one's shape, a scan that found
|
||||||
|
// no used address: AddWallet reads `addresses` from it.
|
||||||
jest.doMock("../../src/shared/balances", () => ({
|
jest.doMock("../../src/shared/balances", () => ({
|
||||||
fetchTokenBalances: jest.fn(async () => []),
|
fetchTokenBalances: jest.fn(async () => []),
|
||||||
refreshBalances: jest.fn(async () => {}),
|
refreshBalances: jest.fn(async () => {}),
|
||||||
lookupTokenInfo: jest.fn(async () => null),
|
lookupTokenInfo: jest.fn(async () => null),
|
||||||
getProvider: () => ({}),
|
getProvider: () => ({}),
|
||||||
scanForAddresses: jest.fn(async () => []),
|
scanForAddresses: jest.fn(async () => ({
|
||||||
|
addresses: [],
|
||||||
|
nextIndex: 1,
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
// filterTransactions() answers in the real one's shape: Home,
|
// filterTransactions() answers in the real one's shape: Home,
|
||||||
// AddressDetail and AddressToken read both fields, and a bare list makes
|
// AddressDetail and AddressToken read both fields, and a bare list makes
|
||||||
|
|||||||
Reference in New Issue
Block a user