From 34561a2aac0365c8f06848f0bc140fd9dd4f1a5f Mon Sep 17 00:00:00 2001 From: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:28:43 +0900 Subject: [PATCH 01/14] add hip3 support --- .../perpetuals/GetPerpetualImpl.kt | 2 + .../68.json | 2020 +++++++++++++++++ .../service/store/database/GemDatabase.kt | 2 +- .../store/database/di/DatabaseModule.kt | 1 + .../store/database/di/Migration_67_68.kt | 12 + .../store/database/entities/DbPerpetual.kt | 3 + .../views/position/PerpetualPositionScene.kt | 1 + .../viewmodels/PerpetualAmountViewModel.kt | 1 + .../PerpetualDetailsDataAggregate.kt | 4 + .../gemwallet/android/model/ConfirmParams.kt | 16 +- .../com/wallet/core/primitives/Perpetual.kt | 4 +- .../kotlin/com/wallet/core/primitives/Scan.kt | 37 +- .../PerpetualPositionViewModel.swift | 15 +- .../ViewModels/PerpetualSceneViewModel.swift | 27 +- .../TestKit/Perpetual+TestKit.swift | 6 +- .../ConfirmRecipientViewModel.swift | 2 +- .../PerpetualOrderFactory.swift | 4 + .../GemPerpetual+GemstonePrimitives.swift | 3 +- ...petualConfirmData+GemstonePrimitives.swift | 2 + .../Extensions/Perpetual+Primitives.swift | 8 + .../Primitives/Sources/Perpetual.swift | 8 +- .../Sources/PerpetualTransferData.swift | 5 +- ios/Packages/Primitives/Sources/Scan.swift | 1 + .../TestKit/Perpetual+PrimitivesTestKit.swift | 6 +- ...rpetualConfirmData+PrimitivesTestKit.swift | 2 + ...petualTransferData+PrimitivesTestKit.swift | 6 +- .../ViewModels/AddressListItemViewModel.swift | 4 +- ios/Packages/Store/Sources/Migrations.swift | 8 + .../Sources/Models/PerpetualRecord.swift | 11 +- .../Perpetuals/PerpetualRequest.swift | 3 +- 30 files changed, 2154 insertions(+), 70 deletions(-) create mode 100644 android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json create mode 100644 android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt diff --git a/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt b/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt index 191ab7860b..20750167da 100644 --- a/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt +++ b/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt @@ -45,4 +45,6 @@ class PerpetualDetailsDataAggregateImpl( override val price: Double = data.perpetual.price override val identifier: String = data.perpetual.identifier + + override val onlyIsolated: Boolean = data.perpetual.onlyIsolated } \ No newline at end of file diff --git a/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json b/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json new file mode 100644 index 0000000000..53ebf71ce8 --- /dev/null +++ b/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json @@ -0,0 +1,2020 @@ +{ + "formatVersion": 1, + "database": { + "version": 68, + "identityHash": "710140be0f07cd0f4db014d5b44a518e", + "entities": [ + { + "tableName": "wallets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `domain_name` TEXT, `type` TEXT NOT NULL, `position` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `index` INTEGER NOT NULL, `source` TEXT NOT NULL DEFAULT 'Import', PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "domainName", + "columnName": "domain_name", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pinned", + "columnName": "pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "index", + "columnName": "index", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'Import'" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `derivation_path` TEXT NOT NULL, `address` TEXT NOT NULL, `chain` TEXT NOT NULL, `extendedPublicKey` TEXT, PRIMARY KEY(`wallet_id`, `address`, `chain`, `derivation_path`))", + "fields": [ + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "derivationPath", + "columnName": "derivation_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "extendedPublicKey", + "columnName": "extendedPublicKey", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "wallet_id", + "address", + "chain", + "derivation_path" + ] + } + }, + { + "tableName": "asset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `symbol` TEXT NOT NULL, `decimals` INTEGER NOT NULL, `type` TEXT NOT NULL, `chain` TEXT NOT NULL, `is_enabled` INTEGER NOT NULL, `is_buy_enabled` INTEGER NOT NULL, `is_sell_enabled` INTEGER NOT NULL, `is_swap_enabled` INTEGER NOT NULL, `is_stake_enabled` INTEGER NOT NULL, `staking_apr` REAL, `rank` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "decimals", + "columnName": "decimals", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "is_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBuyEnabled", + "columnName": "is_buy_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSellEnabled", + "columnName": "is_sell_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSwapEnabled", + "columnName": "is_swap_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isStakeEnabled", + "columnName": "is_stake_enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stakingApr", + "columnName": "staking_apr", + "affinity": "REAL" + }, + { + "fieldPath": "rank", + "columnName": "rank", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updated_at", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "balances", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `account_address` TEXT NOT NULL, `available` TEXT NOT NULL, `available_amount` REAL NOT NULL, `frozen` TEXT NOT NULL, `frozen_amount` REAL NOT NULL, `locked` TEXT NOT NULL, `locked_amount` REAL NOT NULL, `staked` TEXT NOT NULL, `staked_amount` REAL NOT NULL, `pending` TEXT NOT NULL, `pending_amount` REAL NOT NULL, `rewards` TEXT NOT NULL, `rewards_amount` REAL NOT NULL, `reserved` TEXT NOT NULL, `reserved_amount` REAL NOT NULL, `total_amount` REAL NOT NULL, `is_active` INTEGER NOT NULL, `votes` INTEGER NOT NULL DEFAULT 0, `energy_available` INTEGER NOT NULL DEFAULT 0, `energy_total` INTEGER NOT NULL DEFAULT 0, `bandwidth_available` INTEGER NOT NULL DEFAULT 0, `bandwidth_total` INTEGER NOT NULL DEFAULT 0, `updated_at` INTEGER, PRIMARY KEY(`asset_id`, `wallet_id`, `account_address`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountAddress", + "columnName": "account_address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "availableAmount", + "columnName": "available_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "frozen", + "columnName": "frozen", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "frozenAmount", + "columnName": "frozen_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "locked", + "columnName": "locked", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lockedAmount", + "columnName": "locked_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "staked", + "columnName": "staked", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stakedAmount", + "columnName": "staked_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pending", + "columnName": "pending", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pendingAmount", + "columnName": "pending_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rewards", + "columnName": "rewards", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rewardsAmount", + "columnName": "rewards_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "reserved", + "columnName": "reserved", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "reservedAmount", + "columnName": "reserved_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "totalAmount", + "columnName": "total_amount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "isActive", + "columnName": "is_active", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "votes", + "columnName": "votes", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "energyAvailable", + "columnName": "energy_available", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "energyTotal", + "columnName": "energy_total", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "bandwidthAvailable", + "columnName": "bandwidth_available", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "bandwidthTotal", + "columnName": "bandwidth_total", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "updatedAt", + "columnName": "updated_at", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "wallet_id", + "account_address" + ] + }, + "foreignKeys": [ + { + "table": "asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "wallets", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "wallet_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "prices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `value` REAL, `usd_value` REAL, `day_changed` REAL, `currency` TEXT NOT NULL, PRIMARY KEY(`asset_id`))", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL" + }, + { + "fieldPath": "usdValue", + "columnName": "usd_value", + "affinity": "REAL" + }, + { + "fieldPath": "dayChanged", + "columnName": "day_changed", + "affinity": "REAL" + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id" + ] + } + }, + { + "tableName": "transactions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `walletId` TEXT NOT NULL, `hash` TEXT NOT NULL, `assetId` TEXT NOT NULL, `feeAssetId` TEXT NOT NULL, `owner` TEXT NOT NULL, `recipient` TEXT NOT NULL, `contract` TEXT, `metadata` TEXT, `state` TEXT NOT NULL, `type` TEXT NOT NULL, `blockNumber` TEXT NOT NULL, `sequence` TEXT NOT NULL, `fee` TEXT NOT NULL, `value` TEXT NOT NULL, `payload` TEXT, `direction` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`, `walletId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "walletId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "assetId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "feeAssetId", + "columnName": "feeAssetId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "owner", + "columnName": "owner", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contract", + "columnName": "contract", + "affinity": "TEXT" + }, + { + "fieldPath": "metadata", + "columnName": "metadata", + "affinity": "TEXT" + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "blockNumber", + "columnName": "blockNumber", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sequence", + "columnName": "sequence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fee", + "columnName": "fee", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "payload", + "columnName": "payload", + "affinity": "TEXT" + }, + { + "fieldPath": "direction", + "columnName": "direction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "walletId" + ] + } + }, + { + "tableName": "tx_swap_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tx_id` TEXT NOT NULL, `from_asset_id` TEXT NOT NULL, `to_asset_id` TEXT NOT NULL, `from_amount` TEXT NOT NULL, `to_amount` TEXT NOT NULL, PRIMARY KEY(`tx_id`))", + "fields": [ + { + "fieldPath": "txId", + "columnName": "tx_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromAssetId", + "columnName": "from_asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toAssetId", + "columnName": "to_asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromAmount", + "columnName": "from_amount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toAmount", + "columnName": "to_amount", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "tx_id" + ] + } + }, + { + "tableName": "room_connection", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `session_id` TEXT NOT NULL, `state` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `expire_at` INTEGER NOT NULL, `app_name` TEXT NOT NULL, `app_description` TEXT NOT NULL, `app_url` TEXT NOT NULL, `app_icon` TEXT NOT NULL, `redirect_native` TEXT, `redirect_universal` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sessionId", + "columnName": "session_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expireAt", + "columnName": "expire_at", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "appName", + "columnName": "app_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appDescription", + "columnName": "app_description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appUrl", + "columnName": "app_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appIcon", + "columnName": "app_icon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "redirectNative", + "columnName": "redirect_native", + "affinity": "TEXT" + }, + { + "fieldPath": "redirectUniversal", + "columnName": "redirect_universal", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "stake_delegation_validator", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `chain` TEXT NOT NULL, `name` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `commission` REAL NOT NULL, `apr` REAL NOT NULL, `providerType` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isActive", + "columnName": "isActive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "commission", + "columnName": "commission", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "apr", + "columnName": "apr", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "providerType", + "columnName": "providerType", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "stake_delegation_base", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `address` TEXT NOT NULL, `delegation_id` TEXT NOT NULL, `validator_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `state` TEXT NOT NULL, `balance` TEXT NOT NULL, `rewards` TEXT NOT NULL, `completion_date` INTEGER, `price` REAL, `price_change` REAL, `shares` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "delegationId", + "columnName": "delegation_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "validatorId", + "columnName": "validator_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "balance", + "columnName": "balance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rewards", + "columnName": "rewards", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "completionDate", + "columnName": "completion_date", + "affinity": "INTEGER" + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL" + }, + { + "fieldPath": "priceChange", + "columnName": "price_change", + "affinity": "REAL" + }, + { + "fieldPath": "shares", + "columnName": "shares", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "nodes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `status` TEXT NOT NULL, `priority` INTEGER NOT NULL, `chain` TEXT NOT NULL, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + } + }, + { + "tableName": "session", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `wallet_id` TEXT NOT NULL, `currency` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "asset_config", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `is_pinned` INTEGER NOT NULL, `is_visible` INTEGER NOT NULL, `list_position` INTEGER NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`))", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPinned", + "columnName": "is_pinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isVisible", + "columnName": "is_visible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "listPosition", + "columnName": "list_position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "wallet_id" + ] + } + }, + { + "tableName": "banners", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `chain` TEXT, `state` TEXT NOT NULL, `event` TEXT NOT NULL, PRIMARY KEY(`wallet_id`, `asset_id`))", + "fields": [ + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT" + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "event", + "columnName": "event", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "wallet_id", + "asset_id" + ] + }, + "indices": [ + { + "name": "index_banners_event", + "unique": false, + "columnNames": [ + "event" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_banners_event` ON `${TABLE_NAME}` (`event`)" + }, + { + "name": "index_banners_wallet_id", + "unique": false, + "columnNames": [ + "wallet_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_banners_wallet_id` ON `${TABLE_NAME}` (`wallet_id`)" + } + ] + }, + { + "tableName": "price_alerts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assetId` TEXT NOT NULL, `currency` TEXT NOT NULL, `price` REAL, `pricePercentChange` REAL, `priceDirection` TEXT, `lastNotifiedAt` INTEGER, `enabled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "assetId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL" + }, + { + "fieldPath": "pricePercentChange", + "columnName": "pricePercentChange", + "affinity": "REAL" + }, + { + "fieldPath": "priceDirection", + "columnName": "priceDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "lastNotifiedAt", + "columnName": "lastNotifiedAt", + "affinity": "INTEGER" + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "nft_collection", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `chain` TEXT NOT NULL, `contractAddress` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `previewImageUrl` TEXT NOT NULL, `originalSourceUrl` TEXT NOT NULL, `status` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contractAddress", + "columnName": "contractAddress", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "imageUrl", + "columnName": "imageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "previewImageUrl", + "columnName": "previewImageUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalSourceUrl", + "columnName": "originalSourceUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "nft_asset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `collection_id` TEXT NOT NULL, `token_id` TEXT NOT NULL, `token_type` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `chain` TEXT NOT NULL, `contract_address` TEXT, `image_url` TEXT NOT NULL, `preview_image_url` TEXT NOT NULL, `original_image_url` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`collection_id`) REFERENCES `nft_collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "collectionId", + "columnName": "collection_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tokenId", + "columnName": "token_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tokenType", + "columnName": "token_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "chain", + "columnName": "chain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contractAddress", + "columnName": "contract_address", + "affinity": "TEXT" + }, + { + "fieldPath": "imageUrl", + "columnName": "image_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "previewImageUrl", + "columnName": "preview_image_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originalSourceUrl", + "columnName": "original_image_url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "foreignKeys": [ + { + "table": "nft_collection", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "collection_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nft_attributes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `name`), FOREIGN KEY(`asset_id`) REFERENCES `nft_asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "name" + ] + }, + "foreignKeys": [ + { + "table": "nft_asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nft_association", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, PRIMARY KEY(`wallet_id`, `asset_id`), FOREIGN KEY(`asset_id`) REFERENCES `nft_asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "wallet_id", + "asset_id" + ] + }, + "foreignKeys": [ + { + "table": "nft_asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "wallets", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "wallet_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nft_collection_link", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`collection_id` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`collection_id`, `name`), FOREIGN KEY(`collection_id`) REFERENCES `nft_collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "collectionId", + "columnName": "collection_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "collection_id", + "name" + ] + }, + "foreignKeys": [ + { + "table": "nft_collection", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "collection_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "asset_links", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `name`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "name" + ] + }, + "foreignKeys": [ + { + "table": "asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "asset_wallet", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `account_address` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`, `account_address`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountAddress", + "columnName": "account_address", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "wallet_id", + "account_address" + ] + }, + "foreignKeys": [ + { + "table": "asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "wallets", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "wallet_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "asset_market", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `marketCap` REAL, `marketCapFdv` REAL, `marketCapRank` INTEGER, `totalVolume` REAL, `circulatingSupply` REAL, `totalSupply` REAL, `maxSupply` REAL, `allTimeHigh` REAL, `allTimeHighDate` INTEGER, `allTimeHighChangePercentage` REAL, `allTimeLow` REAL, `allTimeLowDate` INTEGER, `allTimeLowChangePercentage` REAL, PRIMARY KEY(`asset_id`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "marketCap", + "columnName": "marketCap", + "affinity": "REAL" + }, + { + "fieldPath": "marketCapFdv", + "columnName": "marketCapFdv", + "affinity": "REAL" + }, + { + "fieldPath": "marketCapRank", + "columnName": "marketCapRank", + "affinity": "INTEGER" + }, + { + "fieldPath": "totalVolume", + "columnName": "totalVolume", + "affinity": "REAL" + }, + { + "fieldPath": "circulatingSupply", + "columnName": "circulatingSupply", + "affinity": "REAL" + }, + { + "fieldPath": "totalSupply", + "columnName": "totalSupply", + "affinity": "REAL" + }, + { + "fieldPath": "maxSupply", + "columnName": "maxSupply", + "affinity": "REAL" + }, + { + "fieldPath": "allTimeHigh", + "columnName": "allTimeHigh", + "affinity": "REAL" + }, + { + "fieldPath": "allTimeHighDate", + "columnName": "allTimeHighDate", + "affinity": "INTEGER" + }, + { + "fieldPath": "allTimeHighChangePercentage", + "columnName": "allTimeHighChangePercentage", + "affinity": "REAL" + }, + { + "fieldPath": "allTimeLow", + "columnName": "allTimeLow", + "affinity": "REAL" + }, + { + "fieldPath": "allTimeLowDate", + "columnName": "allTimeLowDate", + "affinity": "INTEGER" + }, + { + "fieldPath": "allTimeLowChangePercentage", + "columnName": "allTimeLowChangePercentage", + "affinity": "REAL" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id" + ] + }, + "foreignKeys": [ + { + "table": "asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "assets_priority", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`query` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `priority` INTEGER NOT NULL, PRIMARY KEY(`query`, `asset_id`))", + "fields": [ + { + "fieldPath": "query", + "columnName": "query", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "priority", + "columnName": "priority", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "query", + "asset_id" + ] + } + }, + { + "tableName": "currency_rates", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`currency` TEXT NOT NULL, `rate` REAL NOT NULL, PRIMARY KEY(`currency`))", + "fields": [ + { + "fieldPath": "currency", + "columnName": "currency", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "currency" + ] + } + }, + { + "tableName": "recent_assets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `to_asset_id` TEXT, `type` TEXT NOT NULL, `addedAt` INTEGER NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`, `type`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "wallet_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toAssetId", + "columnName": "to_asset_id", + "affinity": "TEXT" + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "addedAt", + "columnName": "addedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "asset_id", + "wallet_id", + "type" + ] + }, + "foreignKeys": [ + { + "table": "asset", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "asset_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "wallets", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "wallet_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "perpetual", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `provider` TEXT NOT NULL, `assetId` TEXT NOT NULL, `identifier` TEXT NOT NULL, `price` REAL NOT NULL, `pricePercentChange24h` REAL NOT NULL, `openInterest` REAL NOT NULL, `volume24h` REAL NOT NULL, `funding` REAL NOT NULL, `maxLeverage` INTEGER NOT NULL, `onlyIsolated` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "provider", + "columnName": "provider", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "assetId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "identifier", + "columnName": "identifier", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "pricePercentChange24h", + "columnName": "pricePercentChange24h", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "openInterest", + "columnName": "openInterest", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "volume24h", + "columnName": "volume24h", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "funding", + "columnName": "funding", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "maxLeverage", + "columnName": "maxLeverage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "onlyIsolated", + "columnName": "onlyIsolated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "perpetual_asset_id_idx", + "unique": false, + "columnNames": [ + "assetId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_asset_id_idx` ON `${TABLE_NAME}` (`assetId`)" + } + ] + }, + { + "tableName": "perpetual_asset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `symbol` TEXT NOT NULL, `decimals` INTEGER NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "decimals", + "columnName": "decimals", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "perpetual_balance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountAddress` TEXT NOT NULL, `available` REAL NOT NULL, `reserved` REAL NOT NULL, `withdrawable` REAL NOT NULL, PRIMARY KEY(`accountAddress`))", + "fields": [ + { + "fieldPath": "accountAddress", + "columnName": "accountAddress", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "reserved", + "columnName": "reserved", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "withdrawable", + "columnName": "withdrawable", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountAddress" + ] + } + }, + { + "tableName": "perpetual_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`perpetualId` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, PRIMARY KEY(`perpetualId`))", + "fields": [ + { + "fieldPath": "perpetualId", + "columnName": "perpetualId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isPinned", + "columnName": "isPinned", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "perpetualId" + ] + } + }, + { + "tableName": "perpetual_position", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `perpetualId` TEXT NOT NULL, `accountAddress` TEXT NOT NULL, `assetId` TEXT NOT NULL, `size` REAL NOT NULL, `sizeValue` REAL NOT NULL, `leverage` INTEGER NOT NULL, `entryPrice` REAL, `liquidationPrice` REAL, `marginType` TEXT NOT NULL, `direction` TEXT NOT NULL, `marginAmount` REAL NOT NULL, `takeProfitPrice` REAL, `takeProfitType` TEXT, `takeProfitOrderId` TEXT, `stopLossPrice` REAL, `stopLossType` TEXT, `stopLossOrderId` TEXT, `pnl` REAL NOT NULL, `funding` REAL, PRIMARY KEY(`id`, `perpetualId`, `accountAddress`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "perpetualId", + "columnName": "perpetualId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountAddress", + "columnName": "accountAddress", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "assetId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "sizeValue", + "columnName": "sizeValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "leverage", + "columnName": "leverage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryPrice", + "columnName": "entryPrice", + "affinity": "REAL" + }, + { + "fieldPath": "liquidationPrice", + "columnName": "liquidationPrice", + "affinity": "REAL" + }, + { + "fieldPath": "marginType", + "columnName": "marginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "direction", + "columnName": "direction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "marginAmount", + "columnName": "marginAmount", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "takeProfitPrice", + "columnName": "takeProfitPrice", + "affinity": "REAL" + }, + { + "fieldPath": "takeProfitType", + "columnName": "takeProfitType", + "affinity": "TEXT" + }, + { + "fieldPath": "takeProfitOrderId", + "columnName": "takeProfitOrderId", + "affinity": "TEXT" + }, + { + "fieldPath": "stopLossPrice", + "columnName": "stopLossPrice", + "affinity": "REAL" + }, + { + "fieldPath": "stopLossType", + "columnName": "stopLossType", + "affinity": "TEXT" + }, + { + "fieldPath": "stopLossOrderId", + "columnName": "stopLossOrderId", + "affinity": "TEXT" + }, + { + "fieldPath": "pnl", + "columnName": "pnl", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "funding", + "columnName": "funding", + "affinity": "REAL" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id", + "perpetualId", + "accountAddress" + ] + }, + "indices": [ + { + "name": "perpetual_position_asset_id_idx", + "unique": false, + "columnNames": [ + "assetId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_asset_id_idx` ON `${TABLE_NAME}` (`assetId`)" + }, + { + "name": "perpetual_position_perpetual_id_idx", + "unique": false, + "columnNames": [ + "perpetualId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_perpetual_id_idx` ON `${TABLE_NAME}` (`perpetualId`)" + }, + { + "name": "perpetual_position_account_address_idx", + "unique": false, + "columnNames": [ + "accountAddress" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_account_address_idx` ON `${TABLE_NAME}` (`accountAddress`)" + } + ] + } + ], + "views": [ + { + "viewName": "asset_info", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT DISTINCT\n asset.id as id,\n asset.name as name,\n asset.symbol as symbol,\n asset.decimals as decimals,\n asset.type as type,\n asset.is_buy_enabled as isBuyEnabled,\n asset.is_sell_enabled as isSellEnabled,\n asset.is_swap_enabled as isSwapEnabled,\n asset.is_stake_enabled as isStakeEnabled,\n asset.staking_apr as stakingApr,\n asset.rank as assetRank,\n asset.chain as chain,\n accounts.address as address,\n accounts.derivation_path as derivationPath,\n accounts.extendedPublicKey as extendedPublicKey,\n asset_config.is_pinned AS pinned,\n asset_config.is_visible AS visible,\n asset_config.list_position AS listPosition,\n session.id AS sessionId,\n prices.currency AS priceCurrency,\n wallets.id as walletId,\n wallets.type AS walletType,\n wallets.name AS walletName,\n prices.value AS priceValue,\n prices.day_changed AS priceDayChanges,\n balances.available AS balanceAvailable,\n balances.available_amount AS balanceAvailableAmount,\n balances.frozen AS balanceFrozen,\n balances.frozen_amount AS balanceFrozenAmount,\n balances.locked AS balanceLocked,\n balances.locked_amount AS balanceLockedAmount,\n balances.staked AS balanceStaked,\n balances.staked_amount AS balanceStakedAmount,\n balances.pending AS balancePending,\n balances.pending_amount AS balancePendingAmount,\n balances.rewards AS balanceRewards,\n balances.rewards_amount AS balanceRewardsAmount,\n balances.reserved AS balanceReserved,\n balances.reserved_amount AS balanceReservedAmount,\n balances.total_amount AS balanceTotalAmount,\n (balances.total_amount * prices.value) AS balanceFiatTotalAmount,\n balances.updated_at AS balanceUpdatedAt,\n balances.is_active AS assetIsActive,\n balances.votes AS votes,\n balances.energy_available as energyAvailable,\n balances.energy_total as energyTotal,\n balances.bandwidth_available as bandwidthAvailable, \n balances.bandwidth_total as bandwidthTotal\n FROM asset\n LEFT JOIN asset_wallet ON asset.id = asset_wallet.asset_id\n LEFT JOIN session ON asset_wallet.wallet_id = session.wallet_id\n LEFT JOIN wallets ON wallets.id = session.wallet_id\n LEFT JOIN accounts ON asset_wallet.account_address = accounts.address AND wallets.id = accounts.wallet_id AND asset.\"chain\" = accounts.\"chain\"\n LEFT JOIN balances ON asset_wallet.account_address = balances.account_address AND asset_wallet.asset_id = balances.asset_id AND wallets.id = balances.wallet_id\n LEFT JOIN prices ON asset.id = prices.asset_id AND prices.currency = (SELECT currency FROM session WHERE id = 1)\n LEFT JOIN asset_config ON asset_wallet.asset_id = asset_config.asset_id AND wallets.id = asset_config.wallet_id" + }, + { + "viewName": "extended_txs", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT\n DISTINCT tx.id,\n tx.hash,\n tx.assetId,\n tx.feeAssetId,\n tx.owner,\n tx.recipient,\n tx.contract,\n tx.state,\n tx.type,\n tx.blockNumber,\n tx.sequence,\n tx.fee,\n tx.value,\n tx.payload,\n tx.metadata,\n tx.direction,\n tx.createdAt,\n tx.updatedAt,\n tx.walletId,\n asset.decimals as assetDecimals,\n asset.name as assetName,\n asset.type as assetType,\n asset.symbol as assetSymbol,\n feeAsset.decimals as feeDecimals,\n feeAsset.name as feeName,\n feeAsset.type as feeType,\n feeAsset.symbol as feeSymbol,\n prices.value as assetPrice,\n prices.day_changed as assetPriceChanged,\n feePrices.value as feePrice,\n feePrices.day_changed as feePriceChanged,\n from_asset.id as assetIdFrom,\n from_asset.name as assetNameFrom,\n from_asset.symbol as assetSymbolFrom,\n from_asset.decimals as assetDecimalsFrom,\n from_asset.type as assetTypeFrom,\n to_asset.id as assetIdTo,\n to_asset.name as assetNameTo,\n to_asset.symbol as assetSymbolTo,\n to_asset.decimals as assetDecimalsTo,\n to_asset.type as assetTypeTo\n FROM transactions as tx\n INNER JOIN asset ON tx.assetId = asset.id \n INNER JOIN asset as feeAsset ON tx.feeAssetId = feeAsset.id\n LEFT JOIN prices ON tx.assetId = prices.asset_id\n LEFT JOIN prices as feePrices ON tx.feeAssetId = feePrices.asset_id\n LEFT JOIN tx_swap_metadata as swap ON tx.id = swap.tx_id\n LEFT JOIN asset as from_asset ON swap.from_asset_id = from_asset.id\n LEFT JOIN asset as to_asset ON swap.to_asset_id = to_asset.id\n WHERE (tx.owner IN (SELECT accounts.address FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1) OR tx.recipient in (SELECT accounts.address FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1))\n AND tx.walletId in (SELECT wallet_id FROM session WHERE session.id = 1)\n AND UPPER(tx.feeAssetId) IN (SELECT UPPER(accounts.chain) FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1)\n GROUP BY tx.id" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '710140be0f07cd0f4db014d5b44a518e')" + ] + } +} \ No newline at end of file diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt index ae44fa33fe..360e24e848 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt @@ -37,7 +37,7 @@ import com.gemwallet.android.data.service.store.database.entities.DbTxSwapMetada import com.gemwallet.android.data.service.store.database.entities.DbWallet @Database( - version = 67, + version = 68, entities = [ DbWallet::class, DbAccount::class, diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt index a68be0ee85..f340765ff7 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt @@ -64,6 +64,7 @@ object DatabaseModule { .addMigrations(Migration_64_65) .addMigrations(Migration_65_66) .addMigrations(Migration_66_67) + .addMigrations(Migration_67_68) .build() @Singleton diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt new file mode 100644 index 0000000000..cada01b9f0 --- /dev/null +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt @@ -0,0 +1,12 @@ +package com.gemwallet.android.data.service.store.database.di + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +object Migration_67_68 : Migration(67, 68) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( + "ALTER TABLE perpetual ADD COLUMN onlyIsolated INTEGER NOT NULL DEFAULT 0" + ) + } +} diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt index 1f251586b3..2c336fd25e 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt @@ -28,6 +28,7 @@ data class DbPerpetual( val volume24h: Double, val funding: Double, val maxLeverage: Int, + val onlyIsolated: Boolean = false, ) @@ -67,6 +68,7 @@ fun DbPerpetual.toDTO(): Perpetual? { volume24h = volume24h, funding = funding, maxLeverage = maxLeverage.toUByte(), + onlyIsolated = onlyIsolated, ) } @@ -87,6 +89,7 @@ fun Perpetual.toDB(): DbPerpetual { volume24h = volume24h, funding = funding, maxLeverage = maxLeverage.toInt(), + onlyIsolated = onlyIsolated, ) } diff --git a/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt b/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt index de33d19671..4863c180da 100644 --- a/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt +++ b/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt @@ -85,6 +85,7 @@ private fun PerpetualPositionScenePreview() { override val maxLeverage: Int = 40 override val price: Double = 0.0 override val identifier: String = "BTC-PERP" + override val onlyIsolated: Boolean = false } val samplePosition = object : PerpetualPositionDetailsDataAggregate { diff --git a/android/features/transfer_amount/viewmodels/src/main/kotlin/com/gemwallet/features/transfer_amount/viewmodels/PerpetualAmountViewModel.kt b/android/features/transfer_amount/viewmodels/src/main/kotlin/com/gemwallet/features/transfer_amount/viewmodels/PerpetualAmountViewModel.kt index 9a4c113d66..c1e001b022 100644 --- a/android/features/transfer_amount/viewmodels/src/main/kotlin/com/gemwallet/features/transfer_amount/viewmodels/PerpetualAmountViewModel.kt +++ b/android/features/transfer_amount/viewmodels/src/main/kotlin/com/gemwallet/features/transfer_amount/viewmodels/PerpetualAmountViewModel.kt @@ -134,6 +134,7 @@ class PerpetualAmountViewModel @Inject constructor( leverage = leverage.value, baseAsset = asset, // USD direction = params.perpetualDirection ?: PerpetualDirection.Long, + marginType = perpetual.marginType, ) else -> throw IllegalArgumentException() } diff --git a/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt b/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt index 87209f0391..8abfc296e9 100644 --- a/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt +++ b/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt @@ -1,6 +1,7 @@ package com.gemwallet.android.domains.perpetual.aggregates import com.wallet.core.primitives.Asset +import com.wallet.core.primitives.PerpetualMarginType import com.wallet.core.primitives.PerpetualProvider interface PerpetualDetailsDataAggregate { @@ -14,4 +15,7 @@ interface PerpetualDetailsDataAggregate { val maxLeverage: Int val price: Double val identifier: String + val onlyIsolated: Boolean + val marginType: PerpetualMarginType + get() = if (onlyIsolated) PerpetualMarginType.Isolated else PerpetualMarginType.Cross } \ No newline at end of file diff --git a/android/gemcore/src/main/kotlin/com/gemwallet/android/model/ConfirmParams.kt b/android/gemcore/src/main/kotlin/com/gemwallet/android/model/ConfirmParams.kt index 2f158ab7b1..7cdb46cfed 100644 --- a/android/gemcore/src/main/kotlin/com/gemwallet/android/model/ConfirmParams.kt +++ b/android/gemcore/src/main/kotlin/com/gemwallet/android/model/ConfirmParams.kt @@ -21,6 +21,7 @@ import com.wallet.core.primitives.Delegation import com.wallet.core.primitives.DelegationValidator import com.wallet.core.primitives.NFTAsset import com.wallet.core.primitives.PerpetualDirection +import com.wallet.core.primitives.PerpetualMarginType import com.wallet.core.primitives.PerpetualProvider import com.wallet.core.primitives.Resource import com.wallet.core.primitives.TransactionType @@ -155,6 +156,7 @@ sealed class ConfirmParams() { leverage: Int, baseAsset: Asset, // USD direction: PerpetualDirection, + marginType: PerpetualMarginType, slippage: Double = 2.0, ): PerpetualParams { val marginAmount = Crypto(amount).value(baseAsset.decimals).toDouble() @@ -162,20 +164,21 @@ sealed class ConfirmParams() { val assetSize = fiatValue / perpetualPrice val slippageFraction = slippage / 100.0 - val slippage = when (direction) { - PerpetualDirection.Short -> 1.0 - slippageFraction // TODO: Why we need slippage on Close = 1.0 + slippageFraction? - PerpetualDirection.Long -> 1.0 + slippageFraction // Close = 1.0 - slippageFraction + val slippageMultiplier = when (direction) { + PerpetualDirection.Short -> 1.0 - slippageFraction + PerpetualDirection.Long -> 1.0 + slippageFraction } val order = PerpetualParams.Order( perpetualId = perpetualId, provider = perpetualProvider, direction = direction, + marginType = marginType, baseAsset = baseAsset, assetIndex = perpetualIdentifier.toInt(), fiatValue = fiatValue, size = assetSize, - slippage = slippage, + slippage = slippageMultiplier, leverage = leverage, marketPrice = perpetualPrice, marginAmount = marginAmount, @@ -600,6 +603,7 @@ sealed class ConfirmParams() { val perpetualId: String, val provider: PerpetualProvider, val direction: PerpetualDirection, + val marginType: PerpetualMarginType, val baseAsset: Asset, val assetIndex: Int, // val price: String, @@ -753,6 +757,10 @@ fun ConfirmParams.PerpetualParams.Order.toGem() = PerpetualConfirmData( PerpetualDirection.Long -> uniffi.gemstone.PerpetualDirection.LONG PerpetualDirection.Short -> uniffi.gemstone.PerpetualDirection.SHORT }, + marginType = when (marginType) { + PerpetualMarginType.Cross -> uniffi.gemstone.GemPerpetualMarginType.CROSS + PerpetualMarginType.Isolated -> uniffi.gemstone.GemPerpetualMarginType.ISOLATED + }, baseAsset = baseAsset.toGem(), assetIndex = assetIndex, price = marketPrice.toString(), diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt index e84d1a0b55..560f353fce 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt @@ -46,7 +46,8 @@ data class Perpetual ( val openInterest: Double, val volume24h: Double, val funding: Double, - val maxLeverage: UByte + val maxLeverage: UByte, + val onlyIsolated: Boolean = false ) @Serializable @@ -66,6 +67,7 @@ data class PerpetualBasic ( @Serializable data class PerpetualConfirmData ( val direction: PerpetualDirection, + val marginType: PerpetualMarginType = PerpetualMarginType.Cross, val baseAsset: Asset, val assetIndex: Int, val price: String, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Scan.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Scan.kt index bc177f3cfe..df7bbe407d 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Scan.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Scan.kt @@ -7,29 +7,6 @@ package com.wallet.core.primitives import kotlinx.serialization.Serializable import kotlinx.serialization.SerialName -@Serializable -enum class AddressType(val string: String) { - @SerialName("address") - Address("address"), - @SerialName("contract") - Contract("contract"), - @SerialName("validator") - Validator("validator"), - @SerialName("contact") - Contact("contact"), -} - -@Serializable -data class ScanAddress ( - val chain: Chain, - val address: String, - val name: String? = null, - val type: AddressType? = null, - val isMalicious: Boolean? = null, - val isMemoRequired: Boolean? = null, - val isVerified: Boolean? = null -) - @Serializable data class ScanAddressTarget ( val assetId: AssetId, @@ -50,3 +27,17 @@ data class ScanTransactionPayload ( val type: TransactionType ) +@Serializable +enum class AddressType(val string: String) { + @SerialName("address") + Address("address"), + @SerialName("contract") + Contract("contract"), + @SerialName("validator") + Validator("validator"), + @SerialName("contact") + Contact("contact"), + @SerialName("internalWallet") + InternalWallet("internalWallet"), +} + diff --git a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift index a9ccd5b8b3..d132aafd64 100644 --- a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift +++ b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift @@ -30,7 +30,7 @@ public struct PerpetualPositionViewModel { } public var nameText: String { - data.asset.name + data.perpetual.name } public var symbolText: String { @@ -127,15 +127,4 @@ extension PerpetualPositionViewModel { extension PerpetualPositionViewModel: Identifiable { public var id: String { data.position.id } -} - -extension PerpetualMarginType { - var displayText: String { - switch self { - case .cross: - return "cross" - case .isolated: - return "isolated" - } - } -} +} \ No newline at end of file diff --git a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualSceneViewModel.swift b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualSceneViewModel.swift index a95a001547..99894d3fe0 100644 --- a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualSceneViewModel.swift +++ b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualSceneViewModel.swift @@ -122,7 +122,7 @@ public final class PerpetualSceneViewModel { } } - private var currentChartSubscription: ChartSubscription { ChartSubscription(coin: perpetual.name, period: currentPeriod) } + private var currentChartSubscription: ChartSubscription { ChartSubscription(coin: perpetual.coin, period: currentPeriod) } } // MARK: - Actions @@ -130,7 +130,7 @@ public final class PerpetualSceneViewModel { public extension PerpetualSceneViewModel { func fetch() { Task { await observerService.update(for: wallet) } - Task { try await perpetualService.updateMarket(symbol: perpetual.name) } + Task { try await perpetualService.updateMarket(symbol: perpetual.coin) } Task { await fetchTransactions() } Task { await updateCandlesticks() } } @@ -152,7 +152,7 @@ public extension PerpetualSceneViewModel { func onScenePhaseChange(_ oldPhase: ScenePhase, _ newPhase: ScenePhase) { switch newPhase { case .active: - Task { try? await perpetualService.updateMarket(symbol: perpetual.name) } + Task { try? await perpetualService.updateMarket(symbol: perpetual.coin) } Task { await fetchTransactions() } Task { await updateCandlesticks() } case .inactive, .background: break @@ -162,9 +162,9 @@ public extension PerpetualSceneViewModel { func onPeriodChange(_ oldPeriod: ChartPeriod, _ newPeriod: ChartPeriod) { Task { - await unsubscribeCandles(ChartSubscription(coin: perpetual.name, period: oldPeriod)) + await unsubscribeCandles(ChartSubscription(coin: perpetual.coin, period: oldPeriod)) await updateCandlesticks() - await subscribeCandles(ChartSubscription(coin: perpetual.name, period: newPeriod)) + await subscribeCandles(ChartSubscription(coin: perpetual.coin, period: newPeriod)) } } @@ -223,7 +223,8 @@ public extension PerpetualSceneViewModel { func onOpenLongPosition() { guard let transferData = createTransferData( direction: .long, - leverage: perpetual.maxLeverage + leverage: perpetual.maxLeverage, + marginType: perpetual.marginType ) else { return } @@ -233,7 +234,8 @@ public extension PerpetualSceneViewModel { func onOpenShortPosition() { guard let transferData = createTransferData( direction: .short, - leverage: perpetual.maxLeverage + leverage: perpetual.maxLeverage, + marginType: perpetual.marginType ) else { return } @@ -244,7 +246,7 @@ public extension PerpetualSceneViewModel { isPresentingModifyAlert = false guard let position = positions.first?.position, - let transferData = createTransferData(direction: position.direction, leverage: position.leverage) + let transferData = createTransferData(direction: position.direction, leverage: position.leverage, marginType: position.marginType) else { return } onPositionAction(.increase(transferData)) @@ -264,7 +266,7 @@ public extension PerpetualSceneViewModel { } }() - guard let transferData = createTransferData(direction: direction, leverage: position.leverage) else { + guard let transferData = createTransferData(direction: direction, leverage: position.leverage, marginType: position.marginType) else { return } @@ -289,7 +291,7 @@ private extension PerpetualSceneViewModel { state = .loading do { let candlesticks = try await perpetualService.candlesticks( - symbol: perpetual.name, + symbol: perpetual.coin, period: currentPeriod ) state = .data(candlesticks) @@ -341,7 +343,7 @@ private extension PerpetualSceneViewModel { state = .data(candlesticks) } - func createTransferData(direction: PerpetualDirection, leverage: UInt8) -> PerpetualTransferData? { + func createTransferData(direction: PerpetualDirection, leverage: UInt8, marginType: PerpetualMarginType) -> PerpetualTransferData? { guard let assetIndex = Int(perpetual.identifier) else { return nil } @@ -353,7 +355,8 @@ private extension PerpetualSceneViewModel { baseAsset: .hypercoreUSDC(), assetIndex: assetIndex, price: perpetual.price, - leverage: leverage + leverage: leverage, + marginType: marginType ) } diff --git a/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift b/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift index 5326e687fa..42fa5d5e7a 100644 --- a/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift +++ b/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift @@ -15,7 +15,8 @@ public extension Perpetual { maxLeverage: UInt8 = 25, openInterest: Double = 1000000, volume24h: Double = 5000000, - funding: Double = 0.01 + funding: Double = 0.01, + onlyIsolated: Bool = false ) -> Perpetual { Perpetual( id: id, @@ -28,7 +29,8 @@ public extension Perpetual { openInterest: openInterest, volume24h: volume24h, funding: funding, - maxLeverage: maxLeverage + maxLeverage: maxLeverage, + onlyIsolated: onlyIsolated ) } } diff --git a/ios/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift b/ios/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift index 9b4b6f27ba..d81f48bffa 100644 --- a/ios/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift +++ b/ios/Features/Transfer/Sources/ViewModels/ConfirmRecipientViewModel.swift @@ -46,7 +46,7 @@ extension ConfirmRecipientViewModel { private var addressNameImage: AssetImage? { switch addressName?.type { case .contact: .image(Images.System.person) - case .address, .contract, .validator, .none: nil + case .address, .contract, .validator, .internalWallet, .none: nil } } diff --git a/ios/Packages/FeatureServices/PerpetualService/PerpetualOrderFactory.swift b/ios/Packages/FeatureServices/PerpetualService/PerpetualOrderFactory.swift index 8922d3bd2e..075e3d0370 100644 --- a/ios/Packages/FeatureServices/PerpetualService/PerpetualOrderFactory.swift +++ b/ios/Packages/FeatureServices/PerpetualService/PerpetualOrderFactory.swift @@ -40,6 +40,7 @@ public struct PerpetualOrderFactory { let data = makePerpetualConfirmData( direction: perpetual.direction, + marginType: perpetual.marginType, baseAsset: perpetual.baseAsset, fiatValue: fiatValue, assetIndex: Int32(perpetual.assetIndex), @@ -81,6 +82,7 @@ public struct PerpetualOrderFactory { return makePerpetualConfirmData( direction: position.direction, + marginType: position.marginType, baseAsset: baseAsset, fiatValue: abs(position.size) * positionPrice, assetIndex: assetIndex, @@ -115,6 +117,7 @@ public struct PerpetualOrderFactory { private func makePerpetualConfirmData( direction: PerpetualDirection, + marginType: PerpetualMarginType, baseAsset: Asset, fiatValue: Double, assetIndex: Int32, @@ -143,6 +146,7 @@ public struct PerpetualOrderFactory { return PerpetualConfirmData( direction: direction, + marginType: marginType, baseAsset: baseAsset, assetIndex: assetIndex, price: price, diff --git a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift index 2f51b5a8f0..2389234bd7 100644 --- a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift +++ b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift @@ -17,7 +17,8 @@ extension GemPerpetual { openInterest: openInterest, volume24h: volume24h, funding: funding, - maxLeverage: maxLeverage + maxLeverage: maxLeverage, + onlyIsolated: onlyIsolated ) } } diff --git a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetualConfirmData+GemstonePrimitives.swift b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetualConfirmData+GemstonePrimitives.swift index f7a2cf593a..586f4fe55e 100644 --- a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetualConfirmData+GemstonePrimitives.swift +++ b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetualConfirmData+GemstonePrimitives.swift @@ -8,6 +8,7 @@ extension Gemstone.PerpetualConfirmData { public func map() throws -> Primitives.PerpetualConfirmData { Primitives.PerpetualConfirmData( direction: direction.map(), + marginType: marginType.map(), baseAsset: try baseAsset.map(), assetIndex: assetIndex, price: price, @@ -29,6 +30,7 @@ extension Primitives.PerpetualConfirmData { public func map() -> Gemstone.PerpetualConfirmData { Gemstone.PerpetualConfirmData( direction: direction.map(), + marginType: marginType.map(), baseAsset: baseAsset.map(), assetIndex: assetIndex, price: price, diff --git a/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift b/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift index de30ac4084..eaac9285c4 100644 --- a/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift +++ b/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift @@ -22,6 +22,14 @@ extension PerpetualDirection { } } +extension Perpetual { + public var coin: String { + assetId.tokenId?.components(separatedBy: AssetId.subTokenSeparator).last ?? name + } + + public var marginType: PerpetualMarginType { onlyIsolated ? .isolated : .cross } +} + extension PerpetualSearchData { public var assetBasic: AssetBasic { AssetBasic( diff --git a/ios/Packages/Primitives/Sources/Perpetual.swift b/ios/Packages/Primitives/Sources/Perpetual.swift index 65fee99f7d..340c069ee5 100644 --- a/ios/Packages/Primitives/Sources/Perpetual.swift +++ b/ios/Packages/Primitives/Sources/Perpetual.swift @@ -55,8 +55,9 @@ public struct Perpetual: Codable, Equatable, Hashable, Sendable { public let volume24h: Double public let funding: Double public let maxLeverage: UInt8 + public let onlyIsolated: Bool - public init(id: String, name: String, provider: PerpetualProvider, assetId: AssetId, identifier: String, price: Double, pricePercentChange24h: Double, openInterest: Double, volume24h: Double, funding: Double, maxLeverage: UInt8) { + public init(id: String, name: String, provider: PerpetualProvider, assetId: AssetId, identifier: String, price: Double, pricePercentChange24h: Double, openInterest: Double, volume24h: Double, funding: Double, maxLeverage: UInt8, onlyIsolated: Bool) { self.id = id self.name = name self.provider = provider @@ -68,6 +69,7 @@ public struct Perpetual: Codable, Equatable, Hashable, Sendable { self.volume24h = volume24h self.funding = funding self.maxLeverage = maxLeverage + self.onlyIsolated = onlyIsolated } } @@ -97,6 +99,7 @@ public struct PerpetualBasic: Codable, Equatable, Hashable, Sendable { public struct PerpetualConfirmData: Codable, Equatable, Hashable, Sendable { public let direction: PerpetualDirection + public let marginType: PerpetualMarginType public let baseAsset: Asset public let assetIndex: Int32 public let price: String @@ -111,8 +114,9 @@ public struct PerpetualConfirmData: Codable, Equatable, Hashable, Sendable { public let takeProfit: String? public let stopLoss: String? - public init(direction: PerpetualDirection, baseAsset: Asset, assetIndex: Int32, price: String, fiatValue: Double, size: String, slippage: Double, leverage: UInt8, pnl: Double?, entryPrice: Double?, marketPrice: Double, marginAmount: Double, takeProfit: String?, stopLoss: String?) { + public init(direction: PerpetualDirection, marginType: PerpetualMarginType, baseAsset: Asset, assetIndex: Int32, price: String, fiatValue: Double, size: String, slippage: Double, leverage: UInt8, pnl: Double?, entryPrice: Double?, marketPrice: Double, marginAmount: Double, takeProfit: String?, stopLoss: String?) { self.direction = direction + self.marginType = marginType self.baseAsset = baseAsset self.assetIndex = assetIndex self.price = price diff --git a/ios/Packages/Primitives/Sources/PerpetualTransferData.swift b/ios/Packages/Primitives/Sources/PerpetualTransferData.swift index 3d5b96b5a5..285bbd51d6 100644 --- a/ios/Packages/Primitives/Sources/PerpetualTransferData.swift +++ b/ios/Packages/Primitives/Sources/PerpetualTransferData.swift @@ -39,6 +39,7 @@ public struct PerpetualTransferData: Codable, Equatable, Hashable, Sendable { public let assetIndex: Int public let price: Double public let leverage: UInt8 + public let marginType: PerpetualMarginType public init( provider: PerpetualProvider, @@ -47,7 +48,8 @@ public struct PerpetualTransferData: Codable, Equatable, Hashable, Sendable { baseAsset: Asset, assetIndex: Int, price: Double, - leverage: UInt8 + leverage: UInt8, + marginType: PerpetualMarginType ) { self.provider = provider self.direction = direction @@ -56,6 +58,7 @@ public struct PerpetualTransferData: Codable, Equatable, Hashable, Sendable { self.assetIndex = assetIndex self.price = price self.leverage = leverage + self.marginType = marginType } } diff --git a/ios/Packages/Primitives/Sources/Scan.swift b/ios/Packages/Primitives/Sources/Scan.swift index 2dce527e58..e5cf1df47a 100644 --- a/ios/Packages/Primitives/Sources/Scan.swift +++ b/ios/Packages/Primitives/Sources/Scan.swift @@ -43,4 +43,5 @@ public enum AddressType: String, Codable, Equatable, Sendable { case contract case validator case contact + case internalWallet } diff --git a/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift b/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift index 42ff754904..43d2648b5b 100644 --- a/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift +++ b/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift @@ -14,7 +14,8 @@ public extension Perpetual { openInterest: Double = 1_000_000, volume24h: Double = 10_000_000, funding: Double = 0.0001, - maxLeverage: UInt8 = 50 + maxLeverage: UInt8 = 50, + onlyIsolated: Bool = false ) -> Perpetual { Perpetual( id: id, @@ -27,7 +28,8 @@ public extension Perpetual { openInterest: openInterest, volume24h: volume24h, funding: funding, - maxLeverage: maxLeverage + maxLeverage: maxLeverage, + onlyIsolated: onlyIsolated ) } } diff --git a/ios/Packages/Primitives/TestKit/PerpetualConfirmData+PrimitivesTestKit.swift b/ios/Packages/Primitives/TestKit/PerpetualConfirmData+PrimitivesTestKit.swift index 79f1a5316c..a5752913b1 100644 --- a/ios/Packages/Primitives/TestKit/PerpetualConfirmData+PrimitivesTestKit.swift +++ b/ios/Packages/Primitives/TestKit/PerpetualConfirmData+PrimitivesTestKit.swift @@ -6,6 +6,7 @@ import Primitives public extension PerpetualConfirmData { static func mock( direction: PerpetualDirection = .long, + marginType: PerpetualMarginType = .cross, baseAsset: Asset = .mock(), assetIndex: Int32 = 0, price: String = "100", @@ -22,6 +23,7 @@ public extension PerpetualConfirmData { ) -> PerpetualConfirmData { PerpetualConfirmData( direction: direction, + marginType: marginType, baseAsset: baseAsset, assetIndex: assetIndex, price: price, diff --git a/ios/Packages/Primitives/TestKit/PerpetualTransferData+PrimitivesTestKit.swift b/ios/Packages/Primitives/TestKit/PerpetualTransferData+PrimitivesTestKit.swift index 01d3e95d36..fac2887bbc 100644 --- a/ios/Packages/Primitives/TestKit/PerpetualTransferData+PrimitivesTestKit.swift +++ b/ios/Packages/Primitives/TestKit/PerpetualTransferData+PrimitivesTestKit.swift @@ -11,7 +11,8 @@ public extension PerpetualTransferData { baseAsset: Asset = .mock(), assetIndex: Int = 0, price: Double = 100.0, - leverage: UInt8 = 3 + leverage: UInt8 = 3, + marginType: PerpetualMarginType = .cross ) -> PerpetualTransferData { PerpetualTransferData( provider: provider, @@ -20,7 +21,8 @@ public extension PerpetualTransferData { baseAsset: baseAsset, assetIndex: assetIndex, price: price, - leverage: leverage + leverage: leverage, + marginType: marginType ) } } diff --git a/ios/Packages/PrimitivesComponents/Sources/ViewModels/AddressListItemViewModel.swift b/ios/Packages/PrimitivesComponents/Sources/ViewModels/AddressListItemViewModel.swift index a848acdf9d..f7230857e0 100644 --- a/ios/Packages/PrimitivesComponents/Sources/ViewModels/AddressListItemViewModel.swift +++ b/ios/Packages/PrimitivesComponents/Sources/ViewModels/AddressListItemViewModel.swift @@ -47,14 +47,14 @@ public struct AddressListItemViewModel { public var assetImageStyle: AssetImageView.Style? { switch account.addressType { case .contact: AssetImageView.Style(foregroundColor: Colors.secondaryText, cornerRadius: 0) - case .address, .contract, .validator, .none: nil + case .address, .contract, .validator, .internalWallet, .none: nil } } public var assetImageSize: CGFloat { switch account.addressType { case .contact: .list.accessory - case .address, .contract, .validator, .none: .list.image + case .address, .contract, .validator, .internalWallet, .none: .list.image } } diff --git a/ios/Packages/Store/Sources/Migrations.swift b/ios/Packages/Store/Sources/Migrations.swift index bdf2715922..248d98764f 100644 --- a/ios/Packages/Store/Sources/Migrations.swift +++ b/ios/Packages/Store/Sources/Migrations.swift @@ -456,6 +456,14 @@ struct Migrations { try? FiatTransactionRecord.create(db: db) } + migrator.registerMigration("Add onlyIsolated to \(PerpetualRecord.databaseTableName)") { db in + try? db.alter(table: PerpetualRecord.databaseTableName) { + $0.add(column: PerpetualRecord.Columns.onlyIsolated.name, .boolean) + .notNull() + .defaults(to: false) + } + } + try migrator.migrate(dbQueue) } } diff --git a/ios/Packages/Store/Sources/Models/PerpetualRecord.swift b/ios/Packages/Store/Sources/Models/PerpetualRecord.swift index 27b96f1758..78bba95bed 100644 --- a/ios/Packages/Store/Sources/Models/PerpetualRecord.swift +++ b/ios/Packages/Store/Sources/Models/PerpetualRecord.swift @@ -19,6 +19,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord static let volume24h = Column("volume24h") static let funding = Column("funding") static let maxLeverage = Column("maxLeverage") + static let onlyIsolated = Column("onlyIsolated") static let isPinned = Column("isPinned") } @@ -33,6 +34,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord var volume24h: Double var funding: Double var maxLeverage: UInt8 + var onlyIsolated: Bool var isPinned: Bool init( @@ -47,6 +49,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord volume24h: Double, funding: Double, maxLeverage: UInt8, + onlyIsolated: Bool = false, isPinned: Bool = false ) { self.id = id @@ -60,6 +63,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord self.volume24h = volume24h self.funding = funding self.maxLeverage = maxLeverage + self.onlyIsolated = onlyIsolated self.isPinned = isPinned } @@ -83,6 +87,7 @@ extension PerpetualRecord: CreateTable { $0.column(Columns.volume24h.name, .double).notNull() $0.column(Columns.funding.name, .double).notNull() $0.column(Columns.maxLeverage.name, .integer).notNull() + $0.column(Columns.onlyIsolated.name, .boolean).notNull().defaults(to: false) $0.column(Columns.isPinned.name, .boolean).notNull().defaults(to: false) } } @@ -101,7 +106,8 @@ extension PerpetualRecord { openInterest: openInterest, volume24h: volume24h, funding: funding, - maxLeverage: maxLeverage + maxLeverage: maxLeverage, + onlyIsolated: onlyIsolated ) } } @@ -119,7 +125,8 @@ extension Perpetual { openInterest: openInterest, volume24h: volume24h, funding: funding, - maxLeverage: maxLeverage + maxLeverage: maxLeverage, + onlyIsolated: onlyIsolated ) } } diff --git a/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift b/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift index 26eee95a91..b0e0d4a40f 100644 --- a/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift +++ b/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift @@ -42,7 +42,8 @@ extension PerpetualData { openInterest: .zero, volume24h: .zero, funding: .zero, - maxLeverage: 1 + maxLeverage: 1, + onlyIsolated: false ), asset: Asset(id: .init(chain: .bitcoin, tokenId: .none), name: "", symbol: "", decimals: 0, type: .native), metadata: PerpetualMetadata(isPinned: false) From 0fc51556052c8f58f4595f8f4d0f05397d5a964b Mon Sep 17 00:00:00 2001 From: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:40:50 +0900 Subject: [PATCH 02/14] Update core --- core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core b/core index f76ef3dabb..058ad0d0e8 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit f76ef3dabb5c5247fa4f99c3ddade5e4b5a8ca71 +Subproject commit 058ad0d0e8e59b3ea4099c28871316f311749590 From a034643285f5bd59b00a00b8a5505b426e43bf27 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:52:06 +0000 Subject: [PATCH 03/14] Rename Perpetual.onlyIsolated to isIsolatedOnly Rename Perpetual.onlyIsolated to isIsolatedOnly across the codebase for clearer naming. Updated the Perpetual model, initializers, extensions, TestKit factories, GemstonePrimitives extension, and Primitives helpers. Updated store layer (PerpetualRecord) to use the new DB column and create-table schema, adjusted the migration to add isIsolatedOnly, and updated request defaults and unit tests to use the renamed property. This is a non-functional rename to standardize naming and keep tests/migrations in sync. --- .../Perpetuals/TestKit/Perpetual+TestKit.swift | 4 ++-- .../GemPerpetual+GemstonePrimitives.swift | 2 +- .../Extensions/Perpetual+Primitives.swift | 2 +- ios/Packages/Primitives/Sources/Perpetual.swift | 6 +++--- .../TestKit/Perpetual+PrimitivesTestKit.swift | 4 ++-- ios/Packages/Store/Sources/Migrations.swift | 4 ++-- .../Store/Sources/Models/PerpetualRecord.swift | 16 ++++++++-------- .../Requests/Perpetuals/PerpetualRequest.swift | 2 +- .../Store/Sources/Types/PerpetualInfo.swift | 2 +- .../Requests/PerpetualRequestTests.swift | 3 ++- ios/skills/code-style.md | 2 +- 11 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift b/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift index 42fa5d5e7a..df4fe7aa0b 100644 --- a/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift +++ b/ios/Features/Perpetuals/TestKit/Perpetual+TestKit.swift @@ -16,7 +16,7 @@ public extension Perpetual { openInterest: Double = 1000000, volume24h: Double = 5000000, funding: Double = 0.01, - onlyIsolated: Bool = false + isIsolatedOnly: Bool = false ) -> Perpetual { Perpetual( id: id, @@ -30,7 +30,7 @@ public extension Perpetual { volume24h: volume24h, funding: funding, maxLeverage: maxLeverage, - onlyIsolated: onlyIsolated + isIsolatedOnly: isIsolatedOnly ) } } diff --git a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift index 2389234bd7..8f206ba902 100644 --- a/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift +++ b/ios/Packages/GemstonePrimitives/Sources/Extensions/GemPerpetual+GemstonePrimitives.swift @@ -18,7 +18,7 @@ extension GemPerpetual { volume24h: volume24h, funding: funding, maxLeverage: maxLeverage, - onlyIsolated: onlyIsolated + isIsolatedOnly: isIsolatedOnly ) } } diff --git a/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift b/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift index eaac9285c4..7c91473fd3 100644 --- a/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift +++ b/ios/Packages/Primitives/Sources/Extensions/Perpetual+Primitives.swift @@ -27,7 +27,7 @@ extension Perpetual { assetId.tokenId?.components(separatedBy: AssetId.subTokenSeparator).last ?? name } - public var marginType: PerpetualMarginType { onlyIsolated ? .isolated : .cross } + public var marginType: PerpetualMarginType { isIsolatedOnly ? .isolated : .cross } } extension PerpetualSearchData { diff --git a/ios/Packages/Primitives/Sources/Perpetual.swift b/ios/Packages/Primitives/Sources/Perpetual.swift index 340c069ee5..67c4827d45 100644 --- a/ios/Packages/Primitives/Sources/Perpetual.swift +++ b/ios/Packages/Primitives/Sources/Perpetual.swift @@ -55,9 +55,9 @@ public struct Perpetual: Codable, Equatable, Hashable, Sendable { public let volume24h: Double public let funding: Double public let maxLeverage: UInt8 - public let onlyIsolated: Bool + public let isIsolatedOnly: Bool - public init(id: String, name: String, provider: PerpetualProvider, assetId: AssetId, identifier: String, price: Double, pricePercentChange24h: Double, openInterest: Double, volume24h: Double, funding: Double, maxLeverage: UInt8, onlyIsolated: Bool) { + public init(id: String, name: String, provider: PerpetualProvider, assetId: AssetId, identifier: String, price: Double, pricePercentChange24h: Double, openInterest: Double, volume24h: Double, funding: Double, maxLeverage: UInt8, isIsolatedOnly: Bool) { self.id = id self.name = name self.provider = provider @@ -69,7 +69,7 @@ public struct Perpetual: Codable, Equatable, Hashable, Sendable { self.volume24h = volume24h self.funding = funding self.maxLeverage = maxLeverage - self.onlyIsolated = onlyIsolated + self.isIsolatedOnly = isIsolatedOnly } } diff --git a/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift b/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift index 43d2648b5b..b76b85f5db 100644 --- a/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift +++ b/ios/Packages/Primitives/TestKit/Perpetual+PrimitivesTestKit.swift @@ -15,7 +15,7 @@ public extension Perpetual { volume24h: Double = 10_000_000, funding: Double = 0.0001, maxLeverage: UInt8 = 50, - onlyIsolated: Bool = false + isIsolatedOnly: Bool = false ) -> Perpetual { Perpetual( id: id, @@ -29,7 +29,7 @@ public extension Perpetual { volume24h: volume24h, funding: funding, maxLeverage: maxLeverage, - onlyIsolated: onlyIsolated + isIsolatedOnly: isIsolatedOnly ) } } diff --git a/ios/Packages/Store/Sources/Migrations.swift b/ios/Packages/Store/Sources/Migrations.swift index 248d98764f..6d7b40dd9a 100644 --- a/ios/Packages/Store/Sources/Migrations.swift +++ b/ios/Packages/Store/Sources/Migrations.swift @@ -456,9 +456,9 @@ struct Migrations { try? FiatTransactionRecord.create(db: db) } - migrator.registerMigration("Add onlyIsolated to \(PerpetualRecord.databaseTableName)") { db in + migrator.registerMigration("Add isIsolatedOnly to \(PerpetualRecord.databaseTableName)") { db in try? db.alter(table: PerpetualRecord.databaseTableName) { - $0.add(column: PerpetualRecord.Columns.onlyIsolated.name, .boolean) + $0.add(column: PerpetualRecord.Columns.isIsolatedOnly.name, .boolean) .notNull() .defaults(to: false) } diff --git a/ios/Packages/Store/Sources/Models/PerpetualRecord.swift b/ios/Packages/Store/Sources/Models/PerpetualRecord.swift index 78bba95bed..47503ac6a2 100644 --- a/ios/Packages/Store/Sources/Models/PerpetualRecord.swift +++ b/ios/Packages/Store/Sources/Models/PerpetualRecord.swift @@ -6,7 +6,7 @@ import Primitives struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord { static let databaseTableName: String = "perpetuals" - + struct Columns { static let id = Column("id") static let name = Column("name") @@ -19,7 +19,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord static let volume24h = Column("volume24h") static let funding = Column("funding") static let maxLeverage = Column("maxLeverage") - static let onlyIsolated = Column("onlyIsolated") + static let isIsolatedOnly = Column("isIsolatedOnly") static let isPinned = Column("isPinned") } @@ -34,7 +34,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord var volume24h: Double var funding: Double var maxLeverage: UInt8 - var onlyIsolated: Bool + var isIsolatedOnly: Bool var isPinned: Bool init( @@ -49,7 +49,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord volume24h: Double, funding: Double, maxLeverage: UInt8, - onlyIsolated: Bool = false, + isIsolatedOnly: Bool = false, isPinned: Bool = false ) { self.id = id @@ -63,7 +63,7 @@ struct PerpetualRecord: Codable, TableRecord, FetchableRecord, PersistableRecord self.volume24h = volume24h self.funding = funding self.maxLeverage = maxLeverage - self.onlyIsolated = onlyIsolated + self.isIsolatedOnly = isIsolatedOnly self.isPinned = isPinned } @@ -87,7 +87,7 @@ extension PerpetualRecord: CreateTable { $0.column(Columns.volume24h.name, .double).notNull() $0.column(Columns.funding.name, .double).notNull() $0.column(Columns.maxLeverage.name, .integer).notNull() - $0.column(Columns.onlyIsolated.name, .boolean).notNull().defaults(to: false) + $0.column(Columns.isIsolatedOnly.name, .boolean).notNull().defaults(to: false) $0.column(Columns.isPinned.name, .boolean).notNull().defaults(to: false) } } @@ -107,7 +107,7 @@ extension PerpetualRecord { volume24h: volume24h, funding: funding, maxLeverage: maxLeverage, - onlyIsolated: onlyIsolated + isIsolatedOnly: isIsolatedOnly ) } } @@ -126,7 +126,7 @@ extension Perpetual { volume24h: volume24h, funding: funding, maxLeverage: maxLeverage, - onlyIsolated: onlyIsolated + isIsolatedOnly: isIsolatedOnly ) } } diff --git a/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift b/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift index b0e0d4a40f..ed08a28f71 100644 --- a/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift +++ b/ios/Packages/Store/Sources/Requests/Perpetuals/PerpetualRequest.swift @@ -43,7 +43,7 @@ extension PerpetualData { volume24h: .zero, funding: .zero, maxLeverage: 1, - onlyIsolated: false + isIsolatedOnly: false ), asset: Asset(id: .init(chain: .bitcoin, tokenId: .none), name: "", symbol: "", decimals: 0, type: .native), metadata: PerpetualMetadata(isPinned: false) diff --git a/ios/Packages/Store/Sources/Types/PerpetualInfo.swift b/ios/Packages/Store/Sources/Types/PerpetualInfo.swift index aa9699108a..c0eea68550 100644 --- a/ios/Packages/Store/Sources/Types/PerpetualInfo.swift +++ b/ios/Packages/Store/Sources/Types/PerpetualInfo.swift @@ -17,4 +17,4 @@ extension PerpetualInfo { metadata: PerpetualMetadata(isPinned: perpetual.isPinned) ) } -} \ No newline at end of file +} diff --git a/ios/Packages/Store/Tests/StoreTests/Requests/PerpetualRequestTests.swift b/ios/Packages/Store/Tests/StoreTests/Requests/PerpetualRequestTests.swift index c3eb66ee71..48aed7502c 100644 --- a/ios/Packages/Store/Tests/StoreTests/Requests/PerpetualRequestTests.swift +++ b/ios/Packages/Store/Tests/StoreTests/Requests/PerpetualRequestTests.swift @@ -13,7 +13,7 @@ struct PerpetualRequestTests { let db = DB.mockAssets() let store = PerpetualStore(db: db) let eth = AssetId(chain: .ethereum) - let perpetual = Perpetual.mock(assetId: eth, price: 2500.0, maxLeverage: 100) + let perpetual = Perpetual.mock(assetId: eth, price: 2500.0, maxLeverage: 100, isIsolatedOnly: true) try store.upsertPerpetuals([perpetual]) try store.setPinned(for: [perpetual.id], value: true) @@ -24,6 +24,7 @@ struct PerpetualRequestTests { #expect(result.perpetual.id == perpetual.id) #expect(result.perpetual.price == 2500.0) + #expect(result.perpetual.isIsolatedOnly == true) #expect(result.asset.id == eth) #expect(result.metadata.isPinned == true) #expect(notFound == .empty) diff --git a/ios/skills/code-style.md b/ios/skills/code-style.md index f09227b280..4c9e359fb7 100644 --- a/ios/skills/code-style.md +++ b/ios/skills/code-style.md @@ -126,7 +126,7 @@ public extension Perpetual { volume24h: 10_000_000, funding: 0.0001, maxLeverage: 50, - onlyIsolated: false + isIsolatedOnly: false ) } } From 5de14853ffdcec32ecc96c93df48d79e427a17e4 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:56:23 +0000 Subject: [PATCH 04/14] Add isIsolatedOnly to perpetual models Introduce isIsolatedOnly for perpetuals and propagate the change across the codebase. Renamed/updated properties and mappings (PerpetualService, GetPerpetualImpl, DbPerpetual, PerpetualDetailsDataAggregate) and added the field to fake data fixtures in FakePerpetualRepository. Refactored asset price fetching in AssetsRepository to use getMarket(...) and map markets by asset id. Removed generated Room schema/migration artifacts (schema 68.json and Migration_67_68) and updated GemDatabase/DatabaseModule accordingly. Also added/updated several primitive types in gemcore (transaction/swap/approval-related types) to support these changes. --- .../blockchain/services/PerpetualService.kt | 3 +- .../perpetuals/GetPerpetualImpl.kt | 6 +- .../repositoreis/assets/AssetsRepository.kt | 35 +- .../perpetual/FakePerpetualRepository.kt | 45 +- .../68.json | 2020 ----------------- .../service/store/database/GemDatabase.kt | 4 +- .../store/database/di/DatabaseModule.kt | 3 +- .../store/database/di/Migration_67_68.kt | 12 - .../store/database/entities/DbPerpetual.kt | 6 +- .../views/position/PerpetualPositionScene.kt | 4 +- .../PerpetualDetailsDataAggregate.kt | 6 +- .../wallet/core/primitives/ApprovalData.kt | 3 + .../wallet/core/primitives/AssetDetails.kt | 3 +- .../kotlin/com/wallet/core/primitives/Auth.kt | 7 - .../core/primitives/ContractCallData.kt | 1 - .../com/wallet/core/primitives/FiatQuote.kt | 6 - .../wallet/core/primitives/FiatTransaction.kt | 32 + .../kotlin/com/wallet/core/primitives/Nft.kt | 7 - .../kotlin/com/wallet/core/primitives/Node.kt | 6 - .../com/wallet/core/primitives/Perpetual.kt | 4 +- .../com/wallet/core/primitives/Simulation.kt | 2 - .../core/primitives/SimulationWarningType.kt | 5 + .../com/wallet/core/primitives/StakeType.kt | 27 +- .../com/wallet/core/primitives/Stream.kt | 2 +- .../wallet/core/primitives/SwapProvider.kt | 2 + .../com/wallet/core/primitives/Transaction.kt | 2 +- .../wallet/core/primitives/TransactionId.kt | 3 + .../primitives/TransactionMetadataTypes.kt | 1 + .../wallet/core/primitives/swap/Approval.kt | 8 - .../com/wallet/core/primitives/swap/Result.kt | 1 - .../core/primitives/swap/SwapProvider.kt | 3 + .../swap/TransactionSwapMetadata.kt | 3 + core | 2 +- 33 files changed, 127 insertions(+), 2147 deletions(-) delete mode 100644 android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json delete mode 100644 android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt create mode 100644 android/gemcore/src/main/kotlin/com/wallet/core/primitives/ApprovalData.kt create mode 100644 android/gemcore/src/main/kotlin/com/wallet/core/primitives/SimulationWarningType.kt create mode 100644 android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionId.kt create mode 100644 android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/SwapProvider.kt create mode 100644 android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/TransactionSwapMetadata.kt diff --git a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/PerpetualService.kt b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/PerpetualService.kt index 7998e42e5e..d9f7f63392 100644 --- a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/PerpetualService.kt +++ b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/PerpetualService.kt @@ -69,6 +69,7 @@ fun GemPerpetualData.toDTO(): PerpetualData? { volume24h = perpetual.volume24h, funding = perpetual.funding, maxLeverage = perpetual.maxLeverage, + isIsolatedOnly = perpetual.isIsolatedOnly, ), asset = asset.toDTO(), metadata = PerpetualMetadata( @@ -145,4 +146,4 @@ fun GemChartCandleStick.toDTO(): ChartCandleStick { fun uniffi.gemstone.PerpetualProvider.toDTO(): PerpetualProvider = when (this) { uniffi.gemstone.PerpetualProvider.HYPERCORE -> PerpetualProvider.Hypercore -} \ No newline at end of file +} diff --git a/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt b/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt index 20750167da..e6f784b11f 100644 --- a/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt +++ b/android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinates/perpetuals/GetPerpetualImpl.kt @@ -24,7 +24,7 @@ class GetPerpetualImpl @Inject constructor( } class PerpetualDetailsDataAggregateImpl( - val data: PerpetualData + private val data: PerpetualData ) : PerpetualDetailsDataAggregate { override val id: String = data.perpetual.id @@ -46,5 +46,5 @@ class PerpetualDetailsDataAggregateImpl( override val identifier: String = data.perpetual.identifier - override val onlyIsolated: Boolean = data.perpetual.onlyIsolated -} \ No newline at end of file + override val isIsolatedOnly: Boolean = data.perpetual.isIsolatedOnly +} diff --git a/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/assets/AssetsRepository.kt b/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/assets/AssetsRepository.kt index a6bfbdeaab..9eabc0bbba 100644 --- a/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/assets/AssetsRepository.kt +++ b/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/assets/AssetsRepository.kt @@ -242,35 +242,32 @@ class AssetsRepository @Inject constructor( searchTokensCase.search(widgetAssetIds, currency) } - val prices = widgetAssetIds.map { assetId -> + val marketsByAssetId = widgetAssetIds.map { assetId -> async { - try { - gemApi.getAsset(assetId.toIdentifier()) + val market = try { + gemApi.getMarket(assetId.toIdentifier(), currency.string) } catch (_: Throwable) { null } + assetId to market } } .awaitAll() - .filterNotNull() + .toMap() (getTokensInfo(widgetAssetIds.map { it.toIdentifier() }).firstOrNull() ?: emptyList()) .map { assetInfo -> - val price = prices.firstOrNull { it.asset.id == assetInfo.asset.id } - if (price == null) { - assetInfo - } else { - assetInfo.copy( - price = AssetPriceInfo( - currency = currency, - price = AssetPrice( - assetId = assetInfo.asset.id, - price = price.price.price, - priceChangePercentage24h = price.price.priceChangePercentage24h, - updatedAt = System.currentTimeMillis() - ) + val marketPrice = marketsByAssetId[assetInfo.asset.id]?.price ?: return@map assetInfo + assetInfo.copy( + price = AssetPriceInfo( + currency = currency, + price = AssetPrice( + assetId = assetInfo.asset.id, + price = marketPrice.price, + priceChangePercentage24h = marketPrice.priceChangePercentage24h, + updatedAt = System.currentTimeMillis() ) ) - } + ) } } @@ -525,4 +522,4 @@ class AssetsRepository @Inject constructor( ): Flow> { return assetsDao.getRecentByType(type).toAssetInfoModel() } -} \ No newline at end of file +} diff --git a/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/perpetual/FakePerpetualRepository.kt b/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/perpetual/FakePerpetualRepository.kt index b8b205ed6a..e220245af1 100644 --- a/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/perpetual/FakePerpetualRepository.kt +++ b/android/data/repositories/src/main/kotlin/com/gemwallet/android/data/repositoreis/perpetual/FakePerpetualRepository.kt @@ -278,7 +278,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 2500000000.0, volume24h = 15000000000.0, funding = 0.0001, - maxLeverage = 100u + maxLeverage = 100u, + isIsolatedOnly = false ), asset = btcAsset, metadata = PerpetualMetadata(isPinned = true) @@ -295,7 +296,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 1200000000.0, volume24h = 8000000000.0, funding = 0.00008, - maxLeverage = 50u + maxLeverage = 50u, + isIsolatedOnly = false ), asset = ethAsset, metadata = PerpetualMetadata(isPinned = true) @@ -312,7 +314,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 500000000.0, volume24h = 3000000000.0, funding = 0.00005, - maxLeverage = 20u + maxLeverage = 20u, + isIsolatedOnly = false ), asset = solAsset, metadata = PerpetualMetadata(isPinned = true) @@ -329,7 +332,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 350000000.0, volume24h = 2000000000.0, funding = 0.00012, - maxLeverage = 50u + maxLeverage = 50u, + isIsolatedOnly = false ), asset = bnbAsset, metadata = PerpetualMetadata(isPinned = false) @@ -346,7 +350,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 120000000.0, volume24h = 800000000.0, funding = 0.00006, - maxLeverage = 25u + maxLeverage = 25u, + isIsolatedOnly = false ), asset = maticAsset, metadata = PerpetualMetadata(isPinned = false) @@ -363,7 +368,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 200000000.0, volume24h = 1200000000.0, funding = 0.00009, - maxLeverage = 30u + maxLeverage = 30u, + isIsolatedOnly = false ), asset = avaxAsset, metadata = PerpetualMetadata(isPinned = false) @@ -380,7 +386,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 180000000.0, volume24h = 950000000.0, funding = 0.00007, - maxLeverage = 20u + maxLeverage = 20u, + isIsolatedOnly = false ), asset = adaAsset, metadata = PerpetualMetadata(isPinned = false) @@ -397,7 +404,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 500000000.0, volume24h = 3000000000.0, funding = 0.00001, - maxLeverage = 10u + maxLeverage = 10u, + isIsolatedOnly = false ), asset = usdtAsset, metadata = PerpetualMetadata(isPinned = false) @@ -414,7 +422,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 450000000.0, volume24h = 2800000000.0, funding = 0.00001, - maxLeverage = 10u + maxLeverage = 10u, + isIsolatedOnly = false ), asset = usdcAsset, metadata = PerpetualMetadata(isPinned = false) @@ -431,7 +440,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 150000000.0, volume24h = 900000000.0, funding = 0.00008, - maxLeverage = 25u + maxLeverage = 25u, + isIsolatedOnly = false ), asset = linkAsset, metadata = PerpetualMetadata(isPinned = false) @@ -448,7 +458,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 100000000.0, volume24h = 600000000.0, funding = 0.00007, - maxLeverage = 20u + maxLeverage = 20u, + isIsolatedOnly = false ), asset = uniAsset, metadata = PerpetualMetadata(isPinned = false) @@ -465,7 +476,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 80000000.0, volume24h = 450000000.0, funding = 0.00009, - maxLeverage = 20u + maxLeverage = 20u, + isIsolatedOnly = false ), asset = aaveAsset, metadata = PerpetualMetadata(isPinned = false) @@ -482,7 +494,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 50000000.0, volume24h = 300000000.0, funding = 0.00005, - maxLeverage = 15u + maxLeverage = 15u, + isIsolatedOnly = false ), asset = cakeAsset, metadata = PerpetualMetadata(isPinned = false) @@ -499,7 +512,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 40000000.0, volume24h = 250000000.0, funding = 0.00006, - maxLeverage = 15u + maxLeverage = 15u, + isIsolatedOnly = false ), asset = rayAsset, metadata = PerpetualMetadata(isPinned = false) @@ -516,7 +530,8 @@ class FakePerpetualRepository @Inject constructor() : PerpetualRepository { openInterest = 35000000.0, volume24h = 200000000.0, funding = 0.00004, - maxLeverage = 15u + maxLeverage = 15u, + isIsolatedOnly = false ), asset = orcaAsset, metadata = PerpetualMetadata(isPinned = false) diff --git a/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json b/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json deleted file mode 100644 index 53ebf71ce8..0000000000 --- a/android/data/services/store/schemas/com.gemwallet.android.data.service.store.database.GemDatabase/68.json +++ /dev/null @@ -1,2020 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 68, - "identityHash": "710140be0f07cd0f4db014d5b44a518e", - "entities": [ - { - "tableName": "wallets", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `domain_name` TEXT, `type` TEXT NOT NULL, `position` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `index` INTEGER NOT NULL, `source` TEXT NOT NULL DEFAULT 'Import', PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "domainName", - "columnName": "domain_name", - "affinity": "TEXT" - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "pinned", - "columnName": "pinned", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "index", - "columnName": "index", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "source", - "columnName": "source", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'Import'" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "accounts", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `derivation_path` TEXT NOT NULL, `address` TEXT NOT NULL, `chain` TEXT NOT NULL, `extendedPublicKey` TEXT, PRIMARY KEY(`wallet_id`, `address`, `chain`, `derivation_path`))", - "fields": [ - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "derivationPath", - "columnName": "derivation_path", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "extendedPublicKey", - "columnName": "extendedPublicKey", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "wallet_id", - "address", - "chain", - "derivation_path" - ] - } - }, - { - "tableName": "asset", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `symbol` TEXT NOT NULL, `decimals` INTEGER NOT NULL, `type` TEXT NOT NULL, `chain` TEXT NOT NULL, `is_enabled` INTEGER NOT NULL, `is_buy_enabled` INTEGER NOT NULL, `is_sell_enabled` INTEGER NOT NULL, `is_swap_enabled` INTEGER NOT NULL, `is_stake_enabled` INTEGER NOT NULL, `staking_apr` REAL, `rank` INTEGER NOT NULL, `updated_at` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "decimals", - "columnName": "decimals", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isEnabled", - "columnName": "is_enabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isBuyEnabled", - "columnName": "is_buy_enabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isSellEnabled", - "columnName": "is_sell_enabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isSwapEnabled", - "columnName": "is_swap_enabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isStakeEnabled", - "columnName": "is_stake_enabled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "stakingApr", - "columnName": "staking_apr", - "affinity": "REAL" - }, - { - "fieldPath": "rank", - "columnName": "rank", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updated_at", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "balances", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `account_address` TEXT NOT NULL, `available` TEXT NOT NULL, `available_amount` REAL NOT NULL, `frozen` TEXT NOT NULL, `frozen_amount` REAL NOT NULL, `locked` TEXT NOT NULL, `locked_amount` REAL NOT NULL, `staked` TEXT NOT NULL, `staked_amount` REAL NOT NULL, `pending` TEXT NOT NULL, `pending_amount` REAL NOT NULL, `rewards` TEXT NOT NULL, `rewards_amount` REAL NOT NULL, `reserved` TEXT NOT NULL, `reserved_amount` REAL NOT NULL, `total_amount` REAL NOT NULL, `is_active` INTEGER NOT NULL, `votes` INTEGER NOT NULL DEFAULT 0, `energy_available` INTEGER NOT NULL DEFAULT 0, `energy_total` INTEGER NOT NULL DEFAULT 0, `bandwidth_available` INTEGER NOT NULL DEFAULT 0, `bandwidth_total` INTEGER NOT NULL DEFAULT 0, `updated_at` INTEGER, PRIMARY KEY(`asset_id`, `wallet_id`, `account_address`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "accountAddress", - "columnName": "account_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "available", - "columnName": "available", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "availableAmount", - "columnName": "available_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "frozen", - "columnName": "frozen", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "frozenAmount", - "columnName": "frozen_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "locked", - "columnName": "locked", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "lockedAmount", - "columnName": "locked_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "staked", - "columnName": "staked", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "stakedAmount", - "columnName": "staked_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "pending", - "columnName": "pending", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pendingAmount", - "columnName": "pending_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "rewards", - "columnName": "rewards", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "rewardsAmount", - "columnName": "rewards_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "reserved", - "columnName": "reserved", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "reservedAmount", - "columnName": "reserved_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "totalAmount", - "columnName": "total_amount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "isActive", - "columnName": "is_active", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "votes", - "columnName": "votes", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "energyAvailable", - "columnName": "energy_available", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "energyTotal", - "columnName": "energy_total", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "bandwidthAvailable", - "columnName": "bandwidth_available", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "bandwidthTotal", - "columnName": "bandwidth_total", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "updatedAt", - "columnName": "updated_at", - "affinity": "INTEGER" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "wallet_id", - "account_address" - ] - }, - "foreignKeys": [ - { - "table": "asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "wallets", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "wallet_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "prices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `value` REAL, `usd_value` REAL, `day_changed` REAL, `currency` TEXT NOT NULL, PRIMARY KEY(`asset_id`))", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL" - }, - { - "fieldPath": "usdValue", - "columnName": "usd_value", - "affinity": "REAL" - }, - { - "fieldPath": "dayChanged", - "columnName": "day_changed", - "affinity": "REAL" - }, - { - "fieldPath": "currency", - "columnName": "currency", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id" - ] - } - }, - { - "tableName": "transactions", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `walletId` TEXT NOT NULL, `hash` TEXT NOT NULL, `assetId` TEXT NOT NULL, `feeAssetId` TEXT NOT NULL, `owner` TEXT NOT NULL, `recipient` TEXT NOT NULL, `contract` TEXT, `metadata` TEXT, `state` TEXT NOT NULL, `type` TEXT NOT NULL, `blockNumber` TEXT NOT NULL, `sequence` TEXT NOT NULL, `fee` TEXT NOT NULL, `value` TEXT NOT NULL, `payload` TEXT, `direction` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`id`, `walletId`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "walletId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "assetId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "feeAssetId", - "columnName": "feeAssetId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "owner", - "columnName": "owner", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contract", - "columnName": "contract", - "affinity": "TEXT" - }, - { - "fieldPath": "metadata", - "columnName": "metadata", - "affinity": "TEXT" - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "blockNumber", - "columnName": "blockNumber", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sequence", - "columnName": "sequence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fee", - "columnName": "fee", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "payload", - "columnName": "payload", - "affinity": "TEXT" - }, - { - "fieldPath": "direction", - "columnName": "direction", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "createdAt", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "updatedAt", - "columnName": "updatedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id", - "walletId" - ] - } - }, - { - "tableName": "tx_swap_metadata", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tx_id` TEXT NOT NULL, `from_asset_id` TEXT NOT NULL, `to_asset_id` TEXT NOT NULL, `from_amount` TEXT NOT NULL, `to_amount` TEXT NOT NULL, PRIMARY KEY(`tx_id`))", - "fields": [ - { - "fieldPath": "txId", - "columnName": "tx_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fromAssetId", - "columnName": "from_asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "toAssetId", - "columnName": "to_asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fromAmount", - "columnName": "from_amount", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "toAmount", - "columnName": "to_amount", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "tx_id" - ] - } - }, - { - "tableName": "room_connection", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `session_id` TEXT NOT NULL, `state` TEXT NOT NULL, `created_at` INTEGER NOT NULL, `expire_at` INTEGER NOT NULL, `app_name` TEXT NOT NULL, `app_description` TEXT NOT NULL, `app_url` TEXT NOT NULL, `app_icon` TEXT NOT NULL, `redirect_native` TEXT, `redirect_universal` TEXT, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sessionId", - "columnName": "session_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "createdAt", - "columnName": "created_at", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "expireAt", - "columnName": "expire_at", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "appName", - "columnName": "app_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appDescription", - "columnName": "app_description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appUrl", - "columnName": "app_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "appIcon", - "columnName": "app_icon", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "redirectNative", - "columnName": "redirect_native", - "affinity": "TEXT" - }, - { - "fieldPath": "redirectUniversal", - "columnName": "redirect_universal", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "stake_delegation_validator", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `chain` TEXT NOT NULL, `name` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `commission` REAL NOT NULL, `apr` REAL NOT NULL, `providerType` TEXT, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isActive", - "columnName": "isActive", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "commission", - "columnName": "commission", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "apr", - "columnName": "apr", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "providerType", - "columnName": "providerType", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "stake_delegation_base", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `address` TEXT NOT NULL, `delegation_id` TEXT NOT NULL, `validator_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `state` TEXT NOT NULL, `balance` TEXT NOT NULL, `rewards` TEXT NOT NULL, `completion_date` INTEGER, `price` REAL, `price_change` REAL, `shares` TEXT, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "delegationId", - "columnName": "delegation_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "validatorId", - "columnName": "validator_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "balance", - "columnName": "balance", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "rewards", - "columnName": "rewards", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "completionDate", - "columnName": "completion_date", - "affinity": "INTEGER" - }, - { - "fieldPath": "price", - "columnName": "price", - "affinity": "REAL" - }, - { - "fieldPath": "priceChange", - "columnName": "price_change", - "affinity": "REAL" - }, - { - "fieldPath": "shares", - "columnName": "shares", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "nodes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `status` TEXT NOT NULL, `priority` INTEGER NOT NULL, `chain` TEXT NOT NULL, PRIMARY KEY(`url`))", - "fields": [ - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "url" - ] - } - }, - { - "tableName": "session", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `wallet_id` TEXT NOT NULL, `currency` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "currency", - "columnName": "currency", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "asset_config", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `is_pinned` INTEGER NOT NULL, `is_visible` INTEGER NOT NULL, `list_position` INTEGER NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`))", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isPinned", - "columnName": "is_pinned", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isVisible", - "columnName": "is_visible", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "listPosition", - "columnName": "list_position", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "wallet_id" - ] - } - }, - { - "tableName": "banners", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `chain` TEXT, `state` TEXT NOT NULL, `event` TEXT NOT NULL, PRIMARY KEY(`wallet_id`, `asset_id`))", - "fields": [ - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT" - }, - { - "fieldPath": "state", - "columnName": "state", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "event", - "columnName": "event", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "wallet_id", - "asset_id" - ] - }, - "indices": [ - { - "name": "index_banners_event", - "unique": false, - "columnNames": [ - "event" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_banners_event` ON `${TABLE_NAME}` (`event`)" - }, - { - "name": "index_banners_wallet_id", - "unique": false, - "columnNames": [ - "wallet_id" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_banners_wallet_id` ON `${TABLE_NAME}` (`wallet_id`)" - } - ] - }, - { - "tableName": "price_alerts", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `assetId` TEXT NOT NULL, `currency` TEXT NOT NULL, `price` REAL, `pricePercentChange` REAL, `priceDirection` TEXT, `lastNotifiedAt` INTEGER, `enabled` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "assetId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "currency", - "columnName": "currency", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "price", - "columnName": "price", - "affinity": "REAL" - }, - { - "fieldPath": "pricePercentChange", - "columnName": "pricePercentChange", - "affinity": "REAL" - }, - { - "fieldPath": "priceDirection", - "columnName": "priceDirection", - "affinity": "TEXT" - }, - { - "fieldPath": "lastNotifiedAt", - "columnName": "lastNotifiedAt", - "affinity": "INTEGER" - }, - { - "fieldPath": "enabled", - "columnName": "enabled", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "nft_collection", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `chain` TEXT NOT NULL, `contractAddress` TEXT NOT NULL, `imageUrl` TEXT NOT NULL, `previewImageUrl` TEXT NOT NULL, `originalSourceUrl` TEXT NOT NULL, `status` TEXT, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contractAddress", - "columnName": "contractAddress", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "imageUrl", - "columnName": "imageUrl", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "previewImageUrl", - "columnName": "previewImageUrl", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "originalSourceUrl", - "columnName": "originalSourceUrl", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "status", - "columnName": "status", - "affinity": "TEXT" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "nft_asset", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `collection_id` TEXT NOT NULL, `token_id` TEXT NOT NULL, `token_type` TEXT NOT NULL, `name` TEXT NOT NULL, `description` TEXT, `chain` TEXT NOT NULL, `contract_address` TEXT, `image_url` TEXT NOT NULL, `preview_image_url` TEXT NOT NULL, `original_image_url` TEXT NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`collection_id`) REFERENCES `nft_collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "collectionId", - "columnName": "collection_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tokenId", - "columnName": "token_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tokenType", - "columnName": "token_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT" - }, - { - "fieldPath": "chain", - "columnName": "chain", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contractAddress", - "columnName": "contract_address", - "affinity": "TEXT" - }, - { - "fieldPath": "imageUrl", - "columnName": "image_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "previewImageUrl", - "columnName": "preview_image_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "originalSourceUrl", - "columnName": "original_image_url", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "foreignKeys": [ - { - "table": "nft_collection", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "collection_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "nft_attributes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `name`), FOREIGN KEY(`asset_id`) REFERENCES `nft_asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "name" - ] - }, - "foreignKeys": [ - { - "table": "nft_asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "nft_association", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wallet_id` TEXT NOT NULL, `asset_id` TEXT NOT NULL, PRIMARY KEY(`wallet_id`, `asset_id`), FOREIGN KEY(`asset_id`) REFERENCES `nft_asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "wallet_id", - "asset_id" - ] - }, - "foreignKeys": [ - { - "table": "nft_asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "wallets", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "wallet_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "nft_collection_link", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`collection_id` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`collection_id`, `name`), FOREIGN KEY(`collection_id`) REFERENCES `nft_collection`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "collectionId", - "columnName": "collection_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "collection_id", - "name" - ] - }, - "foreignKeys": [ - { - "table": "nft_collection", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "collection_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "asset_links", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `name`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "name" - ] - }, - "foreignKeys": [ - { - "table": "asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "asset_wallet", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `account_address` TEXT NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`, `account_address`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "accountAddress", - "columnName": "account_address", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "wallet_id", - "account_address" - ] - }, - "foreignKeys": [ - { - "table": "asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "wallets", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "wallet_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "asset_market", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `marketCap` REAL, `marketCapFdv` REAL, `marketCapRank` INTEGER, `totalVolume` REAL, `circulatingSupply` REAL, `totalSupply` REAL, `maxSupply` REAL, `allTimeHigh` REAL, `allTimeHighDate` INTEGER, `allTimeHighChangePercentage` REAL, `allTimeLow` REAL, `allTimeLowDate` INTEGER, `allTimeLowChangePercentage` REAL, PRIMARY KEY(`asset_id`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "marketCap", - "columnName": "marketCap", - "affinity": "REAL" - }, - { - "fieldPath": "marketCapFdv", - "columnName": "marketCapFdv", - "affinity": "REAL" - }, - { - "fieldPath": "marketCapRank", - "columnName": "marketCapRank", - "affinity": "INTEGER" - }, - { - "fieldPath": "totalVolume", - "columnName": "totalVolume", - "affinity": "REAL" - }, - { - "fieldPath": "circulatingSupply", - "columnName": "circulatingSupply", - "affinity": "REAL" - }, - { - "fieldPath": "totalSupply", - "columnName": "totalSupply", - "affinity": "REAL" - }, - { - "fieldPath": "maxSupply", - "columnName": "maxSupply", - "affinity": "REAL" - }, - { - "fieldPath": "allTimeHigh", - "columnName": "allTimeHigh", - "affinity": "REAL" - }, - { - "fieldPath": "allTimeHighDate", - "columnName": "allTimeHighDate", - "affinity": "INTEGER" - }, - { - "fieldPath": "allTimeHighChangePercentage", - "columnName": "allTimeHighChangePercentage", - "affinity": "REAL" - }, - { - "fieldPath": "allTimeLow", - "columnName": "allTimeLow", - "affinity": "REAL" - }, - { - "fieldPath": "allTimeLowDate", - "columnName": "allTimeLowDate", - "affinity": "INTEGER" - }, - { - "fieldPath": "allTimeLowChangePercentage", - "columnName": "allTimeLowChangePercentage", - "affinity": "REAL" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id" - ] - }, - "foreignKeys": [ - { - "table": "asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "assets_priority", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`query` TEXT NOT NULL, `asset_id` TEXT NOT NULL, `priority` INTEGER NOT NULL, PRIMARY KEY(`query`, `asset_id`))", - "fields": [ - { - "fieldPath": "query", - "columnName": "query", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "query", - "asset_id" - ] - } - }, - { - "tableName": "currency_rates", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`currency` TEXT NOT NULL, `rate` REAL NOT NULL, PRIMARY KEY(`currency`))", - "fields": [ - { - "fieldPath": "currency", - "columnName": "currency", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "rate", - "columnName": "rate", - "affinity": "REAL", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "currency" - ] - } - }, - { - "tableName": "recent_assets", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `wallet_id` TEXT NOT NULL, `to_asset_id` TEXT, `type` TEXT NOT NULL, `addedAt` INTEGER NOT NULL, PRIMARY KEY(`asset_id`, `wallet_id`, `type`), FOREIGN KEY(`asset_id`) REFERENCES `asset`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`wallet_id`) REFERENCES `wallets`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", - "fields": [ - { - "fieldPath": "assetId", - "columnName": "asset_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "walletId", - "columnName": "wallet_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "toAssetId", - "columnName": "to_asset_id", - "affinity": "TEXT" - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "addedAt", - "columnName": "addedAt", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "asset_id", - "wallet_id", - "type" - ] - }, - "foreignKeys": [ - { - "table": "asset", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "asset_id" - ], - "referencedColumns": [ - "id" - ] - }, - { - "table": "wallets", - "onDelete": "CASCADE", - "onUpdate": "NO ACTION", - "columns": [ - "wallet_id" - ], - "referencedColumns": [ - "id" - ] - } - ] - }, - { - "tableName": "perpetual", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `provider` TEXT NOT NULL, `assetId` TEXT NOT NULL, `identifier` TEXT NOT NULL, `price` REAL NOT NULL, `pricePercentChange24h` REAL NOT NULL, `openInterest` REAL NOT NULL, `volume24h` REAL NOT NULL, `funding` REAL NOT NULL, `maxLeverage` INTEGER NOT NULL, `onlyIsolated` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "provider", - "columnName": "provider", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "assetId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "identifier", - "columnName": "identifier", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "price", - "columnName": "price", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "pricePercentChange24h", - "columnName": "pricePercentChange24h", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "openInterest", - "columnName": "openInterest", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "volume24h", - "columnName": "volume24h", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "funding", - "columnName": "funding", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "maxLeverage", - "columnName": "maxLeverage", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "onlyIsolated", - "columnName": "onlyIsolated", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "perpetual_asset_id_idx", - "unique": false, - "columnNames": [ - "assetId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_asset_id_idx` ON `${TABLE_NAME}` (`assetId`)" - } - ] - }, - { - "tableName": "perpetual_asset", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `symbol` TEXT NOT NULL, `decimals` INTEGER NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "decimals", - "columnName": "decimals", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - } - }, - { - "tableName": "perpetual_balance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountAddress` TEXT NOT NULL, `available` REAL NOT NULL, `reserved` REAL NOT NULL, `withdrawable` REAL NOT NULL, PRIMARY KEY(`accountAddress`))", - "fields": [ - { - "fieldPath": "accountAddress", - "columnName": "accountAddress", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "available", - "columnName": "available", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "reserved", - "columnName": "reserved", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "withdrawable", - "columnName": "withdrawable", - "affinity": "REAL", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "accountAddress" - ] - } - }, - { - "tableName": "perpetual_metadata", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`perpetualId` TEXT NOT NULL, `isPinned` INTEGER NOT NULL, PRIMARY KEY(`perpetualId`))", - "fields": [ - { - "fieldPath": "perpetualId", - "columnName": "perpetualId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isPinned", - "columnName": "isPinned", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "perpetualId" - ] - } - }, - { - "tableName": "perpetual_position", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `perpetualId` TEXT NOT NULL, `accountAddress` TEXT NOT NULL, `assetId` TEXT NOT NULL, `size` REAL NOT NULL, `sizeValue` REAL NOT NULL, `leverage` INTEGER NOT NULL, `entryPrice` REAL, `liquidationPrice` REAL, `marginType` TEXT NOT NULL, `direction` TEXT NOT NULL, `marginAmount` REAL NOT NULL, `takeProfitPrice` REAL, `takeProfitType` TEXT, `takeProfitOrderId` TEXT, `stopLossPrice` REAL, `stopLossType` TEXT, `stopLossOrderId` TEXT, `pnl` REAL NOT NULL, `funding` REAL, PRIMARY KEY(`id`, `perpetualId`, `accountAddress`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "perpetualId", - "columnName": "perpetualId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "accountAddress", - "columnName": "accountAddress", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "assetId", - "columnName": "assetId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "size", - "columnName": "size", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "sizeValue", - "columnName": "sizeValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "leverage", - "columnName": "leverage", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryPrice", - "columnName": "entryPrice", - "affinity": "REAL" - }, - { - "fieldPath": "liquidationPrice", - "columnName": "liquidationPrice", - "affinity": "REAL" - }, - { - "fieldPath": "marginType", - "columnName": "marginType", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "direction", - "columnName": "direction", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "marginAmount", - "columnName": "marginAmount", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "takeProfitPrice", - "columnName": "takeProfitPrice", - "affinity": "REAL" - }, - { - "fieldPath": "takeProfitType", - "columnName": "takeProfitType", - "affinity": "TEXT" - }, - { - "fieldPath": "takeProfitOrderId", - "columnName": "takeProfitOrderId", - "affinity": "TEXT" - }, - { - "fieldPath": "stopLossPrice", - "columnName": "stopLossPrice", - "affinity": "REAL" - }, - { - "fieldPath": "stopLossType", - "columnName": "stopLossType", - "affinity": "TEXT" - }, - { - "fieldPath": "stopLossOrderId", - "columnName": "stopLossOrderId", - "affinity": "TEXT" - }, - { - "fieldPath": "pnl", - "columnName": "pnl", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "funding", - "columnName": "funding", - "affinity": "REAL" - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id", - "perpetualId", - "accountAddress" - ] - }, - "indices": [ - { - "name": "perpetual_position_asset_id_idx", - "unique": false, - "columnNames": [ - "assetId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_asset_id_idx` ON `${TABLE_NAME}` (`assetId`)" - }, - { - "name": "perpetual_position_perpetual_id_idx", - "unique": false, - "columnNames": [ - "perpetualId" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_perpetual_id_idx` ON `${TABLE_NAME}` (`perpetualId`)" - }, - { - "name": "perpetual_position_account_address_idx", - "unique": false, - "columnNames": [ - "accountAddress" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `perpetual_position_account_address_idx` ON `${TABLE_NAME}` (`accountAddress`)" - } - ] - } - ], - "views": [ - { - "viewName": "asset_info", - "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT DISTINCT\n asset.id as id,\n asset.name as name,\n asset.symbol as symbol,\n asset.decimals as decimals,\n asset.type as type,\n asset.is_buy_enabled as isBuyEnabled,\n asset.is_sell_enabled as isSellEnabled,\n asset.is_swap_enabled as isSwapEnabled,\n asset.is_stake_enabled as isStakeEnabled,\n asset.staking_apr as stakingApr,\n asset.rank as assetRank,\n asset.chain as chain,\n accounts.address as address,\n accounts.derivation_path as derivationPath,\n accounts.extendedPublicKey as extendedPublicKey,\n asset_config.is_pinned AS pinned,\n asset_config.is_visible AS visible,\n asset_config.list_position AS listPosition,\n session.id AS sessionId,\n prices.currency AS priceCurrency,\n wallets.id as walletId,\n wallets.type AS walletType,\n wallets.name AS walletName,\n prices.value AS priceValue,\n prices.day_changed AS priceDayChanges,\n balances.available AS balanceAvailable,\n balances.available_amount AS balanceAvailableAmount,\n balances.frozen AS balanceFrozen,\n balances.frozen_amount AS balanceFrozenAmount,\n balances.locked AS balanceLocked,\n balances.locked_amount AS balanceLockedAmount,\n balances.staked AS balanceStaked,\n balances.staked_amount AS balanceStakedAmount,\n balances.pending AS balancePending,\n balances.pending_amount AS balancePendingAmount,\n balances.rewards AS balanceRewards,\n balances.rewards_amount AS balanceRewardsAmount,\n balances.reserved AS balanceReserved,\n balances.reserved_amount AS balanceReservedAmount,\n balances.total_amount AS balanceTotalAmount,\n (balances.total_amount * prices.value) AS balanceFiatTotalAmount,\n balances.updated_at AS balanceUpdatedAt,\n balances.is_active AS assetIsActive,\n balances.votes AS votes,\n balances.energy_available as energyAvailable,\n balances.energy_total as energyTotal,\n balances.bandwidth_available as bandwidthAvailable, \n balances.bandwidth_total as bandwidthTotal\n FROM asset\n LEFT JOIN asset_wallet ON asset.id = asset_wallet.asset_id\n LEFT JOIN session ON asset_wallet.wallet_id = session.wallet_id\n LEFT JOIN wallets ON wallets.id = session.wallet_id\n LEFT JOIN accounts ON asset_wallet.account_address = accounts.address AND wallets.id = accounts.wallet_id AND asset.\"chain\" = accounts.\"chain\"\n LEFT JOIN balances ON asset_wallet.account_address = balances.account_address AND asset_wallet.asset_id = balances.asset_id AND wallets.id = balances.wallet_id\n LEFT JOIN prices ON asset.id = prices.asset_id AND prices.currency = (SELECT currency FROM session WHERE id = 1)\n LEFT JOIN asset_config ON asset_wallet.asset_id = asset_config.asset_id AND wallets.id = asset_config.wallet_id" - }, - { - "viewName": "extended_txs", - "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT\n DISTINCT tx.id,\n tx.hash,\n tx.assetId,\n tx.feeAssetId,\n tx.owner,\n tx.recipient,\n tx.contract,\n tx.state,\n tx.type,\n tx.blockNumber,\n tx.sequence,\n tx.fee,\n tx.value,\n tx.payload,\n tx.metadata,\n tx.direction,\n tx.createdAt,\n tx.updatedAt,\n tx.walletId,\n asset.decimals as assetDecimals,\n asset.name as assetName,\n asset.type as assetType,\n asset.symbol as assetSymbol,\n feeAsset.decimals as feeDecimals,\n feeAsset.name as feeName,\n feeAsset.type as feeType,\n feeAsset.symbol as feeSymbol,\n prices.value as assetPrice,\n prices.day_changed as assetPriceChanged,\n feePrices.value as feePrice,\n feePrices.day_changed as feePriceChanged,\n from_asset.id as assetIdFrom,\n from_asset.name as assetNameFrom,\n from_asset.symbol as assetSymbolFrom,\n from_asset.decimals as assetDecimalsFrom,\n from_asset.type as assetTypeFrom,\n to_asset.id as assetIdTo,\n to_asset.name as assetNameTo,\n to_asset.symbol as assetSymbolTo,\n to_asset.decimals as assetDecimalsTo,\n to_asset.type as assetTypeTo\n FROM transactions as tx\n INNER JOIN asset ON tx.assetId = asset.id \n INNER JOIN asset as feeAsset ON tx.feeAssetId = feeAsset.id\n LEFT JOIN prices ON tx.assetId = prices.asset_id\n LEFT JOIN prices as feePrices ON tx.feeAssetId = feePrices.asset_id\n LEFT JOIN tx_swap_metadata as swap ON tx.id = swap.tx_id\n LEFT JOIN asset as from_asset ON swap.from_asset_id = from_asset.id\n LEFT JOIN asset as to_asset ON swap.to_asset_id = to_asset.id\n WHERE (tx.owner IN (SELECT accounts.address FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1) OR tx.recipient in (SELECT accounts.address FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1))\n AND tx.walletId in (SELECT wallet_id FROM session WHERE session.id = 1)\n AND UPPER(tx.feeAssetId) IN (SELECT UPPER(accounts.chain) FROM accounts, session\n WHERE accounts.wallet_id = session.wallet_id AND session.id = 1)\n GROUP BY tx.id" - } - ], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '710140be0f07cd0f4db014d5b44a518e')" - ] - } -} \ No newline at end of file diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt index 360e24e848..47c1844719 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/GemDatabase.kt @@ -37,7 +37,7 @@ import com.gemwallet.android.data.service.store.database.entities.DbTxSwapMetada import com.gemwallet.android.data.service.store.database.entities.DbWallet @Database( - version = 68, + version = 67, entities = [ DbWallet::class, DbAccount::class, @@ -110,4 +110,4 @@ abstract class GemDatabase : RoomDatabase() { abstract fun perpetualPositionDao(): PerpetualPositionDao abstract fun perpetualBalanceDao(): PerpetualBalanceDao -} \ No newline at end of file +} diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt index f340765ff7..7ed8dcf5be 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/DatabaseModule.kt @@ -64,7 +64,6 @@ object DatabaseModule { .addMigrations(Migration_64_65) .addMigrations(Migration_65_66) .addMigrations(Migration_66_67) - .addMigrations(Migration_67_68) .build() @Singleton @@ -134,4 +133,4 @@ object DatabaseModule { @Singleton @Provides fun providePerpetualBalanceDao(db: GemDatabase): PerpetualBalanceDao = db.perpetualBalanceDao() -} \ No newline at end of file +} diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt deleted file mode 100644 index cada01b9f0..0000000000 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/di/Migration_67_68.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.gemwallet.android.data.service.store.database.di - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -object Migration_67_68 : Migration(67, 68) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - "ALTER TABLE perpetual ADD COLUMN onlyIsolated INTEGER NOT NULL DEFAULT 0" - ) - } -} diff --git a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt index 2c336fd25e..42c41f70ea 100644 --- a/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt +++ b/android/data/services/store/src/main/kotlin/com/gemwallet/android/data/service/store/database/entities/DbPerpetual.kt @@ -28,7 +28,6 @@ data class DbPerpetual( val volume24h: Double, val funding: Double, val maxLeverage: Int, - val onlyIsolated: Boolean = false, ) @@ -68,7 +67,7 @@ fun DbPerpetual.toDTO(): Perpetual? { volume24h = volume24h, funding = funding, maxLeverage = maxLeverage.toUByte(), - onlyIsolated = onlyIsolated, + isIsolatedOnly = false, ) } @@ -89,7 +88,6 @@ fun Perpetual.toDB(): DbPerpetual { volume24h = volume24h, funding = funding, maxLeverage = maxLeverage.toInt(), - onlyIsolated = onlyIsolated, ) } @@ -103,4 +101,4 @@ fun DbPerpetualData.toDTO(): PerpetualData? { asset = asset?.toDTO() ?: return null, metadata = metadata?.toDTO() ?: PerpetualMetadata(false), ) -} \ No newline at end of file +} diff --git a/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt b/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt index 4863c180da..32168629d0 100644 --- a/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt +++ b/android/features/perpetual/presents/src/main/kotlin/com/gemwallet/features/perpetual/views/position/PerpetualPositionScene.kt @@ -85,7 +85,7 @@ private fun PerpetualPositionScenePreview() { override val maxLeverage: Int = 40 override val price: Double = 0.0 override val identifier: String = "BTC-PERP" - override val onlyIsolated: Boolean = false + override val isIsolatedOnly: Boolean = false } val samplePosition = object : PerpetualPositionDetailsDataAggregate { @@ -137,4 +137,4 @@ private fun PerpetualPositionScenePreview() { onClose = {} ) } -} \ No newline at end of file +} diff --git a/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt b/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt index 8abfc296e9..4b2ec19d3f 100644 --- a/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt +++ b/android/gemcore/src/main/kotlin/com/gemwallet/android/domains/perpetual/aggregates/PerpetualDetailsDataAggregate.kt @@ -15,7 +15,7 @@ interface PerpetualDetailsDataAggregate { val maxLeverage: Int val price: Double val identifier: String - val onlyIsolated: Boolean + val isIsolatedOnly: Boolean val marginType: PerpetualMarginType - get() = if (onlyIsolated) PerpetualMarginType.Isolated else PerpetualMarginType.Cross -} \ No newline at end of file + get() = if (isIsolatedOnly) PerpetualMarginType.Isolated else PerpetualMarginType.Cross +} diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ApprovalData.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ApprovalData.kt new file mode 100644 index 0000000000..de541d0a84 --- /dev/null +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ApprovalData.kt @@ -0,0 +1,3 @@ +package com.wallet.core.primitives + +typealias ApprovalData = com.wallet.core.primitives.swap.ApprovalData diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/AssetDetails.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/AssetDetails.kt index b6600f5b1b..1041563ecc 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/AssetDetails.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/AssetDetails.kt @@ -41,8 +41,7 @@ data class AssetFull ( val score: AssetScore, val tags: List, val links: List, - val perpetuals: List, - val price: Price, + val perpetuals: List ) @Serializable diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Auth.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Auth.kt index 46d2dae9af..d1f1e0c88a 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Auth.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Auth.kt @@ -13,13 +13,6 @@ data class AuthNonce ( val timestamp: UInt ) -@Serializable -data class AuthMessage ( - val chain: Chain, - val address: String, - val authNonce: AuthNonce -) - @Serializable data class AuthPayload ( val deviceId: String, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ContractCallData.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ContractCallData.kt index f0f4bdef9c..d723cffd7c 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ContractCallData.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/ContractCallData.kt @@ -4,7 +4,6 @@ package com.wallet.core.primitives -import com.wallet.core.primitives.swap.ApprovalData import kotlinx.serialization.Serializable import kotlinx.serialization.SerialName diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatQuote.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatQuote.kt index d028ced0b2..d6a66d832e 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatQuote.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatQuote.kt @@ -18,12 +18,6 @@ data class FiatQuote ( val paymentMethods: List ) -@Serializable -data class FiatQuoteError ( - val provider: String? = null, - val error: String -) - @Serializable data class FiatQuoteUrl ( val redirectUrl: String diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatTransaction.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatTransaction.kt index e1fdd8d807..9ea04a4caa 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatTransaction.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/FiatTransaction.kt @@ -15,3 +15,35 @@ enum class FiatQuoteType(val string: String) { Sell("sell"), } +@Serializable +enum class FiatTransactionStatus(val string: String) { + @SerialName("complete") + Complete("complete"), + @SerialName("pending") + Pending("pending"), + @SerialName("failed") + Failed("failed"), + @SerialName("unknown") + Unknown("unknown"), +} + +@Serializable +data class FiatTransaction ( + val id: String, + val assetId: AssetId, + val transactionType: FiatQuoteType, + val provider: FiatProviderName, + val status: FiatTransactionStatus, + val fiatAmount: Double, + val fiatCurrency: String, + val value: String, + val createdAt: SerializedDate +) + +@Serializable +data class FiatTransactionInfo ( + val transaction: FiatTransaction, + val asset: Asset, + val detailsUrl: String? = null +) + diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Nft.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Nft.kt index 879c4cebed..1e446e6efe 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Nft.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Nft.kt @@ -70,13 +70,6 @@ data class NFTAssetData ( val asset: NFTAsset ) -@Serializable -data class NFTAssetId ( - val chain: Chain, - val contractAddress: String, - val tokenId: String -) - @Serializable data class NFTData ( val collection: NFTCollection, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Node.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Node.kt index aacb7e87dc..50fd638e60 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Node.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Node.kt @@ -34,9 +34,3 @@ data class ChainNodes ( val nodes: List ) -@Serializable -data class NodesResponse ( - val version: Int, - val nodes: List -) - diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt index 560f353fce..0dfb1713cf 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Perpetual.kt @@ -47,7 +47,7 @@ data class Perpetual ( val volume24h: Double, val funding: Double, val maxLeverage: UByte, - val onlyIsolated: Boolean = false + val isIsolatedOnly: Boolean ) @Serializable @@ -67,7 +67,7 @@ data class PerpetualBasic ( @Serializable data class PerpetualConfirmData ( val direction: PerpetualDirection, - val marginType: PerpetualMarginType = PerpetualMarginType.Cross, + val marginType: PerpetualMarginType, val baseAsset: Asset, val assetIndex: Int, val price: String, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Simulation.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Simulation.kt index b89b786eb8..4414569c6e 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Simulation.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Simulation.kt @@ -72,8 +72,6 @@ enum class SimulationSeverity(val string: String) { Critical("critical"), } -enum class SimulationWarningType - @Serializable data class SimulationWarning ( val severity: SimulationSeverity, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SimulationWarningType.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SimulationWarningType.kt new file mode 100644 index 0000000000..7982c125da --- /dev/null +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SimulationWarningType.kt @@ -0,0 +1,5 @@ +package com.wallet.core.primitives + +import kotlinx.serialization.json.JsonElement + +typealias SimulationWarningType = JsonElement diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/StakeType.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/StakeType.kt index 47984eab29..6980df8511 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/StakeType.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/StakeType.kt @@ -8,12 +8,10 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.SerialName @Serializable -enum class FreezeType(val string: String) { - @SerialName("freeze") - Freeze("freeze"), - @SerialName("unfreeze") - Unfreeze("unfreeze"), -} +data class RedelegateData ( + val delegation: Delegation, + val toValidator: DelegationValidator +) @Serializable enum class Resource(val string: String) { @@ -23,18 +21,6 @@ enum class Resource(val string: String) { Energy("energy"), } -@Serializable -data class FreezeData ( - val freezeType: FreezeType, - val resource: Resource -) - -@Serializable -data class RedelegateData ( - val delegation: Delegation, - val toValidator: DelegationValidator -) - @Serializable data class TronUnfreeze ( val resource: Resource, @@ -66,7 +52,10 @@ sealed class StakeType { data class Withdraw(val content: Delegation): StakeType() @Serializable @SerialName("Freeze") - data class Freeze(val content: FreezeData): StakeType() + data class Freeze(val content: Resource): StakeType() + @Serializable + @SerialName("Unfreeze") + data class Unfreeze(val content: Resource): StakeType() } @Serializable diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Stream.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Stream.kt index e9cb3267ba..4f2443abcb 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Stream.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Stream.kt @@ -42,7 +42,7 @@ data class StreamPriceAlertUpdate ( @Serializable data class StreamTransactionsUpdate ( val walletId: WalletId, - val transactions: List + val transactions: List ) @Serializable diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SwapProvider.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SwapProvider.kt index 050358d366..fcf58f0ac5 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SwapProvider.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/SwapProvider.kt @@ -47,5 +47,7 @@ enum class SwapProvider(val string: String) { Hyperliquid("hyperliquid"), @SerialName("orca") Orca("orca"), + @SerialName("squid") + Squid("squid"), } diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Transaction.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Transaction.kt index e1198a1a07..551ea5dfcc 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Transaction.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/Transaction.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.SerialName @Serializable data class Transaction ( - val id: String, + val id: TransactionId, val assetId: AssetId, val from: String, val to: String, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionId.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionId.kt new file mode 100644 index 0000000000..1c4f7f43d6 --- /dev/null +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionId.kt @@ -0,0 +1,3 @@ +package com.wallet.core.primitives + +typealias TransactionId = String diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionMetadataTypes.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionMetadataTypes.kt index fd4add043c..c075383b09 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionMetadataTypes.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/TransactionMetadataTypes.kt @@ -18,6 +18,7 @@ data class TransactionPerpetualMetadata ( val pnl: Double, val price: Double, val direction: PerpetualDirection, + val isLiquidation: Boolean? = null, val provider: PerpetualProvider? = null ) diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Approval.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Approval.kt index 64fb0fb733..653c30c629 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Approval.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Approval.kt @@ -4,7 +4,6 @@ package com.wallet.core.primitives.swap -import com.wallet.core.primitives.SwapProvider import kotlinx.serialization.Serializable import kotlinx.serialization.SerialName @@ -15,13 +14,6 @@ data class ApprovalData ( val value: String ) -@Serializable -data class QuoteAsset ( - val id: String, - val symbol: String, - val decimals: UInt -) - @Serializable data class SwapProviderData ( val provider: SwapProvider, diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Result.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Result.kt index 831e79b6fe..8ed53e9757 100644 --- a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Result.kt +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/Result.kt @@ -4,7 +4,6 @@ package com.wallet.core.primitives.swap -import com.wallet.core.primitives.TransactionSwapMetadata import kotlinx.serialization.Serializable import kotlinx.serialization.SerialName diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/SwapProvider.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/SwapProvider.kt new file mode 100644 index 0000000000..f014712890 --- /dev/null +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/SwapProvider.kt @@ -0,0 +1,3 @@ +package com.wallet.core.primitives.swap + +typealias SwapProvider = com.wallet.core.primitives.SwapProvider diff --git a/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/TransactionSwapMetadata.kt b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/TransactionSwapMetadata.kt new file mode 100644 index 0000000000..a1b6766550 --- /dev/null +++ b/android/gemcore/src/main/kotlin/com/wallet/core/primitives/swap/TransactionSwapMetadata.kt @@ -0,0 +1,3 @@ +package com.wallet.core.primitives.swap + +typealias TransactionSwapMetadata = com.wallet.core.primitives.TransactionSwapMetadata diff --git a/core b/core index 058ad0d0e8..f2f5fe36d5 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 058ad0d0e8e59b3ea4099c28871316f311749590 +Subproject commit f2f5fe36d530ed32c2d5e0a1301e290dece930d9 From ad32d4a702ad65cd8862f00637f6727c39aa3179 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:11:02 +0000 Subject: [PATCH 05/14] Perpetual: rename margin title, move extension Use a unified title property for margin type and relocate the extension to PrimitivesComponents. Updated PerpetualPositionViewModel to use marginType.title instead of displayText, adjusted imports and minor formatting. Updated tests to expect the new capitalized title ("Isolated") and reordered test imports. Moved/renamed PerpetualMarginType extension file and changed the API from displayText to title. Also tweaked justfile test TARGET handling and added a comment. --- .../PerpetualPositionViewModel.swift | 39 ++++++++++--------- .../PerpetualPositionViewModelTests.swift | 23 ++++++----- .../ViewModels/SwapSceneViewModel.swift | 1 - .../ConfirmTransferSceneViewModel.swift | 1 - .../Sources/ViewModels/ReceiveViewModel.swift | 1 - .../SignMessageSceneViewModel.swift | 1 - .../WalletConnectorPresenterTests.swift | 1 - ios/Gem/Services/ServicesFactory.swift | 1 - .../AssetsService/AssetsService.swift | 3 -- .../Keystore/Tests/LocalKeystoreTests.swift | 1 - .../Keystore/Tests/WalletKeyStoreTests.swift | 1 - ...tualMarginType+PrimitivesComponents.swift} | 7 ++-- ios/justfile | 3 +- 13 files changed, 38 insertions(+), 45 deletions(-) rename ios/Packages/{Primitives/Sources/Extensions/PerpetualMarginType+Primitives.swift => PrimitivesComponents/Sources/Extensions/PerpetualMarginType+PrimitivesComponents.swift} (67%) diff --git a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift index d132aafd64..9b37cc1ecb 100644 --- a/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift +++ b/ios/Features/Perpetuals/Sources/ViewModels/PerpetualPositionViewModel.swift @@ -1,13 +1,13 @@ // Copyright (c). Gem Wallet. All rights reserved. +import Components +import Formatters import Foundation +import Localization import Primitives -import Formatters -import SwiftUI -import Style -import Components import PrimitivesComponents -import Localization +import Style +import SwiftUI public struct PerpetualPositionViewModel { public let data: PerpetualPositionData @@ -20,39 +20,39 @@ public struct PerpetualPositionViewModel { currencyStyle: CurrencyFormatterType = .currency ) { self.data = data - self.currencyFormatter = CurrencyFormatter(type: currencyStyle, currencyCode: Currency.usd.rawValue) - self.percentFormatter = CurrencyFormatter(type: .percent, currencyCode: Currency.usd.rawValue) - self.autocloseFormatter = AutocloseFormatter(currencyFormatter: currencyFormatter) + currencyFormatter = CurrencyFormatter(type: currencyStyle, currencyCode: Currency.usd.rawValue) + percentFormatter = CurrencyFormatter(type: .percent, currencyCode: Currency.usd.rawValue) + autocloseFormatter = AutocloseFormatter(currencyFormatter: currencyFormatter) } - + public var assetImage: AssetImage { AssetIdViewModel(assetId: data.perpetual.assetId).assetImage } - + public var nameText: String { data.perpetual.name } - + public var symbolText: String { data.asset.symbol } - + public var leverageText: String { "\(Int(data.position.leverage))x" } - + public var directionText: String { PerpetualDirectionViewModel(direction: data.position.direction).title } - + public var positionTypeText: String { "\(directionText.uppercased()) \(leverageText)" } - + public var positionTypeColor: Color { PerpetualDirectionViewModel(direction: data.position.direction).color } - + public var pnlViewModel: PnLViewModel { PnLViewModel( pnl: data.position.pnl, @@ -61,12 +61,14 @@ public struct PerpetualPositionViewModel { percentFormatter: percentFormatter ) } + public var pnlField: ListItemField { ListItemField( title: TextValue(text: pnlViewModel.title, style: .body), value: TextValue(text: pnlViewModel.text ?? "", style: pnlViewModel.textStyle) ) } + public var pnlColor: Color { pnlViewModel.color } public var pnlPercent: Double { pnlViewModel.percent } public var pnlWithPercentText: String { pnlViewModel.text ?? "" } @@ -85,7 +87,7 @@ public struct PerpetualPositionViewModel { public var marginField: ListItemField { let marginAmount = currencyFormatter.string(data.position.marginAmount) - return ListItemField(title: Localized.Perpetual.margin, value: "\(marginAmount) (\(data.position.marginType.displayText))") + return ListItemField(title: Localized.Perpetual.margin, value: "\(marginAmount) (\(data.position.marginType.title))") } public var fundingPaymentsField: ListItemField { @@ -94,6 +96,7 @@ public struct PerpetualPositionViewModel { value: TextValue(text: fundingPaymentsModel.text ?? "-", style: fundingPaymentsModel.textStyle) ) } + public var fundingPaymentsColor: Color { fundingPaymentsModel.color } public var sizeField: ListItemField { @@ -127,4 +130,4 @@ extension PerpetualPositionViewModel { extension PerpetualPositionViewModel: Identifiable { public var id: String { data.position.id } -} \ No newline at end of file +} diff --git a/ios/Features/Perpetuals/Tests/PerpetualPositionViewModelTests.swift b/ios/Features/Perpetuals/Tests/PerpetualPositionViewModelTests.swift index 6f8a051217..21cb3b3c91 100644 --- a/ios/Features/Perpetuals/Tests/PerpetualPositionViewModelTests.swift +++ b/ios/Features/Perpetuals/Tests/PerpetualPositionViewModelTests.swift @@ -1,33 +1,32 @@ // Copyright (c). Gem Wallet. All rights reserved. -import Testing -import Primitives -import Style import Components -import PerpetualsTestKit @testable import Perpetuals +import PerpetualsTestKit +import Primitives +import Style +import Testing struct PerpetualPositionViewModelTests { - @Test func leverageText() { #expect(createPositionViewModel(.mock(leverage: 10)).leverageText == "10x") } - + @Test func directionText() { #expect(createPositionViewModel(.mock(size: 100)).directionText == "Long") #expect(createPositionViewModel(.mock(size: -100)).directionText == "Short") } - + @Test func positionTypeText() { #expect(createPositionViewModel(.mock(size: 100, leverage: 5)).positionTypeText == "LONG 5x") } - + @Test func marginField() { - #expect(createPositionViewModel(.mock(marginAmount: 1000)).marginField.value.text == "$1,000.00 (isolated)") + #expect(createPositionViewModel(.mock(marginAmount: 1000)).marginField.value.text == "$1,000.00 (Isolated)") } @Test @@ -52,20 +51,20 @@ struct PerpetualPositionViewModelTests { #expect(createPositionViewModel(.mock(liquidationPrice: 0)).liquidationPriceField == nil) #expect(createPositionViewModel(.mock(liquidationPrice: nil)).liquidationPriceField == nil) } - + @Test func positionTypeColor() { #expect(createPositionViewModel(.mock(direction: .short)).positionTypeColor == Colors.red) #expect(createPositionViewModel(.mock(direction: .long)).positionTypeColor == Colors.green) } - + // @Test // func liquidationPriceColor() { // // Long position: entry $2.00, liquidation $1.50 // #expect(createPositionViewModel(.mock(entryPrice: 2.00, currencyPrice: 2.00, liquidationPrice: 1.50)).liquidationPriceColor == Colors.secondaryText) // #expect(createPositionViewModel(.mock(entryPrice: 2.00, currencyPrice: 1.75, liquidationPrice: 1.50)).liquidationPriceColor == Colors.orange) // #expect(createPositionViewModel(.mock(entryPrice: 2.00, currencyPrice: 1.599, liquidationPrice: 1.50)).liquidationPriceColor == Colors.red) -// +// // // Short position: entry $1.27, liquidation $1.91 // #expect(createPositionViewModel(.mock(entryPrice: 1.27, currencyPrice: 1.27, liquidationPrice: 1.91)).liquidationPriceColor == Colors.secondaryText) // #expect(createPositionViewModel(.mock(entryPrice: 1.27, currencyPrice: 1.59, liquidationPrice: 1.91)).liquidationPriceColor == Colors.orange) diff --git a/ios/Features/Swap/Sources/ViewModels/SwapSceneViewModel.swift b/ios/Features/Swap/Sources/ViewModels/SwapSceneViewModel.swift index 2adbb5710c..2c7a9f8922 100644 --- a/ios/Features/Swap/Sources/ViewModels/SwapSceneViewModel.swift +++ b/ios/Features/Swap/Sources/ViewModels/SwapSceneViewModel.swift @@ -212,7 +212,6 @@ extension SwapSceneViewModel { isPresentingInfoSheet = .info(.priceImpact) } - func onSelectAssetPay() { isPresentingInfoSheet = .selectAsset(.pay) } diff --git a/ios/Features/Transfer/Sources/ViewModels/ConfirmTransferSceneViewModel.swift b/ios/Features/Transfer/Sources/ViewModels/ConfirmTransferSceneViewModel.swift index 0b0ccb7d38..15a8aee322 100644 --- a/ios/Features/Transfer/Sources/ViewModels/ConfirmTransferSceneViewModel.swift +++ b/ios/Features/Transfer/Sources/ViewModels/ConfirmTransferSceneViewModel.swift @@ -133,7 +133,6 @@ public final class ConfirmTransferSceneViewModel { ConfirmDetailsViewModel(type: transferData.type, metadata: metadata) } - private var headerType: TransactionHeaderType { if let headerData = simulationState.headerData { return .assetValue(headerData) diff --git a/ios/Features/Transfer/Sources/ViewModels/ReceiveViewModel.swift b/ios/Features/Transfer/Sources/ViewModels/ReceiveViewModel.swift index 5b07cb8ee9..b97f0abbec 100644 --- a/ios/Features/Transfer/Sources/ViewModels/ReceiveViewModel.swift +++ b/ios/Features/Transfer/Sources/ViewModels/ReceiveViewModel.swift @@ -52,7 +52,6 @@ public final class ReceiveViewModel: Sendable { Localized.Common.copy } - var warningMessage: String { [Localized.Receive.warning(assetModel.symbol.boldMarkdown(), assetModel.networkFullName.boldMarkdown()), memoWarningText] .compactMap { $0 } diff --git a/ios/Features/WalletConnector/Sources/WalletConnector/ViewModels/SignMessageSceneViewModel.swift b/ios/Features/WalletConnector/Sources/WalletConnector/ViewModels/SignMessageSceneViewModel.swift index e8d138bf82..90f6913c16 100644 --- a/ios/Features/WalletConnector/Sources/WalletConnector/ViewModels/SignMessageSceneViewModel.swift +++ b/ios/Features/WalletConnector/Sources/WalletConnector/ViewModels/SignMessageSceneViewModel.swift @@ -112,7 +112,6 @@ public final class SignMessageSceneViewModel { TextMessageViewModel(message: plainMessage) } - public var simulationWarnings: [SimulationWarning] { payload.simulation.warnings } diff --git a/ios/Features/WalletConnector/Tests/WalletConnectorTests/WalletConnectorPresenterTests.swift b/ios/Features/WalletConnector/Tests/WalletConnectorTests/WalletConnectorPresenterTests.swift index 4c61254298..c2560564ed 100644 --- a/ios/Features/WalletConnector/Tests/WalletConnectorTests/WalletConnectorPresenterTests.swift +++ b/ios/Features/WalletConnector/Tests/WalletConnectorTests/WalletConnectorPresenterTests.swift @@ -67,7 +67,6 @@ struct WalletConnectorPresenterTests { } } - private func presentedSheet(from presenter: WalletConnectorPresenter) async -> WalletConnectorSheetType? { for _ in 0..<10 { if let sheet = await MainActor.run(body: { presenter.isPresentingSheet }) { diff --git a/ios/Gem/Services/ServicesFactory.swift b/ios/Gem/Services/ServicesFactory.swift index d5160e2a24..2d11f5c21a 100644 --- a/ios/Gem/Services/ServicesFactory.swift +++ b/ios/Gem/Services/ServicesFactory.swift @@ -48,7 +48,6 @@ import ContactService import FiatService import WebSocketClient - struct ServicesFactory { func makeServices(storages: AppResolver.Storages, navigation: NavigationStateManager) -> AppResolver.Services { let storeManager = StoreManager(db: storages.db) diff --git a/ios/Packages/FeatureServices/AssetsService/AssetsService.swift b/ios/Packages/FeatureServices/AssetsService/AssetsService.swift index 0691150318..afb9b610b5 100644 --- a/ios/Packages/FeatureServices/AssetsService/AssetsService.swift +++ b/ios/Packages/FeatureServices/AssetsService/AssetsService.swift @@ -168,9 +168,6 @@ public final class AssetsService: Sendable { } } - - - public func setSwappableAssets(for chains: [Chain]) throws { try assetStore.setAssetIsSwappable(for: chains.map { $0.id }, value: true) } diff --git a/ios/Packages/Keystore/Tests/LocalKeystoreTests.swift b/ios/Packages/Keystore/Tests/LocalKeystoreTests.swift index 5455c4945b..ab2cb5e3c2 100644 --- a/ios/Packages/Keystore/Tests/LocalKeystoreTests.swift +++ b/ios/Packages/Keystore/Tests/LocalKeystoreTests.swift @@ -134,7 +134,6 @@ struct LocalKeystoreTests { #expect(encoded == "5ZRaXVuDePowJjZmKaMjfcuqBVZet6e8QiCjTkGXBn7xhCvoEswUKXiGs2wmPxcqTfJUH28eCC91J1vLSjANNM9v") } - @Test func deriveAddress() async { await #expect(throws: Never.self) { diff --git a/ios/Packages/Keystore/Tests/WalletKeyStoreTests.swift b/ios/Packages/Keystore/Tests/WalletKeyStoreTests.swift index 68f3c61311..86c0d5a738 100644 --- a/ios/Packages/Keystore/Tests/WalletKeyStoreTests.swift +++ b/ios/Packages/Keystore/Tests/WalletKeyStoreTests.swift @@ -81,7 +81,6 @@ final class WalletKeyStoreTests { #expect(address == "GADB4BDKTOE36L6QN2JLIPNNJ7EZPSY5BIVKWXLWYZLIPXNQWIRQQZKT") } - @Test func addImportWallet() async throws { let store = WalletKeyStore.mock() let newWallet = try store.importWallet( diff --git a/ios/Packages/Primitives/Sources/Extensions/PerpetualMarginType+Primitives.swift b/ios/Packages/PrimitivesComponents/Sources/Extensions/PerpetualMarginType+PrimitivesComponents.swift similarity index 67% rename from ios/Packages/Primitives/Sources/Extensions/PerpetualMarginType+Primitives.swift rename to ios/Packages/PrimitivesComponents/Sources/Extensions/PerpetualMarginType+PrimitivesComponents.swift index 274b01af78..e05e9678ac 100644 --- a/ios/Packages/Primitives/Sources/Extensions/PerpetualMarginType+Primitives.swift +++ b/ios/Packages/PrimitivesComponents/Sources/Extensions/PerpetualMarginType+PrimitivesComponents.swift @@ -1,12 +1,13 @@ // Copyright (c). Gem Wallet. All rights reserved. import Foundation +import Primitives -extension PerpetualMarginType { - public var displayText: String { +public extension PerpetualMarginType { + var title: String { switch self { case .cross: "Cross" case .isolated: "Isolated" } } -} \ No newline at end of file +} diff --git a/ios/justfile b/ios/justfile index de3f81f19e..979475402e 100644 --- a/ios/justfile +++ b/ios/justfile @@ -114,8 +114,9 @@ test-all: (_test "test") test-without-building: (_test "test-without-building") +# Example: just test # Example: just test PrimitivesTests -test TARGET: (_test "test" TARGET) +test TARGET="": (_test "test" TARGET) _test-ui action: @set -o pipefail && xcodebuild -project Gem.xcodeproj \ From f94d81ef2bd408e6ff8c3070f8ab88fb5cd5e98f Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:42:00 +0000 Subject: [PATCH 06/14] Update android-ci.yml --- .github/workflows/android-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 992b97778e..44e2fc31ab 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -24,6 +24,8 @@ concurrency: env: UNIT_TESTS: "true" + JAVA_HOME: /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home + ANDROID_HOME: /opt/homebrew/share/android-commandlinetools jobs: unit_test: From 209640942d5f8ed53efcd67568bf7384c7844df2 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:46:41 +0000 Subject: [PATCH 07/14] Update android-ci.yml --- .github/workflows/android-ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 44e2fc31ab..bd03ee454a 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -80,6 +80,10 @@ jobs: # with: # cache-read-only: ${{ github.ref != 'refs/heads/main' }} + - name: Bootstrap + working-directory: android + run: just bootstrap + - name: Build tests working-directory: android run: just build-test From 5d0acfb9a1f139c95d762f81c9d24fb9512ae4a7 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:04:45 +0000 Subject: [PATCH 08/14] Run unit tests in Android CI Update CI to run unit tests instead of the previous test target and add a corresponding justfile task. The workflow step name and command were changed from `Run tests` / `just test` to `Run unit tests` / `just unit-test`, and a new `unit-test` target was added to android/justfile that invokes `./gradlew testGoogleDebugUnitTest` so CI runs JVM unit tests rather than connected instrumentation tests. --- .github/workflows/android-ci.yml | 4 ++-- android/justfile | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index bd03ee454a..99b16d8052 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -91,9 +91,9 @@ jobs: GPR_USERNAME: ${{ github.actor }} GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Run tests + - name: Run unit tests working-directory: android - run: just test + run: just unit-test env: GPR_USERNAME: ${{ github.actor }} GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/android/justfile b/android/justfile index 19405e53b9..d43ceb2e7d 100644 --- a/android/justfile +++ b/android/justfile @@ -27,6 +27,9 @@ build-test: test: @./gradlew connectedGoogleDebugAndroidTest +unit-test: + @./gradlew testGoogleDebugUnitTest + mobsfscan: @command -v uv >/dev/null || { \ echo "uv is not installed. Install it via 'curl -LsSf https://astral.sh/uv/install.sh | sh'."; \ From cd74748c3778d0fe22e7c94bb47c9c07d9e3d738 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:51:47 +0000 Subject: [PATCH 09/14] CI: add emulator lifecycle and instrumented tests Simplify and extend the Android CI workflow to run instrumented tests on an emulator. Removed many commented setup/cache steps (assumes runner provides JDK/SDK/NDK/just), renamed the build step to run `just build`, and added steps to start an emulator, build instrumented tests (`just build-test`), run them (`just test`), and always stop the emulator. Added `start-emulator` and `stop-emulator` justfile targets that create a `ci-emulator` AVD if missing, boot and wait for it, disable animations for stability, and teardown the emulator at the end. GPR credential env handling is kept for the test/build steps. --- .github/workflows/android-ci.yml | 84 +++++++++----------------------- android/justfile | 18 +++++++ 2 files changed, 42 insertions(+), 60 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 99b16d8052..8fafd2c419 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -38,55 +38,13 @@ jobs: with: submodules: recursive - # Re-enable these setup steps if the self-hosted runner stops providing - # Java, Android SDK/NDK, and `just` out of the box. - # - name: Set up JDK 17 - # uses: actions/setup-java@v3 - # with: - # java-version: "17" - # distribution: "temurin" - # - # - name: Setup Android SDK - # uses: android-actions/setup-android@v3 - # - # - name: Install just - # uses: extractions/setup-just@v2 - # - # - name: Cache NDK - # uses: actions/cache@v4 - # with: - # path: ${{ env.ANDROID_HOME }}/ndk/28.1.13356709 - # key: ndk-28.1.13356709-${{ runner.os }} - # - # - name: Setup NDK - # working-directory: android - # run: just install-ndk - - # Re-enable this on ephemeral GitHub-hosted runners where ~/.android/avd - # does not persist between jobs and emulator provisioning cost matters. - # - name: Cache AVD - # uses: actions/cache@v4 - # id: avd-cache - # with: - # path: | - # ~/.android/avd/* - # ~/.android/adb* - # key: avd-35-google-apis-arm64-v8a-${{ runner.os }}-${{ runner.arch }} - - # Re-enable Gradle cache if CI time becomes an issue and the self-hosted - # runner is no longer reusing local Gradle state effectively. - # - name: Gradle cache - # uses: gradle/actions/setup-gradle@v3 - # with: - # cache-read-only: ${{ github.ref != 'refs/heads/main' }} - - name: Bootstrap working-directory: android run: just bootstrap - - name: Build tests + - name: Build working-directory: android - run: just build-test + run: just build env: GPR_USERNAME: ${{ github.actor }} GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -98,19 +56,25 @@ jobs: GPR_USERNAME: ${{ github.actor }} GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Re-enable this on GitHub-hosted runners, or when the self-hosted runner - # does not keep a booted emulator/device available for connected tests. - # - name: Run tests - # uses: reactivecircus/android-emulator-runner@v2 - # with: - # api-level: 35 - # target: google_apis - # arch: arm64-v8a - # force-avd-creation: false - # disk-size: 2048M - # emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - # disable-animations: true - # script: cd android && just test - # env: - # GPR_USERNAME: ${{ github.actor }} - # GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Start emulator + working-directory: android + run: just start-emulator + + - name: Build instrumented tests + working-directory: android + run: just build-test + env: + GPR_USERNAME: ${{ github.actor }} + GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run instrumented tests + working-directory: android + run: just test + env: + GPR_USERNAME: ${{ github.actor }} + GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Stop emulator + if: always() + working-directory: android + run: just stop-emulator diff --git a/android/justfile b/android/justfile index d43ceb2e7d..3b40d808b1 100644 --- a/android/justfile +++ b/android/justfile @@ -30,6 +30,24 @@ test: unit-test: @./gradlew testGoogleDebugUnitTest +start-emulator: + #!/usr/bin/env bash + set -euo pipefail + AVD_NAME="ci-emulator" + if ! avdmanager list avd -c | grep -q "$AVD_NAME"; then + echo "no" | avdmanager create avd -n "$AVD_NAME" -k "system-images;android-35;google_apis;arm64-v8a" --device "pixel_6" + fi + nohup emulator -avd "$AVD_NAME" -no-snapshot-save -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect > /dev/null 2>&1 & + adb wait-for-device + adb shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done' + adb shell settings put global window_animation_scale 0 + adb shell settings put global transition_animation_scale 0 + adb shell settings put global animator_duration_scale 0 + echo "Emulator ready" + +stop-emulator: + adb emu kill || true + mobsfscan: @command -v uv >/dev/null || { \ echo "uv is not installed. Install it via 'curl -LsSf https://astral.sh/uv/install.sh | sh'."; \ From 9de934733775777a0ba47e43a3384a93843d3881 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:54:02 +0000 Subject: [PATCH 10/14] Relax CI triggers; update currency tests Remove path filters from Android and iOS workflow triggers so the CI runs on all pushes and PRs to main. Update CurrencyFormatterTests.swift: reorder imports, rename several test methods (removed leading "test"), replace large numeric literals with underscore-separated forms for readability, adjust expected abbreviated-format strings (UK suffix changes) and minor whitespace/style tweaks. --- .github/workflows/android-ci.yml | 12 ----- .github/workflows/ios-ci.yml | 12 ----- .../CurrencyFormatterTests.swift | 49 +++++++++---------- 3 files changed, 24 insertions(+), 49 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index 8fafd2c419..ee6cb8c429 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -3,20 +3,8 @@ name: Android Tests on: push: branches: ["main"] - paths: - - "android/**" - - "core" - - "core/**" - - ".gitmodules" - - ".github/workflows/android-ci.yml" pull_request: branches: ["main"] - paths: - - "android/**" - - "core" - - "core/**" - - ".gitmodules" - - ".github/workflows/android-ci.yml" concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 4f5c1ab99b..273ebff651 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -3,20 +3,8 @@ name: iOS Unit Tests on: push: branches: ["main"] - paths: - - "ios/**" - - "core" - - "core/**" - - ".gitmodules" - - ".github/workflows/ios-ci.yml" pull_request: branches: ["main"] - paths: - - "ios/**" - - "core" - - "core/**" - - ".gitmodules" - - ".github/workflows/ios-ci.yml" jobs: unit_test: diff --git a/ios/Packages/Formatters/Tests/FormattersTests/CurrencyFormatterTests.swift b/ios/Packages/Formatters/Tests/FormattersTests/CurrencyFormatterTests.swift index 6065f2bd8b..4fbf0b5f10 100644 --- a/ios/Packages/Formatters/Tests/FormattersTests/CurrencyFormatterTests.swift +++ b/ios/Packages/Formatters/Tests/FormattersTests/CurrencyFormatterTests.swift @@ -1,12 +1,11 @@ // Copyright (c). Gem Wallet. All rights reserved. -import Testing +import Formatters import Foundation @testable import Primitives -import Formatters +import Testing final class CurrencyFormatterTests { - let currencyFormatterUS = CurrencyFormatter(locale: .US, currencyCode: Currency.usd.rawValue) let percentFormatterUS = CurrencyFormatter(type: .percent, locale: .US, currencyCode: Currency.usd.rawValue) @@ -25,13 +24,13 @@ final class CurrencyFormatterTests { #expect(currencyFormatterUS.string(0) == "$0.00") #expect(currencyFormatterUS.string(11.12) == "$11.12") #expect(currencyFormatterUS.string(11) == "$11.00") - #expect(currencyFormatterUS.string(12000123) == "$12,000,123.00") + #expect(currencyFormatterUS.string(12_000_123) == "$12,000,123.00") #expect(currencyFormatterUS.string(0.0000000002) == "$0.0000000002") #expect(currencyFormatterUS.string(0.0000000000001) == "$0.00") } @Test - func testSmallValue() { + func smallValue() { #expect(currencyFormatterUS.string(0.10) == "$0.1") #expect(currencyFormatterUS.string(0.11) == "$0.11") #expect(currencyFormatterUS.string(0.2) == "$0.2") @@ -49,17 +48,17 @@ final class CurrencyFormatterTests { } @Test - func testCurrencyIncludeSign() { + func currencyIncludeSign() { #expect(currencyFormatterUS.string(0) == "$0.00") #expect(currencyFormatterUS.string(-1.2) == "-$1.20") } @Test - func testCurrencyGBPLocale() { + func currencyGBPLocale() { #expect(currencyFormatterUK.string(0.0002) == "£0.0002") #expect(currencyFormatterUK.string(11.12) == "£11.12") #expect(currencyFormatterUK.string(11) == "£11.00") - #expect(currencyFormatterUK.string(12000123) == "£12,000,123.00") + #expect(currencyFormatterUK.string(12_000_123) == "£12,000,123.00") } @Test @@ -67,12 +66,12 @@ final class CurrencyFormatterTests { #expect(percentFormatterUS.string(-1.23) == "-1.23%") #expect(percentFormatterUS.string(11.12) == "+11.12%") #expect(percentFormatterUS.string(11) == "+11.00%") - #expect(percentFormatterUS.string(12000123) == "+12,000,123.00%") + #expect(percentFormatterUS.string(12_000_123) == "+12,000,123.00%") #expect(percentFormatterUK.string(-1.23) == "-1.23%") #expect(percentFormatterUK.string(11.12) == "+11.12%") #expect(percentFormatterUK.string(11) == "+11.00%") - #expect(percentFormatterUK.string(12000123) == "+12,000,123.00%") + #expect(percentFormatterUK.string(12_000_123) == "+12,000,123.00%") } @Test @@ -80,17 +79,17 @@ final class CurrencyFormatterTests { #expect(percentSignLess.string(-1.23) == "1.23%") #expect(percentSignLess.string(11.12) == "11.12%") #expect(percentSignLess.string(11) == "11.00%") - #expect(percentSignLess.string(12000123) == "12,000,123.00%") + #expect(percentSignLess.string(12_000_123) == "12,000,123.00%") } @Test - func testDecimal() { + func decimal() { #expect(cryptoFormatter.string(double: 0.12) == "0.12") #expect(cryptoFormatter.string(double: 0.00012) == "0.00012") #expect(cryptoFormatter.string(double: 11.12) == "11.12") #expect(cryptoFormatter.string(double: 11) == "11.00") - #expect(cryptoFormatter.string(double: 12000123) == "12,000,123.00") + #expect(cryptoFormatter.string(double: 12_000_123) == "12,000,123.00") #expect(cryptoFormatterUA.string(double: 0.003011) == cryptoFormatterUA.string(double: 0.003011).trimmingCharacters(in: .whitespaces)) #expect(cryptoFormatterUA.string(double: 92500) == "92 500,00") @@ -98,7 +97,7 @@ final class CurrencyFormatterTests { } @Test - func testStringDecimalWithSymbolVariants() { + func stringDecimalWithSymbolVariants() { #expect(cryptoFormatter.string(double: 1234.56, symbol: "BTC") == "1,234.56 BTC") #expect(cryptoFormatter.string(double: 0.0001234, symbol: "BTC") == "0.0001234 BTC") } @@ -107,30 +106,30 @@ final class CurrencyFormatterTests { func testAbbreviated() { #expect(abbreviatedFormatterUS.string(0) == "$0.00") #expect(abbreviatedFormatterUS.string(12) == "$12.00") - #expect(abbreviatedFormatterUS.string(1_234) == "$1,234.00") + #expect(abbreviatedFormatterUS.string(1234) == "$1,234.00") #expect(abbreviatedFormatterUS.string(100_000) == "$100K") #expect(abbreviatedFormatterUS.string(-1234) == "-$1,234.00") #expect(abbreviatedFormatterUS.string(-5_600_000) == "-$5.6M") - #expect(abbreviatedFormatterUK.string(123_456) == "£123.46K") - #expect(abbreviatedFormatterUK.string(5_000_000) == "£5M") - #expect(abbreviatedFormatterUK.string(7_890_000_000) == "£7.89B") - #expect(abbreviatedFormatterUK.string(1_200_000_000_000) == "£1.2T") - #expect(abbreviatedFormatterUK.string(-9_999_999_999) == "-£10B") + #expect(abbreviatedFormatterUK.string(123_456) == "£123.46k") + #expect(abbreviatedFormatterUK.string(5_000_000) == "£5m") + #expect(abbreviatedFormatterUK.string(7_890_000_000) == "£7.89bn") + #expect(abbreviatedFormatterUK.string(1_200_000_000_000) == "£1.2tn") + #expect(abbreviatedFormatterUK.string(-9_999_999_999) == "-£10bn") } - + @Test func abbreviatedStringSymbol() { #expect(abbreviatedFormatterUS.string(double: 0, symbol: "BTC") == "0.00 BTC") #expect(abbreviatedFormatterUS.string(double: 12, symbol: "BTC") == "12.00 BTC") - #expect(abbreviatedFormatterUS.string(double: 1_234, symbol: "BTC") == "1,234.00 BTC") + #expect(abbreviatedFormatterUS.string(double: 1234, symbol: "BTC") == "1,234.00 BTC") #expect(abbreviatedFormatterUS.string(double: 5_000_000, symbol: "BTC") == "5M BTC") #expect(abbreviatedFormatterUS.string(double: 7_890_000_000, symbol: "BTC") == "7.89B BTC") - - #expect(abbreviatedFormatterUS.string(double: 1_234) == "1,234.00") + + #expect(abbreviatedFormatterUS.string(double: 1234) == "1,234.00") #expect(abbreviatedFormatterUS.string(double: 5_000_000) == "5M") } - + @Test func testNormalizedDouble() { // Basic formatting From cf864adf9a05c1a2fc8a3480ba166aab9f2649f2 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:02:17 +0000 Subject: [PATCH 11/14] Update actions/checkout to v5 in workflows Bump actions/checkout from v4 to v5 across CI workflows to use the newer checkout action. Updated files: .github/workflows/android-ci.yml, android-docker.yml, android-publish-base-image.yml, android-verify-apk.yml, ios-ci.yml, ios-ui-tests.yml. Preserves existing options such as submodules: recursive. --- .github/workflows/android-ci.yml | 2 +- .github/workflows/android-docker.yml | 2 +- .github/workflows/android-publish-base-image.yml | 2 +- .github/workflows/android-verify-apk.yml | 2 +- .github/workflows/ios-ci.yml | 2 +- .github/workflows/ios-ui-tests.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index ee6cb8c429..ca9e82341b 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/android-docker.yml b/.github/workflows/android-docker.yml index 79f8a2789e..63c9bdf473 100644 --- a/.github/workflows/android-docker.yml +++ b/.github/workflows/android-docker.yml @@ -38,7 +38,7 @@ jobs: packages: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/android-publish-base-image.yml b/.github/workflows/android-publish-base-image.yml index cbe7156e02..60b024ab4e 100644 --- a/.github/workflows/android-publish-base-image.yml +++ b/.github/workflows/android-publish-base-image.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Read base image tag id: base_tag diff --git a/.github/workflows/android-verify-apk.yml b/.github/workflows/android-verify-apk.yml index e432c42312..a1af1cc3e3 100644 --- a/.github/workflows/android-verify-apk.yml +++ b/.github/workflows/android-verify-apk.yml @@ -45,7 +45,7 @@ jobs: VERIFY_REPO_URL: https://github.com/${{ github.repository }}.git steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 273ebff651..1ac5b3a8e8 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive diff --git a/.github/workflows/ios-ui-tests.yml b/.github/workflows/ios-ui-tests.yml index eb42b11963..aca08048a1 100644 --- a/.github/workflows/ios-ui-tests.yml +++ b/.github/workflows/ios-ui-tests.yml @@ -43,7 +43,7 @@ jobs: echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: recursive From 78e3ea7ae1ea28d19238fa4596263bab0d108fac Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Sat, 28 Mar 2026 00:04:06 +0000 Subject: [PATCH 12/14] Add Android emulator script and CI checks Remove hardcoded JAVA_HOME and ANDROID_HOME from the GitHub Actions workflow and add a runner toolchain validation step that verifies JAVA_HOME and ANDROID_HOME and exposes ANDROID_SDK_ROOT. Add android/scripts/android_emulator.sh to install SDK components, create/start an AVD and shut it down robustly (supports arch detection, local.properties fallback, configurable env vars like ANDROID_API, ANDROID_DEVICE, ANDROID_EMULATOR_BOOT_TIMEOUT, and boot timeout handling). Update android/justfile to call the new script for start-emulator and stop-emulator. These changes improve CI portability and emulator startup reliability. --- .github/workflows/android-ci.yml | 9 ++- android/justfile | 16 +---- android/scripts/android_emulator.sh | 101 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 android/scripts/android_emulator.sh diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index ca9e82341b..f41f1c6a63 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -12,8 +12,6 @@ concurrency: env: UNIT_TESTS: "true" - JAVA_HOME: /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home - ANDROID_HOME: /opt/homebrew/share/android-commandlinetools jobs: unit_test: @@ -26,6 +24,13 @@ jobs: with: submodules: recursive + - name: Validate Runner Toolchain + run: | + [ -n "${JAVA_HOME:-}" ] || { echo "JAVA_HOME is not set on runner"; exit 1; } + [ -x "${JAVA_HOME}/bin/java" ] || { echo "JAVA_HOME is invalid: ${JAVA_HOME}"; exit 1; } + [ -n "${ANDROID_HOME:-}" ] || { echo "ANDROID_HOME is not set on runner"; exit 1; } + [ -d "${ANDROID_HOME}" ] || { echo "ANDROID_HOME does not exist: ${ANDROID_HOME}"; exit 1; } + - name: Bootstrap working-directory: android run: just bootstrap diff --git a/android/justfile b/android/justfile index 3b40d808b1..1073f95653 100644 --- a/android/justfile +++ b/android/justfile @@ -31,22 +31,10 @@ unit-test: @./gradlew testGoogleDebugUnitTest start-emulator: - #!/usr/bin/env bash - set -euo pipefail - AVD_NAME="ci-emulator" - if ! avdmanager list avd -c | grep -q "$AVD_NAME"; then - echo "no" | avdmanager create avd -n "$AVD_NAME" -k "system-images;android-35;google_apis;arm64-v8a" --device "pixel_6" - fi - nohup emulator -avd "$AVD_NAME" -no-snapshot-save -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect > /dev/null 2>&1 & - adb wait-for-device - adb shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done' - adb shell settings put global window_animation_scale 0 - adb shell settings put global transition_animation_scale 0 - adb shell settings put global animator_duration_scale 0 - echo "Emulator ready" + @bash ./scripts/android_emulator.sh setup stop-emulator: - adb emu kill || true + @bash ./scripts/android_emulator.sh shutdown mobsfscan: @command -v uv >/dev/null || { \ diff --git a/android/scripts/android_emulator.sh b/android/scripts/android_emulator.sh new file mode 100644 index 0000000000..551412c135 --- /dev/null +++ b/android/scripts/android_emulator.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +set -euo pipefail + +SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}" +if [[ -z "${SDK_ROOT}" ]] && [[ -f local.properties ]]; then + SDK_ROOT="$(sed -n 's/^sdk\.dir=//p' local.properties | head -n 1)" +fi +[[ -n "${SDK_ROOT}" ]] || { echo "ANDROID_HOME is not set"; exit 1; } + +SDKMANAGER="${SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager" +AVDMANAGER="${SDK_ROOT}/cmdline-tools/latest/bin/avdmanager" +EMULATOR="${SDK_ROOT}/emulator/emulator" +ADB="${SDK_ROOT}/platform-tools/adb" + +AVD_NAME="${ANDROID_DEVICE:-ci-emulator}" +API="${ANDROID_API:-35}" +DEVICE="${ANDROID_EMULATOR_DEVICE:-pixel_6}" +BOOT_TIMEOUT_SECONDS="${ANDROID_EMULATOR_BOOT_TIMEOUT:-120}" + +if [[ "$(uname -m)" == "arm64" ]]; then + ARCH="arm64-v8a" +else + ARCH="x86_64" +fi + +IMAGE="system-images;android-${API};google_apis;${ARCH}" +IMAGE_DIR="${SDK_ROOT}/system-images/android-${API}/google_apis/${ARCH}" + +setup() { + [[ -x "${SDKMANAGER}" && -x "${AVDMANAGER}" ]] || { + echo "Android command-line tools are missing under ${SDK_ROOT}" >&2 + exit 1 + } + + if [[ ! -x "${EMULATOR}" || ! -x "${ADB}" || ! -d "${IMAGE_DIR}" ]]; then + yes | "${SDKMANAGER}" --sdk_root="${SDK_ROOT}" --install \ + "platform-tools" \ + "emulator" \ + "${IMAGE}" + fi + + "${ADB}" emu kill 2>/dev/null || true + sleep 2 + + if ! "${EMULATOR}" -list-avds | grep -Fxq "${AVD_NAME}"; then + echo "no" | "${AVDMANAGER}" create avd \ + -n "${AVD_NAME}" \ + -k "${IMAGE}" \ + --device "${DEVICE}" \ + --force + fi + + EMULATOR_LOG="${TMPDIR:-/tmp}/${AVD_NAME}.log" + nohup "${EMULATOR}" -avd "${AVD_NAME}" -no-window -no-audio -no-boot-anim -no-snapshot-save -gpu swiftshader_indirect > "${EMULATOR_LOG}" 2>&1 & + EMU_PID=$! + sleep 5 + if ! kill -0 "${EMU_PID}" 2>/dev/null; then + echo "Emulator failed to start" >&2 + cat "${EMULATOR_LOG}" >&2 || true + exit 1 + fi + + "${ADB}" wait-for-device + boot_waited=0 + for boot_waited in $(seq 1 "${BOOT_TIMEOUT_SECONDS}"); do + if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" == "1" ]]; then + break + fi + if ! kill -0 "${EMU_PID}" 2>/dev/null; then + echo "Emulator failed during boot after ${boot_waited}s" >&2 + cat "${EMULATOR_LOG}" >&2 || true + exit 1 + fi + sleep 1 + done + if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]]; then + echo "Emulator failed to boot within ${BOOT_TIMEOUT_SECONDS}s" >&2 + cat "${EMULATOR_LOG}" >&2 || true + exit 1 + fi + if ! "${ADB}" devices | grep -q '^emulator-'; then + echo "Emulator is not visible to adb" >&2 + cat "${EMULATOR_LOG}" >&2 || true + exit 1 + fi + "${ADB}" shell settings put global window_animation_scale 0 + "${ADB}" shell settings put global transition_animation_scale 0 + "${ADB}" shell settings put global animator_duration_scale 0 + echo "Emulator ${AVD_NAME} ready after ${boot_waited}s" +} + +shutdown() { + [[ -x "${ADB}" ]] || exit 0 + "${ADB}" emu kill >/dev/null 2>&1 || true +} + +case "${1:-}" in + setup) setup ;; + shutdown) shutdown ;; + *) echo "Usage: $0 {setup|shutdown}" >&2; exit 1 ;; +esac From a89b5472e9fe318699447f38e41b8eb3fbd4ba12 Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Sat, 28 Mar 2026 00:48:19 +0000 Subject: [PATCH 13/14] CI: Add Android emulator provisioning Add emulator provisioning to CI and make Android SDK handling more robust. - Workflow: add Android env vars (ANDROID_COMPILE_SDK, ANDROID_BUILD_TOOLS, ANDROID_API, ANDROID_AVD_NAME, ANDROID_EMULATOR_DEVICE), resolve brew/JDK/Android command-line tools, export JAVA_HOME and ANDROID_HOME, and run a new provision step before build. - justfile: add provision-emulator task that calls the emulator script. - android_emulator.sh: refactor and extend script to support a new 'provision' action, parameterize compile SDK, build-tools, API, AVD name, device and architecture, add helper functions (require_tool, install_sdk_packages, create_avd_if_missing), improve error messages, and add usage for provision/setup/shutdown. These changes ensure the CI can install required SDK components and create/start an AVD reliably before running Android builds/tests. --- .github/workflows/android-ci.yml | 23 +++- android/justfile | 3 + android/scripts/android_emulator.sh | 201 +++++++++++++++++++++------- 3 files changed, 177 insertions(+), 50 deletions(-) diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml index f41f1c6a63..0c3c0467b1 100644 --- a/.github/workflows/android-ci.yml +++ b/.github/workflows/android-ci.yml @@ -12,6 +12,10 @@ concurrency: env: UNIT_TESTS: "true" + ANDROID_COMPILE_SDK: "36" + ANDROID_API: "35" + ANDROID_AVD_NAME: "ci-emulator" + ANDROID_EMULATOR_DEVICE: "pixel_6" jobs: unit_test: @@ -24,17 +28,28 @@ jobs: with: submodules: recursive - - name: Validate Runner Toolchain + - name: Resolve Runner Toolchain run: | - [ -n "${JAVA_HOME:-}" ] || { echo "JAVA_HOME is not set on runner"; exit 1; } + JAVA_HOME="$(/usr/libexec/java_home -v 17)" + ANDROID_HOME="/opt/homebrew/share/android-commandlinetools" + [ -x "${JAVA_HOME}/bin/java" ] || { echo "JAVA_HOME is invalid: ${JAVA_HOME}"; exit 1; } - [ -n "${ANDROID_HOME:-}" ] || { echo "ANDROID_HOME is not set on runner"; exit 1; } - [ -d "${ANDROID_HOME}" ] || { echo "ANDROID_HOME does not exist: ${ANDROID_HOME}"; exit 1; } + [ -x "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" ] || { echo "sdkmanager is missing under ${ANDROID_HOME}"; exit 1; } + + { + echo "JAVA_HOME=${JAVA_HOME}" + echo "ANDROID_HOME=${ANDROID_HOME}" + echo "ANDROID_SDK_ROOT=${ANDROID_HOME}" + } >> "${GITHUB_ENV}" - name: Bootstrap working-directory: android run: just bootstrap + - name: Provision Android SDK + working-directory: android + run: just provision-emulator + - name: Build working-directory: android run: just build diff --git a/android/justfile b/android/justfile index 1073f95653..fe140dd2af 100644 --- a/android/justfile +++ b/android/justfile @@ -30,6 +30,9 @@ test: unit-test: @./gradlew testGoogleDebugUnitTest +provision-emulator: + @bash ./scripts/android_emulator.sh provision + start-emulator: @bash ./scripts/android_emulator.sh setup diff --git a/android/scripts/android_emulator.sh b/android/scripts/android_emulator.sh index 551412c135..a65ac8da0e 100644 --- a/android/scripts/android_emulator.sh +++ b/android/scripts/android_emulator.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -euo pipefail -SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}" -if [[ -z "${SDK_ROOT}" ]] && [[ -f local.properties ]]; then +SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-/opt/homebrew/share/android-commandlinetools}}" +if [[ ! -d "${SDK_ROOT}" ]] && [[ -f local.properties ]]; then SDK_ROOT="$(sed -n 's/^sdk\.dir=//p' local.properties | head -n 1)" fi [[ -n "${SDK_ROOT}" ]] || { echo "ANDROID_HOME is not set"; exit 1; } @@ -11,82 +11,188 @@ SDKMANAGER="${SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager" AVDMANAGER="${SDK_ROOT}/cmdline-tools/latest/bin/avdmanager" EMULATOR="${SDK_ROOT}/emulator/emulator" ADB="${SDK_ROOT}/platform-tools/adb" +DEVICES_FILE="${ANDROID_EMULATOR_DEVICES_FILE:-}" -AVD_NAME="${ANDROID_DEVICE:-ci-emulator}" -API="${ANDROID_API:-35}" -DEVICE="${ANDROID_EMULATOR_DEVICE:-pixel_6}" BOOT_TIMEOUT_SECONDS="${ANDROID_EMULATOR_BOOT_TIMEOUT:-120}" +ARCH="arm64-v8a" -if [[ "$(uname -m)" == "arm64" ]]; then - ARCH="arm64-v8a" -else - ARCH="x86_64" -fi - -IMAGE="system-images;android-${API};google_apis;${ARCH}" -IMAGE_DIR="${SDK_ROOT}/system-images/android-${API}/google_apis/${ARCH}" +require_tool() { + [[ -x "${1}" ]] || { + echo "${2} is missing under ${SDK_ROOT}" >&2 + exit 1 + } +} -setup() { - [[ -x "${SDKMANAGER}" && -x "${AVDMANAGER}" ]] || { - echo "Android command-line tools are missing under ${SDK_ROOT}" >&2 +list_devices() { + [[ -f "${DEVICES_FILE}" ]] || { + echo "ANDROID_EMULATOR_DEVICES_FILE is not set or does not exist" >&2 exit 1 } + awk -F: '!/^#/ && NF { print $1 }' "${DEVICES_FILE}" +} - if [[ ! -x "${EMULATOR}" || ! -x "${ADB}" || ! -d "${IMAGE_DIR}" ]]; then - yes | "${SDKMANAGER}" --sdk_root="${SDK_ROOT}" --install \ - "platform-tools" \ - "emulator" \ - "${IMAGE}" +default_avd_name() { + [[ -f "${DEVICES_FILE}" ]] || return 1 + awk -F: '!/^#/ && NF { print $1; exit }' "${DEVICES_FILE}" +} + +api_for_avd_name() { + local avd_name="$1" + [[ -f "${DEVICES_FILE}" ]] || return 1 + awk -F: -v avd_name="${avd_name}" '$1 == avd_name { print $2; exit }' "${DEVICES_FILE}" +} + +resolve_avd_name() { + if [[ -n "${ANDROID_AVD_NAME:-}" ]]; then + echo "${ANDROID_AVD_NAME}" + else + default_avd_name fi +} - "${ADB}" emu kill 2>/dev/null || true - sleep 2 +resolve_api() { + local avd_name="$1" + if [[ -n "${ANDROID_API:-}" ]]; then + echo "${ANDROID_API}" + else + api_for_avd_name "${avd_name}" + fi +} + +install_sdk_packages() { + local image="$1" + local compile_sdk="${2:-}" + local packages=("platform-tools" "emulator" "${image}") + + if [[ -n "${compile_sdk}" ]]; then + packages+=("platforms;android-${compile_sdk}" "build-tools;${compile_sdk}.0.0") + fi + + set +o pipefail + yes | "${SDKMANAGER}" --sdk_root="${SDK_ROOT}" --install "${packages[@]}" + install_status=$? + set -o pipefail + [[ ${install_status} -eq 0 ]] || exit "${install_status}" +} + +create_avd_if_missing() { + local avd_name="$1" + local image="$2" + local device="${3:-}" + + if ! "${EMULATOR}" -list-avds | grep -Fxq "${avd_name}"; then + local create_args=(-n "${avd_name}" -k "${image}" --force) + if [[ -n "${device}" ]]; then + create_args+=(--device "${device}") + fi + echo "no" | "${AVDMANAGER}" create avd "${create_args[@]}" + fi +} + +provision_one() { + local avd_name="$1" + local api="$2" + local compile_sdk="${ANDROID_COMPILE_SDK:-}" + local device="${ANDROID_EMULATOR_DEVICE:-}" + local image="system-images;android-${api};google_apis;${ARCH}" + + require_tool "${SDKMANAGER}" "Android sdkmanager" + require_tool "${AVDMANAGER}" "Android avdmanager" + install_sdk_packages "${image}" "${compile_sdk}" + require_tool "${EMULATOR}" "Android emulator" + require_tool "${ADB}" "Android adb" + create_avd_if_missing "${avd_name}" "${image}" "${device}" +} - if ! "${EMULATOR}" -list-avds | grep -Fxq "${AVD_NAME}"; then - echo "no" | "${AVDMANAGER}" create avd \ - -n "${AVD_NAME}" \ - -k "${IMAGE}" \ - --device "${DEVICE}" \ - --force +provision() { + if [[ -f "${DEVICES_FILE}" && -z "${ANDROID_AVD_NAME:-}" && -z "${ANDROID_API:-}" ]]; then + while IFS=: read -r avd_name api; do + [[ "${avd_name}" =~ ^#.*$ || -z "${avd_name}" ]] && continue + provision_one "${avd_name}" "${api}" + done < "${DEVICES_FILE}" + return fi - EMULATOR_LOG="${TMPDIR:-/tmp}/${AVD_NAME}.log" - nohup "${EMULATOR}" -avd "${AVD_NAME}" -no-window -no-audio -no-boot-anim -no-snapshot-save -gpu swiftshader_indirect > "${EMULATOR_LOG}" 2>&1 & + local avd_name + local api + + avd_name="$(resolve_avd_name)" + [[ -n "${avd_name}" ]] || { echo "ANDROID_AVD_NAME is not set" >&2; exit 1; } + + api="$(resolve_api "${avd_name}")" + [[ -n "${api}" ]] || { echo "ANDROID_API is not set for ${avd_name}" >&2; exit 1; } + + provision_one "${avd_name}" "${api}" +} + +setup() { + local avd_name + local api + local image + local image_dir + local boot_checks=$((BOOT_TIMEOUT_SECONDS / 2)) + + avd_name="$(resolve_avd_name)" + [[ -n "${avd_name}" ]] || { echo "ANDROID_AVD_NAME is not set" >&2; exit 1; } + + api="$(resolve_api "${avd_name}")" + [[ -n "${api}" ]] || { echo "ANDROID_API is not set for ${avd_name}" >&2; exit 1; } + + image="system-images;android-${api};google_apis;${ARCH}" + image_dir="${SDK_ROOT}/system-images/android-${api}/google_apis/${ARCH}" + + require_tool "${EMULATOR}" "Android emulator" + require_tool "${ADB}" "Android adb" + [[ -d "${image_dir}" ]] || { + echo "System image ${image} is missing under ${SDK_ROOT}. Run '$0 provision' first." >&2 + exit 1 + } + "${EMULATOR}" -list-avds | grep -Fxq "${avd_name}" || { + echo "AVD ${avd_name} is missing under ${SDK_ROOT}. Run '$0 provision' first." >&2 + exit 1 + } + + "${ADB}" emu kill 2>/dev/null || true + sleep 2 + + "${EMULATOR}" -avd "${avd_name}" -no-window -no-audio & EMU_PID=$! sleep 5 if ! kill -0 "${EMU_PID}" 2>/dev/null; then echo "Emulator failed to start" >&2 - cat "${EMULATOR_LOG}" >&2 || true exit 1 fi "${ADB}" wait-for-device - boot_waited=0 - for boot_waited in $(seq 1 "${BOOT_TIMEOUT_SECONDS}"); do - if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" == "1" ]]; then + for _ in $(seq 1 "${boot_checks}"); do + if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null)" == "1" ]]; then break fi if ! kill -0 "${EMU_PID}" 2>/dev/null; then - echo "Emulator failed during boot after ${boot_waited}s" >&2 - cat "${EMULATOR_LOG}" >&2 || true + echo "Emulator failed during boot" >&2 exit 1 fi - sleep 1 + sleep 2 done - if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]]; then + if [[ "$("${ADB}" shell getprop sys.boot_completed 2>/dev/null)" != "1" ]]; then echo "Emulator failed to boot within ${BOOT_TIMEOUT_SECONDS}s" >&2 - cat "${EMULATOR_LOG}" >&2 || true exit 1 fi if ! "${ADB}" devices | grep -q '^emulator-'; then echo "Emulator is not visible to adb" >&2 - cat "${EMULATOR_LOG}" >&2 || true exit 1 fi - "${ADB}" shell settings put global window_animation_scale 0 - "${ADB}" shell settings put global transition_animation_scale 0 - "${ADB}" shell settings put global animator_duration_scale 0 - echo "Emulator ${AVD_NAME} ready after ${boot_waited}s" + echo "Emulator ${avd_name} ready" +} + +install() { + local version="$1" + local apk="/tmp/gem_wallet_${version}.apk" + + require_tool "${ADB}" "Android adb" + curl -L -o "${apk}" "https://apk.gemwallet.com/gem_wallet_universal_${version}.apk" + "${ADB}" uninstall com.gemwallet.android 2>/dev/null || true + "${ADB}" install "${apk}" } shutdown() { @@ -95,7 +201,10 @@ shutdown() { } case "${1:-}" in + list) list_devices ;; + provision) provision ;; setup) setup ;; shutdown) shutdown ;; - *) echo "Usage: $0 {setup|shutdown}" >&2; exit 1 ;; + install) install "$2" ;; + *) echo "Usage: $0 {list|provision|setup|shutdown|install VERSION}" >&2; exit 1 ;; esac From 5c835827689fa4c65f0f7c6f837a2a6c959eeccd Mon Sep 17 00:00:00 2001 From: gemcoder21 <104884878+gemcoder21@users.noreply.github.com> Date: Sat, 28 Mar 2026 00:54:50 +0000 Subject: [PATCH 14/14] Update CONTRIBUTING.md --- CONTRIBUTING.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0414e704a6..71b147f99a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,4 @@ This repository is a monorepo: * iOS application code lives under `ios/` * Android application code lives under `android/` -* Shared Rust core is the `core/` submodule - -### Commit Convention -We use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages. Please read the guide through if you aren't familiar with it already. +* Shared Rust core is the `core/` submodule \ No newline at end of file