diff --git a/android/app/build.gradle b/android/app/build.gradle index f5a2eb71..f1fe9cc2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -52,7 +52,7 @@ android { applicationId "xyz.citizenwallet.wallet" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion Math.max(flutter.minSdkVersion, 21) + minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/settings.gradle b/android/settings.gradle index 38584c19..fa553851 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.9.0' apply false + id "com.android.application" version '8.9.1' apply false id "org.jetbrains.kotlin.android" version "2.1.10" apply false id "com.google.gms.google-services" version "4.4.0" apply false } diff --git a/assets/config/v4/communities.json b/assets/config/v4/communities.json index 1e3f8688..9a2ae859 100644 --- a/assets/config/v4/communities.json +++ b/assets/config/v4/communities.json @@ -161,6 +161,19 @@ "action": "topup", "signature": true, "featured": true + }, + { + "name": "Offboard", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "url": "https://my.citizenpay.xyz/cw-migrate", + "launch_mode": "webview", + "signature": true, + "action": "offboard", + "meta": { + "title": "Migrate to Citizen Pay", + "desc": "This community has moved. Migrate your balance over to the Citizen Pay App.", + "button": "Start Migration" + } } ], "config_location": "https://wallet.pay.brussels/config/community.json", diff --git a/assets/config/v4/communities.test.json b/assets/config/v4/communities.test.json index e3d83def..086c4b3d 100644 --- a/assets/config/v4/communities.test.json +++ b/assets/config/v4/communities.test.json @@ -161,6 +161,19 @@ "action": "topup", "signature": true, "featured": true + }, + { + "name": "Offboard", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "url": "https://my.citizenpay.xyz/cw-migrate", + "launch_mode": "webview", + "signature": true, + "action": "offboard", + "meta": { + "title": "Migrate to Citizen Pay", + "desc": "This community has moved. Migrate your balance over to the Citizen Pay App.", + "button": "Start Migration" + } } ], "config_location": "https://wallet.pay.brussels/config/community.json", diff --git a/assets/config/v5/communities.json b/assets/config/v5/communities.json new file mode 100644 index 00000000..3c8b8a1e --- /dev/null +++ b/assets/config/v5/communities.json @@ -0,0 +1,2483 @@ +[ + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "cards": { + "137:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 137, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607": { + "name": "Citizen Wallet", + "symbol": "CTZN", + "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://citizenwallet.xyz/pay-with-ctzn", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "About", + "launch_mode": "browser" + }, + { + "url": "https://my.citizenwallet.xyz/onramp", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "Top Up", + "action": "topup", + "signature": true + } + ], + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x3A3E25871c5C6C84D5f397829FF316a37F7FD596", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "Citizen Wallet (CTZN)", + "alias": "ctzn", + "theme": { + "primary": "#9563D3" + }, + "profile": { + "address": "0x8dA817724Eb6A2aA47c0F8d8b8A98b9B3C2Ddb68", + "chain_id": 137 + }, + "description": "The token powering the Citizen Wallet economy.", + "primary_token": { + "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", + "chain_id": 137 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/ctzn" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0x1EaF6B6A6967608aF6c77224f087b042095891EB": { + "type": "classic", + "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", + "chain_id": 100 + }, + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "brussels-pay" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://engine.pay.brussels", + "ws_url": "wss://engine.pay.brussels" + } + } + }, + "tokens": { + "100:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "pay.brussels", + "symbol": "EURb", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://checkout.pay.brussels/topup", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "name": "Top Up", + "action": "topup", + "signature": true + }, + { + "name": "Migrate to Citizen Pay", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "url": "https://checkout.citizenpay.xyz/cw-cp-migration", + "launch_mode": "webview", + "signature": true, + "hidden": true, + "action": "offboard", + "meta": { + "title": "Brussels Pay is moving", + "desc": "In order to focus our support efforts we are moving to Citizen Pay. It is a very simple migration process, you will just send your tokens to your new Citizen Pay account. More information on the next page.", + "button": "Let's go" + } + } + ], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0xE69C843898E21C0E95eA7DD310cD850AAc0aB897", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "100:0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE": { + "chain_id": 100, + "paymaster_type": "cw", + "paymaster_address": "0xcA1B9EC1117340818C1c1fdd1B48Ea79E57C140F", + "entrypoint_address": "0xAAEb9DC18aDadae9b3aE7ec2b47842565A81113f", + "account_factory_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://pay.brussels", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "name": "Brussels Pay", + "alias": "wallet.pay.brussels", + "theme": { + "primary": "#4a90e2" + }, + "hidden": false, + "closed": true, + "profile": { + "address": "0x56Cc38bDa01bE6eC6D854513C995f6621Ee71229", + "chain_id": 100 + }, + "description": "A community for the city of Brussels", + "custom_domain": "wallet.pay.brussels", + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.pay.brussels" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "cards": { + "42220:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "Gratitude Token", + "symbol": "GT", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220, + "decimals": 0, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xF05ba2641b31AF70c2678e3324eD8b9C53093FbE", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/gratitude", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", + "name": "Gratitude Token", + "alias": "gratitude", + "theme": { + "primary": "#4EC19D" + }, + "profile": { + "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", + "chain_id": 42220 + }, + "description": "Express your gratitude towards someone by sending them a token of gratitude.", + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/gratitude" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "cards": { + "80094:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 80094, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "80094": { + "id": 80094, + "node": { + "url": "https://80094.engine.citizenwallet.xyz", + "ws_url": "wss://80094.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "80094:0x881cad4f885c6701d8481c0ed347f6d35444ea7e": { + "name": "SFLUV V1.1", + "symbol": "SFLUV", + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "chain_id": 80094, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://app.sfluv.org", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "About", + "hidden": true, + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "80094:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 80094, + "paymaster_type": "cw-safe", + "paymaster_address": "0x9A5be02B65f9Aa00060cB8c951dAFaBAB9B860cd", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "80094:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 80094, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://sfluv.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "SFLUV Community", + "alias": "wallet.berachain.sfluv.org", + "theme": { + "primary": "#eb6c6c" + }, + "profile": { + "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", + "chain_id": 80094 + }, + "description": "A community currency for the city of San Francisco.", + "custom_domain": "wallet.sfluv.org", + "primary_token": { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "chain_id": 80094 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 80094 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 80094 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 80094 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.berachain.sfluv.org" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x58a2993A618Afee681DE23dECBCF535A58A080BA": { + "name": "SFLUV V1.1", + "symbol": "SFLUV", + "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://app.sfluv.org", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "About", + "hidden": true, + "signature": true, + "launch_mode": "webview" + }, + { + "url": "https://sfluv.org/communitymigration", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "Offoard", + "hidden": true, + "signature": true, + "launch_mode": "webview", + "action": "offboard", + "meta": { + "title": "SFLUV has Migrated", + "desc": "SFLUV has migrated from Polygon to BeraChain, so this community (SFLUV Polygon) has been closed. Click the link below to create an account in the new SFLUV community. Your SFLUV Polygon balance will be transferred over once you have a new account.", + "button": "Migrate" + } + }, + { + "url": "https://app.sfluv.org/map", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "Map", + "featured": true, + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "137:0x5e987a6c4bb4239d498E78c34e986acf29c81E8e": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0x7FC98D0a2bd7f766bAca37388eB0F6Db37666B33", + "entrypoint_address": "0x2d01C5E40Aa6a8478eD0FFbF2784EBb9bf67C46A", + "account_factory_address": "0x5e987a6c4bb4239d498E78c34e986acf29c81E8e" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://sfluv.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "SFLUV Community (Polygon)", + "alias": "wallet.sfluv.org", + "hidden": true, + "closed": true, + "theme": { + "primary": "#eb6c6c" + }, + "profile": { + "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", + "chain_id": 137 + }, + "description": "A community currency for the city of San Francisco.", + "custom_domain": "wallet.polygon.sfluv.org", + "primary_token": { + "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x5e987a6c4bb4239d498E78c34e986acf29c81E8e", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.sfluv.org" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0x6c6611244547a6E9AaCfBA8744115ca1076756fc": { + "name": "Txirrin", + "symbol": "TXI", + "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x33500E7Eb3452421e56c2f4117530B1C4C85E0A5", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/txirrin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/txirrin.png", + "name": "Txirrin", + "alias": "txirrin", + "theme": { + "primary": "#FB7502" + }, + "hidden": false, + "profile": { + "address": "0xd47f7198bf335bfe66dD29C0f3EeEf0cFE9D05D8", + "chain_id": 100 + }, + "description": "A community for Txirrin", + "primary_token": { + "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/txirrin" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { + "name": "(PoS) Tether USD", + "symbol": "USDT", + "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x9a81Bd50D56485Cc863Ecb169812c7a821996C8c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://www.ethereumbolivia.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/boliviapay.png", + "name": "Bolivia Pay", + "alias": "boliviapay", + "theme": { + "primary": "#009393" + }, + "profile": { + "address": "0x898C2737f2Cb52622711A89D85A1D5E0B881BDeA", + "chain_id": 137 + }, + "description": "A community for Ethereum Bolivia.", + "primary_token": { + "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/boliviapay" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3": { + "name": "Breadchain Community Token", + "symbol": "BREAD", + "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", + "chain_id": 100, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://topup.citizenspring.earth/bread", + "icon": "https://bread.citizenwallet.xyz/uploads/logo.svg", + "name": "Top Up", + "action": "topup" + }, + { + "url": "https://marketplace.citizenwallet.xyz/bread", + "icon": "https://bread.citizenwallet.xyz/uploads/logo.svg", + "name": "Market", + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x5987e57e85014B5A56C880313580346c20a5d1c1", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "100:0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9": { + "chain_id": 100, + "paymaster_type": "cw", + "paymaster_address": "0xbE2Cb3358aa14621134e923B68b8429315368E32", + "entrypoint_address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", + "account_factory_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://breadchain.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/bread.svg", + "name": "Breadchain Community Token", + "alias": "bread", + "profile": { + "address": "0x6b3a1f4277391526413F583c23D5B9EF4d2fE986", + "chain_id": 100 + }, + "description": "BREAD is a digital community token and solidarity primitive developed by Breadchain Cooperative that generates yield for post-capitalist organizations", + "primary_token": { + "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/bread" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007": { + "name": "Labor Hour Token", + "symbol": "HOUR", + "address": "0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 4, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0xa7fa16C933f51d8623f39FA0dF34D3065B99Bd1c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://breadchain.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/laborhour.png", + "name": "Labor Hour Token", + "alias": "laborhour", + "theme": { + "primary": "#E52D2D" + }, + "profile": { + "address": "0x673601Eb36820bC9718214AC041E96f79383351B", + "chain_id": 100 + }, + "description": "Labor Hour Token aims to reward contributors for hours of labor, particularly targeting non-blockchain native users", + "primary_token": { + "address": "0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://config.internal.citizenwallet.xyz/v4/laborhour.citizenwallet.xyz.json" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "8453:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 8453, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x3595ca37596d5895b70efab592ac315d5b9809b2": { + "name": "Azos Stablecoin", + "symbol": "AZUSD", + "address": "0x3595ca37596d5895b70efab592ac315d5b9809b2", + "chain_id": 8453, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0x02cd43b3d7a946dCaa8dF12ac0A6Cd50aF9cDd64", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "8453:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 8453, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0x0c2e78DB6FaF8D4d68984A830339764AD9f800D4" + } + }, + "community": { + "url": "https://rooted.finance/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rooted.png", + "name": "Rooted.Finance", + "alias": "rooted", + "theme": { + "primary": "#ff4c02" + }, + "profile": { + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 8453 + }, + "description": "Rooted in your values.", + "primary_token": { + "address": "0x3595ca37596d5895b70efab592ac315d5b9809b2", + "chain_id": 8453 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/communities/rooted" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "cards": { + "42220:0xc0F9e0907C8de79fd5902b61e463dFEdc5dc8570": { + "type": "classic", + "address": "0xc0F9e0907C8de79fd5902b61e463dFEdc5dc8570", + "chain_id": 42220 + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x65DD32834927de9E57E72a3E2130a19f81C6371D": { + "name": "Commons Hub Token", + "symbol": "CHT", + "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/wallet.commonshub.brussels", + "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x4E127A1DAa66568B4a91E8c5615120a6Ea5442E3", + "entrypoint_address": "0xb7608dDA592d319687C89c4479e320b5a7740117", + "account_factory_address": "0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x4860C0f127500F0cbF4a5Bd797cBb5aA50Eb0FbA", + "entrypoint_address": "0xb7608dDA592d319687C89c4479e320b5a7740117", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://commonshub.brussels", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/chb.png", + "name": "Commons Hub Brussels", + "alias": "wallet.commonshub.brussels", + "theme": { + "primary": "#ff4c02" + }, + "profile": { + "address": "0xc06bE1BbbeEAF2f34F3d5b76069D2560aee184Ae", + "chain_id": 42220 + }, + "description": "Community Token for the Commons Hub Brussels community", + "custom_domain": "wallet.commonshub.brussels", + "primary_token": { + "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.commonshub.brussels" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "cards": { + "42220:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220, + "instance_id": "cw-seldesalm" + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2": { + "name": "Myrtille", + "symbol": "MYRT", + "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Informations Générales", + "launch_mode": "webview" + }, + { + "url": "https://marketplace.citizenwallet.xyz/seldesalm", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Échanges", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xd07412020dA5054c3b49f47Ca61224637F1703af", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Sel de Salm", + "alias": "seldesalm", + "theme": { + "primary": "#6B5CA4" + }, + "profile": { + "address": "0x4083724953cC1cC13e76b436149B2b1e1a3E5970", + "chain_id": 42220 + }, + "description": "La communauté de Sel de Salm", + "primary_token": { + "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", + "chain_id": 42220 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/seldesalm" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://engine.my.techi.be", + "ws_url": "wss://engine.my.techi.be" + } + } + }, + "tokens": { + "100:0x01D0E7117510b371Ac38f52Cc6689ff8875280FA": { + "name": "TECHI", + "symbol": "TECHI", + "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x68c5a20f233264DB124a3c95a200bbD20b3b9762", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://my.techi.be", + "logo": "https://my.techi.be/assets/token.svg", + "name": "TECHI", + "alias": "my.techi.be", + "theme": { + "primary": "#617FF8" + }, + "hidden": false, + "profile": { + "address": "0x80C141861607b8FEfD53C9E71a9c7D2D3e2e76dc", + "chain_id": 100 + }, + "description": "A community for TECHI users", + "primary_token": { + "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/my.techi.be" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { + "name": "Regens Unite Token", + "symbol": "RGN", + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x250711045d58b6310f0635C7D110BFe663cE1da5", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "137:0x9406Cc6185a346906296840746125a0E44976454": { + "chain_id": 137, + "paymaster_type": "payg", + "paymaster_address": "0x250711045d58b6310f0635C7D110BFe663cE1da5", + "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "gas_extra_percentage": 50, + "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454" + } + }, + "community": { + "url": "https://www.regensunite.earth/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Regens Unite", + "alias": "wallet.regensunite.earth", + "hidden": true, + "profile": { + "address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", + "chain_id": 137 + }, + "description": "A community currency for the Regens Unite community.", + "custom_domain": "wallet.regensunite.earth", + "primary_token": { + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.regensunite.earth" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "Gratitude Token", + "symbol": "GT", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220, + "decimals": 0, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD" + } + }, + "community": { + "url": "https://citizenwallet.xyz/gratitude", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", + "name": "Gratitude Token", + "alias": "gt.celo", + "theme": { + "primary": "#a256ff" + }, + "hidden": true, + "profile": { + "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", + "chain_id": 42220 + }, + "description": "Express your gratitude towards someone by sending them a token.", + "custom_domain": null, + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/gt.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73": { + "name": "Celo Euro", + "symbol": "cEUR", + "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", + "chain_id": 42220, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F", + "entrypoint_address": "0xc3142BCBA2285d0a48A38e7Ea9Cbf28a12B235bB", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F", + "entrypoint_address": "0xc3142BCBA2285d0a48A38e7Ea9Cbf28a12B235bB", + "account_factory_address": "0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098" + } + }, + "community": { + "url": "https://celo.org/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ceur.svg", + "name": "Celo Euro", + "alias": "ceur.celo", + "theme": { + "primary": "#a256ff" + }, + "hidden": true, + "profile": { + "address": "0x0334C579E61aF6922D5deFEF02A361FBb2D6f406", + "chain_id": 42220 + }, + "description": "Celo Euro is a stablecoin for the Celo Community.", + "primary_token": { + "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/ceur.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6": { + "name": "EUR emoney", + "symbol": "EURe", + "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec", + "entrypoint_address": "0x2027Bde7C276D5F128587E3107c68A488ee31c72", + "gas_extra_percentage": 50, + "account_factory_address": "0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://monerium.com/tokens/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/eure.svg", + "name": "EUR e-money", + "alias": "eure.polygon", + "hidden": true, + "profile": { + "address": "0xF5F7317EDb8E88CaE09071B0C4F0fd6EA20B21f9", + "chain_id": 137 + }, + "description": "Token by Monerium EMI, a regulated entity, licensed in the EEA.", + "primary_token": { + "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/eure.polygon" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174": { + "name": "USD Coin", + "symbol": "USDC", + "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x270758454C012A1f51428b68aE473D728CCdFe88": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xB5D1C0167E6325466E2918e9fda8cc41384C0291", + "entrypoint_address": "0x466AA6ed2B7Bb829841F5aAEA9e82B840eC0feF9", + "gas_extra_percentage": 50, + "account_factory_address": "0x270758454C012A1f51428b68aE473D728CCdFe88" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xB5D1C0167E6325466E2918e9fda8cc41384C0291", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", + "name": "USDC on Polygon", + "alias": "app", + "theme": { + "primary": "#0052ff" + }, + "hidden": true, + "profile": { + "address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "chain_id": 137 + }, + "description": "The community of people using USDC on Polygon.", + "primary_token": { + "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/app" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://basescan.org", + "name": "Base Explorer" + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": { + "name": "USD Coin", + "symbol": "USDC", + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "chain_id": 8453, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99": { + "chain_id": 8453, + "paymaster_type": "cw", + "paymaster_address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "entrypoint_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", + "gas_extra_percentage": 50, + "account_factory_address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99" + }, + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", + "name": "USDC on Base", + "alias": "usdc.base", + "theme": { + "primary": "#0052ff" + }, + "hidden": true, + "profile": { + "address": "0x51Ef5Add405CCF63c206A80AF8c2B3cEE0282830", + "chain_id": 8453 + }, + "description": "The community of people using USDC on Base.", + "primary_token": { + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/usdc.base" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://basescan.org", + "name": "Base Explorer" + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x845598Da418890a674cbaBA26b70807aF0c61dFE": { + "name": "OAK Community Currency", + "symbol": "OAK", + "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", + "chain_id": 8453, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0x123", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "8453:0x9406Cc6185a346906296840746125a0E44976454": { + "chain_id": 8453, + "paymaster_type": "payg", + "paymaster_address": "0x123", + "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454" + } + }, + "community": { + "url": "https://www.oak.community/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/oak.svg", + "name": "OAK Community", + "alias": "wallet.oak.community", + "hidden": true, + "profile": { + "address": "0xFE213c74e25505B232CE4C7f89647408bE6f71d2", + "chain_id": 8453 + }, + "description": "A community currency for the city of Oakland.", + "custom_domain": "wallet.oak.community", + "primary_token": { + "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.oak.community" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798": { + "name": "Stable Coin", + "symbol": "SBC", + "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0x123", + "entrypoint_address": "0xe84423Ba1A3f3535B09237245e22dBda5E27eB88", + "account_factory_address": "0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x123", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://brale.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sbc.svg", + "name": "Stable Coin", + "alias": "sbc.polygon", + "hidden": true, + "profile": { + "address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", + "chain_id": 137 + }, + "description": "SBC is a digital dollar stablecoin issued by Brale", + "primary_token": { + "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/sbc.polygon" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D": { + "name": "Zinne.brussels Token", + "symbol": "ZINNE", + "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x11af2639817692D2b805BcE0e1e405E530B20006": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b", + "entrypoint_address": "0xF5507B3042f1C63625D856a2ABFF046243A5D74e", + "account_factory_address": "0x11af2639817692D2b805BcE0e1e405E530B20006" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://zinne.brussels", + "logo": "https://citizenwallet.xyz/zinne/zinne-coin.svg", + "name": "Zinne.brussels", + "alias": "zinne", + "hidden": true, + "profile": { + "address": "0x23DB3D3Da510e60aF40902A04850E1F3a744905c", + "chain_id": 137 + }, + "description": "A community currency for the city of Brussels", + "primary_token": { + "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/zinne" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x186DaBD027e228C988777907465807FDab270894": { + "name": "Regen Hour", + "symbol": "rHour", + "address": "0x186DaBD027e228C988777907465807FDab270894", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/timebank.regensunite.earth", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x39b77d77f7677997871b304094a05295eb71e240": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xe45858bf63176595c2920822581917c7C705a12f", + "entrypoint_address": "0x41176F0C9b8f795Cb99e2DD5Db16017978eeFa4d", + "account_factory_address": "0x39b77d77f7677997871b304094a05295eb71e240" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xe45858bf63176595c2920822581917c7C705a12f", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://regensunite.earth", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Regens Unite Time Bank", + "alias": "timebank.regensunite.earth", + "hidden": true, + "profile": { + "address": "0x605A827DF8C405D16Ec70AAb8d9a47D21db45c09", + "chain_id": 42220 + }, + "description": "Make time to regen", + "primary_token": { + "address": "0x186DaBD027e228C988777907465807FDab270894", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/timebank.regensunite.earth" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3": { + "name": "MOOS Token", + "symbol": "MOOS", + "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/moos", + "icon": "https://moos.citizenwallet.xyz/wallet-config/_images/moos.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x671f0662de72268d0f3966Fb62dFc6ee6389e244": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482", + "entrypoint_address": "0x45a8e6AaDCc48D1Ce19eCbE07Ccd3a536EF712ed", + "account_factory_address": "0x671f0662de72268d0f3966Fb62dFc6ee6389e244" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://www.moos.garden/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/moos.svg", + "name": "MOOS Token", + "alias": "moos", + "hidden": true, + "profile": { + "address": "0x2e4542Be47408d05F41703386eFaf4338Ee1D341", + "chain_id": 42220 + }, + "description": "A community currency for MOOS.", + "primary_token": { + "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/moos" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3": { + "name": "Bonne Heure", + "symbol": "BHR", + "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/selcoupdepouce", + "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", + "name": "Market", + "signature": true, + "launch_mode": "browser" + } + ], + "version": 5, + "accounts": { + "42220:0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x635032605337aB36A46D767905108e67EE687a72", + "entrypoint_address": "0xA90904F33df36899d810d040b8d5b3b77265Bb05", + "account_factory_address": "0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x635032605337aB36A46D767905108e67EE687a72", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://selcoupdepouce.be", + "logo": "https://topup.citizenwallet.xyz/communities/selcoupdepouce/sel-coin.svg", + "name": "Bonne Heure", + "alias": "selcoupdepouce", + "hidden": true, + "profile": { + "address": "0xfB8F1e7ED42599638B3c509679E2F43937002C56", + "chain_id": 42220 + }, + "description": "Système d'Échange Local de Villers-la-Ville", + "primary_token": { + "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/selcoupdepouce" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9": { + "name": "CI token", + "symbol": "CIT", + "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x452F7ff3e55fe29f481841985dE7f4939FD645fa", + "entrypoint_address": "0xB8d9412f3A91A00ca762B5c35cd0863E9b716D68", + "account_factory_address": "0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x452F7ff3e55fe29f481841985dE7f4939FD645fa", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://citizenwallet.xyz/cit", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/cit.celo.svg", + "name": "CI token", + "alias": "cit.celo", + "hidden": true, + "profile": { + "address": "0x4cB296BEc9FAd0B5e1E4FF1A2F307B425724AC82", + "chain_id": 42220 + }, + "description": "Monnaie locale du Cercle Informatique de l’ULB", + "primary_token": { + "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/cit.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "cards": { + "42220:0xA3E1446E332a098A1f3b0555c5d149b4784A095F": { + "type": "classic", + "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", + "chain_id": 42220 + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451": { + "name": "Wolu", + "symbol": "WOLU", + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0x8474153A00C959f2cB64852949954DBC68415Bb3": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c", + "entrypoint_address": "0x0F805BC1ED718FB9C7C18439cB11E1C17C6538C4", + "account_factory_address": "0x8474153A00C959f2cB64852949954DBC68415Bb3" + } + }, + "community": { + "url": "https://wolugo.be", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/wolugo.svg", + "name": "Wolugo", + "alias": "wallet.wolugo.be", + "theme": { + "primary": "#81e2c1" + }, + "hidden": true, + "profile": { + "address": "0x07e7b95B35866302b3A089feF4CFA3061061a51d", + "chain_id": 42220 + }, + "description": "A community for the Woluwe-Saint-Pierre civic engagement platform", + "card_factory": { + "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", + "chain_id": 42220 + }, + "custom_domain": "wallet.wolugo.be", + "primary_token": { + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.wolugo.be" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0xc53Cb35591959cA62471dA9fF6AC16629A89874a": { + "name": "Woluwe Test Coin", + "symbol": "WTC", + "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xE79E19594A749330036280c685E2719d58d99052": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9", + "entrypoint_address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", + "account_factory_address": "0xE79E19594A749330036280c685E2719d58d99052" + } + }, + "community": { + "url": "https://wollet-v2.citizenwallet.net/token", + "logo": "https://wtc.celo.citizenwallet.xyz/wallet-config/_images/wtc.celo.svg", + "name": "Woluwe Test", + "alias": "wtc.celo", + "hidden": true, + "profile": { + "address": "0xB99a7B1574f051020EB4cb2fce5d48EE07592AfF", + "chain_id": 42220 + }, + "description": "Local currency for the Woluwe Test community.", + "primary_token": { + "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wtc.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://sepolia.basescan.org", + "name": "Base Sepolia Explorer" + }, + "chains": { + "84532": { + "id": 84532, + "node": { + "url": "https://84532.engine.citizenwallet.xyz", + "ws_url": "wss://84532.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "84532:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { + "name": "ETHGlobal London Token", + "symbol": "ETHLDN", + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 84532, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "84532:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 84532, + "paymaster_type": "cw-safe", + "paymaster_address": "0x389182aCCeE26D953d5188BF4b92c49339DcC9FC", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "84532:0xc1654087C580f868F08E34cd1c01eDB1d3673b82": { + "chain_id": 84532, + "paymaster_type": "cw", + "paymaster_address": "0x389182aCCeE26D953d5188BF4b92c49339DcC9FC", + "entrypoint_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE", + "account_factory_address": "0xc1654087C580f868F08E34cd1c01eDB1d3673b82" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/testnet-ethldn.svg", + "name": "ETHGlobal London Token", + "alias": "testnet-ethldn", + "hidden": true, + "profile": { + "address": "0x0785D720279f42326846D5396b5F44b97d0BfECd", + "chain_id": 84532 + }, + "description": "The community of people using ETHLDN on Base.", + "primary_token": { + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 84532 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 84532 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/testnet-ethldn" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x3C960E72BBbD837293e75080E1d0Fee6a4640357": { + "name": "Celo Community Point", + "symbol": "CeloC", + "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x7f4011845Ea914b6cefc60629e1e00600c972c75", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x7f4011845Ea914b6cefc60629e1e00600c972c75", + "entrypoint_address": "0x66fE9c22CcA49B257dd4F00508AC90198d99Bf27", + "account_factory_address": "0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA" + } + }, + "community": { + "url": "https://citizenwallet.xyz", + "logo": "https://celo-c.citizenwallet.xyz/uploads/logo.svg", + "name": "Celo Community Point", + "alias": "celo-c.citizenwallet.xyz", + "hidden": true, + "profile": { + "address": "0x14004E13907282cFaD05f742022E56926eE92dAd", + "chain_id": 42220 + }, + "description": "This is a community for the Celo Point", + "primary_token": { + "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/celo-c.citizenwallet.xyz" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x56744910f7dEcD48c1a7FA61B4C317b15E99F156": { + "name": "KFMEDIA℠ Pathways for LATAM™", + "symbol": "KFMPFL", + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220, + "decimals": 0, + "standard": "erc1155" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x02BDA8370d9497A5C808B2db237cfaA8f0733F36", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://kingfishersmedia.io", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/kfmpfl.png", + "name": "KFMEDIA℠", + "alias": "wallet.kingfishersmedia.io", + "theme": { + "primary": "#88292c" + }, + "profile": { + "address": "0x5f6FEb03ad8EfeCdD2a837FAA1a29DEA2bAcfd55", + "chain_id": 42220 + }, + "description": "Certified Education Organization. Solving systemic educational disparity using Web3 solutions, removing barriers of entry for underdeveloped economies.", + "custom_domain": "wallet.kingfishersmedia.io", + "primary_token": { + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.kingfishersmedia.io" + } +] diff --git a/assets/config/v5/communities.test.json b/assets/config/v5/communities.test.json new file mode 100644 index 00000000..3c8b8a1e --- /dev/null +++ b/assets/config/v5/communities.test.json @@ -0,0 +1,2483 @@ +[ + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "cards": { + "137:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 137, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607": { + "name": "Citizen Wallet", + "symbol": "CTZN", + "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://citizenwallet.xyz/pay-with-ctzn", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "About", + "launch_mode": "browser" + }, + { + "url": "https://my.citizenwallet.xyz/onramp", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "Top Up", + "action": "topup", + "signature": true + } + ], + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x3A3E25871c5C6C84D5f397829FF316a37F7FD596", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ctzn.svg", + "name": "Citizen Wallet (CTZN)", + "alias": "ctzn", + "theme": { + "primary": "#9563D3" + }, + "profile": { + "address": "0x8dA817724Eb6A2aA47c0F8d8b8A98b9B3C2Ddb68", + "chain_id": 137 + }, + "description": "The token powering the Citizen Wallet economy.", + "primary_token": { + "address": "0x0D9B0790E97e3426C161580dF4Ee853E4A7C4607", + "chain_id": 137 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/ctzn" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0x1EaF6B6A6967608aF6c77224f087b042095891EB": { + "type": "classic", + "address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", + "chain_id": 100 + }, + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "brussels-pay" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://engine.pay.brussels", + "ws_url": "wss://engine.pay.brussels" + } + } + }, + "tokens": { + "100:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "pay.brussels", + "symbol": "EURb", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://checkout.pay.brussels/topup", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "name": "Top Up", + "action": "topup", + "signature": true + }, + { + "name": "Migrate to Citizen Pay", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "url": "https://checkout.citizenpay.xyz/cw-cp-migration", + "launch_mode": "webview", + "signature": true, + "hidden": true, + "action": "offboard", + "meta": { + "title": "Brussels Pay is moving", + "desc": "In order to focus our support efforts we are moving to Citizen Pay. It is a very simple migration process, you will just send your tokens to your new Citizen Pay account. More information on the next page.", + "button": "Let's go" + } + } + ], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0xE69C843898E21C0E95eA7DD310cD850AAc0aB897", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "100:0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE": { + "chain_id": 100, + "paymaster_type": "cw", + "paymaster_address": "0xcA1B9EC1117340818C1c1fdd1B48Ea79E57C140F", + "entrypoint_address": "0xAAEb9DC18aDadae9b3aE7ec2b47842565A81113f", + "account_factory_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://pay.brussels", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/wallet.pay.brussels.png", + "name": "Brussels Pay", + "alias": "wallet.pay.brussels", + "theme": { + "primary": "#4a90e2" + }, + "hidden": false, + "closed": true, + "profile": { + "address": "0x56Cc38bDa01bE6eC6D854513C995f6621Ee71229", + "chain_id": 100 + }, + "description": "A community for the city of Brussels", + "custom_domain": "wallet.pay.brussels", + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.pay.brussels" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "cards": { + "42220:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "Gratitude Token", + "symbol": "GT", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220, + "decimals": 0, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xF05ba2641b31AF70c2678e3324eD8b9C53093FbE", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/gratitude", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", + "name": "Gratitude Token", + "alias": "gratitude", + "theme": { + "primary": "#4EC19D" + }, + "profile": { + "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", + "chain_id": 42220 + }, + "description": "Express your gratitude towards someone by sending them a token of gratitude.", + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/gratitude" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "cards": { + "80094:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 80094, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "80094": { + "id": 80094, + "node": { + "url": "https://80094.engine.citizenwallet.xyz", + "ws_url": "wss://80094.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "80094:0x881cad4f885c6701d8481c0ed347f6d35444ea7e": { + "name": "SFLUV V1.1", + "symbol": "SFLUV", + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "chain_id": 80094, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://app.sfluv.org", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "About", + "hidden": true, + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "80094:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 80094, + "paymaster_type": "cw-safe", + "paymaster_address": "0x9A5be02B65f9Aa00060cB8c951dAFaBAB9B860cd", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "80094:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 80094, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://sfluv.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "SFLUV Community", + "alias": "wallet.berachain.sfluv.org", + "theme": { + "primary": "#eb6c6c" + }, + "profile": { + "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", + "chain_id": 80094 + }, + "description": "A community currency for the city of San Francisco.", + "custom_domain": "wallet.sfluv.org", + "primary_token": { + "address": "0x881cad4f885c6701d8481c0ed347f6d35444ea7e", + "chain_id": 80094 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 80094 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 80094 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 80094 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.berachain.sfluv.org" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x58a2993A618Afee681DE23dECBCF535A58A080BA": { + "name": "SFLUV V1.1", + "symbol": "SFLUV", + "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://app.sfluv.org", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "About", + "hidden": true, + "signature": true, + "launch_mode": "webview" + }, + { + "url": "https://sfluv.org/communitymigration", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "Offoard", + "hidden": true, + "signature": true, + "launch_mode": "webview", + "action": "offboard", + "meta": { + "title": "SFLUV has Migrated", + "desc": "SFLUV has migrated from Polygon to BeraChain, so this community (SFLUV Polygon) has been closed. Click the link below to create an account in the new SFLUV community. Your SFLUV Polygon balance will be transferred over once you have a new account.", + "button": "Migrate" + } + }, + { + "url": "https://app.sfluv.org/map", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "Map", + "featured": true, + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "137:0x5e987a6c4bb4239d498E78c34e986acf29c81E8e": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0x7FC98D0a2bd7f766bAca37388eB0F6Db37666B33", + "entrypoint_address": "0x2d01C5E40Aa6a8478eD0FFbF2784EBb9bf67C46A", + "account_factory_address": "0x5e987a6c4bb4239d498E78c34e986acf29c81E8e" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://sfluv.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sfluv.svg", + "name": "SFLUV Community (Polygon)", + "alias": "wallet.sfluv.org", + "hidden": true, + "closed": true, + "theme": { + "primary": "#eb6c6c" + }, + "profile": { + "address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99", + "chain_id": 137 + }, + "description": "A community currency for the city of San Francisco.", + "custom_domain": "wallet.polygon.sfluv.org", + "primary_token": { + "address": "0x58a2993A618Afee681DE23dECBCF535A58A080BA", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x5e987a6c4bb4239d498E78c34e986acf29c81E8e", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.sfluv.org" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0x6c6611244547a6E9AaCfBA8744115ca1076756fc": { + "name": "Txirrin", + "symbol": "TXI", + "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x33500E7Eb3452421e56c2f4117530B1C4C85E0A5", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/txirrin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/txirrin.png", + "name": "Txirrin", + "alias": "txirrin", + "theme": { + "primary": "#FB7502" + }, + "hidden": false, + "profile": { + "address": "0xd47f7198bf335bfe66dD29C0f3EeEf0cFE9D05D8", + "chain_id": 100 + }, + "description": "A community for Txirrin", + "primary_token": { + "address": "0x6c6611244547a6E9AaCfBA8744115ca1076756fc", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/txirrin" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { + "name": "(PoS) Tether USD", + "symbol": "USDT", + "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x9a81Bd50D56485Cc863Ecb169812c7a821996C8c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "137:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 137, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://www.ethereumbolivia.org", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/boliviapay.png", + "name": "Bolivia Pay", + "alias": "boliviapay", + "theme": { + "primary": "#009393" + }, + "profile": { + "address": "0x898C2737f2Cb52622711A89D85A1D5E0B881BDeA", + "chain_id": 137 + }, + "description": "A community for Ethereum Bolivia.", + "primary_token": { + "address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/boliviapay" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3": { + "name": "Breadchain Community Token", + "symbol": "BREAD", + "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", + "chain_id": 100, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://topup.citizenspring.earth/bread", + "icon": "https://bread.citizenwallet.xyz/uploads/logo.svg", + "name": "Top Up", + "action": "topup" + }, + { + "url": "https://marketplace.citizenwallet.xyz/bread", + "icon": "https://bread.citizenwallet.xyz/uploads/logo.svg", + "name": "Market", + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x5987e57e85014B5A56C880313580346c20a5d1c1", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "100:0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9": { + "chain_id": 100, + "paymaster_type": "cw", + "paymaster_address": "0xbE2Cb3358aa14621134e923B68b8429315368E32", + "entrypoint_address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", + "account_factory_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://breadchain.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/bread.svg", + "name": "Breadchain Community Token", + "alias": "bread", + "profile": { + "address": "0x6b3a1f4277391526413F583c23D5B9EF4d2fE986", + "chain_id": 100 + }, + "description": "BREAD is a digital community token and solidarity primitive developed by Breadchain Cooperative that generates yield for post-capitalist organizations", + "primary_token": { + "address": "0xa555d5344f6fb6c65da19e403cb4c1ec4a1a5ee3", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/bread" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "100:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://100.engine.citizenwallet.xyz", + "ws_url": "wss://100.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "100:0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007": { + "name": "Labor Hour Token", + "symbol": "HOUR", + "address": "0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 4, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0xa7fa16C933f51d8623f39FA0dF34D3065B99Bd1c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://breadchain.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/laborhour.png", + "name": "Labor Hour Token", + "alias": "laborhour", + "theme": { + "primary": "#E52D2D" + }, + "profile": { + "address": "0x673601Eb36820bC9718214AC041E96f79383351B", + "chain_id": 100 + }, + "description": "Labor Hour Token aims to reward contributors for hours of labor, particularly targeting non-blockchain native users", + "primary_token": { + "address": "0x3d36ddFfa4666Ef12a176CaA8C3e67C1047bC007", + "chain_id": 100 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://config.internal.citizenwallet.xyz/v4/laborhour.citizenwallet.xyz.json" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "cards": { + "8453:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 8453, + "instance_id": "cw-discord-1" + } + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x3595ca37596d5895b70efab592ac315d5b9809b2": { + "name": "Azos Stablecoin", + "symbol": "AZUSD", + "address": "0x3595ca37596d5895b70efab592ac315d5b9809b2", + "chain_id": 8453, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0x02cd43b3d7a946dCaa8dF12ac0A6Cd50aF9cDd64", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "8453:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 8453, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0x0c2e78DB6FaF8D4d68984A830339764AD9f800D4" + } + }, + "community": { + "url": "https://rooted.finance/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rooted.png", + "name": "Rooted.Finance", + "alias": "rooted", + "theme": { + "primary": "#ff4c02" + }, + "profile": { + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 8453 + }, + "description": "Rooted in your values.", + "primary_token": { + "address": "0x3595ca37596d5895b70efab592ac315d5b9809b2", + "chain_id": 8453 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/communities/rooted" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "cards": { + "42220:0xc0F9e0907C8de79fd5902b61e463dFEdc5dc8570": { + "type": "classic", + "address": "0xc0F9e0907C8de79fd5902b61e463dFEdc5dc8570", + "chain_id": 42220 + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x65DD32834927de9E57E72a3E2130a19f81C6371D": { + "name": "Commons Hub Token", + "symbol": "CHT", + "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/wallet.commonshub.brussels", + "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x4E127A1DAa66568B4a91E8c5615120a6Ea5442E3", + "entrypoint_address": "0xb7608dDA592d319687C89c4479e320b5a7740117", + "account_factory_address": "0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x4860C0f127500F0cbF4a5Bd797cBb5aA50Eb0FbA", + "entrypoint_address": "0xb7608dDA592d319687C89c4479e320b5a7740117", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://commonshub.brussels", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/chb.png", + "name": "Commons Hub Brussels", + "alias": "wallet.commonshub.brussels", + "theme": { + "primary": "#ff4c02" + }, + "profile": { + "address": "0xc06bE1BbbeEAF2f34F3d5b76069D2560aee184Ae", + "chain_id": 42220 + }, + "description": "Community Token for the Commons Hub Brussels community", + "custom_domain": "wallet.commonshub.brussels", + "primary_token": { + "address": "0x65DD32834927de9E57E72a3E2130a19f81C6371D", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.commonshub.brussels" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "cards": { + "42220:0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28": { + "type": "safe", + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220, + "instance_id": "cw-seldesalm" + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2": { + "name": "Myrtille", + "symbol": "MYRT", + "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Informations Générales", + "launch_mode": "webview" + }, + { + "url": "https://marketplace.citizenwallet.xyz/seldesalm", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Échanges", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xd07412020dA5054c3b49f47Ca61224637F1703af", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://citizenwallet.xyz/community-currency-documentation/sel-de-salm", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/myrt.png", + "name": "Sel de Salm", + "alias": "seldesalm", + "theme": { + "primary": "#6B5CA4" + }, + "profile": { + "address": "0x4083724953cC1cC13e76b436149B2b1e1a3E5970", + "chain_id": 42220 + }, + "description": "La communauté de Sel de Salm", + "primary_token": { + "address": "0x83DfEB42347a7Ce46F1497F307a5c156D1f19CB2", + "chain_id": 42220 + }, + "primary_card_manager": { + "address": "0xBA861e2DABd8316cf11Ae7CdA101d110CF581f28", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/seldesalm" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://gnosisscan.io", + "name": "Gnosis Explorer" + }, + "chains": { + "100": { + "id": 100, + "node": { + "url": "https://engine.my.techi.be", + "ws_url": "wss://engine.my.techi.be" + } + } + }, + "tokens": { + "100:0x01D0E7117510b371Ac38f52Cc6689ff8875280FA": { + "name": "TECHI", + "symbol": "TECHI", + "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", + "chain_id": 100, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "100:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 100, + "paymaster_type": "cw-safe", + "paymaster_address": "0x68c5a20f233264DB124a3c95a200bbD20b3b9762", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "100:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 100, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://my.techi.be", + "logo": "https://my.techi.be/assets/token.svg", + "name": "TECHI", + "alias": "my.techi.be", + "theme": { + "primary": "#617FF8" + }, + "hidden": false, + "profile": { + "address": "0x80C141861607b8FEfD53C9E71a9c7D2D3e2e76dc", + "chain_id": 100 + }, + "description": "A community for TECHI users", + "primary_token": { + "address": "0x01D0E7117510b371Ac38f52Cc6689ff8875280FA", + "chain_id": 100 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 100 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 100 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/my.techi.be" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { + "name": "Regens Unite Token", + "symbol": "RGN", + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x250711045d58b6310f0635C7D110BFe663cE1da5", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "137:0x9406Cc6185a346906296840746125a0E44976454": { + "chain_id": 137, + "paymaster_type": "payg", + "paymaster_address": "0x250711045d58b6310f0635C7D110BFe663cE1da5", + "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "gas_extra_percentage": 50, + "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454" + } + }, + "community": { + "url": "https://www.regensunite.earth/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Regens Unite", + "alias": "wallet.regensunite.earth", + "hidden": true, + "profile": { + "address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", + "chain_id": 137 + }, + "description": "A community currency for the Regens Unite community.", + "custom_domain": "wallet.regensunite.earth", + "primary_token": { + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.regensunite.earth" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1": { + "name": "Gratitude Token", + "symbol": "GT", + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220, + "decimals": 0, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8", + "entrypoint_address": "0x985ec7d08D9d15Ea79876E35FAdEFD58A627187E", + "account_factory_address": "0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD" + } + }, + "community": { + "url": "https://citizenwallet.xyz/gratitude", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/gt.svg", + "name": "Gratitude Token", + "alias": "gt.celo", + "theme": { + "primary": "#a256ff" + }, + "hidden": true, + "profile": { + "address": "0xEEc0F3257369c6bCD2Fd8755CbEf8A95b12Bc4c9", + "chain_id": 42220 + }, + "description": "Express your gratitude towards someone by sending them a token.", + "custom_domain": null, + "primary_token": { + "address": "0x5815E61eF72c9E6107b5c5A05FD121F334f7a7f1", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/gt.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73": { + "name": "Celo Euro", + "symbol": "cEUR", + "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", + "chain_id": 42220, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F", + "entrypoint_address": "0xc3142BCBA2285d0a48A38e7Ea9Cbf28a12B235bB", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F", + "entrypoint_address": "0xc3142BCBA2285d0a48A38e7Ea9Cbf28a12B235bB", + "account_factory_address": "0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098" + } + }, + "community": { + "url": "https://celo.org/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/ceur.svg", + "name": "Celo Euro", + "alias": "ceur.celo", + "theme": { + "primary": "#a256ff" + }, + "hidden": true, + "profile": { + "address": "0x0334C579E61aF6922D5deFEF02A361FBb2D6f406", + "chain_id": 42220 + }, + "description": "Celo Euro is a stablecoin for the Celo Community.", + "primary_token": { + "address": "0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/ceur.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6": { + "name": "EUR emoney", + "symbol": "EURe", + "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec", + "entrypoint_address": "0x2027Bde7C276D5F128587E3107c68A488ee31c72", + "gas_extra_percentage": 50, + "account_factory_address": "0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://monerium.com/tokens/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/eure.svg", + "name": "EUR e-money", + "alias": "eure.polygon", + "hidden": true, + "profile": { + "address": "0xF5F7317EDb8E88CaE09071B0C4F0fd6EA20B21f9", + "chain_id": 137 + }, + "description": "Token by Monerium EMI, a regulated entity, licensed in the EEA.", + "primary_token": { + "address": "0x18ec0A6E18E5bc3784fDd3a3634b31245ab704F6", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/eure.polygon" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174": { + "name": "USD Coin", + "symbol": "USDC", + "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x270758454C012A1f51428b68aE473D728CCdFe88": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xB5D1C0167E6325466E2918e9fda8cc41384C0291", + "entrypoint_address": "0x466AA6ed2B7Bb829841F5aAEA9e82B840eC0feF9", + "gas_extra_percentage": 50, + "account_factory_address": "0x270758454C012A1f51428b68aE473D728CCdFe88" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xB5D1C0167E6325466E2918e9fda8cc41384C0291", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", + "name": "USDC on Polygon", + "alias": "app", + "theme": { + "primary": "#0052ff" + }, + "hidden": true, + "profile": { + "address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "chain_id": 137 + }, + "description": "The community of people using USDC on Polygon.", + "primary_token": { + "address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/app" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://basescan.org", + "name": "Base Explorer" + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913": { + "name": "USD Coin", + "symbol": "USDC", + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "chain_id": 8453, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99": { + "chain_id": 8453, + "paymaster_type": "cw", + "paymaster_address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "entrypoint_address": "0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9", + "gas_extra_percentage": 50, + "account_factory_address": "0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99" + }, + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/usdc.svg", + "name": "USDC on Base", + "alias": "usdc.base", + "theme": { + "primary": "#0052ff" + }, + "hidden": true, + "profile": { + "address": "0x51Ef5Add405CCF63c206A80AF8c2B3cEE0282830", + "chain_id": 8453 + }, + "description": "The community of people using USDC on Base.", + "primary_token": { + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/usdc.base" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://basescan.org", + "name": "Base Explorer" + }, + "chains": { + "8453": { + "id": 8453, + "node": { + "url": "https://8453.engine.citizenwallet.xyz", + "ws_url": "wss://8453.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "8453:0x845598Da418890a674cbaBA26b70807aF0c61dFE": { + "name": "OAK Community Currency", + "symbol": "OAK", + "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", + "chain_id": 8453, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "8453:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 8453, + "paymaster_type": "cw-safe", + "paymaster_address": "0x123", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "8453:0x9406Cc6185a346906296840746125a0E44976454": { + "chain_id": 8453, + "paymaster_type": "payg", + "paymaster_address": "0x123", + "entrypoint_address": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", + "account_factory_address": "0x9406Cc6185a346906296840746125a0E44976454" + } + }, + "community": { + "url": "https://www.oak.community/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/oak.svg", + "name": "OAK Community", + "alias": "wallet.oak.community", + "hidden": true, + "profile": { + "address": "0xFE213c74e25505B232CE4C7f89647408bE6f71d2", + "chain_id": 8453 + }, + "description": "A community currency for the city of Oakland.", + "custom_domain": "wallet.oak.community", + "primary_token": { + "address": "0x845598Da418890a674cbaBA26b70807aF0c61dFE", + "chain_id": 8453 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 8453 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.oak.community" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798": { + "name": "Stable Coin", + "symbol": "SBC", + "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", + "chain_id": 137, + "decimals": 18, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0x123", + "entrypoint_address": "0xe84423Ba1A3f3535B09237245e22dBda5E27eB88", + "account_factory_address": "0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0x123", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://brale.xyz/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/sbc.svg", + "name": "Stable Coin", + "alias": "sbc.polygon", + "hidden": true, + "profile": { + "address": "0xcA0a75EF803a364C83c5EAE7Eb889aE7419c9dF2", + "chain_id": 137 + }, + "description": "SBC is a digital dollar stablecoin issued by Brale", + "primary_token": { + "address": "0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/sbc.polygon" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://polygonscan.com", + "name": "Polygon Explorer" + }, + "chains": { + "137": { + "id": 137, + "node": { + "url": "https://137.engine.citizenwallet.xyz", + "ws_url": "wss://137.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "137:0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D": { + "name": "Zinne.brussels Token", + "symbol": "ZINNE", + "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", + "chain_id": 137, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [], + "version": 5, + "accounts": { + "137:0x11af2639817692D2b805BcE0e1e405E530B20006": { + "chain_id": 137, + "paymaster_type": "cw", + "paymaster_address": "0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b", + "entrypoint_address": "0xF5507B3042f1C63625D856a2ABFF046243A5D74e", + "account_factory_address": "0x11af2639817692D2b805BcE0e1e405E530B20006" + }, + "137:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 137, + "paymaster_type": "cw-safe", + "paymaster_address": "0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://zinne.brussels", + "logo": "https://citizenwallet.xyz/zinne/zinne-coin.svg", + "name": "Zinne.brussels", + "alias": "zinne", + "hidden": true, + "profile": { + "address": "0x23DB3D3Da510e60aF40902A04850E1F3a744905c", + "chain_id": 137 + }, + "description": "A community currency for the city of Brussels", + "primary_token": { + "address": "0x5491a3d35F148a44F0af4D718B9636A6e55eBc2D", + "chain_id": 137 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 137 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/zinne" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x186DaBD027e228C988777907465807FDab270894": { + "name": "Regen Hour", + "symbol": "rHour", + "address": "0x186DaBD027e228C988777907465807FDab270894", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/timebank.regensunite.earth", + "icon": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x39b77d77f7677997871b304094a05295eb71e240": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xe45858bf63176595c2920822581917c7C705a12f", + "entrypoint_address": "0x41176F0C9b8f795Cb99e2DD5Db16017978eeFa4d", + "account_factory_address": "0x39b77d77f7677997871b304094a05295eb71e240" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xe45858bf63176595c2920822581917c7C705a12f", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://regensunite.earth", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/rgn.svg", + "name": "Regens Unite Time Bank", + "alias": "timebank.regensunite.earth", + "hidden": true, + "profile": { + "address": "0x605A827DF8C405D16Ec70AAb8d9a47D21db45c09", + "chain_id": 42220 + }, + "description": "Make time to regen", + "primary_token": { + "address": "0x186DaBD027e228C988777907465807FDab270894", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/timebank.regensunite.earth" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3": { + "name": "MOOS Token", + "symbol": "MOOS", + "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/moos", + "icon": "https://moos.citizenwallet.xyz/wallet-config/_images/moos.svg", + "name": "Market", + "signature": true, + "launch_mode": "webview" + } + ], + "version": 5, + "accounts": { + "42220:0x671f0662de72268d0f3966Fb62dFc6ee6389e244": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482", + "entrypoint_address": "0x45a8e6AaDCc48D1Ce19eCbE07Ccd3a536EF712ed", + "account_factory_address": "0x671f0662de72268d0f3966Fb62dFc6ee6389e244" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://www.moos.garden/", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/moos.svg", + "name": "MOOS Token", + "alias": "moos", + "hidden": true, + "profile": { + "address": "0x2e4542Be47408d05F41703386eFaf4338Ee1D341", + "chain_id": 42220 + }, + "description": "A community currency for MOOS.", + "primary_token": { + "address": "0x230542eda83346929e4E54f4a98e1ca1A4BFc0c3", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/moos" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3": { + "name": "Bonne Heure", + "symbol": "BHR", + "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "plugins": [ + { + "url": "https://marketplace.citizenwallet.xyz/selcoupdepouce", + "icon": "https://marketplace.citizenwallet.xyz/marketplace.svg", + "name": "Market", + "signature": true, + "launch_mode": "browser" + } + ], + "version": 5, + "accounts": { + "42220:0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x635032605337aB36A46D767905108e67EE687a72", + "entrypoint_address": "0xA90904F33df36899d810d040b8d5b3b77265Bb05", + "account_factory_address": "0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x635032605337aB36A46D767905108e67EE687a72", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://selcoupdepouce.be", + "logo": "https://topup.citizenwallet.xyz/communities/selcoupdepouce/sel-coin.svg", + "name": "Bonne Heure", + "alias": "selcoupdepouce", + "hidden": true, + "profile": { + "address": "0xfB8F1e7ED42599638B3c509679E2F43937002C56", + "chain_id": 42220 + }, + "description": "Système d'Échange Local de Villers-la-Ville", + "primary_token": { + "address": "0x5Cdbc862BF4E20D98456D4c41D4A5239aDd496d3", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/selcoupdepouce" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9": { + "name": "CI token", + "symbol": "CIT", + "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x452F7ff3e55fe29f481841985dE7f4939FD645fa", + "entrypoint_address": "0xB8d9412f3A91A00ca762B5c35cd0863E9b716D68", + "account_factory_address": "0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970" + }, + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x452F7ff3e55fe29f481841985dE7f4939FD645fa", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "community": { + "url": "https://citizenwallet.xyz/cit", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/cit.celo.svg", + "name": "CI token", + "alias": "cit.celo", + "hidden": true, + "profile": { + "address": "0x4cB296BEc9FAd0B5e1E4FF1A2F307B425724AC82", + "chain_id": 42220 + }, + "description": "Monnaie locale du Cercle Informatique de l’ULB", + "primary_token": { + "address": "0x12e26FAED228c425BceA8a8dd7658a9CeD944dd9", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/cit.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "cards": { + "42220:0xA3E1446E332a098A1f3b0555c5d149b4784A095F": { + "type": "classic", + "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", + "chain_id": 42220 + } + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451": { + "name": "Wolu", + "symbol": "WOLU", + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0x8474153A00C959f2cB64852949954DBC68415Bb3": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c", + "entrypoint_address": "0x0F805BC1ED718FB9C7C18439cB11E1C17C6538C4", + "account_factory_address": "0x8474153A00C959f2cB64852949954DBC68415Bb3" + } + }, + "community": { + "url": "https://wolugo.be", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/wolugo.svg", + "name": "Wolugo", + "alias": "wallet.wolugo.be", + "theme": { + "primary": "#81e2c1" + }, + "hidden": true, + "profile": { + "address": "0x07e7b95B35866302b3A089feF4CFA3061061a51d", + "chain_id": 42220 + }, + "description": "A community for the Woluwe-Saint-Pierre civic engagement platform", + "card_factory": { + "address": "0xA3E1446E332a098A1f3b0555c5d149b4784A095F", + "chain_id": 42220 + }, + "custom_domain": "wallet.wolugo.be", + "primary_token": { + "address": "0x13Dd4B3cD2f2Be3eb41cD0C3E2ce9F8d8C93A451", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.wolugo.be" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0xc53Cb35591959cA62471dA9fF6AC16629A89874a": { + "name": "Woluwe Test Coin", + "symbol": "WTC", + "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xE79E19594A749330036280c685E2719d58d99052": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9", + "entrypoint_address": "0x1EaF6B6A6967608aF6c77224f087b042095891EB", + "account_factory_address": "0xE79E19594A749330036280c685E2719d58d99052" + } + }, + "community": { + "url": "https://wollet-v2.citizenwallet.net/token", + "logo": "https://wtc.celo.citizenwallet.xyz/wallet-config/_images/wtc.celo.svg", + "name": "Woluwe Test", + "alias": "wtc.celo", + "hidden": true, + "profile": { + "address": "0xB99a7B1574f051020EB4cb2fce5d48EE07592AfF", + "chain_id": 42220 + }, + "description": "Local currency for the Woluwe Test community.", + "primary_token": { + "address": "0xc53Cb35591959cA62471dA9fF6AC16629A89874a", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wtc.celo" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://sepolia.basescan.org", + "name": "Base Sepolia Explorer" + }, + "chains": { + "84532": { + "id": 84532, + "node": { + "url": "https://84532.engine.citizenwallet.xyz", + "ws_url": "wss://84532.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "84532:0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e": { + "name": "ETHGlobal London Token", + "symbol": "ETHLDN", + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 84532, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "84532:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 84532, + "paymaster_type": "cw-safe", + "paymaster_address": "0x389182aCCeE26D953d5188BF4b92c49339DcC9FC", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "84532:0xc1654087C580f868F08E34cd1c01eDB1d3673b82": { + "chain_id": 84532, + "paymaster_type": "cw", + "paymaster_address": "0x389182aCCeE26D953d5188BF4b92c49339DcC9FC", + "entrypoint_address": "0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE", + "account_factory_address": "0xc1654087C580f868F08E34cd1c01eDB1d3673b82" + } + }, + "community": { + "url": "https://en.wikipedia.org/wiki/USD_Coin", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/testnet-ethldn.svg", + "name": "ETHGlobal London Token", + "alias": "testnet-ethldn", + "hidden": true, + "profile": { + "address": "0x0785D720279f42326846D5396b5F44b97d0BfECd", + "chain_id": 84532 + }, + "description": "The community of people using ETHLDN on Base.", + "primary_token": { + "address": "0x9b1a0D2951b11Ac26A6cBbd5aEf2c4cb014b3B6e", + "chain_id": 84532 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 84532 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/testnet-ethldn" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "CELO Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x3C960E72BBbD837293e75080E1d0Fee6a4640357": { + "name": "Celo Community Point", + "symbol": "CeloC", + "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", + "chain_id": 42220, + "decimals": 6, + "standard": "erc20" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x7f4011845Ea914b6cefc60629e1e00600c972c75", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + }, + "42220:0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA": { + "chain_id": 42220, + "paymaster_type": "cw", + "paymaster_address": "0x7f4011845Ea914b6cefc60629e1e00600c972c75", + "entrypoint_address": "0x66fE9c22CcA49B257dd4F00508AC90198d99Bf27", + "account_factory_address": "0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA" + } + }, + "community": { + "url": "https://citizenwallet.xyz", + "logo": "https://celo-c.citizenwallet.xyz/uploads/logo.svg", + "name": "Celo Community Point", + "alias": "celo-c.citizenwallet.xyz", + "hidden": true, + "profile": { + "address": "0x14004E13907282cFaD05f742022E56926eE92dAd", + "chain_id": 42220 + }, + "description": "This is a community for the Celo Point", + "primary_token": { + "address": "0x3C960E72BBbD837293e75080E1d0Fee6a4640357", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/celo-c.citizenwallet.xyz" + }, + { + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x56744910f7dEcD48c1a7FA61B4C317b15E99F156": { + "name": "KFMEDIA℠ Pathways for LATAM™", + "symbol": "KFMPFL", + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220, + "decimals": 0, + "standard": "erc1155" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x02BDA8370d9497A5C808B2db237cfaA8f0733F36", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://kingfishersmedia.io", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/kfmpfl.png", + "name": "KFMEDIA℠", + "alias": "wallet.kingfishersmedia.io", + "theme": { + "primary": "#88292c" + }, + "profile": { + "address": "0x5f6FEb03ad8EfeCdD2a837FAA1a29DEA2bAcfd55", + "chain_id": 42220 + }, + "description": "Certified Education Organization. Solving systemic educational disparity using Web3 solutions, removing barriers of entry for underdeveloped economies.", + "custom_domain": "wallet.kingfishersmedia.io", + "primary_token": { + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.kingfishersmedia.io" + } +] diff --git a/assets/config/v5/debug.json b/assets/config/v5/debug.json new file mode 100644 index 00000000..c1b27ccd --- /dev/null +++ b/assets/config/v5/debug.json @@ -0,0 +1,74 @@ +{ + "ipfs": { + "url": "https://ipfs.internal.citizenwallet.xyz" + }, + "scan": { + "url": "https://celoscan.io", + "name": "Celo Explorer" + }, + "chains": { + "42220": { + "id": 42220, + "node": { + "url": "https://42220.engine.citizenwallet.xyz", + "ws_url": "wss://42220.engine.citizenwallet.xyz" + } + } + }, + "tokens": { + "42220:0x56744910f7dEcD48c1a7FA61B4C317b15E99F156": { + "name": "KFMEDIA℠ Pathways for LATAM™", + "symbol": "KFMPFL", + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220, + "decimals": 0, + "standard": "erc1155" + } + }, + "version": 5, + "accounts": { + "42220:0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185": { + "chain_id": 42220, + "paymaster_type": "cw-safe", + "paymaster_address": "0x02BDA8370d9497A5C808B2db237cfaA8f0733F36", + "entrypoint_address": "0x7079253c0358eF9Fd87E16488299Ef6e06F403B6", + "account_factory_address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185" + } + }, + "sessions": { + "42220:0xE2F3DC3E638113b9496060349e5332963d9C1152": { + "chain_id": 42220, + "module_address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "factory_address": "0xEd0cD3886b84369A0e29Db9a4480ADF5051c76C9", + "provider_address": "0xF3004A1690f97Cf5d307eDc5958a7F76b62f9FC9" + } + }, + "community": { + "url": "https://kingfishersmedia.io", + "logo": "https://assets.citizenwallet.xyz/wallet-config/_images/kfmpfl.png", + "name": "KFMEDIA℠", + "alias": "wallet.kingfishersmedia.io", + "theme": { + "primary": "#88292c" + }, + "profile": { + "address": "0x5f6FEb03ad8EfeCdD2a837FAA1a29DEA2bAcfd55", + "chain_id": 42220 + }, + "description": "Certified Education Organization. Solving systemic educational disparity using Web3 solutions, removing barriers of entry for underdeveloped economies.", + "custom_domain": "wallet.kingfishersmedia.io", + "primary_token": { + "address": "0x56744910f7dEcD48c1a7FA61B4C317b15E99F156", + "chain_id": 42220 + }, + "primary_account_factory": { + "address": "0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185", + "chain_id": 42220 + }, + "primary_session_manager": { + "address": "0xE2F3DC3E638113b9496060349e5332963d9C1152", + "chain_id": 42220 + } + }, + "config_location": "https://my.citizenwallet.xyz/api/communities/wallet.kingfishersmedia.io" +} diff --git a/assets/contracts/accounts/Account.abi.json b/assets/contracts/accounts/Account.abi.json new file mode 100644 index 00000000..9ce756ce --- /dev/null +++ b/assets/contracts/accounts/Account.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract IEntryPoint","name":"anEntryPoint","type":"address"},{"internalType":"contract ITokenEntryPoint","name":"aTokenEntryPoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IEntryPoint","name":"entryPoint","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"AccountInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"addDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"entryPoint","outputs":[{"internalType":"contract IEntryPoint","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dest","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"func","type":"bytes"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"dest","type":"address[]"},{"internalType":"bytes[]","name":"func","type":"bytes[]"}],"name":"executeBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"anOwner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"recoverOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenEntryPoint","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"tokensReceived","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"},{"internalType":"uint256","name":"missingAccountFunds","type":"uint256"}],"name":"validateUserOp","outputs":[{"internalType":"uint256","name":"validationData","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"},{"internalType":"bytes32","name":"userOpHash","type":"bytes32"}],"name":"validateUserOp","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"withdrawAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawDepositTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/assets/contracts/accounts/AccountFactory.abi.json b/assets/contracts/accounts/AccountFactory.abi.json new file mode 100644 index 00000000..c701232b --- /dev/null +++ b/assets/contracts/accounts/AccountFactory.abi.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AccountCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"accountImplementation","outputs":[{"internalType":"contract Account","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"createAccount","outputs":[{"internalType":"contract Account","name":"ret","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"getAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IEntryPoint","name":"_entryPoint","type":"address"},{"internalType":"contract ITokenEntryPoint","name":"_tokenEntryPoint","type":"address"},{"internalType":"address","name":"anOwner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/accounts/TokenEntryPoint.abi.json b/assets/contracts/accounts/TokenEntryPoint.abi.json new file mode 100644 index 00000000..5984ad9f --- /dev/null +++ b/assets/contracts/accounts/TokenEntryPoint.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract INonceManager","name":"anEntryPoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint192","name":"key","type":"uint192"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation","name":"userOp","type":"tuple"}],"name":"getUserOpHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"initCode","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"callGasLimit","type":"uint256"},{"internalType":"uint256","name":"verificationGasLimit","type":"uint256"},{"internalType":"uint256","name":"preVerificationGas","type":"uint256"},{"internalType":"uint256","name":"maxFeePerGas","type":"uint256"},{"internalType":"uint256","name":"maxPriorityFeePerGas","type":"uint256"},{"internalType":"bytes","name":"paymasterAndData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct UserOperation[]","name":"ops","type":"tuple[]"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"name":"handleOps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"key","type":"uint192"}],"name":"incrementNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"anOwner","type":"address"},{"internalType":"address","name":"aPaymaster","type":"address"},{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint192","name":"","type":"uint192"}],"name":"nonceSequenceNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymaster","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPaymaster","type":"address"}],"name":"updatePaymaster","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"}],"name":"updateWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/apps/Profile.abi.json b/assets/contracts/apps/Profile.abi.json new file mode 100644 index 00000000..305e731d --- /dev/null +++ b/assets/contracts/apps/Profile.abi.json @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_toTokenId","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROFILE_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"fromAddressToId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"fromIdToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"get","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"username","type":"bytes32"}],"name":"getFromUsername","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"username","type":"bytes32"}],"name":"profiles","outputs":[{"internalType":"address","name":"profile","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"},{"internalType":"bytes32","name":"_username","type":"bytes32"},{"internalType":"string","name":"_uri","type":"string"}],"name":"set","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"usernames","outputs":[{"internalType":"bytes32","name":"username","type":"bytes32"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/apps/SimpleFaucet.abi.json b/assets/contracts/apps/SimpleFaucet.abi.json new file mode 100644 index 00000000..d36d3deb --- /dev/null +++ b/assets/contracts/apps/SimpleFaucet.abi.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDEEM_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint48","name":"_redeemInterval","type":"uint48"},{"internalType":"address","name":"_redeemAdmin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redeemInterval","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"redeemTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"redeemed","outputs":[{"internalType":"uint48","name":"time","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/external/CardFactory.abi.json b/assets/contracts/external/CardFactory.abi.json new file mode 100644 index 00000000..16307234 --- /dev/null +++ b/assets/contracts/external/CardFactory.abi.json @@ -0,0 +1,216 @@ +[ + { + "inputs": [ + { + "internalType": "contract IEntryPoint", + "name": "_entryPoint", + "type": "address" + }, + { + "internalType": "contract ITokenEntryPoint", + "name": "_tokenEntryPoint", + "type": "address" + }, + { + "internalType": "address[]", + "name": "_whitelistAddresses", + "type": "address[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "voucher", + "type": "address" + } + ], + "name": "CardCreated", + "type": "event" + }, + { + "inputs": [], + "name": "cardImplementation", + "outputs": [ + { + "internalType": "contract Card", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "contractAddress", + "type": "address" + } + ], + "name": "contractExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "cardHash", + "type": "bytes32" + } + ], + "name": "createCard", + "outputs": [ + { + "internalType": "contract Card", + "name": "ret", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "cardHash", + "type": "bytes32" + } + ], + "name": "getCardAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "serial", + "type": "uint256" + } + ], + "name": "getCardHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "cardHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferCardOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "addresses", + "type": "address[]" + } + ], + "name": "updateWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "cardHash", + "type": "bytes32" + }, + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/assets/contracts/external/DERC20.abi.json b/assets/contracts/external/DERC20.abi.json new file mode 100644 index 00000000..1065a7bf --- /dev/null +++ b/assets/contracts/external/DERC20.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"address payable","name":"relayerAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionSignature","type":"bytes"}],"name":"MetaTransactionExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"CHILD_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CHILD_CHAIN_ID_BYTES","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSITOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC712_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROOT_CHAIN_ID_BYTES","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"bytes","name":"functionSignature","type":"bytes"},{"internalType":"bytes32","name":"sigR","type":"bytes32"},{"internalType":"bytes32","name":"sigS","type":"bytes32"},{"internalType":"uint8","name":"sigV","type":"uint8"}],"name":"executeMetaTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNonce","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/assets/contracts/external/SimpleAccountFactory.abi.json b/assets/contracts/external/SimpleAccountFactory.abi.json new file mode 100644 index 00000000..c0877873 --- /dev/null +++ b/assets/contracts/external/SimpleAccountFactory.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract IEntryPoint","name":"_entryPoint","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"accountImplementation","outputs":[{"internalType":"contract SimpleAccount","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"createAccount","outputs":[{"internalType":"contract SimpleAccount","name":"ret","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"name":"getAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/standards/ERC1155.abi.json b/assets/contracts/standards/ERC1155.abi.json new file mode 100644 index 00000000..57eaea05 --- /dev/null +++ b/assets/contracts/standards/ERC1155.abi.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/standards/ERC20.abi.json b/assets/contracts/standards/ERC20.abi.json new file mode 100644 index 00000000..9c1f0997 --- /dev/null +++ b/assets/contracts/standards/ERC20.abi.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/assets/contracts/standards/IAccessControlUpgradeable.abi.json b/assets/contracts/standards/IAccessControlUpgradeable.abi.json new file mode 100644 index 00000000..c73da1ae --- /dev/null +++ b/assets/contracts/standards/IAccessControlUpgradeable.abi.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f1ccaa00..bbdd1317 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter - - credential_manager (1.0.4): + - credential_manager_ios (2.0.5): - Flutter - DKImagePickerController/Core (4.3.9): - DKImagePickerController/ImageDataManager @@ -156,6 +156,9 @@ PODS: - Flutter - FlutterMacOS - PromisesObjC (2.4.0) + - reown_yttrium (0.0.1): + - Flutter + - YttriumWrapper (= 0.8.35) - SDWebImage (5.21.2): - SDWebImage/Core (= 5.21.2) - SDWebImage/Core (5.21.2) @@ -170,11 +173,12 @@ PODS: - SwiftyGif (5.4.5) - url_launcher_ios (0.0.1): - Flutter + - YttriumWrapper (0.8.35) DEPENDENCIES: - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - - credential_manager (from `.symlinks/plugins/credential_manager/ios`) + - credential_manager_ios (from `.symlinks/plugins/credential_manager_ios/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) @@ -187,6 +191,7 @@ DEPENDENCIES: - nfc_manager (from `.symlinks/plugins/nfc_manager/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - reown_yttrium (from `.symlinks/plugins/reown_yttrium/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) @@ -213,14 +218,15 @@ SPEC REPOS: - PromisesObjC - SDWebImage - SwiftyGif + - YttriumWrapper EXTERNAL SOURCES: audioplayers_darwin: :path: ".symlinks/plugins/audioplayers_darwin/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" - credential_manager: - :path: ".symlinks/plugins/credential_manager/ios" + credential_manager_ios: + :path: ".symlinks/plugins/credential_manager_ios/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" firebase_core: @@ -245,6 +251,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + reown_yttrium: + :path: ".symlinks/plugins/reown_yttrium/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: @@ -259,7 +267,7 @@ SPEC CHECKSUMS: AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d - credential_manager: feb21034894e469e3686461dc96fb24bb7d350e4 + credential_manager_ios: fd1d96ef11fa3d76f868f038342de171c3bc2b28 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: be9a674155d9f334323856cb266e0d145d75d5c0 @@ -270,7 +278,7 @@ SPEC CHECKSUMS: FirebaseCoreInternal: 9afa45b1159304c963da48addb78275ef701c6b4 FirebaseInstallations: 317270fec08a5d418fdbc8429282238cab3ac843 FirebaseMessaging: 3b26e2cee503815e01c3701236b020aa9b576f09 - Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017 @@ -285,14 +293,16 @@ SPEC CHECKSUMS: nfc_manager: d7da7cb781f7744b94df5fe9dbca904ac4a0939e OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 - path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + reown_yttrium: c0e87e5965fa60a3559564cc35cffbba22976089 SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f - shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + YttriumWrapper: 31e937fe9fbe0f1314d2ca6be9ce9b379a059966 PODFILE CHECKSUM: f90b7b7d52ec0d905039aa6f51266424548151c7 diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index de8ccc05..59d3bf79 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -199,10 +199,14 @@ "emptyBalanceText2": "Redeem this voucher to your account.", "tapToScan": "Tap to scan", "communityCurrentlyOffline": "Community currently offline", + "communityClosed": "This community is currently closed", + "communityClosedDescription": "This community is not accepting transactions at this time. Contact the community administrator for more information.", + "learnMore": "Learn More", "topup": "Top Up", "close": "Close", "more": "More", "start": "Start", "connecting": "Connecting", - "accountNotFound": "Account not found" + "accountNotFound": "Account not found", + "sendTip": "Send Tip" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index b62ffab5..8caa754e 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -199,10 +199,14 @@ "emptyBalanceText2": "Canjea este vale en tu cuenta.", "tapToScan": "Toca para escanear", "communityCurrentlyOffline": "Comunidad actualmente fuera de línea", + "communityClosed": "Esta comunidad está actualmente cerrada", + "communityClosedDescription": "Esta comunidad no acepta transacciones en este momento. Ponte en contacto con el administrador de la comunidad para más información.", + "learnMore": "Más información", "topup": "Recargar", "close": "Cerrar", "more": "Más", "start": "Iniciar", "connecting": "Conectando", - "accountNotFound": "Cuenta no encontrada" + "accountNotFound": "Cuenta no encontrada", + "sendTip": "Enviar propina" } \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 9b4bd69e..4be88eaa 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -198,10 +198,14 @@ "emptyBalanceText2": "Échangez ce bon sur votre compte.", "tapToScan": "Scannez votre carte", "communityCurrentlyOffline": "Communauté actuellement hors ligne", + "communityClosed": "Cette communauté est actuellement fermée", + "communityClosedDescription": "Cette communauté n'accepte pas les transactions pour le moment. Contactez l'administrateur de la communauté pour plus d'informations.", + "learnMore": "En savoir plus", "topup": "Recharger", "more": "Plus", "close": "Fermer", "start": "Commencer", "connecting": "Connexion", - "accountNotFound": "Compte non trouvé" + "accountNotFound": "Compte non trouvé", + "sendTip": "Envoyer un pourboire" } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 42b9531c..f5c60b62 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1296,6 +1296,24 @@ abstract class AppLocalizations { /// **'Community currently offline'** String get communityCurrentlyOffline; + /// No description provided for @communityClosed. + /// + /// In en, this message translates to: + /// **'This community is currently closed'** + String get communityClosed; + + /// No description provided for @communityClosedDescription. + /// + /// In en, this message translates to: + /// **'This community is not accepting transactions at this time. Contact the community administrator for more information.'** + String get communityClosedDescription; + + /// No description provided for @learnMore. + /// + /// In en, this message translates to: + /// **'Learn More'** + String get learnMore; + /// No description provided for @topup. /// /// In en, this message translates to: @@ -1331,6 +1349,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Account not found'** String get accountNotFound; + + /// No description provided for @sendTip. + /// + /// In en, this message translates to: + /// **'Send Tip'** + String get sendTip; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index cf7a7b21..d78641b0 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -650,6 +650,16 @@ class AppLocalizationsEn extends AppLocalizations { @override String get communityCurrentlyOffline => 'Community currently offline'; + @override + String get communityClosed => 'This community is currently closed'; + + @override + String get communityClosedDescription => + 'This community is not accepting transactions at this time. Contact the community administrator for more information.'; + + @override + String get learnMore => 'Learn More'; + @override String get topup => 'Top Up'; @@ -667,4 +677,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get accountNotFound => 'Account not found'; + + @override + String get sendTip => 'Send Tip'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 7b0cfb8d..d40cd430 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -654,6 +654,16 @@ class AppLocalizationsEs extends AppLocalizations { String get communityCurrentlyOffline => 'Comunidad actualmente fuera de línea'; + @override + String get communityClosed => 'Esta comunidad está actualmente cerrada'; + + @override + String get communityClosedDescription => + 'Esta comunidad no acepta transacciones en este momento. Ponte en contacto con el administrador de la comunidad para más información.'; + + @override + String get learnMore => 'Más información'; + @override String get topup => 'Recargar'; @@ -671,4 +681,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get accountNotFound => 'Cuenta no encontrada'; + + @override + String get sendTip => 'Enviar propina'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index f2153012..bab5d9e5 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -655,6 +655,16 @@ class AppLocalizationsFr extends AppLocalizations { @override String get communityCurrentlyOffline => 'Communauté actuellement hors ligne'; + @override + String get communityClosed => 'Cette communauté est actuellement fermée'; + + @override + String get communityClosedDescription => + 'Cette communauté n\'accepte pas les transactions pour le moment. Contactez l\'administrateur de la communauté pour plus d\'informations.'; + + @override + String get learnMore => 'En savoir plus'; + @override String get topup => 'Recharger'; @@ -672,4 +682,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get accountNotFound => 'Compte non trouvé'; + + @override + String get sendTip => 'Envoyer un pourboire'; } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 7eac1ca0..29e961a5 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -657,6 +657,16 @@ class AppLocalizationsNl extends AppLocalizations { @override String get communityCurrentlyOffline => 'Gemeenschap momenteel offline'; + @override + String get communityClosed => 'Deze gemeenschap is momenteel gesloten'; + + @override + String get communityClosedDescription => + 'Deze gemeenschap accepteert momenteel geen transacties. Neem contact op met de gemeenschapsbeheerder voor meer informatie.'; + + @override + String get learnMore => 'Meer informatie'; + @override String get topup => 'Opwaarderen'; @@ -674,4 +684,7 @@ class AppLocalizationsNl extends AppLocalizations { @override String get accountNotFound => 'Account niet gevonden'; + + @override + String get sendTip => 'Fooi versturen'; } diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 171bdea4..40e71526 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -198,10 +198,14 @@ "emptyBalanceText2": "Claim deze voucher op je account", "tapToScan": "Tik om te scannen", "communityCurrentlyOffline": "Gemeenschap momenteel offline", + "communityClosed": "Deze gemeenschap is momenteel gesloten", + "communityClosedDescription": "Deze gemeenschap accepteert momenteel geen transacties. Neem contact op met de gemeenschapsbeheerder voor meer informatie.", + "learnMore": "Meer informatie", "topup": "Opwaarderen", "more": "Meer", "close": "Sluiten", "start": "Start", "connecting": "Verbinding maken", - "accountNotFound": "Account niet gevonden" + "accountNotFound": "Account niet gevonden", + "sendTip": "Fooi versturen" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index f40375a6..41741671 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -76,7 +76,7 @@ FutureOr appRunner() async { } final AppDBService appDBService = AppDBService(); - await appDBService.init('appv4'); + await appDBService.init('appv5'); final numConfigs = (await appDBService.communities.getAll()).length; config.singleCommunityMode = numConfigs < 2; diff --git a/lib/models/send_transaction.dart b/lib/models/send_transaction.dart deleted file mode 100644 index cf278cb0..00000000 --- a/lib/models/send_transaction.dart +++ /dev/null @@ -1,40 +0,0 @@ -class SendTransaction { - String? _to; - String? _amount; - String? _description; - String? _tipTo; - String? _tipAmount; - String? _tipDescription; - - SendTransaction({ - String? to, - String? amount, - String? description, - String? tipTo, - String? tipAmount, - String? tipDescription, - }) : _to = to, - _amount = amount, - _description = description, - _tipTo = tipTo, - _tipAmount = tipAmount, - _tipDescription = tipDescription; - - String? get to => _to; - set to(String? value) => _to = value; - - String? get amount => _amount; - set amount(String? value) => _amount = value; - - String? get description => _description; - set description(String? value) => _description = value; - - String? get tipTo => _tipTo; - set tipTo(String? value) => _tipTo = value; - - String? get tipAmount => _tipAmount; - set tipAmount(String? value) => _tipAmount = value; - - String? get tipDescription => _tipDescription; - set tipDescription(String? value) => _tipDescription = value; -} diff --git a/lib/router/router.dart b/lib/router/router.dart index a5a5daca..1e085cf9 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -90,6 +90,14 @@ GoRouter createRouter( sendToParams += '&description=${uri.queryParameters['description']}'; } + if (uri.queryParameters['tipAmount'] != null) { + sendToParams += + '&tipAmount=${uri.queryParameters['tipAmount']}'; + } + if (uri.queryParameters['tipDescription'] != null) { + sendToParams += + '&tipDescription=${uri.queryParameters['tipDescription']}'; + } } else if (eip681 != null) { sendToParams = encodeParams(uri.toString().replaceFirst('/?', '')); @@ -181,6 +189,9 @@ GoRouter createRouter( final tipTo = state.uri.queryParameters['tipTo']; final amount = state.uri.queryParameters['amount']; final description = state.uri.queryParameters['description']; + final tipAmount = state.uri.queryParameters['tipAmount']; + final tipDescription = + state.uri.queryParameters['tipDescription']; if (sendTo != null) { String params = 'sendto=$sendTo'; if (tipTo != null) { @@ -192,6 +203,12 @@ GoRouter createRouter( if (description != null) { params += '&description=$description'; } + if (tipAmount != null) { + params += '&tipAmount=$tipAmount'; + } + if (tipDescription != null) { + params += '&tipDescription=$tipDescription'; + } sendToURL = 'https://app.citizenwallet.xyz/?$params'; } } @@ -259,7 +276,6 @@ GoRouter createRouter( voucherLogic: extra['voucherLogic'], isMinting: extra['isMinting'] ?? false, sendToURL: extra['sendToURL'], - sendTransaction: extra['sendTransaction'], ); }, ), @@ -316,7 +332,6 @@ GoRouter createRouter( walletLogic: extra['walletLogic'], profilesLogic: extra['profilesLogic'], isMinting: extra['isMinting'] ?? false, - sendTransaction: extra['sendTransaction'], ); }, ), @@ -347,7 +362,6 @@ GoRouter createRouter( to: state.pathParameters['to'], isMinting: extra?['isMinting'] ?? false, profilesLogic: extra?['profilesLogic'], - sendTransaction: extra?['sendTransaction'], walletLogic: extra?['walletLogic'], ); }, @@ -544,7 +558,6 @@ GoRouter createWebRouter( } } - return WebLandingScreen( voucher: state.uri.queryParameters['voucher'], voucherParams: state.uri.queryParameters['params'], diff --git a/lib/screens/send/send_details.dart b/lib/screens/send/send_details.dart index ca9d9a2e..a197ee6c 100644 --- a/lib/screens/send/send_details.dart +++ b/lib/screens/send/send_details.dart @@ -1,5 +1,4 @@ // import 'package:citizenwallet/l10n/app_localizations.dart'; -import 'package:citizenwallet/models/send_transaction.dart'; import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/wallet/utils.dart'; import 'package:citizenwallet/state/profiles/logic.dart'; @@ -230,17 +229,13 @@ class _SendDetailsScreenState extends State { final toAccount = selectedAddress ?? walletLogic.addressController.value.text; - - final sendTransaction = SendTransaction( - amount: walletLogic.amountController.value.text, - to: toAccount, - description: walletLogic.messageController.value.text.trim(), - ); + final amount = walletLogic.amountController.value.text; + final description = walletLogic.messageController.value.text.trim(); walletLogic.sendTransaction( - sendTransaction.amount!, - sendTransaction.to!, - message: sendTransaction.description!, + amount, + toAccount, + message: description, ); await Future.delayed(const Duration(milliseconds: 50)); @@ -253,7 +248,6 @@ class _SendDetailsScreenState extends State { 'isMinting': widget.isMinting, 'walletLogic': walletLogic, 'profilesLogic': widget.profilesLogic, - 'sendTransaction': sendTransaction, }); walletLogic.clearInProgressTransaction(); @@ -429,8 +423,9 @@ class _SendDetailsScreenState extends State { final formattedAddress = formatHexAddress(walletLogic.addressController.value.text); + final hasAmountText = walletLogic.amountController.value.text.isNotEmpty; final isSendingValid = (hasAddress || isLink) && - hasAmount && + (hasAmount || hasAmountText) && !invalidAmount && (!invalidAddress || isLink) && !(!widget.isMinting && balance <= 0 && topUpPlugin == null); @@ -581,7 +576,10 @@ class _SendDetailsScreenState extends State { ? amountFormatter : integerAmountFormatter, ], - onChanged: (_) => handleThrottledUpdateAmount(), + onChanged: (_) { + setState(() {}); + handleThrottledUpdateAmount(); + }, onSubmitted: (_) { FocusManager.instance.primaryFocus?.unfocus(); }, diff --git a/lib/screens/send/send_progress.dart b/lib/screens/send/send_progress.dart index 136c97f7..b53d210f 100644 --- a/lib/screens/send/send_progress.dart +++ b/lib/screens/send/send_progress.dart @@ -1,6 +1,5 @@ // import 'package:citizenwallet/l10n/app_localizations.dart'; import 'dart:async'; -import 'package:citizenwallet/models/send_transaction.dart'; import 'package:citizenwallet/models/transaction.dart'; import 'package:citizenwallet/services/wallet/utils.dart'; import 'package:citizenwallet/state/profiles/logic.dart'; @@ -8,6 +7,7 @@ import 'package:citizenwallet/state/profiles/state.dart'; import 'package:citizenwallet/state/wallet/logic.dart'; import 'package:citizenwallet/state/wallet/state.dart'; import 'package:citizenwallet/theme/provider.dart'; +import 'package:citizenwallet/utils/send.dart'; import 'package:citizenwallet/widgets/button.dart'; import 'package:citizenwallet/widgets/coin_logo.dart'; import 'package:citizenwallet/widgets/loaders/progress_circle.dart'; @@ -23,7 +23,6 @@ class SendProgress extends StatefulWidget { final bool isMinting; final WalletLogic? walletLogic; final ProfilesLogic? profilesLogic; - final SendTransaction? sendTransaction; const SendProgress({ super.key, @@ -31,7 +30,6 @@ class SendProgress extends StatefulWidget { this.isMinting = false, this.walletLogic, this.profilesLogic, - this.sendTransaction, }); @override @@ -76,25 +74,32 @@ class _SendProgressState extends State { }); } - Future handleSendTip(BuildContext context) async { + Future handleSendTip( + BuildContext context, + SendDestination tipping, + ) async { if (!context.mounted) { return; } final navigator = GoRouter.of(context); - final toAccount = widget.sendTransaction?.to ?? - widget.walletLogic?.addressController.value.text; + final toAccount = tipping.to; - await navigator.push( + final tipSent = await navigator.push( '/wallet/${widget.walletLogic?.account}/send/$toAccount/tip', extra: { 'walletLogic': widget.walletLogic, 'profilesLogic': widget.profilesLogic, 'isMinting': widget.isMinting, - 'sendTransaction': widget.sendTransaction, }, ); + + // If tip was sent successfully, navigate to wallet home + if (tipSent == true && context.mounted) { + widget.walletLogic?.clearTipping(); + handleDone(context); + } } @override @@ -118,9 +123,12 @@ class _SendProgressState extends State { (WalletState state) => state.inProgressTransactionError, ); + final tipping = context.select((WalletState state) => state.tipping); + if (inProgressTransaction.state == TransactionState.success && _previousState != TransactionState.success) { - final hasTip = context.read().hasTip; + final hasTip = + context.select((WalletState state) => state.tipping) != null; if (!hasTip) { handleStartCloseScreenTimer(context); } @@ -135,7 +143,8 @@ class _SendProgressState extends State { if (inProgressTransaction.state == TransactionState.fail && _previousState != TransactionState.fail && !_isClosing) { - final hasTip = context.read().hasTip; + final hasTip = + context.select((WalletState state) => state.tipping) != null; if (!hasTip) { handleStartCloseScreenTimer(context); } @@ -359,12 +368,11 @@ class _SendProgressState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - context.select((WalletState state) => state.hasTip) + tipping != null ? Column( children: [ Button( - text: - "${AppLocalizations.of(context)!.send} Tip", + text: AppLocalizations.of(context)!.sendTip, color: Theme.of(context) .colors .primary @@ -373,7 +381,8 @@ class _SendProgressState extends State { .colors .white .resolveFrom(context), - onPressed: () => handleSendTip(context), + onPressed: () => + handleSendTip(context, tipping), minWidth: 200, maxWidth: width - 60, ), @@ -384,6 +393,8 @@ class _SendProgressState extends State { onPressed: () { final navigator = GoRouter.of(context); + widget.walletLogic?.clearTipping(); + navigator.go( '/wallet/${widget.walletLogic?.account}'); }, diff --git a/lib/screens/send/send_to.dart b/lib/screens/send/send_to.dart index 2774855d..75c74cd5 100644 --- a/lib/screens/send/send_to.dart +++ b/lib/screens/send/send_to.dart @@ -1,5 +1,4 @@ // import 'package:citizenwallet/l10n/app_localizations.dart'; -import 'package:citizenwallet/models/send_transaction.dart'; import 'package:citizenwallet/services/wallet/contracts/profile.dart'; import 'package:citizenwallet/services/wallet/utils.dart'; import 'package:citizenwallet/state/profiles/logic.dart'; @@ -34,7 +33,6 @@ class SendToScreen extends StatefulWidget { final ProfilesLogic profilesLogic; final VoucherLogic? voucherLogic; final String? sendToURL; - final SendTransaction? sendTransaction; final bool isMinting; @@ -45,7 +43,6 @@ class SendToScreen extends StatefulWidget { this.voucherLogic, this.isMinting = false, this.sendToURL, - this.sendTransaction, }); @override @@ -57,7 +54,6 @@ class _SendToScreenState extends State { final ScanLogic _scanLogic = ScanLogic(); String? _currentSendToURL; final _scrollController = ScrollController(); - // late SendTransaction _sendTransaction; late void Function() debouncedAddressUpdate; diff --git a/lib/screens/send/tip_details.dart b/lib/screens/send/tip_details.dart index a1feea4f..3da79361 100644 --- a/lib/screens/send/tip_details.dart +++ b/lib/screens/send/tip_details.dart @@ -1,5 +1,4 @@ // import 'package:citizenwallet/l10n/app_localizations.dart'; -import 'package:citizenwallet/models/send_transaction.dart'; import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/wallet/utils.dart'; import 'package:citizenwallet/state/profiles/logic.dart'; @@ -29,7 +28,6 @@ class TipDetailsScreen extends StatefulWidget { final WalletLogic walletLogic; final ProfilesLogic profilesLogic; final VoucherLogic? voucherLogic; - final SendTransaction? sendTransaction; final bool isMinting; final bool isLink; @@ -39,7 +37,6 @@ class TipDetailsScreen extends StatefulWidget { required this.walletLogic, required this.profilesLogic, this.voucherLogic, - this.sendTransaction, this.isMinting = false, this.isLink = false, }); @@ -60,12 +57,10 @@ class _TipDetailsScreenState extends State { late void Function() debouncedAmountUpdate; bool _isSending = false; - late SendTransaction _sendTransaction; @override void initState() { super.initState(); - _sendTransaction = widget.sendTransaction ?? SendTransaction(); // Clear amount controller when tip screen initializes // to ensure hasAmount state is reset @@ -73,12 +68,30 @@ class _TipDetailsScreenState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { final walletLogic = widget.walletLogic; - final tipTo = context.read().tipTo; + final tipping = context.read().tipping; - if (tipTo != null) { - widget.profilesLogic.getProfile(tipTo).then((profile) { + if (tipping != null) { + // Set address state + walletLogic.setHasAddress(true); + + // Pre-fill amount and description if provided + if (tipping.amount != null) { + walletLogic.amountController.text = tipping.amount!; + } + if (tipping.description != null) { + walletLogic.messageController.text = tipping.description!; + } + + // Update amount after setting text + walletLogic.updateAmount(unlimited: widget.isMinting); + + // Load profile for tip recipient + widget.profilesLogic.getProfile(tipping.to).then((profile) { if (profile != null) { widget.profilesLogic.selectProfile(profile); + } else { + // If no profile found, set the raw address so handleSend can use it + walletLogic.addressController.text = tipping.to; } }); } @@ -92,18 +105,6 @@ class _TipDetailsScreenState extends State { }); } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final tipTo = context.read().tipTo; - if (tipTo != null) { - widget.walletLogic.setHasTip(true); - widget.walletLogic.setHasAddress(true); - // Reset amount state since tip screen starts with empty amount - widget.walletLogic.updateAmount(unlimited: widget.isMinting); - } - } - @override void dispose() { amountFocusNode.dispose(); @@ -112,8 +113,12 @@ class _TipDetailsScreenState extends State { final walletLogic = widget.walletLogic; - walletLogic.clearAmountController(); - walletLogic.resetInputErrorState(); + // Schedule controller clearing after the current frame to avoid + // triggering rebuilds during navigation + WidgetsBinding.instance.addPostFrameCallback((_) { + walletLogic.clearAmountController(); + walletLogic.resetInputErrorState(); + }); super.dispose(); } @@ -225,15 +230,14 @@ class _TipDetailsScreenState extends State { } } - void handleSend( - BuildContext context, String? selectedAddress, String? tipTo) async { + void handleSend(BuildContext context, String? selectedAddress) async { if (_isSending) { return; } final walletLogic = widget.walletLogic; - if (tipTo == null) { + if (selectedAddress == null) { return; } @@ -249,7 +253,7 @@ class _TipDetailsScreenState extends State { final isValid = walletLogic.validateSendFields( walletLogic.amountController.value.text, - selectedAddress ?? walletLogic.addressController.value.text, + selectedAddress, ); if (!isValid) { @@ -259,41 +263,30 @@ class _TipDetailsScreenState extends State { return; } - final toAccount = - selectedAddress ?? walletLogic.addressController.value.text; - - final sendTip = SendTransaction( - tipAmount: walletLogic.amountController.value.text, - tipTo: tipTo, - tipDescription: walletLogic.messageController.value.text.trim(), - ); - try { walletLogic.sendTransaction( - sendTip.tipAmount!, - sendTip.tipTo!, - message: sendTip.tipDescription!, + walletLogic.amountController.value.text, + selectedAddress, + message: walletLogic.messageController.value.text.trim(), ); } catch (e, stackTrace) { - print('error: $e'); - print('stack: $stackTrace'); + debugPrint('error: $e'); + debugPrint('stack: $stackTrace'); } - widget.walletLogic.setHasTip(false); + widget.walletLogic.clearTipping(); widget.walletLogic.setHasAddress(false); - widget.walletLogic.setTipTo(null); await Future.delayed(const Duration(milliseconds: 50)); HapticFeedback.heavyImpact(); final sent = await navigator.push( - '/wallet/${walletLogic.account}/send/$toAccount/progress', + '/wallet/${walletLogic.account}/send/$selectedAddress/progress', extra: { 'isMinting': widget.isMinting, 'walletLogic': walletLogic, 'profilesLogic': widget.profilesLogic, - 'sendTransaction': sendTip, }); if (sent == true) { @@ -305,7 +298,7 @@ class _TipDetailsScreenState extends State { await Future.delayed(const Duration(milliseconds: 50)); if (navigator.canPop()) { - navigator.go('/wallet/${walletLogic.account}'); + navigator.pop(true); } else { navigator.go('/wallet/${walletLogic.account}'); } @@ -416,10 +409,6 @@ class _TipDetailsScreenState extends State { (WalletState state) => state.wallet, ); - final tipTo = context.select( - (WalletState state) => state.tipTo, - ); - final balance = double.tryParse(wallet != null ? wallet.balance : '0.0') ?? 0.0; @@ -472,8 +461,9 @@ class _TipDetailsScreenState extends State { final formattedAddress = formatHexAddress(walletLogic.addressController.value.text); + final hasAmountText = walletLogic.amountController.value.text.isNotEmpty; final isSendingValid = (hasAddress || isLink) && - hasAmount && + (hasAmount || hasAmountText) && !invalidAmount && (!invalidAddress || isLink) && !(balance <= 0 && topUpPlugin == null); @@ -506,6 +496,8 @@ class _TipDetailsScreenState extends State { const ScrollPhysics(parent: BouncingScrollPhysics()), scrollDirection: Axis.vertical, children: [ + // Show profile section if there's a profile or an address to display + // formattedAddress already includes tipTo if available, otherwise falls back to address controller if (selectedProfile != null || formattedAddress.isNotEmpty) ...[ const SizedBox(height: 10), @@ -541,7 +533,7 @@ class _TipDetailsScreenState extends State { ? formattedAddress : (selectedProfile.username.isNotEmpty ? '@${selectedProfile.username}' - : ''), + : formattedAddress), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -624,7 +616,10 @@ class _TipDetailsScreenState extends State { ? amountFormatter : integerAmountFormatter, ], - onChanged: (_) => handleThrottledUpdateAmount(), + onChanged: (_) { + setState(() {}); + handleThrottledUpdateAmount(); + }, onSubmitted: (_) { FocusManager.instance.primaryFocus?.unfocus(); }, @@ -826,8 +821,11 @@ class _TipDetailsScreenState extends State { context, selectedProfile?.account ?? searchedProfile - ?.account, - tipTo, + ?.account ?? + walletLogic + .addressController + .value + .text, ) : null, enabled: isSendingValid, @@ -836,8 +834,10 @@ class _TipDetailsScreenState extends State { ? AppLocalizations.of(context)! .swipeToMint : isLink - ? "${AppLocalizations.of(context)!.swipeToConfirm} Tip" - : "${AppLocalizations.of(context)!.swipeToSend} Tip", + ? AppLocalizations.of(context)! + .swipeToConfirm + : AppLocalizations.of(context)! + .swipeToSend, completionLabelColor: Theme.of(context) .colors .primary diff --git a/lib/screens/wallet/receive.dart b/lib/screens/wallet/receive.dart index 30afe332..fba2d4d4 100644 --- a/lib/screens/wallet/receive.dart +++ b/lib/screens/wallet/receive.dart @@ -80,6 +80,13 @@ class ReceiveScreenState extends State { widget.logic.clearInputControllers(); widget.profilesLogic.clearSearch(notify: false); + // Defer state updates until after the current frame to avoid widget tree lock + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.logic.clearTipping(); + widget.logic.updateMessage(); + widget.logic.updateListenerAmount(); + }); + super.dispose(); } @@ -117,7 +124,7 @@ class ReceiveScreenState extends State { _selectedProfile = context.read().selectedProfile; }); - widget.logic.setTipTo(result); + widget.logic.setTipping(to: result); // Update QR code with new tip information widget.logic.updateReceiveQR(); } @@ -130,7 +137,7 @@ class ReceiveScreenState extends State { }); widget.profilesLogic.deSelectProfile(); widget.logic.clearAddressController(); - widget.logic.setTipTo(null); + widget.logic.clearTipping(); widget.logic.updateReceiveQR(); } diff --git a/lib/screens/wallet/screen.dart b/lib/screens/wallet/screen.dart index 73aaf54f..932153ca 100644 --- a/lib/screens/wallet/screen.dart +++ b/lib/screens/wallet/screen.dart @@ -34,6 +34,7 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:provider/provider.dart'; import 'package:citizenwallet/l10n/app_localizations.dart'; import 'package:citizenwallet/widgets/communities/offline_banner.dart'; +import 'package:citizenwallet/widgets/communities/community_closed_banner.dart'; import 'package:reown_walletkit/reown_walletkit.dart'; import 'dart:async'; @@ -85,6 +86,8 @@ class WalletScreenState extends State String? _deepLinkParams; String? _sendToURL; Config? _config; + bool _isClosedBannerDismissed = false; + int _bannerResetKey = 0; @override void initState() { @@ -142,6 +145,7 @@ class WalletScreenState extends State _receiveParams = widget.receiveParams; _deepLink = widget.deepLink; _deepLinkParams = widget.deepLinkParams; + _sendToURL = widget.sendToURL; WidgetsBinding.instance.addPostFrameCallback((_) { onLoad(); @@ -149,6 +153,20 @@ class WalletScreenState extends State } } + @override + Future didChangeAppLifecycleState(AppLifecycleState state) async { + switch (state) { + case AppLifecycleState.resumed: + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); + break; + default: + } + } + void onScrollUpdate() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 300) { @@ -371,6 +389,11 @@ class WalletScreenState extends State _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); navigator.go('/wallet/$_address'); } @@ -585,13 +608,18 @@ class WalletScreenState extends State }); if (result != true && sendToURL != null) { - _logic.clearTipTo(); + _logic.clearTipping(); _sendToURL = null; } _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } void handleReceive() async { @@ -599,10 +627,16 @@ class WalletScreenState extends State final navigator = GoRouter.of(context); - navigator.push('/wallet/$_address/receive', extra: { + await navigator.push('/wallet/$_address/receive', extra: { 'logic': _logic, 'profilesLogic': _profilesLogic, }); + + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } Future handlePlugin(PluginConfig pluginConfig) async { @@ -697,6 +731,11 @@ class WalletScreenState extends State _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } Future handleMint({String? receiveParams}) async { @@ -718,6 +757,11 @@ class WalletScreenState extends State _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } void handleVouchers() async { @@ -739,6 +783,11 @@ class WalletScreenState extends State _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } void handleCopy(String value) { @@ -765,6 +814,11 @@ class WalletScreenState extends State _profileLogic.resume(); _profilesLogic.resume(); _voucherLogic.resume(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); } void handleLoad(String address) async { @@ -782,6 +836,11 @@ class WalletScreenState extends State if (args == null) { _logic.resumeFetching(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); return; } @@ -789,6 +848,11 @@ class WalletScreenState extends State if (address == _address && alias == _alias) { _logic.resumeFetching(); + _logic.updateWalletConfigFromRemote(); + setState(() { + _isClosedBannerDismissed = false; + _bannerResetKey++; + }); return; } @@ -1123,7 +1187,7 @@ class WalletScreenState extends State _receiveParams = null; _deepLink = deepLink; _deepLinkParams = deepLinkParams; - + if (voucher != null && voucherParams != null) { _sendToURL = null; } else { @@ -1195,16 +1259,16 @@ class WalletScreenState extends State final eventServiceState = context.select((WalletState state) => state.eventServiceState); - final eventServiceIntentionalDisconnect = context - .select((WalletState state) => state.eventServiceIntentionalDisconnect); - final isOffline = eventServiceState == EventServiceState.error || eventServiceState == EventServiceState.connecting; - final showOfflineBanner = isOffline && !eventServiceIntentionalDisconnect; + final config = context.select((WalletState state) => state.config); + final isCommunityClosed = (config?.community.closed ?? false); + final offboardPlugin = context.select( + (WalletState state) => state.config!.getOffboardPlugin(), + ); final cleaningUp = context.select((WalletState state) => state.cleaningUp); - final config = context.select((WalletState state) => state.config); final hasActiveSessions = context.select((WalletConnectState state) => state.hasActiveSessions); @@ -1262,10 +1326,6 @@ class WalletScreenState extends State ), ), ), - // Positioned( - // bottom: 60, - // left: 0, - // right: 0, Align( alignment: Alignment.bottomCenter, child: Padding( @@ -1275,7 +1335,9 @@ class WalletScreenState extends State crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( - onTap: config?.online == false ? () => () : handleQRScan, + onTap: (config?.online == false || isCommunityClosed) + ? () => () + : handleQRScan, child: Container( height: 90, width: 90, @@ -1286,9 +1348,10 @@ class WalletScreenState extends State .resolveFrom(context), borderRadius: BorderRadius.circular(45), border: Border.all( - color: config?.online == false - ? scanQrDisabledColor - : Theme.of(context).colors.surfacePrimary, + color: + (config?.online == false || isCommunityClosed) + ? scanQrDisabledColor + : Theme.of(context).colors.surfacePrimary, width: 3, ), boxShadow: [ @@ -1309,9 +1372,10 @@ class WalletScreenState extends State child: Icon( CupertinoIcons.qrcode_viewfinder, size: 60, - color: config?.online == false - ? scanQrDisabledColor - : Theme.of(context).colors.surfacePrimary, + color: + (config?.online == false || isCommunityClosed) + ? scanQrDisabledColor + : Theme.of(context).colors.surfacePrimary, ), ), ), @@ -1398,6 +1462,19 @@ class WalletScreenState extends State ), ), ), + CommunityClosedBanner( + key: ValueKey('community_closed_banner_$_bannerResetKey'), + handleOffboardPlugin: offboardPlugin != null + ? () => handlePlugin(offboardPlugin) + : null, + onDismiss: () { + setState(() { + _isClosedBannerDismissed = true; + }); + }, + display: isCommunityClosed && !_isClosedBannerDismissed, + offboardPlugin: offboardPlugin, + ), OfflineBanner( communityUrl: config?.community.url ?? '', display: isOffline, diff --git a/lib/screens/wallet/wallet_actions.dart b/lib/screens/wallet/wallet_actions.dart index 12ae17d3..25bef986 100644 --- a/lib/screens/wallet/wallet_actions.dart +++ b/lib/screens/wallet/wallet_actions.dart @@ -63,24 +63,30 @@ class _WalletActionsState extends State { final actionButton = context.select(selectActionButtonToShow); final plugins = context.select(selectVisiblePlugins); final featuredPlugins = context.select(selectFeaturedPlugins); - final featuredPlugin = featuredPlugins.isNotEmpty ? featuredPlugins.first : null; + final featuredPlugin = + featuredPlugins.isNotEmpty ? featuredPlugins.first : null; final onePlugin = plugins.isNotEmpty ? plugins.first : null; final imageSmall = context.select((ProfileState state) => state.imageSmall); final username = context.select((ProfileState state) => state.username); - final withOfflineBanner = - eventServiceState != EventServiceState.connected && - eventServiceState != EventServiceState.disconnected; + final withOfflineBanner = eventServiceState == EventServiceState.error || + eventServiceState == EventServiceState.connecting; + + final isCommunityClosed = eventServiceState == EventServiceState.closed; final blockSending = context.select(selectShouldBlockSending) || loading || firstLoad || - widget.handleSendScreen == null; + widget.handleSendScreen == null || + isCommunityClosed; final sendLoading = context.read().transactionSendLoading; - final blockReceive = - loading || firstLoad || widget.handleReceive == null || sendLoading; + final blockReceive = loading || + firstLoad || + widget.handleReceive == null || + sendLoading || + isCommunityClosed; final hasPending = context.select(selectHasProcessingTransactions); final newBalance = context.select(selectWalletBalance); @@ -121,22 +127,22 @@ class _WalletActionsState extends State { final buttonSeparator = buttonCount > 3 ? (baseButtonSeparator * 0.5).clamp(5.0, 20.0) : baseButtonSeparator; - + final buttonBarHeight = (1 - widget.shrink) < 0.7 ? 60.0 : progressiveClamp(40, 120, widget.shrink); - + // Reduce button size when there are more buttons final baseButtonSize = (1 - widget.shrink) < 0.9 ? 60.0 : 80.0; final buttonSize = buttonCount > 3 ? (baseButtonSize * 0.85).clamp(50.0, 70.0) : baseButtonSize; - + final baseButtonIconSize = (1 - widget.shrink) < 0.9 ? 20.0 : 40.0; final buttonIconSize = buttonCount > 3 ? (baseButtonIconSize * 0.85).clamp(18.0, 35.0) : baseButtonIconSize; - + final buttonFontSize = (1 - widget.shrink) < 0.9 ? 12.0 : progressiveClamp(10, buttonCount > 3 ? 12 : 14, widget.shrink); @@ -363,11 +369,13 @@ class _WalletActionsState extends State { if (showActionButton && featuredPlugin != null) ...[ Flexible( child: WalletActionButton( - key: Key('featured_plugin_action_button_${featuredPlugin.name}'), + key: Key( + 'featured_plugin_action_button_${featuredPlugin.name}'), customIcon: featuredPlugin.icon != null ? SvgPicture.network( featuredPlugin.icon!, - semanticsLabel: '${featuredPlugin.name} icon', + semanticsLabel: + '${featuredPlugin.name} icon', height: buttonIconSize, width: buttonIconSize, placeholderBuilder: (_) => Icon( @@ -390,7 +398,8 @@ class _WalletActionsState extends State { alt: true, loading: sendLoading, disabled: sendLoading, - onPressed: () => widget.handlePlugin!(featuredPlugin), + onPressed: () => + widget.handlePlugin!(featuredPlugin), ), ), SizedBox( diff --git a/lib/services/accounts/accounts.dart b/lib/services/accounts/accounts.dart index 3edeaf64..a939c4d8 100644 --- a/lib/services/accounts/accounts.dart +++ b/lib/services/accounts/accounts.dart @@ -14,7 +14,7 @@ abstract class AccountsOptionsInterface {} /// /// This is used to store wallet backups and the implementation is platform specific. abstract class AccountsServiceInterface { - final int _version = 4; + final int _version = 7; int get version => _version; diff --git a/lib/services/accounts/backup.dart b/lib/services/accounts/backup.dart index f658f697..32c5fee5 100644 --- a/lib/services/accounts/backup.dart +++ b/lib/services/accounts/backup.dart @@ -81,3 +81,31 @@ class BackupWallet { String get key => '$address@$alias'; String get value => privateKey; } + +class BackupWalletV5 extends BackupWallet { + final String accountFactoryAddress; + + BackupWalletV5({ + required super.address, + required super.alias, + required super.privateKey, + required String accountFactoryAddress, + }) : accountFactoryAddress = + EthereumAddress.fromHex(accountFactoryAddress).hexEip55; + + // Fixes the 'json' super parameter lint + BackupWalletV5.fromJson(super.json) + : accountFactoryAddress = + EthereumAddress.fromHex(json['accountFactoryAddress']).hexEip55, + super.fromJson(); + + @override + Map toJson() { + final json = super.toJson(); + json['accountFactoryAddress'] = accountFactoryAddress; + return json; + } + + @override + String get key => '$address@$accountFactoryAddress@$alias'; +} diff --git a/lib/services/accounts/native/android.dart b/lib/services/accounts/native/android.dart index 83a074a1..ef22ded1 100644 --- a/lib/services/accounts/native/android.dart +++ b/lib/services/accounts/native/android.dart @@ -1,3 +1,4 @@ +import 'package:citizenwallet/services/config/utils.dart'; import 'package:citizenwallet/services/credentials/credentials.dart'; import 'package:citizenwallet/services/db/backup/db.dart'; import 'package:citizenwallet/utils/encrypt.dart'; @@ -6,6 +7,7 @@ import 'package:citizenwallet/services/db/backup/accounts.dart'; import 'package:citizenwallet/services/accounts/backup.dart'; import 'package:citizenwallet/services/accounts/accounts.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:web3dart/crypto.dart'; @@ -66,6 +68,8 @@ class AndroidAccountsService extends AccountsServiceInterface { alias: legacyBackup.alias, address: EthereumAddress.fromHex(legacyBackup.address), name: legacyBackup.name, + accountFactoryAddress: EthereumAddress.fromHex( + getAccountFactoryAddressByAlias(legacyBackup.alias)), ); await _accountsDB.accounts.insert(account); @@ -90,6 +94,161 @@ class AndroidAccountsService extends AccountsServiceInterface { } } }, + 5: () async { + // bad migration, https://github.com/citizenwallet/app/blob/d4f72940e11f1812c34dfb47c0bffe7488a1c32e/lib/services/accounts/native/android.dart#L146 + }, + 6: () async { + // bad migration, https://github.com/citizenwallet/app/blob/d4f72940e11f1812c34dfb47c0bffe7488a1c32e/lib/services/accounts/native/android.dart#L251 + }, + 7: () async { + // This migration handles two paths: + // - AppKevin (v6 -> v7): Clean dirty keys with wrong accountFactoryAddress + // - AppOthers (v4 -> v7): Migrate from old format to new format + + // Read all credentials from secure storage + final allValues = await _credentials.readAll(); + + // Separate keys into different formats + final oldFormatKeys = []; // address@alias + final dirtyNewFormatKeys = + []; // address@accountFactoryAddress@alias (dirty) + + for (final key in allValues.keys) { + if (key.startsWith(backupPrefix) || + key == versionPrefix || + key == pinCodeKey || + key == pinCodeCheckKey) { + continue; + } + + final parts = key.split('@'); + + // Check for old format: address@alias (2 parts) + if (parts.length == 2) { + try { + EthereumAddress.fromHex(parts[0]); + oldFormatKeys.add(key); + } catch (_) { + // Not a valid address, skip + } + } + // Check for new format: address@accountFactoryAddress@alias (3 parts) + else if (parts.length == 3) { + try { + EthereumAddress.fromHex(parts[0]); + EthereumAddress.fromHex(parts[1]); + dirtyNewFormatKeys.add(key); + } catch (_) { + // Not valid addresses, skip + } + } + } + + final toDelete = []; + + // Handle AppOthers path: Migrate old format keys to new format + for (final oldKey in oldFormatKeys) { + final privateKeyValue = allValues[oldKey]; + if (privateKeyValue == null) { + continue; + } + + final parts = oldKey.split('@'); + if (parts.length != 2) { + continue; + } + + final address = parts[0]; + final alias = parts[1]; + + try { + // Get the correct account factory address for this alias + final accountFactoryAddress = + getAccountFactoryAddressByAlias(alias); + + // Create a BackupWalletV5 with the new format + final backup = BackupWalletV5( + address: address, + alias: alias, + accountFactoryAddress: accountFactoryAddress, + privateKey: privateKeyValue, + ); + + // Write the credential with the new key format + await _credentials.write(backup.key, backup.value); + + debugPrint('Migrated old format key: $oldKey -> ${backup.key}'); + + // Mark old key for deletion + toDelete.add(oldKey); + } catch (e) { + debugPrint('Error migrating key $oldKey: $e'); + continue; + } + } + + // Handle AppKevin path: Clean dirty new format keys + for (final dirtyKey in dirtyNewFormatKeys) { + final privateKeyValue = allValues[dirtyKey]; + if (privateKeyValue == null) { + continue; + } + + final parts = dirtyKey.split('@'); + if (parts.length != 3) { + continue; + } + + final address = parts[0]; + final dirtyAccountFactoryAddress = parts[1]; + final alias = parts[2]; + + try { + // Get the CORRECT account factory address for this alias + final correctAccountFactoryAddress = + getAccountFactoryAddressByAlias(alias); + + // Check if the dirty key has the wrong accountFactoryAddress + if (dirtyAccountFactoryAddress.toLowerCase() != + correctAccountFactoryAddress.toLowerCase()) { + debugPrint( + 'Found dirty key with wrong accountFactoryAddress: $dirtyKey'); + debugPrint(' Dirty: $dirtyAccountFactoryAddress'); + debugPrint(' Correct: $correctAccountFactoryAddress'); + + // Create a BackupWalletV5 with the CORRECT account factory address + final cleanBackup = BackupWalletV5( + address: address, + alias: alias, + accountFactoryAddress: correctAccountFactoryAddress, + privateKey: privateKeyValue, + ); + + // Write the credential with the correct key format + await _credentials.write(cleanBackup.key, cleanBackup.value); + + debugPrint('Cleaned dirty key: $dirtyKey -> ${cleanBackup.key}'); + + // Mark dirty key for deletion + toDelete.add(dirtyKey); + } else { + // Key already has correct accountFactoryAddress, no action needed + debugPrint('Key already correct: $dirtyKey'); + } + } catch (e,s) { + debugPrint('Error cleaning dirty key $dirtyKey: $e'); + debugPrintStack(stackTrace: s); + continue; + } + } + + // Delete all old and dirty keys + // TODO: delete old keys after testing + // for (final key in toDelete) { + // await _credentials.delete(key); + // debugPrint('Deleted old/dirty key: $key'); + // } + }, }; // run all migrations @@ -115,7 +274,14 @@ class AndroidAccountsService extends AccountsServiceInterface { final List accounts = await _accountsDB.accounts.all(); for (final account in accounts) { - final privateKey = await _credentials.read(account.id); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + + final privateKey = await _credentials.read(backupKey); if (privateKey == null) { continue; } @@ -135,9 +301,16 @@ class AndroidAccountsService extends AccountsServiceInterface { return; } + final backup = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: bytesToHex(account.privateKey!.privateKey), + ); + await _credentials.write( - account.id, - bytesToHex(account.privateKey!.privateKey), + backup.key, + backup.value, ); } @@ -153,7 +326,14 @@ class AndroidAccountsService extends AccountsServiceInterface { return null; } - final privateKey = await _credentials.read(account.id); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + + final privateKey = await _credentials.read(backupKey); if (privateKey == null) { return account; } @@ -172,16 +352,28 @@ class AndroidAccountsService extends AccountsServiceInterface { // delete wallet backup @override Future deleteAccount(String address, String alias) async { + // Get the account before deleting it + final account = + await _accountsDB.accounts.get(EthereumAddress.fromHex(address), alias); + + if (account == null) { + return; + } + await _accountsDB.accounts.delete( EthereumAddress.fromHex(address), alias, ); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + await _credentials.delete( - getAccountID( - EthereumAddress.fromHex(address), - alias, - ), + backupKey, ); } @@ -279,9 +471,16 @@ class AndroidAccountsService extends AccountsServiceInterface { final allAccounts = await getAllAccounts(); // accounts with private keys for (final account in allAccounts) { + final backup = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: bytesToHex(account.privateKey!.privateKey), + ); + await _credentials.write( - account.id, - bytesToHex(account.privateKey!.privateKey), + backup.key, + backup.value, ); // null private key before updating in DB diff --git a/lib/services/accounts/native/apple.dart b/lib/services/accounts/native/apple.dart index 849f9f1f..371e5c91 100644 --- a/lib/services/accounts/native/apple.dart +++ b/lib/services/accounts/native/apple.dart @@ -2,10 +2,12 @@ import 'package:citizenwallet/services/accounts/backup.dart'; import 'package:citizenwallet/services/accounts/accounts.dart'; import 'package:citizenwallet/services/accounts/options.dart'; import 'package:citizenwallet/services/accounts/utils.dart'; +import 'package:citizenwallet/services/config/utils.dart'; import 'package:citizenwallet/services/credentials/credentials.dart'; import 'package:citizenwallet/services/credentials/native/apple.dart'; import 'package:citizenwallet/services/db/backup/accounts.dart'; import 'package:citizenwallet/services/db/backup/db.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:web3dart/credentials.dart'; import 'package:web3dart/crypto.dart'; @@ -170,6 +172,8 @@ class AppleAccountsService extends AccountsServiceInterface { alias: legacyBackup.alias, address: EthereumAddress.fromHex(legacyBackup.address), name: legacyBackup.name, + accountFactoryAddress: EthereumAddress.fromHex( + getAccountFactoryAddressByAlias(legacyBackup.alias)), ); await _accountsDB.accounts.insert(account); @@ -203,6 +207,160 @@ class AppleAccountsService extends AccountsServiceInterface { } } }, + 5: () async { + // bad migration, https://github.com/citizenwallet/app/blob/d4f72940e11f1812c34dfb47c0bffe7488a1c32e/lib/services/accounts/native/apple.dart#L154 + }, + 6: () async { + // bad migration, https://github.com/citizenwallet/app/blob/d4f72940e11f1812c34dfb47c0bffe7488a1c32e/lib/services/accounts/native/apple.dart#L264 + }, + 7: () async { + // This migration handles two paths: + // - AppKevin (v6 -> v7): Clean dirty keys with wrong accountFactoryAddress + // - AppOthers (v4 -> v7): Migrate from old format to new format + + // Read all credentials from Keychain + final allValues = await _credentials.readAll(); + + // Separate keys into different formats + final oldFormatKeys = []; // address@alias + final dirtyNewFormatKeys = + []; // address@accountFactoryAddress@alias (dirty) + + for (final key in allValues.keys) { + if (key.startsWith(backupPrefix) || key == versionPrefix) { + continue; + } + + final parts = key.split('@'); + + // Check for old format: address@alias (2 parts) + if (parts.length == 2) { + try { + EthereumAddress.fromHex(parts[0]); + oldFormatKeys.add(key); + } catch (_) { + // Not a valid address, skip + } + } + // Check for new format: address@accountFactoryAddress@alias (3 parts) + else if (parts.length == 3) { + try { + EthereumAddress.fromHex(parts[0]); + EthereumAddress.fromHex(parts[1]); + dirtyNewFormatKeys.add(key); + } catch (_) { + // Not valid addresses, skip + } + } + } + + final toDelete = []; + + // Handle AppOthers path: Migrate old format keys to new format + for (final oldKey in oldFormatKeys) { + final privateKeyValue = allValues[oldKey]; + if (privateKeyValue == null) { + continue; + } + + final parts = oldKey.split('@'); + if (parts.length != 2) { + continue; + } + + final address = parts[0]; + final alias = parts[1]; + + try { + // Get the correct account factory address for this alias + final accountFactoryAddress = + getAccountFactoryAddressByAlias(alias); + + // Create a BackupWalletV5 with the new format + final backup = BackupWalletV5( + address: address, + alias: alias, + accountFactoryAddress: accountFactoryAddress, + privateKey: privateKeyValue, + ); + + // Write the credential with the new key format + await _credentials.write(backup.key, backup.value); + + debugPrint('Migrated old format key: $oldKey -> ${backup.key}'); + + // Mark old key for deletion + toDelete.add(oldKey); + } catch (e) { + debugPrint('Error migrating key $oldKey: $e'); + continue; + } + } + + // Handle AppKevin path: Clean dirty new format keys + for (final dirtyKey in dirtyNewFormatKeys) { + final privateKeyValue = allValues[dirtyKey]; + if (privateKeyValue == null) { + continue; + } + + final parts = dirtyKey.split('@'); + if (parts.length != 3) { + continue; + } + + final address = parts[0]; + final dirtyAccountFactoryAddress = parts[1]; + final alias = parts[2]; + + try { + // Get the CORRECT account factory address for this alias + final correctAccountFactoryAddress = + getAccountFactoryAddressByAlias(alias); + + // Check if the dirty key has the wrong accountFactoryAddress + if (dirtyAccountFactoryAddress.toLowerCase() != + correctAccountFactoryAddress.toLowerCase()) { + debugPrint( + 'Found dirty key with wrong accountFactoryAddress: $dirtyKey'); + debugPrint(' Dirty: $dirtyAccountFactoryAddress'); + debugPrint(' Correct: $correctAccountFactoryAddress'); + + // Create a BackupWalletV5 with the CORRECT account factory address + final cleanBackup = BackupWalletV5( + address: address, + alias: alias, + accountFactoryAddress: correctAccountFactoryAddress, + privateKey: privateKeyValue, + ); + + // Write the credential with the correct key format + await _credentials.write(cleanBackup.key, cleanBackup.value); + + debugPrint('Cleaned dirty key: $dirtyKey -> ${cleanBackup.key}'); + + // Mark dirty key for deletion + toDelete.add(dirtyKey); + } else { + // Key already has correct accountFactoryAddress, no action needed + debugPrint('Key already correct: $dirtyKey'); + } + } catch (e,s) { + debugPrint('Error cleaning dirty key $dirtyKey: $e'); + debugPrintStack(stackTrace: s); + continue; + } + } + + // Delete all old format keys + // TODO: delete old keys after testing + // for (final key in toDelete) { + // final saved = await _credentials.containsKey(key); + // if (saved) { + // await _credentials.delete(key); + // } + // } + }, }; // run all migrations @@ -228,7 +386,14 @@ class AppleAccountsService extends AccountsServiceInterface { final List accounts = await _accountsDB.accounts.all(); for (final account in accounts) { - final privateKey = await _credentials.read(account.id); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + + final privateKey = await _credentials.read(backupKey); if (privateKey == null) { continue; } @@ -248,9 +413,16 @@ class AppleAccountsService extends AccountsServiceInterface { return; } + final backup = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: bytesToHex(account.privateKey!.privateKey), + ); + await _credentials.write( - account.id, - bytesToHex(account.privateKey!.privateKey), + backup.key, + backup.value, ); } @@ -266,7 +438,14 @@ class AppleAccountsService extends AccountsServiceInterface { return null; } - final privateKey = await _credentials.read(account.id); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + + final privateKey = await _credentials.read(backupKey); if (privateKey == null) { return account; } @@ -285,16 +464,29 @@ class AppleAccountsService extends AccountsServiceInterface { // delete wallet backup @override Future deleteAccount(String address, String alias) async { + final account = await _accountsDB.accounts.get( + EthereumAddress.fromHex(address), + alias, + ); + + if (account == null) { + return; + } + await _accountsDB.accounts.delete( EthereumAddress.fromHex(address), alias, ); + final backupKey = BackupWalletV5( + address: account.address.hexEip55, + alias: account.alias, + accountFactoryAddress: account.accountFactoryAddress.hexEip55, + privateKey: '', + ).key; + await _credentials.delete( - getAccountID( - EthereumAddress.fromHex(address), - alias, - ), + backupKey, ); } diff --git a/lib/services/accounts/utils.dart b/lib/services/accounts/utils.dart index 023ca3bb..c48ad373 100644 --- a/lib/services/accounts/utils.dart +++ b/lib/services/accounts/utils.dart @@ -15,7 +15,6 @@ import 'package:web3dart/web3dart.dart'; Future getLegacyAccountAddress( LegacyBackupWallet backup) async { try { - // final config = await ConfigService().getConfig(backup.alias); final community = await AppDBService().communities.get(backup.alias); if (community == null) { diff --git a/lib/services/config/config.dart b/lib/services/config/config.dart index 01b02434..f4a3c5df 100644 --- a/lib/services/config/config.dart +++ b/lib/services/config/config.dart @@ -62,6 +62,7 @@ class CommunityConfig { final String logo; final String? customDomain; final bool hidden; + final bool closed; final ColorTheme theme; final ContractLocation profile; final ContractLocation primaryToken; @@ -76,6 +77,7 @@ class CommunityConfig { required this.logo, this.customDomain, this.hidden = false, + this.closed = false, required this.theme, required this.profile, required this.primaryToken, @@ -96,6 +98,7 @@ class CommunityConfig { logo: json['logo'] ?? '', customDomain: json['custom_domain'], hidden: json['hidden'] ?? false, + closed: json['closed'] ?? false, theme: theme, profile: ContractLocation.fromJson(json['profile']), primaryToken: ContractLocation.fromJson(json['primary_token']), @@ -117,6 +120,7 @@ class CommunityConfig { 'logo': logo, 'custom_domain': customDomain, 'hidden': hidden, + 'closed': closed, 'theme': theme, 'profile': profile.toJson(), 'primary_token': primaryToken.toJson(), @@ -370,6 +374,7 @@ class PluginConfig { final bool hidden; final bool signature; final bool featured; + final Map? meta; PluginConfig({ required this.name, @@ -380,6 +385,7 @@ class PluginConfig { this.hidden = false, this.signature = false, this.featured = false, + this.meta, }); factory PluginConfig.fromJson(Map json) { @@ -394,6 +400,7 @@ class PluginConfig { hidden: json['hidden'] ?? false, signature: json['signature'] ?? false, featured: json['featured'] ?? false, + meta: json['meta'] as Map?, ); } @@ -412,13 +419,14 @@ class PluginConfig { 'hidden': hidden, 'signature': signature, 'featured': featured, + if (meta != null) 'meta': meta, }; } // to string @override String toString() { - return 'PluginConfig{name: $name, icon: $icon, url: $url, launchMode: $launchMode, action: $action, hidden: $hidden, signature: $signature, featured: $featured}'; + return 'PluginConfig{name: $name, icon: $icon, url: $url, launchMode: $launchMode, action: $action, hidden: $hidden, signature: $signature, featured: $featured, meta: $meta}'; } } @@ -706,6 +714,10 @@ class Config { return plugins?.firstWhereOrNull((plugin) => plugin.action == 'topup'); } + PluginConfig? getOffboardPlugin() { + return plugins?.firstWhereOrNull((plugin) => plugin.action == 'offboard'); + } + TokenConfig getPrimaryToken() { final primaryToken = tokens[community.primaryToken.fullAddress]; if (primaryToken == null) { @@ -726,6 +738,25 @@ class Config { return primaryAccountAbstraction; } + ERC4337Config getAccountAbstractionConfig({ + required String accountFactoryAddress, + }) { + // Build the full address key using chainId:accountFactoryAddress format + final chainId = community.primaryToken.chainId; + final fullAddress = '$chainId:$accountFactoryAddress'; + + // Try to find the account config + final accountConfig = accounts[fullAddress]; + + if (accountConfig == null) { + throw Exception( + 'Account Abstraction Config not found for address: $fullAddress', + ); + } + + return accountConfig; + } + CardsConfig? getPrimaryCardManager() { return cards?[community.primaryCardManager?.fullAddress]; } @@ -740,13 +771,21 @@ class Config { return chain.node.url; } - String getRpcUrl(String chainId) { + String getRpcUrl({ + required String chainId, + required String accountFactoryAddress, + }) { final chain = chains[chainId]; if (chain == null) { throw Exception('Chain not found'); } - return '${chain.node.url}/v1/rpc/${getPrimaryAccountAbstractionConfig().paymasterAddress}'; + // Get the account config (primary or specific based on accountFactoryAddress) + final accountConfig = getAccountAbstractionConfig( + accountFactoryAddress: accountFactoryAddress, + ); + + return '${chain.node.url}/v1/rpc/${accountConfig.paymasterAddress}'; } } diff --git a/lib/services/config/legacy.dart b/lib/services/config/legacy.dart index 7fe9c7e0..a9c5bf4c 100644 --- a/lib/services/config/legacy.dart +++ b/lib/services/config/legacy.dart @@ -571,4 +571,8 @@ class LegacyConfig { LegacyPluginConfig? getTopUpPlugin() { return plugins.firstWhereOrNull((plugin) => plugin.action == 'topup'); } + + LegacyPluginConfig? getOffboardPlugin() { + return plugins.firstWhereOrNull((plugin) => plugin.action == 'offboard'); + } } diff --git a/lib/services/config/service.dart b/lib/services/config/service.dart index 89b998ec..12c5c743 100644 --- a/lib/services/config/service.dart +++ b/lib/services/config/service.dart @@ -30,7 +30,7 @@ class ConfigService { static const String communityConfigListS3FileName = 'communities'; static const String communityDebugFileName = 'debug'; - static const int version = 4; + static const int version = 5; final PreferencesService _pref = PreferencesService(); late APIService _api; @@ -105,6 +105,14 @@ class ConfigService { } } + // NEW: Try direct fetch first (more efficient) + try { + final config = await getConfigByAlias(alias); + if (config != null) { + return config; + } + } catch (_) {} + try { // fetch the config and await _configs = await getConfigs(location: location); @@ -145,25 +153,54 @@ class ConfigService { return [Config.fromJson(response)]; } - final response = await _api.get( - url: - '/v$version/$communityConfigListFileName.json?cachebuster=${generateCacheBusterValue()}'); + final response = await _api.get(url: '/api/communities'); _pref.setConfigs(response); - final configs = (response as List).map((e) => Config.fromJson(e)).toList(); + // The API returns an array of wrapper objects with the config nested in the 'json' field + final configs = (response as List).map((e) { + final configData = e['json'] as Map; + return Config.fromJson(configData); + }).toList(); return configs; } Future> getLocalConfigs() async { - final localConfigs = jsonDecode(await rootBundle.loadString( - 'assets/config/v$version/$communityConfigListFileName.json')); - - final configs = - (localConfigs as List).map((e) => Config.fromJson(e)).toList(); + try { + debugPrint( + '📄 Loading JSON file: assets/config/v$version/$communityConfigListFileName.json'); + final jsonString = await rootBundle.loadString( + 'assets/config/v$version/$communityConfigListFileName.json'); + + debugPrint('📄 JSON loaded, size: ${jsonString.length} chars'); + debugPrint('📄 Parsing JSON...'); + final localConfigs = jsonDecode(jsonString); + + debugPrint( + '📄 JSON parsed, found ${(localConfigs as List).length} items'); + debugPrint('📄 Converting to Config objects...'); + + final configs = []; + for (var i = 0; i < localConfigs.length; i++) { + try { + final config = Config.fromJson(localConfigs[i]); + configs.add(config); + if ((i + 1) % 10 == 0 || i == localConfigs.length - 1) { + debugPrint('📄 Converted ${i + 1}/${localConfigs.length} configs'); + } + } catch (e) { + debugPrint('❌ Error converting config $i: $e'); + } + } - return configs; + debugPrint('📄 All configs converted successfully'); + return configs; + } catch (e, s) { + debugPrint('ERROR in getLocalConfigs: $e'); + debugPrintStack(stackTrace: s); + return []; + } } Future getRemoteConfig(String remoteConfigUrl) async { @@ -184,7 +221,19 @@ class ConfigService { final dynamic response = await remote.get(url: '?cachebuster=${generateCacheBusterValue()}'); - final config = Config.fromJson(response); + if (response == null) { + debugPrint('Empty response for remote config'); + return null; + } + + // The API returns a wrapper object with the config nested in the 'json' field + final configData = response['json'] as Map?; + if (configData == null) { + debugPrint('No json field in response for remote config'); + return null; + } + + final config = Config.fromJson(configData); return config; } catch (e, s) { @@ -206,16 +255,47 @@ class ConfigService { return configs; } - final List response = await _api.get( - url: - '/v$version/$communityConfigListS3FileName.json?cachebuster=${generateCacheBusterValue()}'); + final List response = await _api.get(url: '/api/communities'); - final List communities = - response.map((item) => Config.fromJson(item)).toList(); + // The API returns an array of wrapper objects with the config nested in the 'json' field + final List communities = response.map((item) { + final configData = item['json'] as Map; + return Config.fromJson(configData); + }).toList(); return communities; } + Future getConfigByAlias(String alias) async { + if (kDebugMode) { + final localConfigs = jsonDecode(await rootBundle.loadString( + 'assets/config/v$version/$communityConfigListFileName.json')); + + final configs = + (localConfigs as List).map((e) => Config.fromJson(e)).toList(); + return configs.firstWhereOrNull((c) => c.community.alias == alias); + } + + try { + final response = await _api.get(url: '/api/communities/$alias'); + if (response == null) { + debugPrint('Empty response for config: $alias'); + return null; + } + // The API returns a wrapper object with the config nested in the 'json' field + final configData = response['json'] as Map?; + if (configData == null) { + debugPrint('No json field in response for config: $alias'); + return null; + } + return Config.fromJson(configData); + } catch (e, s) { + debugPrint('Error fetching config for $alias: $e'); + debugPrint('Stacktrace: $s'); + return null; + } + } + Future isCommunityOnline(String indexerUrl) async { final indexer = APIService(baseURL: indexerUrl, netTimeoutSeconds: 20); diff --git a/lib/services/config/utils.dart b/lib/services/config/utils.dart index 7c8a3829..667df7d5 100644 --- a/lib/services/config/utils.dart +++ b/lib/services/config/utils.dart @@ -15,3 +15,87 @@ 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', + '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.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', +}; + +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/lib/services/db/app/communities.dart b/lib/services/db/app/communities.dart index 46eefac1..fceece75 100644 --- a/lib/services/db/app/communities.dart +++ b/lib/services/db/app/communities.dart @@ -3,6 +3,7 @@ import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/config/legacy.dart'; import 'package:citizenwallet/services/config/service.dart'; import 'package:citizenwallet/services/db/db.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:sqflite/sqflite.dart'; @@ -28,6 +29,50 @@ Future> legacyToV4(Database db, String name) async { return v4Configs; } +Future> V5Migration(Database db, String name) async { + try { + final ConfigService config = ConfigService(); + final localConfigs = await config.getLocalConfigs(); + + final List> maps = await db.query(name); + final existingCommunities = List.generate(maps.length, (i) { + return DBCommunity.fromMap(maps[i]); + }); + + final List updatedConfigs = []; + + for (final localConfig in localConfigs) { + final existingCommunity = existingCommunities.firstWhereOrNull( + (c) => c.alias == localConfig.community.alias, + ); + + if (existingCommunity != null) { + // Update existing community, preserve online status + final updatedCommunity = DBCommunity( + alias: localConfig.community.alias, + config: localConfig.toJson(), + hidden: localConfig.community.hidden, + version: localConfig.version, + online: existingCommunity.online, + ); + updatedConfigs.add(updatedCommunity); + } else { + // New community in v5 + updatedConfigs.add(DBCommunity.fromConfig(localConfig)); + } + } + + return updatedConfigs; + } catch (e, s) { + debugPrint('ERROR in V5Migration: $e'); + debugPrintStack(stackTrace: s); + + // Return existing data unchanged on error + final List> maps = await db.query(name); + return List.generate(maps.length, (i) => DBCommunity.fromMap(maps[i])); + } +} + class DBCommunity { final String alias; // index final bool hidden; @@ -118,6 +163,9 @@ class CommunityTable extends DBTable { 2: [ 'V4Migration', ], + 3: [ + 'V5Migration', + ], }; for (var i = oldVersion + 1; i <= newVersion; i++) { @@ -131,6 +179,10 @@ class CommunityTable extends DBTable { final updatedConfigs = await legacyToV4(db, name); await upsert(updatedConfigs); continue; + case 'V5Migration': + final updatedConfigs = await V5Migration(db, name); + await upsert(updatedConfigs); + continue; } await db.execute(query); @@ -144,26 +196,61 @@ class CommunityTable extends DBTable { } Future seed() async { - final localConfigs = await _config.getLocalConfigs(); + try { + debugPrint('🌱 Starting seed process...'); - // Check if the table is empty - final count = - Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM $name')); - if (count != null && count > 0) { - return; // Table is not empty, skip seeding - } + // Check if the table is empty + debugPrint('🌱 Checking if table is empty...'); + final count = Sqflite.firstIntValue( + await this.db.rawQuery('SELECT COUNT(*) FROM $name')); - // Prepare batch operation for efficient insertion - final batch = db.batch(); + if (count != null && count > 0) { + debugPrint('🌱 Table already has $count entries, skipping seed'); + return; // Table is not empty, skip seeding + } - for (final config in localConfigs) { - batch.insert( - name, - DBCommunity.fromConfig(config).toMap(), - ); - } + debugPrint('🌱 Loading local configs...'); + final localConfigs = await _config.getLocalConfigs(); + debugPrint('🌱 Loaded ${localConfigs.length} configs'); - await batch.commit(noResult: true); + if (localConfigs.isEmpty) { + debugPrint('🌱 No configs to seed'); + return; + } + + debugPrint('🌱 Converting configs to DB format...'); + final communities = []; + for (var i = 0; i < localConfigs.length; i++) { + try { + final community = DBCommunity.fromConfig(localConfigs[i]); + communities.add(community); + if ((i + 1) % 10 == 0) { + debugPrint('🌱 Converted ${i + 1}/${localConfigs.length} configs'); + } + } catch (e) { + debugPrint('❌ Error converting config $i: $e'); + } + } + debugPrint('🌱 Converted ${communities.length} configs total'); + + debugPrint('🌱 Preparing batch insert...'); + final batch = this.db.batch(); + + for (final community in communities) { + batch.insert( + name, + community.toMap(), + ); + } + + debugPrint('🌱 Committing batch with ${communities.length} inserts...'); + await batch.commit(noResult: true); + debugPrint('🌱 Seed completed successfully!'); + } catch (e, s) { + debugPrint('❌ Error seeding communities table: $e'); + debugPrintStack(stackTrace: s); + rethrow; // Rethrow to surface the error + } } Future upsert(List communities) async { diff --git a/lib/services/db/app/db.dart b/lib/services/db/app/db.dart index f73c927d..65e25b60 100644 --- a/lib/services/db/app/db.dart +++ b/lib/services/db/app/db.dart @@ -27,7 +27,7 @@ class AppDBService extends DBService { await communities.migrate(db, oldVersion, newVersion); return; }, - version: 2, + version: 3, ); final db = await databaseFactory.openDatabase( diff --git a/lib/services/db/backup/accounts.dart b/lib/services/db/backup/accounts.dart index 13404623..20248722 100644 --- a/lib/services/db/backup/accounts.dart +++ b/lib/services/db/backup/accounts.dart @@ -1,8 +1,8 @@ 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'; import 'package:flutter/foundation.dart'; import 'package:sqflite/sqlite_api.dart'; import 'package:web3dart/crypto.dart'; @@ -12,6 +12,7 @@ class DBAccount { final String id; final String alias; final EthereumAddress address; + final EthereumAddress accountFactoryAddress; final String name; final UserHandle? userHandle; final String? username; @@ -21,6 +22,7 @@ class DBAccount { DBAccount({ required this.alias, required this.address, + required this.accountFactoryAddress, required this.name, this.username, this.privateKey, @@ -34,6 +36,7 @@ class DBAccount { 'id': id, 'alias': alias, 'address': address.hexEip55, + 'accountFactoryAddress': accountFactoryAddress.hexEip55, if (name.isNotEmpty) 'name': name, 'username': username, 'privateKey': @@ -47,6 +50,8 @@ class DBAccount { return DBAccount( alias: map['alias'], address: EthereumAddress.fromHex(map['address']), + accountFactoryAddress: + EthereumAddress.fromHex(map['accountFactoryAddress']), name: map['name'], username: map['username'], privateKey: map['privateKey'] != null @@ -96,7 +101,8 @@ class AccountsTable extends DBTable { name TEXT NOT NULL, username TEXT, privateKey TEXT, - profile TEXT + profile TEXT, + accountFactoryAddress TEXT NOT NULL ) '''; @@ -113,6 +119,17 @@ class AccountsTable extends DBTable { ], 3: [ 'ALTER TABLE $name ADD COLUMN username TEXT DEFAULT NULL', + ], + 4: [ + // bad migration,https://github.com/citizenwallet/app/blob/d4f72940e11f1812c34dfb47c0bffe7488a1c32e/lib/services/db/backup/accounts.dart#L123 + ], + 5: [ + // This migration handles both paths: + // - AppKevin (v4 -> v5): column already exists, just populate + // - AppOthers (v3 -> v5): column doesn't exist, add it then populate + 'AddAccountFactoryAddressIfNotExists', + 'PopulateAccountFactoryAddressMigration', + 'CleanDirtyV4Accounts', ] }; @@ -122,6 +139,20 @@ class AccountsTable extends DBTable { if (queries != null) { for (final query in queries) { try { + switch (query) { + case 'AddAccountFactoryAddressIfNotExists': + await _addAccountFactoryAddressIfNotExists(db, name); + continue; + + case 'PopulateAccountFactoryAddressMigration': + await _populateAccountFactoryAddressMigration(db, name); + continue; + + case 'CleanDirtyV4Accounts': + await _cleanDirtyV4Accounts(db, name); + continue; + } + await db.execute(query); } catch (e, s) { debugPrint('Migration error: $e'); @@ -132,6 +163,145 @@ class AccountsTable extends DBTable { } } + Future _addAccountFactoryAddressIfNotExists( + Database db, + String name, + ) async { + final columnName = 'accountFactoryAddress'; + + // Check if column exists + final tableInfo = await db.rawQuery('PRAGMA table_info($name)'); + final hasColumn = tableInfo.any((col) => col['name'] == columnName); + + if (hasColumn) { + return; + } + + await db + .execute('ALTER TABLE $name ADD COLUMN $columnName TEXT DEFAULT ""'); + } + + 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); + + // Update the accountFactoryAddress column (ID still in old format $address@$alias) + await db.update( + name, + {'accountFactoryAddress': accountFactoryAddress}, + where: 'id = ?', + whereArgs: [oldId], + ); + } + } + + Future _cleanDirtyV4Accounts(Database db, String name) async { + // Get all accounts from the database + List> accounts = await db.query(name); + + for (final Map account in accounts) { + final String currentId = account['id'] as String; + final String alias = account['alias'] as String; + final String addressStr = account['address'] as String; + final String accountFactoryAddressStr = + account['accountFactoryAddress'] as String; + + // Construct what the ID should be in the old format + final String oldFormatId = getAccountID(EthereumAddress.fromHex(addressStr), alias); + + // Construct what the ID would be in the new (bad) format + final String newFormatId = '$addressStr@$accountFactoryAddressStr@$alias'; + + // Check if current ID matches the new (bad) format + if (currentId == newFormatId) { + debugPrint('Cleaning dirty account: $currentId -> $oldFormatId'); + + // Check if an account with the old format ID already exists + final existingOldFormat = await db.query( + name, + where: 'id = ?', + whereArgs: [oldFormatId], + ); + + if (existingOldFormat.isEmpty) { + // No conflict: Insert new row with old ID format + final Map cleanAccount = Map.from(account); + cleanAccount['id'] = oldFormatId; + + await db.insert( + name, + cleanAccount, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + debugPrint('Inserted clean account with old format ID: $oldFormatId'); + } else { + // Conflict exists: Keep the existing old format, just log + debugPrint( + 'Old format ID already exists, keeping existing: $oldFormatId'); + } + + // Delete the row with new (bad) format ID + await db.delete( + name, + where: 'id = ?', + whereArgs: [currentId], + ); + + debugPrint('Deleted dirty account with new format ID: $currentId'); + } else if (currentId == oldFormatId) { + // Already in correct old format, do nothing + debugPrint('Account already in correct format: $currentId'); + } else { + // Unexpected format - force to old format + debugPrint( + 'Warning: Unexpected ID format, forcing to old format: $currentId -> $oldFormatId'); + + // Check if an account with the old format ID already exists + final existingOldFormat = await db.query( + name, + where: 'id = ?', + whereArgs: [oldFormatId], + ); + + if (existingOldFormat.isEmpty) { + // No conflict: Insert new row with old ID format, preserving all other columns + final Map cleanAccount = Map.from(account); + cleanAccount['id'] = oldFormatId; + + await db.insert( + name, + cleanAccount, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + debugPrint( + 'Inserted account with corrected old format ID: $oldFormatId'); + } else { + // Conflict exists: Keep the existing old format + debugPrint( + 'Old format ID already exists, keeping existing: $oldFormatId'); + } + + // Delete the row with unexpected format ID + await db.delete( + name, + where: 'id = ?', + whereArgs: [currentId], + ); + + debugPrint('Deleted account with unexpected format ID: $currentId'); + } + } + } + // 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..595fc9b3 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: 5, ); final db = await databaseFactory.openDatabase( diff --git a/lib/services/engine/events.dart b/lib/services/engine/events.dart index 076faf1e..85618514 100644 --- a/lib/services/engine/events.dart +++ b/lib/services/engine/events.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show WebSocket; +import 'dart:io' show WebSocket, WebSocketException; import 'package:citizenwallet/utils/delay.dart'; @@ -9,6 +9,7 @@ enum EventServiceState { connecting, connected, error, + closed, } class WebSocketEvent { diff --git a/lib/services/preferences/preferences.dart b/lib/services/preferences/preferences.dart index 14d8a7d6..a6e89ecd 100644 --- a/lib/services/preferences/preferences.dart +++ b/lib/services/preferences/preferences.dart @@ -81,6 +81,15 @@ class PreferencesService { String? get lastAlias => _preferences.getString('lastAlias'); + // save the last account factory address that was opened + Future setLastAccountFactoryAddress(String accountFactoryAddress) async { + await _preferences.setString( + 'lastAccountFactoryAddress', accountFactoryAddress); + } + + String? get lastAccountFactoryAddress => + _preferences.getString('lastAccountFactoryAddress'); + // save the last link that was opened on web Future setLastWalletLink(String link) async { await _preferences.setString('lastWalletLink', link); @@ -160,5 +169,4 @@ class PreferencesService { String? getLanguageCode() { return _preferences.getString('languageCode'); } - } diff --git a/lib/services/wallet/contracts/account_factory.dart b/lib/services/wallet/contracts/account_factory.dart index cf27bffc..113b162d 100644 --- a/lib/services/wallet/contracts/account_factory.dart +++ b/lib/services/wallet/contracts/account_factory.dart @@ -14,7 +14,10 @@ Future accountFactoryServiceFromConfig(Config config, {String? customAccountFactory}) async { final primaryAccountFactory = config.community.primaryAccountFactory; - final url = config.getRpcUrl(primaryAccountFactory.chainId.toString()); + final url = config.getRpcUrl( + chainId: primaryAccountFactory.chainId.toString(), + accountFactoryAddress: primaryAccountFactory.address, + ); // final wsurl = // config.chains[primaryAccountFactory.chainId.toString()]!.node.wsUrl; print('url: $url'); 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)); diff --git a/lib/services/wallet/wallet.dart b/lib/services/wallet/wallet.dart index afbe4beb..5e592284 100644 --- a/lib/services/wallet/wallet.dart +++ b/lib/services/wallet/wallet.dart @@ -78,6 +78,8 @@ class WalletService { late EthPrivateKey _credentials; // Represents a private key for an Ethereum account. late EthereumAddress _account; // Represents an Ethereum address. + late EthereumAddress _accountFactoryAddress; + late SigAuthService _sigAuth; late StackupEntryPoint @@ -100,6 +102,7 @@ class WalletService { EthPrivateKey get credentials => _credentials; EthereumAddress get address => _credentials.address; EthereumAddress get account => _account; + EthereumAddress get accountFactoryAddress => _accountFactoryAddress; /// retrieves the current balance of the address Future getBalance({String? addr, BigInt? tokenId}) async { @@ -154,6 +157,7 @@ class WalletService { Future init( EthereumAddress account, + EthereumAddress accountFactoryAddress, EthPrivateKey privateKey, NativeCurrency currency, Config config, { @@ -161,16 +165,19 @@ class WalletService { void Function(bool)? onFinished, }) async { _alias = config.community.alias; + _accountFactoryAddress = accountFactoryAddress; final token = config.getPrimaryToken(); - final accountAbstractionConfig = - config.getPrimaryAccountAbstractionConfig(); + + final accountAbstractionConfig = config.getAccountAbstractionConfig( + accountFactoryAddress: accountFactoryAddress.hexEip55); + final chain = config.chains[token.chainId.toString()]; _url = chain!.node.url; _wsurl = chain.node.wsUrl; - final rpcUrl = config.getRpcUrl(token.chainId.toString()); + final rpcUrl = config.getRpcUrl(chainId: token.chainId.toString(), accountFactoryAddress: accountFactoryAddress.hexEip55); _ethClient = Web3Client( rpcUrl, diff --git a/lib/state/app/logic.dart b/lib/state/app/logic.dart index 7ece074d..b8a090ba 100644 --- a/lib/state/app/logic.dart +++ b/lib/state/app/logic.dart @@ -83,8 +83,6 @@ class AppLogic { final address = dbWallet.address.hexEip55; - // final config = await _config.getConfig(dbWallet.alias); - final community = await _appDBService.communities.get(dbWallet.alias); if (community == null) { @@ -132,8 +130,6 @@ class AppLogic { await delay( const Duration(milliseconds: 500)); // smoother launch experience - // final config = await _config.getConfig(alias); - final community = await _appDBService.communities.get(alias); if (community == null) { @@ -193,6 +189,8 @@ class AppLogic { privateKey: credentials, name: token.name, alias: communityConfig.community.alias, + accountFactoryAddress: EthereumAddress.fromHex(communityConfig.community.primaryAccountFactory.address), + )); _theme.changeTheme(communityConfig.community.theme); @@ -285,8 +283,6 @@ class AppLogic { throw Exception('Invalid private key'); } - // final config = await _config.getConfig(alias); - final community = await _appDBService.communities.get(alias); if (community == null) { @@ -308,6 +304,7 @@ class AppLogic { privateKey: credentials, name: name, alias: communityConfig.community.alias, + accountFactoryAddress: EthereumAddress.fromHex(communityConfig.community.primaryAccountFactory.address), ), ); @@ -348,8 +345,6 @@ class AppLogic { final credentials = wallet.privateKey; - // final config = await _config.getConfig(alias); - final community = await _appDBService.communities.get(alias); if (community == null) { @@ -374,6 +369,8 @@ class AppLogic { privateKey: credentials, name: '${token.symbol} Web Account', alias: communityConfig.community.alias, + accountFactoryAddress: EthereumAddress.fromHex(communityConfig.community.primaryAccountFactory.address), + ), ); diff --git a/lib/state/profile/logic.dart b/lib/state/profile/logic.dart index 6c53f64a..6bfc1290 100644 --- a/lib/state/profile/logic.dart +++ b/lib/state/profile/logic.dart @@ -150,6 +150,7 @@ class ProfileLogic { Future loadProfile({String? account, bool online = false}) async { final ethAccount = _wallet.account; final alias = _wallet.alias ?? ''; + final accountFactoryAddress = _wallet.accountFactoryAddress.hexEip55; final acc = account ?? ethAccount.hexEip55; resume(); @@ -210,6 +211,7 @@ class ProfileLogic { _accountBackupDBService.accounts.update(DBAccount( alias: alias, address: ethAccount, + accountFactoryAddress: EthereumAddress.fromHex(accountFactoryAddress), name: profile.name, username: profile.username, privateKey: null, @@ -325,6 +327,7 @@ class ProfileLogic { DBAccount( alias: _wallet.alias!, address: EthereumAddress.fromHex(newProfile.account), + accountFactoryAddress: _wallet.accountFactoryAddress, name: newProfile.name, username: newProfile.username, privateKey: null, @@ -407,6 +410,7 @@ class ProfileLogic { DBAccount( alias: _wallet.alias!, address: EthereumAddress.fromHex(newProfile.account), + accountFactoryAddress: _wallet.accountFactoryAddress, name: newProfile.name, username: newProfile.username, privateKey: null, @@ -471,6 +475,7 @@ class ProfileLogic { final address = _wallet.account.hexEip55; final alias = _wallet.alias ?? ''; + final accountFactoryAddress = _wallet.accountFactoryAddress.hexEip55; final account = await _accountBackupDBService.accounts .get(EthereumAddress.fromHex(address), alias); @@ -554,6 +559,7 @@ class ProfileLogic { DBAccount( alias: alias, address: EthereumAddress.fromHex(address), + accountFactoryAddress: EthereumAddress.fromHex(accountFactoryAddress), name: newProfile.name, username: newProfile.username, profile: newProfile, diff --git a/lib/state/profiles/logic.dart b/lib/state/profiles/logic.dart index febe6767..23e983de 100644 --- a/lib/state/profiles/logic.dart +++ b/lib/state/profiles/logic.dart @@ -232,6 +232,7 @@ class ProfilesLogic extends WidgetsBindingObserver { DBAccount( alias: account.alias, address: account.address, + accountFactoryAddress: account.accountFactoryAddress, name: updatedProfile.name, username: updatedProfile.username, profile: updatedProfile, diff --git a/lib/state/wallet/logic.dart b/lib/state/wallet/logic.dart index 81b1264e..85f410d8 100644 --- a/lib/state/wallet/logic.dart +++ b/lib/state/wallet/logic.dart @@ -8,6 +8,7 @@ import 'package:citizenwallet/services/cache/contacts.dart'; import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/config/service.dart'; import 'package:citizenwallet/services/db/account/db.dart'; +import 'package:citizenwallet/services/db/app/communities.dart'; import 'package:citizenwallet/services/db/backup/accounts.dart'; import 'package:citizenwallet/services/db/app/db.dart'; import 'package:citizenwallet/services/db/account/transactions.dart'; @@ -27,6 +28,7 @@ import 'package:citizenwallet/state/notifications/logic.dart'; import 'package:citizenwallet/state/theme/logic.dart'; import 'package:citizenwallet/state/wallet/state.dart'; import 'package:citizenwallet/utils/delay.dart'; +import 'package:citizenwallet/utils/platform.dart'; import 'package:citizenwallet/utils/qr.dart'; import 'package:citizenwallet/utils/random.dart'; import 'package:citizenwallet/utils/uint8.dart'; @@ -373,6 +375,7 @@ class WalletLogic extends WidgetsBindingObserver { await _wallet.init( dbWallet.address, + dbWallet.accountFactoryAddress, dbWallet.privateKey!, nativeCurrency, communityConfig, @@ -390,17 +393,9 @@ class WalletLogic extends WidgetsBindingObserver { ContactsCache().init(_accountDBService); - _config - .isCommunityOnline( - communityConfig.chains[token.chainId.toString()]!.node.url) - .then((isOnline) { - communityConfig.online = isOnline; - - _state.setWalletConfig(communityConfig); + _state.setWalletConfig(communityConfig); - _appDBService.communities - .updateOnlineStatus(communityConfig.community.alias, isOnline); - }); + updateWalletConfigFromRemote(); _state.setWallet( CWWallet( @@ -452,8 +447,6 @@ class WalletLogic extends WidgetsBindingObserver { final credentials = EthPrivateKey.createRandom(Random.secure()); - // final config = await _config.getConfig(alias); - final community = await _appDBService.communities.get(alias); if (community == null) { @@ -486,6 +479,8 @@ class WalletLogic extends WidgetsBindingObserver { privateKey: credentials, name: 'New ${token.symbol} Account', alias: communityConfig.community.alias, + accountFactoryAddress: EthereumAddress.fromHex( + communityConfig.community.primaryAccountFactory.address), )); _theme.changeTheme(communityConfig.community.theme); @@ -553,6 +548,8 @@ class WalletLogic extends WidgetsBindingObserver { privateKey: credentials, name: name, alias: communityConfig.community.alias, + accountFactoryAddress: EthereumAddress.fromHex( + communityConfig.community.primaryAccountFactory.address), )); _theme.changeTheme(communityConfig.community.theme); @@ -582,6 +579,7 @@ class WalletLogic extends WidgetsBindingObserver { privateKey: dbWallet.privateKey, name: name, alias: dbWallet.alias, + accountFactoryAddress: dbWallet.accountFactoryAddress, )); loadDBWallets(); @@ -1095,12 +1093,37 @@ class WalletLogic extends WidgetsBindingObserver { return false; } - final balance = double.tryParse(_state.wallet?.balance ?? '0.0') ?? 0.0; - final doubleAmount = double.parse(toUnit( - amount.replaceAll(',', '.'), + if (amount.isEmpty) { + return false; + } + + // Handle trailing decimal separator - validate what's before the separator + final trimmedAmount = amount.trim(); + if (trimmedAmount.endsWith(',') || trimmedAmount.endsWith('.')) { + // Remove trailing separator and validate the partial amount + final withoutTrailing = + trimmedAmount.substring(0, trimmedAmount.length - 1); + if (withoutTrailing.isEmpty) { + // Just "," or "." - treat as empty (not invalid, but also not valid) + return false; + } + // Validate what's before the trailing separator + amount = withoutTrailing; + } + + // Balance is stored in smallest units, convert to human-readable format for comparison + final balanceRaw = _state.wallet?.balance ?? '0.0'; + final balance = double.parse(fromDoubleUnit( + balanceRaw, decimals: _wallet.currency.decimals, - ).toString()); + )); + + // Parse the amount as a double in human-readable format + // Handle both comma and dot as decimal separators + final normalizedAmount = amount.replaceAll(',', '.'); + final doubleAmount = double.tryParse(normalizedAmount) ?? 0.0; + // If parsing fails or amount is 0 or greater than balance, it's invalid return doubleAmount == 0 || doubleAmount > balance; } @@ -1665,16 +1688,20 @@ class WalletLogic extends WidgetsBindingObserver { _amountController.clear(); } - void clearTipTo() { - _state.clearTipTo(); - } - - void setTipTo(String? tipTo) { - _state.setTipTo(tipTo); + void setTipping({ + required String to, + String? amount, + String? description, + }) { + _state.setTipping( + to: to, + amount: amount, + description: description, + ); } - void setHasTip(bool value) { - _state.setHasTip(value); + void clearTipping() { + _state.clearTipping(); } void setHasAddress(bool value) { @@ -1699,7 +1726,10 @@ class WalletLogic extends WidgetsBindingObserver { _state.setInvalidAddress(true); } - void updateAmount({bool unlimited = false}) { + Future updateAmount({bool unlimited = false}) async { + // Fetch current balance before validating to ensure we check against the latest balance + await updateBalance(); + _state.setHasAmount( _amountController.text.isNotEmpty, isInvalidAmount(_amountController.value.text, unlimited: unlimited), @@ -1791,15 +1821,21 @@ class WalletLogic extends WidgetsBindingObserver { } if (parsedData.amount != null) { - if (format == QRFormat.eip681Transfer) { - final amount = fromDoubleUnit( - parsedData.amount!, - decimals: _wallet.currency.decimals, - ); - _amountController.text = amount; + // Parse amount value + final numValue = double.tryParse(parsedData.amount!) ?? 0; + + // Format amount based on community decimal support + final decimalDigits = _state.wallet?.decimalDigits ?? 0; + if (decimalDigits == 0) { + // No decimal support - use integer format + _amountController.text = numValue.toInt().toString(); } else { - _amountController.text = parsedData.amount!; + // Decimal support - format with appropriate precision + _amountController.text = numValue + .toStringAsFixed(decimalDigits) + .replaceAll(RegExp(r'\.?0+$'), ''); } + updateAmount(); } @@ -1827,8 +1863,30 @@ class WalletLogic extends WidgetsBindingObserver { // Handle tip information if present if (parsedData.tip != null) { - _state.setTipTo(parsedData.tip!.to); - _state.setHasTip(true); + String? formattedTipAmount; + + // Format tip amount if provided, based on community decimal support + if (parsedData.tip!.amount != null) { + final tipNumValue = double.tryParse(parsedData.tip!.amount!) ?? 0; + final decimalDigits = _state.wallet?.decimalDigits ?? 0; + + if (decimalDigits == 0) { + // No decimal support - use integer format + formattedTipAmount = tipNumValue.toInt().toString(); + } else { + // Decimal support - format with appropriate precision + formattedTipAmount = tipNumValue + .toStringAsFixed(decimalDigits) + .replaceAll(RegExp(r'\.?0+$'), ''); + } + } + + // Always create tipping state if tip destination is present + _state.setTipping( + to: parsedData.tip!.to, + amount: formattedTipAmount, // Will be null if not provided + description: parsedData.tip!.description, + ); } return addressToUse; @@ -1894,9 +1952,9 @@ class WalletLogic extends WidgetsBindingObserver { } // Add tipTo parameter if it exists in the state - final tipTo = _state.tipTo; - if (tipTo != null && tipTo.isNotEmpty) { - params += '&tipTo=$tipTo'; + final tipping = _state.tipping; + if (tipping != null && tipping.to.isNotEmpty) { + params += '&tipTo=${tipping.to}'; } // Check if URL already has query parameters in the fragment @@ -2029,17 +2087,47 @@ class WalletLogic extends WidgetsBindingObserver { final encodedRedirectUrl = Uri.encodeComponent(redirectUrl); final parsedURL = Uri.parse(appUniversalURL); + + // Determine platform value + final platformValue = isPlatformAndroid() ? 'android' : 'ios'; + + // Parse the plugin URL + final pluginUri = Uri.parse(pluginConfig.url); if (pluginConfig.signature) { + // Parse existing connection query params + final connectionParams = Uri(query: connection.queryParams).queryParameters; + + // Merge with platform parameter + final updatedUri = pluginUri.replace( + queryParameters: { + ...pluginUri.queryParameters, + ...connectionParams, + 'platform': platformValue, + }, + ); + return ( - '${pluginConfig.url}${pluginConfig.url.contains('?') ? '&' : '?'}${connection.queryParams}', + updatedUri.toString(), parsedURL.scheme != 'https' ? parsedURL.scheme : null, redirectUrl, ); } + // For non-signature case, add all parameters including platform + final updatedUri = pluginUri.replace( + queryParameters: { + ...pluginUri.queryParameters, + 'account': _wallet.account.hexEip55, + 'expiry': now.millisecondsSinceEpoch.toString(), + 'redirectUrl': encodedRedirectUrl, + 'signature': '0x123', + 'platform': platformValue, + }, + ); + return ( - '${pluginConfig.url}?account=${_wallet.account.hexEip55}&expiry=${now.millisecondsSinceEpoch}&redirectUrl=$encodedRedirectUrl&signature=0x123', + updatedUri.toString(), parsedURL.scheme != 'https' ? parsedURL.scheme : null, redirectUrl, ); @@ -2117,6 +2205,66 @@ class WalletLogic extends WidgetsBindingObserver { cleanupWalletService(); } + Future updateWalletConfigFromRemote() async { + try { + if (_wallet.alias == null) { + return; + } + + final community = await _appDBService.communities.get(_wallet.alias!); + + if (community == null) { + return; + } + + Config communityConfig = Config.fromJson(community.config); + + final remoteConfigUrl = communityConfig.configLocation; + + if (remoteConfigUrl.isEmpty) { + return; + } + + final remoteConfig = await _config.getRemoteConfig(remoteConfigUrl); + + if (remoteConfig == null) { + return; + } + + // Update the wallet config with the remote config + _state.setWalletConfig(remoteConfig); + + final token = remoteConfig.getPrimaryToken(); + + remoteConfig.online = await _config.isCommunityOnline( + remoteConfig.chains[token.chainId.toString()]!.node.url); + + _state.setWalletConfig(remoteConfig); + + // Update the database with the new config + await _appDBService.communities.upsert( + [DBCommunity.fromConfig(remoteConfig)], + ); + debugPrint('Remote config updated'); + + // Update wallet properties with the new config + if (_state.wallet != null) { + final updatedWallet = _state.wallet!.copyWith( + currencyName: token.name, + symbol: token.symbol, + currencyLogo: remoteConfig.community.logo, + decimalDigits: token.decimals, + plugins: remoteConfig.plugins ?? [], + ); + _state.setWallet(updatedWallet); + debugPrint('Wallet properties updated with remote config'); + } + } catch (e, s) { + debugPrint('Error updating remote config: $e'); + debugPrint('Stacktrace: $s'); + } + } + @override Future didChangeAppLifecycleState(AppLifecycleState state) async { switch (state) { @@ -2129,24 +2277,7 @@ class WalletLogic extends WidgetsBindingObserver { } await updateBalance(); - - final community = await _appDBService.communities.get(_wallet.alias!); - - if (community == null) { - return; - } - - Config communityConfig = Config.fromJson(community.config); - - final token = communityConfig.getPrimaryToken(); - - communityConfig.online = await _config.isCommunityOnline( - communityConfig.chains[token.chainId.toString()]!.node.url); - - await _appDBService.communities.updateOnlineStatus( - communityConfig.community.alias, communityConfig.online); - - _state.setWalletConfig(communityConfig); + await updateWalletConfigFromRemote(); break; default: diff --git a/lib/state/wallet/state.dart b/lib/state/wallet/state.dart index 4eb75cba..d87d6292 100644 --- a/lib/state/wallet/state.dart +++ b/lib/state/wallet/state.dart @@ -4,6 +4,7 @@ import 'package:citizenwallet/services/config/config.dart'; import 'package:citizenwallet/services/engine/events.dart'; import 'package:citizenwallet/services/preferences/preferences.dart'; import 'package:citizenwallet/state/wallet/utils.dart'; +import 'package:citizenwallet/utils/send.dart'; import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; @@ -80,9 +81,7 @@ class WalletState with ChangeNotifier { bool cwWalletsLoading = false; bool cwWalletsError = false; - String? tipTo; - bool _hasTip = false; - bool get hasTip => _hasTip; + SendDestination? tipping; void setEventServiceState(EventServiceState state) { eventServiceState = state; @@ -701,19 +700,23 @@ class WalletState with ChangeNotifier { notifyListeners(); } - void setTipTo(String? tipTo) { - this.tipTo = tipTo; + void setTipping({ + required String to, + String? amount, + String? description, + }) { + tipping = SendDestination( + to: to, + amount: amount, + description: description, + ); notifyListeners(); } - void setHasTip(bool value) { - _hasTip = value; + void clearTipping() { + tipping = null; notifyListeners(); } - void clearTipTo() { - tipTo = null; - _hasTip = false; - notifyListeners(); - } + bool get hasTip => tipping != null; } diff --git a/lib/utils/qr.dart b/lib/utils/qr.dart index fb0507a7..cce1ee7a 100644 --- a/lib/utils/qr.dart +++ b/lib/utils/qr.dart @@ -133,7 +133,6 @@ ParsedQRData parseSendtoUrl(String raw) { } // Handle malformed query strings (convert ? to & after the first one) - // e.g., "alias=...?sendto=..." becomes "alias=...&sendto=..." if (queryString.contains('?')) { final firstQuestionMark = queryString.indexOf('?'); queryString = queryString.substring(0, firstQuestionMark) + @@ -148,24 +147,54 @@ ParsedQRData parseSendtoUrl(String raw) { uriData = parsedUri; } - final sendToParam = uriData.queryParameters['sendto']; - final amountParam = uriData.queryParameters['amount']; - final descriptionParam = uriData.queryParameters['description']; + // Use 'var' so we can update them if hidden params are found + var sendToParam = uriData.queryParameters['sendto']; + var amountParam = uriData.queryParameters['amount']; + var descriptionParam = uriData.queryParameters['description']; - final tipToParam = uriData.queryParameters['tipTo']; - final tipAmountParam = uriData.queryParameters['tipAmount']; - final tipDescriptionParam = uriData.queryParameters['tipDescription']; + var tipToParam = uriData.queryParameters['tipTo']; + var tipAmountParam = uriData.queryParameters['tipAmount']; + var tipDescriptionParam = uriData.queryParameters['tipDescription']; + + // 1. Handle encoded params inside sendto + if (sendToParam != null && sendToParam.contains('&')) { + final firstAmpIndex = sendToParam.indexOf('&'); + final realSendTo = sendToParam.substring(0, firstAmpIndex); + final hiddenParamsString = sendToParam.substring(firstAmpIndex + 1); + + sendToParam = realSendTo; + + final hiddenParams = Uri.splitQueryString(hiddenParamsString); + amountParam ??= hiddenParams['amount']; + descriptionParam ??= hiddenParams['description']; + } + + // 2. Handle encoded params inside tipTo (NEW LOGIC) + if (tipToParam != null && tipToParam.contains('&')) { + final firstAmpIndex = tipToParam.indexOf('&'); + final realTipTo = tipToParam.substring(0, firstAmpIndex); + final hiddenParamsString = tipToParam.substring(firstAmpIndex + 1); + + // Clean the tipTo param + tipToParam = realTipTo; + + // Parse the hidden string + final hiddenTipParams = Uri.splitQueryString(hiddenParamsString); + + // Populate tipAmount and tipDescription if they are null + tipAmountParam ??= hiddenTipParams['tipAmount']; + tipDescriptionParam ??= hiddenTipParams['tipDescription']; + } if (sendToParam == null) { return ParsedQRData(address: ''); } final address = sendToParam.split('@').first; - // Extract alias from sendto parameter (after @) as fallback - final aliasFromSendto = sendToParam.contains('@') - ? sendToParam.split('@').last - : null; - // Use explicit alias parameter if present, otherwise fallback to alias from sendto + + final aliasFromSendto = + sendToParam.contains('@') ? sendToParam.split('@').last : null; + final alias = uriData.queryParameters['alias'] ?? aliasFromSendto; final tip = tipToParam != null diff --git a/lib/widgets/communities/community_closed_banner.dart b/lib/widgets/communities/community_closed_banner.dart new file mode 100644 index 00000000..5111c4c6 --- /dev/null +++ b/lib/widgets/communities/community_closed_banner.dart @@ -0,0 +1,403 @@ +import 'dart:async'; + +import 'package:citizenwallet/services/config/config.dart'; +import 'package:citizenwallet/theme/provider.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:citizenwallet/l10n/app_localizations.dart'; + +// The CommunityClosedBanner class remains the same. +class CommunityClosedBanner extends StatefulWidget { + final VoidCallback? handleOffboardPlugin; + final VoidCallback? onDismiss; + final bool display; + final PluginConfig? offboardPlugin; + + const CommunityClosedBanner({ + super.key, + this.handleOffboardPlugin, + this.onDismiss, + this.display = false, + this.offboardPlugin, + }); + + @override + State createState() => _CommunityClosedBannerState(); +} + +// ==================================================================== +// Refactored State Class +// ==================================================================== + +class _CommunityClosedBannerState extends State + with SingleTickerProviderStateMixin { + // ADD this Mixin + + // Existing state variables + bool _display = false; + double _slideOffset = 100; + bool _isDismissed = false; + + // Keep these for tracking the current drag and its position + double _dragOffset = 0; + bool _isDragging = false; + + // New: Animation Controller for the drag effect (snap-back or final dismissal) + late AnimationController _dragAnimationController; + late Animation _dragAnimation; + + Timer? _showTimer; + Timer? _hideTimer; + + @override + void initState() { + super.initState(); + + _display = widget.display; + _slideOffset = widget.display ? 0 : 100; + + _dragAnimationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + )..addListener(_handleAnimationUpdate); + + // 💡 FIX: Initialize _dragAnimation here with a default value of 0 + _dragAnimation = Tween( + begin: 0, + end: 0, + ).animate(_dragAnimationController); + } + + // New: Listener for the drag animation + void _handleAnimationUpdate() { + setState(() { + // The current drag offset is controlled by the animation when not dragging + _dragOffset = _dragAnimation.value; + }); + // If the animation is completing and it was a dismissal animation, call hide + if (_dragAnimationController.isCompleted && !_isDragging && _isDismissed) { + // This hide call completes the process by setting _display = false + // and animating the main widget's slide offset back to 100 + hide(); + } + } + + @override + void didUpdateWidget(CommunityClosedBanner oldWidget) { + super.didUpdateWidget(oldWidget); + + if (_isDismissed) return; + + if (widget.display != oldWidget.display) { + if (widget.display) { + show(); + } else { + hide(); + } + } + } + + @override + void dispose() { + _showTimer?.cancel(); + _hideTimer?.cancel(); + _dragAnimationController.dispose(); // DISPOSE the controller + super.dispose(); + } + + void show() async { + if (_isDismissed) return; + + setState(() { + _display = true; + _dragOffset = 0; // Ensure reset on show + _isDragging = false; // Ensure reset on show + }); + + _hideTimer?.cancel(); + _showTimer = Timer(const Duration(milliseconds: 50), () { + HapticFeedback.heavyImpact(); + + setState(() { + _slideOffset = 0; + }); + }); + } + + void hide() async { + setState(() { + _slideOffset = 100; + }); + + _showTimer?.cancel(); + _hideTimer = Timer(const Duration(milliseconds: 400), () { + HapticFeedback.lightImpact(); + + setState(() { + _display = false; + }); + }); + } + + void handleLearnMore() { + widget.handleOffboardPlugin?.call(); + } + + void handleDismiss() { + // Only set _isDismissed and call onDismiss. The visual dismissal animation + // is now handled by _handleDragEnd/AnimatedBuilder. + setState(() { + _isDismissed = true; + }); + widget.onDismiss?.call(); + // hide() is called after the dismissal animation completes in _handleAnimationUpdate + } + + void _handleDragStart(DragStartDetails details) { + // Stop any ongoing animation before starting a new drag + _dragAnimationController.stop(); + setState(() { + _isDragging = true; + }); + } + + void _handleDragUpdate(DragUpdateDetails details) { + setState(() { + // 1. Update the drag offset based on user movement (details.delta.dy) + // Dragging down (dy > 0) increases _dragOffset. + // Dragging up (dy < 0) decreases _dragOffset. + _dragOffset += details.delta.dy * 0.8; + + // 2. ⚠️ Recommended FIX: Clamp the drag offset to prevent it from becoming negative. + // A negative _dragOffset translates the banner UP. By clamping at 0, + // we ensure the banner cannot move up past its fully displayed state. + if (_dragOffset < 0) { + _dragOffset = 0; + } + + // The previous 'if (_dragOffset < 0 && _slideOffset < 100)' block + // is no longer necessary, as the clamp handles the restriction. + // Any content related to upward drag logic (like the -50 limit) + // is also overridden by the strict clamp at 0. + }); + } + + void _handleDragEnd(DragEndDetails details) { + final velocity = details.primaryVelocity ?? 0; + final screenHeight = MediaQuery.of(context).size.height; + // Lower the threshold to make dismissal easier + final dismissThreshold = screenHeight * 0.15; // 15% of screen height + + // Calculate the target end position for the animation + final double targetEndOffset; + final bool shouldDismiss; + + if (_dragOffset > dismissThreshold || velocity > 400) { + // ⬇️ DRAG DOWN TO DISMISS + HapticFeedback.mediumImpact(); + shouldDismiss = true; + targetEndOffset = screenHeight; + } else if (_dragOffset < 0 && _dragOffset.abs() > 10) { + // ⬆️ DRAG UP (Optional: check if drag up gesture was significant) + // If you wanted a "compacted" state on drag up, this is where you'd set targetEndOffset to a small negative value. + // For now, we'll just snap back to 0. + HapticFeedback.lightImpact(); + shouldDismiss = false; + targetEndOffset = 0; // Snap back to origin + } else { + // ➖ SNAP BACK (Not enough drag in either direction) + HapticFeedback.lightImpact(); + shouldDismiss = false; + targetEndOffset = 0; + } + + // Set dragging to false + setState(() { + _isDragging = false; + }); + + if (shouldDismiss) { + handleDismiss(); // Call handleDismiss to set _isDismissed = true + } + + // Configure and start the animation for snap-back or dismissal + _dragAnimation = Tween( + begin: _dragOffset, + end: targetEndOffset, + ).animate(CurvedAnimation( + parent: _dragAnimationController, + curve: shouldDismiss + ? Curves.easeOut + : Curves.easeOutCubic, // Different curves for dismissal vs snap-back + )); + + // Reset controller and start the animation + _dragAnimationController.reset(); + _dragAnimationController.forward(); + + // Note: The final call to hide() happens in _handleAnimationUpdate + // when the dismissal animation completes. + } + + @override + Widget build(BuildContext context) { + if (!_display) { + return const SizedBox(); + } + + final screenHeight = MediaQuery.of(context).size.height; + final safeBottomPadding = MediaQuery.of(context).padding.bottom; + final bannerHeight = screenHeight - 300; + + // Use a temporary variable for the current drag offset: + // It's either the real-time drag (if dragging) or the animated value (if snapping back/dismissing) + final currentDragOffset = _isDragging ? _dragOffset : _dragAnimation.value; + + return Positioned( + bottom: 0, + left: 0, + right: 0, + child: GestureDetector( + onVerticalDragStart: _handleDragStart, + onVerticalDragUpdate: _handleDragUpdate, + onVerticalDragEnd: _handleDragEnd, + child: AnimatedContainer( + duration: const Duration(milliseconds: 400), + curve: Curves.easeOut, + transform: Matrix4.translationValues(0, _slideOffset, 0), + height: bannerHeight, + child: AnimatedBuilder( + animation: _dragAnimationController, + builder: (context, child) { + return Transform.translate( + offset: Offset(0, currentDragOffset), + child: child, + ); + }, + child: Container( + padding: EdgeInsets.fromLTRB(20, 10, 20, safeBottomPadding + 20), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F0), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + boxShadow: [ + BoxShadow( + color: Theme.of(context).colors.black.withOpacity(0.3), + blurRadius: 20, + offset: const Offset(0, -5), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Drag handle indicator + Container( + width: 40, + height: 4, + margin: const EdgeInsets.only(top: 8, bottom: 30), + decoration: BoxDecoration( + color: Theme.of(context).colors.black.withOpacity(0.2), + borderRadius: BorderRadius.circular(2), + ), + ), + // Content + Expanded( + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + CupertinoIcons.info_circle_fill, + size: 60, + color: Theme.of(context) + .colors + .primary + .resolveFrom(context), + ), + const SizedBox(height: 20), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + widget.offboardPlugin?.meta?['title'] + as String? ?? + AppLocalizations.of(context)!.communityClosed, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Theme.of(context).colors.black, + ), + ), + ), + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: Text( + widget.offboardPlugin?.meta?['desc'] as String? ?? + AppLocalizations.of(context)! + .communityClosedDescription, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.normal, + color: Theme.of(context) + .colors + .black + .withOpacity(0.7), + height: 1.5, + ), + ), + ), + const SizedBox(height: 30), + if (widget.handleOffboardPlugin != null) + CupertinoButton( + padding: const EdgeInsets.symmetric( + horizontal: 32, + vertical: 14, + ), + color: Theme.of(context) + .colors + .primary + .resolveFrom(context), + borderRadius: BorderRadius.circular(25), + onPressed: handleLearnMore, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.offboardPlugin?.meta?['button'] + as String? ?? + AppLocalizations.of(context)!.learnMore, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context).colors.white, + ), + ), + const SizedBox(width: 8), + Icon( + CupertinoIcons.arrow_right_circle_fill, + color: Theme.of(context).colors.white, + size: 20, + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index f771960c..1dbe5b53 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -16,7 +16,6 @@ import google_sign_in_ios import icloud_storage import mobile_scanner import package_info_plus -import path_provider_foundation import share_plus import shared_preferences_foundation import sqflite_darwin @@ -34,7 +33,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { IcloudStoragePlugin.register(with: registry.registrar(forPlugin: "IcloudStoragePlugin")) MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/pubspec.lock b/pubspec.lock index caf39144..25f1cb75 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "91.0.0" _flutterfire_internals: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "7.7.1" + version: "8.4.1" archive: dependency: "direct main" description: @@ -189,10 +189,10 @@ packages: dependency: transitive description: name: cbor - sha256: f5239dd6b6ad24df67d1449e87d7180727d6f43b87b3c9402e6398c7a2d9609b + sha256: "259230d0c7f3ae58cb68cbc17b95484a038b2f63b15963b019d4bd9d28bf3fe0" url: "https://pub.dev" source: hosted - version: "6.3.7" + version: "6.5.0" characters: dependency: transitive description: @@ -241,6 +241,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" + url: "https://pub.dev" + source: hosted + version: "1.0.0" collection: dependency: "direct main" description: @@ -253,10 +261,10 @@ packages: dependency: transitive description: name: connectivity_plus - sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec + sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "7.0.0" connectivity_plus_platform_interface: dependency: transitive description: @@ -294,18 +302,42 @@ packages: dependency: "direct main" description: name: credential_manager - sha256: "4dbd38e17fb9a8fd30d591826246b7d327d69d7c33bd15e8a9192aa40c178374" + sha256: "3d09d3a753e9975f07dd8d4ba777ce33dbd0269d5196fc1bc3c349da84b1ff5b" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.8" + credential_manager_android: + dependency: transitive + description: + name: credential_manager_android + sha256: cac9b34efc2629fb927ed565795d1e324dbd942d7e6c39e97ed090b5f0ce5f3e + url: "https://pub.dev" + source: hosted + version: "2.0.8" + credential_manager_ios: + dependency: transitive + description: + name: credential_manager_ios + sha256: f174ec4b9a98c893ed79fc2149e4d703b3c04ba2168ba7d8e06b9715ff52f154 + url: "https://pub.dev" + source: hosted + version: "2.0.8" + credential_manager_platform_interface: + dependency: transitive + description: + name: credential_manager_platform_interface + sha256: e36781cc73dee360853275cd2fc7a535305d7368b5c3c7be7fb38f4be6bec3e7 + url: "https://pub.dev" + source: hosted + version: "2.0.8" cross_file: dependency: transitive description: name: cross_file - sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" + sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608" url: "https://pub.dev" source: hosted - version: "0.3.5" + version: "0.3.5+1" crypto: dependency: "direct main" description: @@ -318,10 +350,10 @@ packages: dependency: "direct main" description: name: cryptography - sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 + sha256: "3eda3029d34ec9095a27a198ac9785630fe525c0eb6a49f3d575272f8e792ef0" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.9.0" csslib: dependency: transitive description: @@ -350,10 +382,10 @@ packages: dependency: transitive description: name: dev_build - sha256: fda8a54458b2a873a84e0cd1513f4323a1fb0599ed5455245359bc0398bad9ee + sha256: "1d9aa167c05cbe4be9fbaf863c76dcee9bec302fb861270672beb6d6be0bc8f4" url: "https://pub.dev" source: hosted - version: "1.1.2+11" + version: "1.1.3+1" ed25519_edwards: dependency: transitive description: @@ -414,10 +446,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" file: dependency: transitive description: @@ -430,10 +462,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: f2d9f173c2c14635cc0e9b14c143c49ef30b4934e8d1d274d6206fcb0086a06f + sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" url: "https://pub.dev" source: hosted - version: "10.3.3" + version: "10.3.10" firebase_core: dependency: "direct main" description: @@ -608,10 +640,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "306f0596590e077338312f38837f595c04f28d6cdeeac392d3d74df2f0003687" + sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 url: "https://pub.dev" source: hosted - version: "2.0.32" + version: "2.0.33" flutter_secure_storage: dependency: "direct main" description: @@ -664,10 +696,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -790,6 +822,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.3" + hooks: + dependency: transitive + description: + name: hooks + sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7" + url: "https://pub.dev" + source: hosted + version: "1.0.0" html: dependency: transitive description: @@ -802,10 +842,10 @@ packages: dependency: "direct main" description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -830,22 +870,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - ieee754: - dependency: transitive - description: - name: ieee754 - sha256: "7d87451c164a56c156180d34a4e93779372edd191d2c219206100b976203128c" - url: "https://pub.dev" - source: hosted - version: "1.0.3" image: dependency: "direct main" description: name: image - sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c" url: "https://pub.dev" source: hosted - version: "4.5.4" + version: "4.7.2" intl: dependency: "direct main" description: @@ -970,10 +1002,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -986,10 +1018,10 @@ packages: dependency: "direct main" description: name: mobile_scanner - sha256: "023a71afb4d7cfb5529d0f2636aa8b43db66257905b9486d702085989769c5f2" + sha256: c6184bf2913dd66be244108c9c27ca04b01caf726321c44b0e7a7a1e32d41044 url: "https://pub.dev" source: hosted - version: "7.1.3" + version: "7.1.4" mocktail: dependency: "direct dev" description: @@ -1014,6 +1046,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + native_toolchain_c: + dependency: transitive + description: + name: native_toolchain_c + sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac" + url: "https://pub.dev" + source: hosted + version: "0.17.4" nested: dependency: transitive description: @@ -1054,6 +1094,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537" + url: "https://pub.dev" + source: hosted + version: "9.2.4" octo_image: dependency: transitive description: @@ -1114,18 +1162,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e url: "https://pub.dev" source: hosted - version: "2.2.20" + version: "2.2.22" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.6.0" path_provider_linux: dependency: transitive description: @@ -1250,26 +1298,26 @@ packages: dependency: transitive description: name: reown_core - sha256: "8d5d14b4e8d008b09ec9db964ab8913b4fc17000d666eb1fece20a80a4b5e37a" + sha256: "41295f6bc7a656af3b7bf20f69c0e41a4ee83a1d10803269e46f2213184e136b" url: "https://pub.dev" source: hosted - version: "1.3.6" + version: "1.3.8" reown_sign: dependency: transitive description: name: reown_sign - sha256: c2fec55ed3d0042d0802c80d7fc36f9b1937eac6ef9c2907dae3b8146188cffa + sha256: "28457489a9473bb68968df0a71f46b16b1e9d4636515c523018e801abe4bee2b" url: "https://pub.dev" source: hosted - version: "1.3.7" + version: "1.3.8" reown_walletkit: dependency: "direct main" description: name: reown_walletkit - sha256: f96cc9f6e264138b3411fd1635e06d437d5912d7a21ef28af21974e2680dfd70 + sha256: c15985ef4227e714d5b6be694e867cd6e55468da517663eb037331185f77f533 url: "https://pub.dev" source: hosted - version: "1.3.7" + version: "1.3.8" rxdart: dependency: "direct main" description: @@ -1306,26 +1354,26 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" + sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f url: "https://pub.dev" source: hosted - version: "2.4.15" + version: "2.4.20" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" url: "https://pub.dev" source: hosted - version: "2.5.5" + version: "2.5.6" shared_preferences_linux: dependency: transitive description: @@ -1464,18 +1512,18 @@ packages: dependency: transitive description: name: sqflite_common_ffi - sha256: "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988" + sha256: c59fcdc143839a77581f7a7c4de018e53682408903a0a0800b95ef2dc4033eff url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.4.0+2" sqflite_common_ffi_web: dependency: "direct main" description: name: sqflite_common_ffi_web - sha256: "793c1ff5b0c95ac618e7731e209db99e96abff59ad3432a3c91bd2b1454a00d5" + sha256: "79338d0b69521d70cea10f841209ac87ce617921aaf7d33e7380682c83da1f06" url: "https://pub.dev" source: hosted - version: "1.0.1+2" + version: "1.1.1" sqflite_darwin: dependency: transitive description: @@ -1496,10 +1544,10 @@ packages: dependency: transitive description: name: sqlite3 - sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" + sha256: c6cfe9b1cc159c9eb8ba174b533a60b5126f9db8c6e34efb127d2bc04bc45034 url: "https://pub.dev" source: hosted - version: "2.9.4" + version: "3.1.4" stack_trace: dependency: transitive description: @@ -1552,26 +1600,26 @@ packages: dependency: transitive description: name: test - sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" url: "https://pub.dev" source: hosted - version: "1.26.2" + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.7" test_core: dependency: transitive description: name: test_core - sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "0.6.11" + version: "0.6.12" timeago: dependency: "direct main" description: @@ -1592,18 +1640,18 @@ packages: dependency: "direct main" description: name: universal_html - sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + sha256: c0bcae5c733c60f26c7dfc88b10b0fd27cbcc45cb7492311cdaa6067e21c9cd4 url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.3.0" universal_io: dependency: "direct main" description: name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.1" url_launcher: dependency: "direct main" description: @@ -1616,34 +1664,34 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9" + sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611" url: "https://pub.dev" source: hosted - version: "6.3.24" + version: "6.3.28" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "6b63f1441e4f653ae799166a72b50b1767321ecc263a57aadf825a7a2a5477d9" + sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad url: "https://pub.dev" source: hosted - version: "6.3.5" + version: "6.3.6" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "8262208506252a3ed4ff5c0dc1e973d2c0e0ef337d0a074d35634da5d44397c9" + sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" url: "https://pub.dev" source: hosted - version: "3.2.4" + version: "3.2.5" url_launcher_platform_interface: dependency: transitive description: @@ -1656,18 +1704,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" username_gen: dependency: "direct main" description: @@ -1704,10 +1752,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + sha256: "201e876b5d52753626af64b6359cd13ac6011b80728731428fd34bc840f71c9b" url: "https://pub.dev" source: hosted - version: "1.1.19" + version: "1.1.20" vector_math: dependency: transitive description: @@ -1736,10 +1784,10 @@ packages: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.1" web: dependency: transitive description: @@ -1821,5 +1869,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.10.3 <4.0.0" + flutter: ">=3.38.4" diff --git a/pubspec.yaml b/pubspec.yaml index 9a8c05fd..a9058c13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: citizenwallet -version: 2.0.29+286 +version: 2.0.31+301 publish_to: none description: A mobile wallet for your community. environment: @@ -131,6 +131,9 @@ flutter: - assets/config/v4/communities.json - assets/config/v4/communities.test.json - assets/config/v4/debug.json + - assets/config/v5/communities.json + - assets/config/v5/communities.test.json + - assets/config/v5/debug.json - assets/icons/switch_accounts.svg - assets/config/v3/communities.json - assets/config/v3/communities.test.json @@ -145,6 +148,7 @@ flutter: - assets/lottie/wallet_receive.json - assets/lottie/qr_scan_success.json - assets/lottie/piggie_bank.json + - packages/smartcontracts/contracts/accounts/Account.abi.json - packages/smartcontracts/contracts/accounts/AccountFactory.abi.json - packages/smartcontracts/contracts/external/DERC20.abi.json @@ -157,6 +161,19 @@ flutter: - packages/smartcontracts/contracts/standards/ERC1155.abi.json - packages/smartcontracts/contracts/apps/Profile.abi.json - packages/smartcontracts/contracts/external/CardFactory.abi.json + + - assets/contracts/accounts/Account.abi.json + - assets/contracts/accounts/AccountFactory.abi.json + - assets/contracts/accounts/TokenEntryPoint.abi.json + - assets/contracts/apps/Profile.abi.json + - assets/contracts/apps/SimpleFaucet.abi.json + - assets/contracts/external/CardFactory.abi.json + - assets/contracts/external/DERC20.abi.json + - assets/contracts/external/SimpleAccountFactory.abi.json + - assets/contracts/standards/ERC20.abi.json + - assets/contracts/standards/ERC1155.abi.json + - assets/contracts/standards/IAccessControlUpgradeable.abi.json + - packages/contractforge/abi/CardManagerModule.json - packages/contractforge/abi/Safe.json - packages/contractforge/abi/CommunityModule.json diff --git a/test/services/config/config_v5_test.dart b/test/services/config/config_v5_test.dart new file mode 100644 index 00000000..5f1bf230 --- /dev/null +++ b/test/services/config/config_v5_test.dart @@ -0,0 +1,613 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:citizenwallet/services/config/config.dart'; +import 'package:test/test.dart'; + +const Map> communitiesWithMultipleAccountFactories = { + 'wallet.pay.brussels': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE', + ], + 'gratitude': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD' + ], + 'bread': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9' + ], + 'wallet.commonshub.brussels': [ + '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'wallet.regensunite.earth': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0x9406Cc6185a346906296840746125a0E44976454' + ], + 'gt.celo': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD' + ], + 'ceur.celo': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098' + ], + 'eure.polygon': [ + '0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'app': [ + '0x270758454C012A1f51428b68aE473D728CCdFe88', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'usdc.base': [ + '0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'wallet.oak.community': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0x9406Cc6185a346906296840746125a0E44976454' + ], + 'sbc.polygon': [ + '0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'zinne': [ + '0x11af2639817692D2b805BcE0e1e405E530B20006', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'timebank.regensunite.earth': [ + '0x39b77d77f7677997871b304094a05295eb71e240', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'moos': [ + '0x671f0662de72268d0f3966Fb62dFc6ee6389e244', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'selcoupdepouce': [ + '0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'cit.celo': [ + '0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185' + ], + 'wallet.wolugo.be': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0x8474153A00C959f2cB64852949954DBC68415Bb3' + ], + 'wtc.celo': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xE79E19594A749330036280c685E2719d58d99052' + ], + 'testnet-ethldn': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xc1654087C580f868F08E34cd1c01eDB1d3673b82' + ], + 'celo-c.citizenwallet.xyz': [ + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185', + '0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA' + ], +}; + +const Map> communityRpcUrls = { + 'ctzn': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0x3A3E25871c5C6C84D5f397829FF316a37F7FD596', + }, + 'wallet.pay.brussels': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://engine.pay.brussels/v1/rpc/0xE69C843898E21C0E95eA7DD310cD850AAc0aB897', + '0xBABCf159c4e3186cf48e4a48bC0AeC17CF9d90FE': + 'https://engine.pay.brussels/v1/rpc/0xcA1B9EC1117340818C1c1fdd1B48Ea79E57C140F', + }, + 'gratitude': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xF05ba2641b31AF70c2678e3324eD8b9C53093FbE', + '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8' + }, + 'bread': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://100.engine.citizenwallet.xyz/v1/rpc/0x5987e57e85014B5A56C880313580346c20a5d1c1', + '0xAE76B1C6818c1DD81E20ccefD3e72B773068ABc9': + 'https://100.engine.citizenwallet.xyz/v1/rpc/0xbE2Cb3358aa14621134e923B68b8429315368E32' + }, + 'wallet.commonshub.brussels': { + '0x307A9456C4057F7C7438a174EFf3f25fc0eA6e87': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x4E127A1DAa66568B4a91E8c5615120a6Ea5442E3', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x4860C0f127500F0cbF4a5Bd797cBb5aA50Eb0FbA' + }, + 'wallet.regensunite.earth': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0x250711045d58b6310f0635C7D110BFe663cE1da5', + '0x9406Cc6185a346906296840746125a0E44976454': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0x250711045d58b6310f0635C7D110BFe663cE1da5' + }, + 'gt.celo': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8', + '0xAE6E18a9Cd26de5C8f89B886283Fc3f0bE5f04DD': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x8dd43eE72f6A816b8eB0411B712D96cDd95246d8' + }, + 'ceur.celo': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F', + '0xdA529eBEd3D459dac9d9D3D45b8Cae2D5796c098': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xedbEA8c0F25B34510149EaD4f72867B0d3D2264F' + }, + 'eure.polygon': { + '0x5bA08d9fC7b90f79B2b856bdB09FC9EB32e83616': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xB2cb6b75C2357Ca94dBdF58897E468E45fAC83Ec' + }, + 'app': { + '0x270758454C012A1f51428b68aE473D728CCdFe88': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xB5D1C0167E6325466E2918e9fda8cc41384C0291', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xB5D1C0167E6325466E2918e9fda8cc41384C0291' + }, + 'usdc.base': { + '0x05e2Fb34b4548990F96B3ba422eA3EF49D5dAa99': + 'https://8453.engine.citizenwallet.xyz/v1/rpc/0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://8453.engine.citizenwallet.xyz/v1/rpc/0xA63DFccB8a39a3DFE4479b33190b12019Ee594E7' + }, + 'wallet.oak.community': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://8453.engine.citizenwallet.xyz/v1/rpc/0x123', + '0x9406Cc6185a346906296840746125a0E44976454': + 'https://8453.engine.citizenwallet.xyz/v1/rpc/0x123' + }, + 'sbc.polygon': { + '0x3Be13D9325C8C9174C3819d3d868D5D3aB8Fc8a5': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0x123', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0x123' + }, + 'zinne': { + '0x11af2639817692D2b805BcE0e1e405E530B20006': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://137.engine.citizenwallet.xyz/v1/rpc/0xBb796D122Ec1aBDeD081D50B06a072f981c7E62b' + }, + 'timebank.regensunite.earth': { + '0x39b77d77f7677997871b304094a05295eb71e240': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xe45858bf63176595c2920822581917c7C705a12f', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xe45858bf63176595c2920822581917c7C705a12f' + }, + 'moos': { + '0x671f0662de72268d0f3966Fb62dFc6ee6389e244': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x55E519bfD63c7152D9F7B88Acd712A37F0BEC482' + }, + 'selcoupdepouce': { + '0x4Cc883b7E8E0BCB2e293703EF06426F9b4A5A284': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x635032605337aB36A46D767905108e67EE687a72', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x635032605337aB36A46D767905108e67EE687a72' + }, + 'cit.celo': { + '0x0a9f4B7e7Ec393fF25dc9267289Be259Ec3FB970': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x452F7ff3e55fe29f481841985dE7f4939FD645fa', + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x452F7ff3e55fe29f481841985dE7f4939FD645fa' + }, + 'wallet.wolugo.be': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c', + '0x8474153A00C959f2cB64852949954DBC68415Bb3': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0xF2EFEC3cBFaDE0bB6108620cbF7Cc608d27DCF3c' + }, + 'wtc.celo': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9', + '0xE79E19594A749330036280c685E2719d58d99052': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x3fefC19674f3F6E43B1dFf1861E07c303B9eAAc9' + }, + 'testnet-ethldn': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://84532.engine.citizenwallet.xyz/v1/rpc/0x389182aCCeE26D953d5188BF4b92c49339DcC9FC', + '0xc1654087C580f868F08E34cd1c01eDB1d3673b82': + 'https://84532.engine.citizenwallet.xyz/v1/rpc/0x389182aCCeE26D953d5188BF4b92c49339DcC9FC' + }, + 'celo-c.citizenwallet.xyz': { + '0x7cC54D54bBFc65d1f0af7ACee5e4042654AF8185': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x7f4011845Ea914b6cefc60629e1e00600c972c75', + '0xcd8b1B9E760148c5026Bc5B0D56a5374e301FDcA': + 'https://42220.engine.citizenwallet.xyz/v1/rpc/0x7f4011845Ea914b6cefc60629e1e00600c972c75' + }, +}; + +void main() { + late List configs; + + setUpAll(() async { + // Load and parse JSON file + final jsonString = + await File('assets/config/v5/communities.json').readAsString(); + final jsonList = jsonDecode(jsonString) as List; + configs = jsonList + .map((json) => Config.fromJson(json as Map)) + .toList(); + }); + + group('V5 Config Parsing', () { + test('loads all configs successfully', () { + expect(configs.length, greaterThan(0)); + print('Loaded ${configs.length} configs'); + }); + + test('all configs have valid community data', () { + for (final config in configs) { + expect(config.community.name, isNotEmpty, + reason: 'Community name should not be empty'); + expect(config.community.alias, isNotEmpty, + reason: 'Community alias should not be empty'); + expect(config.community.description, isNotEmpty, + reason: 'Community description should not be empty'); + expect(config.community.url, isNotEmpty, + reason: 'Community URL should not be empty'); + expect(config.community.logo, isNotEmpty, + reason: 'Community logo should not be empty'); + } + }); + + test('all configs have required maps populated', () { + for (final config in configs) { + expect(config.tokens, isNotEmpty, + reason: + 'Config for ${config.community.alias} should have at least one token'); + expect(config.accounts, isNotEmpty, + reason: + 'Config for ${config.community.alias} should have at least one account'); + expect(config.chains, isNotEmpty, + reason: + 'Config for ${config.community.alias} should have at least one chain'); + } + }); + + test('all configs have valid scan configuration', () { + for (final config in configs) { + expect(config.scan.url, isNotEmpty, + reason: + 'Scan URL should not be empty for ${config.community.alias}'); + expect(config.scan.name, isNotEmpty, + reason: + 'Scan name should not be empty for ${config.community.alias}'); + } + }); + + test('all configs have valid IPFS configuration', () { + for (final config in configs) { + expect(config.ipfs.url, isNotEmpty, + reason: + 'IPFS URL should not be empty for ${config.community.alias}'); + } + }); + + test('all configs have valid version field', () { + for (final config in configs) { + expect(config.version, greaterThanOrEqualTo(4), + reason: + 'Version should be 4 or higher for ${config.community.alias}'); + expect(config.version, lessThanOrEqualTo(5), + reason: + 'Version should be 5 or lower for ${config.community.alias}'); + } + }); + + test('all configs have valid config location', () { + for (final config in configs) { + expect(config.configLocation, isNotEmpty, + reason: + 'Config location should not be empty for ${config.community.alias}'); + expect(config.configLocation, startsWith('https://'), + reason: + 'Config location should be HTTPS URL for ${config.community.alias}'); + } + }); + + test('primary token exists in tokens map', () { + for (final config in configs) { + final primaryTokenKey = config.community.primaryToken.fullAddress; + expect(config.tokens.containsKey(primaryTokenKey), isTrue, + reason: + 'Primary token should exist in tokens map for ${config.community.alias}'); + } + }); + + test('primary account factory exists in accounts map', () { + for (final config in configs) { + final primaryAccountKey = + config.community.primaryAccountFactory.fullAddress; + expect(config.accounts.containsKey(primaryAccountKey), isTrue, + reason: + 'Primary account factory should exist in accounts map for ${config.community.alias}'); + } + }); + + test('primary card manager exists in cards map if specified', () { + for (final config in configs) { + if (config.community.primaryCardManager != null) { + final primaryCardKey = + config.community.primaryCardManager!.fullAddress; + expect(config.cards?.containsKey(primaryCardKey) ?? false, isTrue, + reason: + 'Primary card manager should exist in cards map for ${config.community.alias}'); + } + } + }); + + test('chain IDs match between community and chains map', () { + for (final config in configs) { + final primaryChainId = config.community.primaryToken.chainId.toString(); + expect(config.chains.containsKey(primaryChainId), isTrue, + reason: + 'Primary chain ID should exist in chains map for ${config.community.alias}'); + } + }); + + test('getPrimaryToken returns valid token', () { + for (final config in configs) { + final token = config.getPrimaryToken(); + expect(token.name, isNotEmpty, + reason: + 'Token name should not be empty for ${config.community.alias}'); + expect(token.symbol, isNotEmpty, + reason: + 'Token symbol should not be empty for ${config.community.alias}'); + expect(token.decimals, greaterThanOrEqualTo(0), + reason: + 'Token decimals should be non-negative for ${config.community.alias}'); + } + }); + + test('getPrimaryAccountAbstractionConfig returns valid config', () { + for (final config in configs) { + final aaConfig = config.getAccountAbstractionConfig( + accountFactoryAddress: + config.community.primaryAccountFactory.address); + expect(aaConfig.entrypointAddress, isNotEmpty, + reason: + 'Entrypoint address should not be empty for ${config.community.alias}'); + expect(aaConfig.accountFactoryAddress, isNotEmpty, + reason: + 'Account factory address should not be empty for ${config.community.alias}'); + expect(aaConfig.paymasterType, isNotEmpty, + reason: + 'Paymaster type should not be empty for ${config.community.alias}'); + } + }); + + test('plugins list is properly parsed', () { + for (final config in configs) { + if (config.plugins != null && config.plugins!.isNotEmpty) { + for (final plugin in config.plugins!) { + expect(plugin.name, isNotEmpty, + reason: + 'Plugin name should not be empty for ${config.community.alias}'); + expect(plugin.url, isNotEmpty, + reason: + 'Plugin URL should not be empty for ${config.community.alias}'); + } + } + } + }); + + test('custom domain matches alias pattern when present', () { + for (final config in configs) { + if (config.community.customDomain != null) { + // Custom domain should typically match or be related to the alias + expect(config.community.customDomain, isNotEmpty, + reason: + 'Custom domain should not be empty when specified for ${config.community.alias}'); + } + } + }); + + test('wallet URL generation works correctly', () { + const deepLinkBaseUrl = 'https://app.citizenwallet.xyz'; + for (final config in configs) { + final walletUrl = config.community.walletUrl(deepLinkBaseUrl); + expect(walletUrl, startsWith(deepLinkBaseUrl), + reason: + 'Wallet URL should start with base URL for ${config.community.alias}'); + expect(walletUrl, contains('alias=${config.community.alias}'), + reason: + 'Wallet URL should contain alias parameter for ${config.community.alias}'); + } + }); + }); + + group('getAccountAbstractionConfig', () { + test('returns primary config when no address provided', () { + for (final config in configs) { + final aaConfig = config.getAccountAbstractionConfig( + accountFactoryAddress: + config.community.primaryAccountFactory.address); + expect(aaConfig, isA(), + reason: + 'Should return ERC4337Config for ${config.community.alias}'); + expect(aaConfig.accountFactoryAddress, + config.community.primaryAccountFactory.address, + reason: + 'Should return primary account factory address for ${config.community.alias}'); + } + }); + + test('throws exception when empty address provided', () { + final config = configs.first; + expect( + () => config.getAccountAbstractionConfig(accountFactoryAddress: ''), + throwsException, + reason: + 'Should throw exception when empty account factory address is provided', + ); + }); + + test( + 'returns correct config for each account factory in multi-factory communities', + () { + for (final config in configs) { + final alias = config.community.alias; + if (communitiesWithMultipleAccountFactories.containsKey(alias)) { + final factories = communitiesWithMultipleAccountFactories[alias]!; + for (final factoryAddress in factories) { + final aaConfig = config.getAccountAbstractionConfig( + accountFactoryAddress: factoryAddress); + expect(aaConfig, isA(), + reason: + 'Should return ERC4337Config for $alias with factory $factoryAddress'); + expect(aaConfig.accountFactoryAddress, factoryAddress, + reason: + 'Should return correct account factory address for $alias'); + } + } + } + }); + + test('throws exception for non-existent account factory address', () { + final config = configs.first; + expect( + () => config.getAccountAbstractionConfig( + accountFactoryAddress: '0xNonExistentAddress'), + throwsException, + reason: + 'Should throw exception for non-existent account factory address', + ); + }); + + test('all account factories in map exist in their respective configs', () { + for (final config in configs) { + final alias = config.community.alias; + if (communitiesWithMultipleAccountFactories.containsKey(alias)) { + final factories = communitiesWithMultipleAccountFactories[alias]!; + for (final factoryAddress in factories) { + final chainId = config.community.primaryToken.chainId; + final fullAddress = '$chainId:$factoryAddress'; + expect(config.accounts.containsKey(fullAddress), isTrue, + reason: + 'Account factory $factoryAddress should exist in accounts map for $alias'); + } + } + } + }); + }); + + group('getRpcUrl', () { + test('returns correct URL for primary account factory', () { + for (final config in configs) { + final alias = config.community.alias; + + // Skip if no expected RPC URL defined for this community + if (!communityRpcUrls.containsKey(alias)) { + continue; + } + + final chainId = config.community.primaryToken.chainId.toString(); + final primaryAccountFactory = + config.community.primaryAccountFactory.address; + final expectedUrls = communityRpcUrls[alias]!; + + // Get the expected URL for the primary account factory + final expectedUrl = expectedUrls[primaryAccountFactory]; + + if (expectedUrl != null) { + final actualUrl = config.getRpcUrl( + chainId: chainId, accountFactoryAddress: primaryAccountFactory); + expect(actualUrl, equals(expectedUrl), + reason: 'RPC URL mismatch for $alias (primary account factory)'); + } + } + }); + + test('returns correct URL for specific account factory', () { + for (final config in configs) { + final alias = config.community.alias; + + // Skip if no expected RPC URLs defined for this community + if (!communityRpcUrls.containsKey(alias)) { + continue; + } + + final chainId = config.community.primaryToken.chainId.toString(); + final expectedUrls = communityRpcUrls[alias]!; + + // Test each account factory address + for (final accountFactory in expectedUrls.keys) { + final expectedUrl = expectedUrls[accountFactory]!; + final actualUrl = config.getRpcUrl( + chainId: chainId, + accountFactoryAddress: accountFactory, + ); + + expect(actualUrl, equals(expectedUrl), + reason: + 'RPC URL mismatch for $alias with account factory $accountFactory'); + } + } + }); + + test('works for communities with multiple account factories', () { + for (final alias in communitiesWithMultipleAccountFactories.keys) { + final config = configs.firstWhere((c) => c.community.alias == alias); + final accountFactories = + communitiesWithMultipleAccountFactories[alias]!; + final chainId = config.community.primaryToken.chainId.toString(); + + expect(accountFactories.length, greaterThan(1), + reason: '$alias should have multiple account factories'); + + // Verify each account factory returns a different RPC URL + final urls = {}; + for (final accountFactory in accountFactories) { + final url = config.getRpcUrl( + chainId: chainId, + accountFactoryAddress: accountFactory, + ); + urls.add(url); + } + + // All URLs should be valid and non-empty + for (final url in urls) { + expect(url, isNotEmpty); + expect(url, startsWith('https://')); + expect(url, contains('/v1/rpc/')); + } + } + }); + + test('throws exception for invalid chain ID', () { + final config = configs.first; + + expect( + () => config.getRpcUrl( + chainId: '99999', + accountFactoryAddress: + config.community.primaryAccountFactory.address), + throwsException, + reason: 'Should throw exception for non-existent chain ID', + ); + }); + + test('throws exception for non-existent account factory', () { + final config = configs.first; + final chainId = config.community.primaryToken.chainId.toString(); + + expect( + () => config.getRpcUrl( + chainId: chainId, + accountFactoryAddress: '0xNonExistentAddress', + ), + throwsException, + reason: + 'Should throw exception for non-existent account factory address', + ); + }); + }); +} 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, + ); + }); + }); + }); +}