From 65e1a1da7a0b7ea8873a221a7890d6c828740356 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 11:23:01 +0530 Subject: [PATCH 01/10] migration to t_accounts - new column 'accountFactoryAddress' - class DBAccount with attribute 'accountFactoryAddress' - _populateAccountFactoryAddressMigration function to populate acc ount factory address of existing account --- lib/services/db/backup/accounts.dart | 30 +++++++++++++++++++++++++++- lib/services/db/backup/db.dart | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/services/db/backup/accounts.dart b/lib/services/db/backup/accounts.dart index 13404623..9dd03fe1 100644 --- a/lib/services/db/backup/accounts.dart +++ b/lib/services/db/backup/accounts.dart @@ -17,6 +17,7 @@ class DBAccount { final String? username; EthPrivateKey? privateKey; final ProfileV1? profile; + final String accountFactoryAddress; // Add this field DBAccount({ required this.alias, @@ -25,6 +26,7 @@ class DBAccount { this.username, this.privateKey, this.profile, + required this.accountFactoryAddress, }) : id = getAccountID(address, alias), userHandle = username != null ? UserHandle(username, alias) : null; @@ -39,6 +41,7 @@ class DBAccount { 'privateKey': privateKey != null ? bytesToHex(privateKey!.privateKey) : null, if (profile != null) 'profile': jsonEncode(profile!.toJson()), + 'accountFactoryAddress': accountFactoryAddress, // Add this line }; } @@ -55,6 +58,7 @@ class DBAccount { profile: map['profile'] != null ? ProfileV1.fromJson(jsonDecode(map['profile'])) : null, + accountFactoryAddress: map['accountFactoryAddress'], ); } } @@ -96,7 +100,8 @@ class AccountsTable extends DBTable { name TEXT NOT NULL, username TEXT, privateKey TEXT, - profile TEXT + profile TEXT, + accountFactoryAddress TEXT NOT NULL ) '''; @@ -113,6 +118,10 @@ class AccountsTable extends DBTable { ], 3: [ 'ALTER TABLE $name ADD COLUMN username TEXT DEFAULT NULL', + ], + 4: [ + 'ALTER TABLE $name ADD COLUMN accountFactoryAddress TEXT DEFAULT ""', + 'PopulateAccountFactoryAddressMigration', ] }; @@ -122,6 +131,12 @@ class AccountsTable extends DBTable { if (queries != null) { for (final query in queries) { try { + switch (query) { + case 'PopulateAccountFactoryAddressMigration': + await _populateAccountFactoryAddressMigration(db, name); + continue; + } + await db.execute(query); } catch (e, s) { debugPrint('Migration error: $e'); @@ -132,6 +147,19 @@ class AccountsTable extends DBTable { } } + + Future _populateAccountFactoryAddressMigration(Database db, String name) async { + final allAccounts = await all(); + for (final account in allAccounts) { + await db.update( + name, + {'accountFactoryAddress': account.accountFactoryAddress}, + ); + } + } + + + // get account by id Future get(EthereumAddress address, String alias) async { final List> maps = await db.query( diff --git a/lib/services/db/backup/db.dart b/lib/services/db/backup/db.dart index 71ae81af..ea620ab1 100644 --- a/lib/services/db/backup/db.dart +++ b/lib/services/db/backup/db.dart @@ -40,7 +40,7 @@ class AccountBackupDBService extends DBService { return; }, - version: 3, + version: 4, ); final db = await databaseFactory.openDatabase( From ef871a15ec272f163aac50f70b524c2236304aa2 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 11:23:19 +0530 Subject: [PATCH 02/10] map of accoutn factory addresses of some aliases --- lib/services/config/utils.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/services/config/utils.dart b/lib/services/config/utils.dart index 7c8a3829..f930495f 100644 --- a/lib/services/config/utils.dart +++ b/lib/services/config/utils.dart @@ -15,3 +15,13 @@ String fixLegacyAliases(String alias) { return alias == 'localhost' || alias == '' ? defaultAlias : alias; } + +/// migrate the accounts from the accounts migration db (when migrating from old app and you want to put a value in the account secret) +/// hard coded values for these communities +/// the others just take the primary account factory +const Map migrationAccountFactoryAddresses = { + 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', + 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', +}; From d739601cb2800ea304af0fcf1ef532a710574c82 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 13:34:17 +0530 Subject: [PATCH 03/10] lookup table for v4 config primary account factory addresses --- lib/services/config/utils.dart | 36 +++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/services/config/utils.dart b/lib/services/config/utils.dart index f930495f..19177017 100644 --- a/lib/services/config/utils.dart +++ b/lib/services/config/utils.dart @@ -16,10 +16,44 @@ String fixLegacyAliases(String alias) { return alias == 'localhost' || alias == '' ? defaultAlias : alias; } +const Map configV4PrimaryAccountFactoryMap = { + 'ctzn': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'wallet.pay.brussels': '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', + 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + 'wallet.berachain.sfluv.org': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', + 'txirrin': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'boliviapay': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', + 'laborhour': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + 'rooted': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + 'seldesalm': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'my.techi.be': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'wallet.regensunite.earth': '0x9406Cc6185a346906296840746125a0E44976454', + 'gt.celo': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + 'ceur.celo': '0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098', + 'eure.polygon': '0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616', + 'app': '0x270758454C012A1f51428b68aE473D728CCdFe88', + 'usdc.base': '0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99', + 'wallet.oak.community': '0x9406Cc6185a346906296840746125a0E44976454', + 'sbc.polygon': '0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5', + 'zinne': '0x11af2639817692D2b805BcE0e1e405E530B20006', + 'timebank.regensunite.earth': '0x39b77d77f7677997871b304094a05295eb71e240', + 'moos': '0x671f0662de72268d0f3966Fb62dFc6ee6389e244', + 'selcoupdepouce': '0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284', + 'cit.celo': '0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970', + 'wallet.wolugo.be': '0x8474153A00C959f2cB64852949954DBC68415Bb3', + 'wtc.celo': '0xE79E19594A749330036280c685E2719d58d99052', + 'testnet-ethldn': '0xc1654087C580f868F08E34cd1c01eDB1d3673b82', + 'celo-c.citizenwallet.xyz': '0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA', + 'wallet.kingfishersmedia.io': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', +}; + /// migrate the accounts from the accounts migration db (when migrating from old app and you want to put a value in the account secret) /// hard coded values for these communities /// the others just take the primary account factory -const Map migrationAccountFactoryAddresses = { +const Map configV5AccountFactoryMap = { 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', From b7bf3381284055a0d05a97cd37c66e91bcccb6b7 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 15:41:21 +0530 Subject: [PATCH 04/10] feat(config): add account factory migration utility and update V4 map - Implement getAccountFactoryAddressByAlias with priority override logic. - Map old cw-safe factory (0x940C...) to new factory (0x7cC5...) for specific communities. - Update configV4PrimaryAccountFactoryMap with missing community aliases. - Clean up unused configV5AccountFactoryMap. --- lib/services/config/utils.dart | 74 ++++++++++---- test/services/config/utils_test.dart | 146 +++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 17 deletions(-) create mode 100644 test/services/config/utils_test.dart diff --git a/lib/services/config/utils.dart b/lib/services/config/utils.dart index 19177017..667df7d5 100644 --- a/lib/services/config/utils.dart +++ b/lib/services/config/utils.dart @@ -16,20 +16,29 @@ String fixLegacyAliases(String alias) { return alias == 'localhost' || alias == '' ? defaultAlias : alias; } +/// migrate the accounts from the accounts migration db (when migrating from old app and you want to put a value in the account secret) +/// hard coded values for these communities (gratitude, bread, wallet.commonshub.brussels, wallet.sfluv.org) +/// if account factory address is '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', return '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' +/// the others just take the primary account factory const Map configV4PrimaryAccountFactoryMap = { + /****cw-safe (old)*****/ 'ctzn': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', - 'wallet.pay.brussels': '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', - 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', - 'wallet.berachain.sfluv.org': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', - 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', 'txirrin': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', 'boliviapay': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'seldesalm': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'my.techi.be': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + 'wallet.kingfishersmedia.io': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + /*********/ + 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', + 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', + /****cw-safe (new)*****/ + 'wallet.berachain.sfluv.org': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', 'laborhour': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', 'rooted': '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', - 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', - 'seldesalm': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', - 'my.techi.be': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', + /*********/ + 'wallet.pay.brussels': '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', 'wallet.regensunite.earth': '0x9406Cc6185a346906296840746125a0E44976454', 'gt.celo': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', 'ceur.celo': '0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098', @@ -47,15 +56,46 @@ const Map configV4PrimaryAccountFactoryMap = { 'wtc.celo': '0xE79E19594A749330036280c685E2719d58d99052', 'testnet-ethldn': '0xc1654087C580f868F08E34cd1c01eDB1d3673b82', 'celo-c.citizenwallet.xyz': '0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA', - 'wallet.kingfishersmedia.io': '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', }; -/// migrate the accounts from the accounts migration db (when migrating from old app and you want to put a value in the account secret) -/// hard coded values for these communities -/// the others just take the primary account factory -const Map configV5AccountFactoryMap = { - 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', - 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', - 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', - 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', -}; +const String oldSafeFactory = '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2'; +const String newSafeFactory = '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185'; + +/// Returns the correct account factory address for a given community alias during database migration. +/// +/// Priority logic: +/// 1. Specific hardcoded overrides for: gratitude, bread, wallet.commonshub.brussels, wallet.sfluv.org +/// 2. Safe factory redirection: if the mapped address is '0x940Cbb155161dc0C4aade27a4826a16Ed8ca0cb2', +/// return '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' instead +/// 3. General fallback: return the address from the map +/// 4. Safety: if alias not found, return '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' +String getAccountFactoryAddressByAlias(String alias) { + // List of specific aliases that should keep their original addresses + const Set hardcodedOverrides = { + 'gratitude', + 'bread', + 'wallet.commonshub.brussels', + 'wallet.sfluv.org', + }; + + // 1. Check if this is a hardcoded override + if (hardcodedOverrides.contains(alias)) { + return configV4PrimaryAccountFactoryMap[alias]!; + } + + // Get the address from the map + final String? mappedAddress = configV4PrimaryAccountFactoryMap[alias]; + + // 4. Safety: if alias not found, return new safe factory + if (mappedAddress == null) { + return newSafeFactory; + } + + // 2. Safe factory redirection: if old safe factory, return new safe factory + if (mappedAddress == oldSafeFactory) { + return newSafeFactory; + } + + // 3. General fallback: return the mapped address + return mappedAddress; +} diff --git a/test/services/config/utils_test.dart b/test/services/config/utils_test.dart new file mode 100644 index 00000000..5fd3701e --- /dev/null +++ b/test/services/config/utils_test.dart @@ -0,0 +1,146 @@ +import 'package:citizenwallet/services/config/utils.dart'; +import 'package:test/test.dart'; + +/// Expected outcomes map for testing getAccountFactoryAddressByAlias +/// Covers all four logic branches: +/// 1. Hardcoded overrides (gratitude, bread, wallet.commonshub.brussels, wallet.sfluv.org) +/// 2. Safe factory redirection (old safe factory -> new safe factory) +/// 3. General fallback (return mapped address as-is) +/// 4. Unknown alias (return new safe factory as safety default) +const Map expectedOutcomes = { + // 1. Hardcoded Overrides - these should return their ORIGINAL addresses + 'gratitude': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + 'bread': '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', + 'wallet.commonshub.brussels': '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + 'wallet.sfluv.org': '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', + + // 2. Safe Factory Redirection - old safe factory should redirect to new safe factory + 'ctzn': newSafeFactory, + 'txirrin': newSafeFactory, + 'boliviapay': newSafeFactory, + 'seldesalm': newSafeFactory, + 'my.techi.be': newSafeFactory, + 'wallet.kingfishersmedia.io': newSafeFactory, + + // 3. General Fallback - return mapped addresses as-is + 'wallet.berachain.sfluv.org': newSafeFactory, + 'laborhour': newSafeFactory, + 'rooted': newSafeFactory, + 'wallet.pay.brussels': '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', + 'wallet.regensunite.earth': '0x9406Cc6185a346906296840746125a0E44976454', + 'gt.celo': '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + 'ceur.celo': '0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098', + 'eure.polygon': '0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616', + 'app': '0x270758454C012A1f51428b68aE473D728CCdFe88', + 'usdc.base': '0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99', + 'wallet.oak.community': '0x9406Cc6185a346906296840746125a0E44976454', + 'sbc.polygon': '0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5', + 'zinne': '0x11af2639817692D2b805BcE0e1e405E530B20006', + 'timebank.regensunite.earth': '0x39b77d77f7677997871b304094a05295eb71e240', + 'moos': '0x671f0662de72268d0f3966Fb62dFc6ee6389e244', + 'selcoupdepouce': '0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284', + 'cit.celo': '0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970', + 'wallet.wolugo.be': '0x8474153A00C959f2cB64852949954DBC68415Bb3', + 'wtc.celo': '0xE79E19594A749330036280c685E2719d58d99052', + 'testnet-ethldn': '0xc1654087C580f868F08E34cd1c01eDB1d3673b82', + 'celo-c.citizenwallet.xyz': '0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA', + + // 4. Unknown Alias - should return new safe factory as safety default + 'non-existent-alias': newSafeFactory, + 'unknown-community': newSafeFactory, + 'test-alias-not-in-map': newSafeFactory, +}; + +void main() { + group('getAccountFactoryAddressByAlias', () { + test('returns correct addresses for all test cases', () { + expectedOutcomes.forEach((alias, expectedAddress) { + final result = getAccountFactoryAddressByAlias(alias); + expect( + result, + expectedAddress, + reason: 'Failed for alias: $alias', + ); + }); + }); + + group('specific logic branch tests', () { + test('hardcoded overrides return original addresses', () { + // These four should return their original addresses, not redirected + expect( + getAccountFactoryAddressByAlias('gratitude'), + '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD', + reason: 'gratitude should return its original address', + ); + expect( + getAccountFactoryAddressByAlias('bread'), + '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9', + reason: 'bread should return its original address', + ); + expect( + getAccountFactoryAddressByAlias('wallet.commonshub.brussels'), + '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + reason: + 'wallet.commonshub.brussels should return its original address', + ); + expect( + getAccountFactoryAddressByAlias('wallet.sfluv.org'), + '0x5e987a6c4bb4239d498E78c34e986acf29c81E8e', + reason: 'wallet.sfluv.org should return its original address', + ); + }); + + test('old safe factory addresses are redirected to new safe factory', () { + // All these aliases map to old safe factory and should be redirected + final oldSafeFactoryAliases = [ + 'ctzn', + 'txirrin', + 'boliviapay', + 'seldesalm', + 'my.techi.be', + 'wallet.kingfishersmedia.io', + ]; + + for (final alias in oldSafeFactoryAliases) { + expect( + getAccountFactoryAddressByAlias(alias), + newSafeFactory, + reason: '$alias should be redirected to new safe factory', + ); + } + }); + + test('general fallback returns mapped addresses as-is', () { + // Sample of aliases that should return their mapped addresses unchanged + expect( + getAccountFactoryAddressByAlias('wallet.pay.brussels'), + '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', + ); + expect( + getAccountFactoryAddressByAlias('app'), + '0x270758454C012A1f51428b68aE473D728CCdFe88', + ); + expect( + getAccountFactoryAddressByAlias('usdc.base'), + '0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99', + ); + }); + + test('unknown aliases return new safe factory as default', () { + // Test various unknown aliases + expect( + getAccountFactoryAddressByAlias('non-existent-alias'), + newSafeFactory, + ); + expect( + getAccountFactoryAddressByAlias('unknown-community'), + newSafeFactory, + ); + expect( + getAccountFactoryAddressByAlias('random-test-123'), + newSafeFactory, + ); + }); + }); + }); +} From 7f40e82624838438d408ccbfc129fee81765eee2 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 15:55:18 +0530 Subject: [PATCH 05/10] temp remove 'accountFactoryAddress' from class 'DBAccount' --- lib/services/db/backup/accounts.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/services/db/backup/accounts.dart b/lib/services/db/backup/accounts.dart index 9dd03fe1..de761484 100644 --- a/lib/services/db/backup/accounts.dart +++ b/lib/services/db/backup/accounts.dart @@ -17,7 +17,6 @@ class DBAccount { final String? username; EthPrivateKey? privateKey; final ProfileV1? profile; - final String accountFactoryAddress; // Add this field DBAccount({ required this.alias, @@ -26,7 +25,6 @@ class DBAccount { this.username, this.privateKey, this.profile, - required this.accountFactoryAddress, }) : id = getAccountID(address, alias), userHandle = username != null ? UserHandle(username, alias) : null; @@ -41,7 +39,6 @@ class DBAccount { 'privateKey': privateKey != null ? bytesToHex(privateKey!.privateKey) : null, if (profile != null) 'profile': jsonEncode(profile!.toJson()), - 'accountFactoryAddress': accountFactoryAddress, // Add this line }; } @@ -58,7 +55,6 @@ class DBAccount { profile: map['profile'] != null ? ProfileV1.fromJson(jsonDecode(map['profile'])) : null, - accountFactoryAddress: map['accountFactoryAddress'], ); } } From 27c933cdbf539c24ab1d4c06a3f7ee77f324a657 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 16:41:15 +0530 Subject: [PATCH 06/10] feat(db): implement v4 account migration for account factory addresses - Add accountFactoryAddress column to t_accounts table. - Implement _populateAccountFactoryAddressMigration to backfill factory addresses using community aliases. - Introduce getAccountIDNew to support a more granular account indexing format. - Implement _insertRowsInNewIdFormatMigration to migrate existing rows to the new ID format ($address@$accountFactoryAddress@$alias). --- lib/services/db/backup/accounts.dart | 60 ++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/services/db/backup/accounts.dart b/lib/services/db/backup/accounts.dart index de761484..e5e5de6b 100644 --- a/lib/services/db/backup/accounts.dart +++ b/lib/services/db/backup/accounts.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:citizenwallet/services/config/utils.dart'; import 'package:citizenwallet/services/db/db.dart'; import 'package:citizenwallet/services/wallet/contracts/profile.dart'; import 'package:citizenwallet/services/wallet/wallet.dart'; @@ -63,6 +64,13 @@ String getAccountID(EthereumAddress address, String alias) { return '${address.hexEip55}@$alias'; } +String getAccountIDNew( + {required EthereumAddress address, + required String alias, + required String accountFactoryAddress}) { + return '${address.hexEip55}@$accountFactoryAddress@$alias'; +} + class UserHandle { final String username; final String communityAlias; @@ -118,6 +126,7 @@ class AccountsTable extends DBTable { 4: [ 'ALTER TABLE $name ADD COLUMN accountFactoryAddress TEXT DEFAULT ""', 'PopulateAccountFactoryAddressMigration', + 'InsertRowsInNewIdFormatMigration', // Insert the rows in the new format $address@$accountFactoryAddress@$alias ] }; @@ -131,6 +140,10 @@ class AccountsTable extends DBTable { case 'PopulateAccountFactoryAddressMigration': await _populateAccountFactoryAddressMigration(db, name); continue; + + case 'InsertRowsInNewIdFormatMigration': + await _insertRowsInNewIdFormatMigration(db, name); + continue; } await db.execute(query); @@ -143,18 +156,57 @@ class AccountsTable extends DBTable { } } + Future _populateAccountFactoryAddressMigration( + Database db, String name) async { + // Work directly with raw DB data, not DBAccount objects + List> accounts = await db.query(name); + + for (final Map account in accounts) { + final alias = account['alias'] as String; + final oldId = account['id'] as String; + + final accountFactoryAddress = getAccountFactoryAddressByAlias(alias); - Future _populateAccountFactoryAddressMigration(Database db, String name) async { - final allAccounts = await all(); - for (final account in allAccounts) { + // Update the accountFactoryAddress column (ID still in old format $address@$alias) await db.update( name, - {'accountFactoryAddress': account.accountFactoryAddress}, + {'accountFactoryAddress': accountFactoryAddress}, + where: 'id = ?', + whereArgs: [oldId], ); } } + Future _insertRowsInNewIdFormatMigration( + Database db, String name) async { + List> accounts = await db.query(name); // + for (final Map account in accounts) { + final address = account['address'] as String; + final accountFactoryAddress = account['accountFactoryAddress'] as String; + final alias = account['alias'] as String; + + final newId = getAccountIDNew( + address: EthereumAddress.fromHex(address), + alias: alias, + accountFactoryAddress: accountFactoryAddress); + + await db.insert( + name, + { + 'id': newId, + 'alias': account['alias'], + 'address': account['address'], + 'accountFactoryAddress': account['accountFactoryAddress'], + 'name': account['name'], + 'username': account['username'], + 'privateKey': account['privateKey'], + 'profile': account['profile'], + }, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + } // get account by id Future get(EthereumAddress address, String alias) async { From 33e8601d48ca280f910a49e36356769c9641ce91 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 17:26:25 +0530 Subject: [PATCH 07/10] todo --- lib/services/db/backup/accounts.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/db/backup/accounts.dart b/lib/services/db/backup/accounts.dart index e5e5de6b..0a8bb868 100644 --- a/lib/services/db/backup/accounts.dart +++ b/lib/services/db/backup/accounts.dart @@ -127,6 +127,7 @@ class AccountsTable extends DBTable { 'ALTER TABLE $name ADD COLUMN accountFactoryAddress TEXT DEFAULT ""', 'PopulateAccountFactoryAddressMigration', 'InsertRowsInNewIdFormatMigration', // Insert the rows in the new format $address@$accountFactoryAddress@$alias + // TODO: delete the rows in the old format $address@$alias ] }; From 71680fd696466f2d68e5f7c7b905901dcc3958d8 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 17:28:38 +0530 Subject: [PATCH 08/10] TODO --- lib/services/config/config.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/services/config/config.dart b/lib/services/config/config.dart index 82e36416..4316b913 100644 --- a/lib/services/config/config.dart +++ b/lib/services/config/config.dart @@ -726,6 +726,8 @@ class Config { return primaryAccountAbstraction; } + // TODO: force required accountFactoryAddress + // TODO: remove use of getPrimaryAccountAbstractionConfig ERC4337Config getAccountAbstractionConfig({String? accountFactoryAddress}) { // If no accountFactoryAddress is provided, return the primary config if (accountFactoryAddress == null || accountFactoryAddress.isEmpty) { @@ -762,6 +764,7 @@ class Config { return chain.node.url; } + // TODO: force required accountFactoryAddress String getRpcUrl(String chainId, {String? accountFactoryAddress}) { final chain = chains[chainId]; From 59250fb32d11aa6285cf15f017cd2bc64ba8251f Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Wed, 17 Dec 2025 17:36:31 +0530 Subject: [PATCH 09/10] todo --- lib/services/config/config.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/config/config.dart b/lib/services/config/config.dart index 4316b913..5c263d7e 100644 --- a/lib/services/config/config.dart +++ b/lib/services/config/config.dart @@ -715,6 +715,7 @@ class Config { return primaryToken; } +// TODO: remove use of getPrimaryAccountAbstractionConfig ERC4337Config getPrimaryAccountAbstractionConfig() { final primaryAccountAbstraction = accounts[community.primaryAccountFactory.fullAddress]; From 00fb3ae92df9cf4d95465f8c4f1400e803e386a4 Mon Sep 17 00:00:00 2001 From: sajee_techi Date: Thu, 18 Dec 2025 11:24:20 +0530 Subject: [PATCH 10/10] todo --- lib/services/wallet/contracts/profile.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/wallet/contracts/profile.dart b/lib/services/wallet/contracts/profile.dart index e2296980..d5c1ee71 100644 --- a/lib/services/wallet/contracts/profile.dart +++ b/lib/services/wallet/contracts/profile.dart @@ -167,10 +167,12 @@ class ProfileContract { rcontract = DeployedContract(cabi, EthereumAddress.fromHex(addr)); } +// TODO: await return Future getURL(String addr) async { return contract.get(EthereumAddress.fromHex(addr)); } +// TODO: await return Future getURLFromUsername(String username) async { return contract.getFromUsername( convertStringToUint8List(username, forcePadLength: 32));