feat: split share pool into A (100亿) and B (200万) accounts

Backend changes:
- mining-wallet-service: Split SHARE_POOL into SHARE_POOL_A (100亿, for burning)
  and SHARE_POOL_B (200万, for mining distribution)
- Add /pool-accounts/share-pool-balance API endpoint to get total balance
- Update pool initialization logic and seed data
- Fix Kong routing for mining-wallet-service (strip_path: true)
- Fix Kong routing for trading-service (strip_path: true)

Constant updates (100.02亿 = 10,002,000,000):
- mining-service: TOTAL_SHARES
- trading-service: TOTAL_SHARES, trading config defaults
- trading-service seed: initial green points = 5760

Frontend changes:
- Add sharePoolBalanceProvider to fetch pool balance from mining-wallet-service
- Update contribution page to display real-time share pool balance (A + B)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-15 05:55:52 -08:00
parent a1508b208e
commit 40869ef00f
15 changed files with 170 additions and 56 deletions

View File

@ -357,18 +357,19 @@ services:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Mining Wallet Service 2.0 - 挖矿钱包服务 # Mining Wallet Service 2.0 - 挖矿钱包服务
# 前端路径: /api/v2/mining-wallet/... -> 后端路径: /api/v2/...
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
- name: mining-wallet-service - name: mining-wallet-service
url: http://192.168.1.111:3025 url: http://192.168.1.111:3025/api/v2
routes: routes:
- name: mining-wallet-api - name: mining-wallet-api
paths: paths:
- /api/v2/mining-wallet - /api/v2/mining-wallet
strip_path: false strip_path: true
- name: mining-wallet-health - name: mining-wallet-health
paths: paths:
- /api/v2/mining-wallet/health - /api/v2/mining-wallet/health
strip_path: false strip_path: true
# ============================================================================= # =============================================================================
# Plugins - 全局插件配置 # Plugins - 全局插件配置

View File

@ -12,7 +12,7 @@ datasource db {
// 挖矿全局配置 // 挖矿全局配置
model MiningConfig { model MiningConfig {
id String @id @default(uuid()) id String @id @default(uuid())
totalShares Decimal @db.Decimal(30, 8) // 总积分股数量 (100.02B) totalShares Decimal @db.Decimal(30, 8) // 总积分股数量 (100.02亿)
distributionPool Decimal @db.Decimal(30, 8) // 分配池 (200M) distributionPool Decimal @db.Decimal(30, 8) // 分配池 (200M)
remainingDistribution Decimal @db.Decimal(30, 8) // 剩余可分配 remainingDistribution Decimal @db.Decimal(30, 8) // 剩余可分配
halvingPeriodYears Int @default(2) // 减半周期(年) halvingPeriodYears Int @default(2) // 减半周期(年)

View File

@ -19,7 +19,7 @@ async function main() {
const now = new Date(); const now = new Date();
// 常量 // 常量
const TOTAL_SHARES = new Decimal('100020000000'); // 100.02B const TOTAL_SHARES = new Decimal('10002000000'); // 100.02亿
const DISTRIBUTION_POOL = new Decimal('2000000'); // 200万 const DISTRIBUTION_POOL = new Decimal('2000000'); // 200万
const ERA1_DISTRIBUTION = new Decimal('1000000'); // 100万第一个两年 const ERA1_DISTRIBUTION = new Decimal('1000000'); // 100万第一个两年
const BURN_TARGET = new Decimal('10000000000'); // 100亿 const BURN_TARGET = new Decimal('10000000000'); // 100亿

View File

@ -6,8 +6,8 @@ import { Price } from '../value-objects/price.vo';
* *
*/ */
export class MiningCalculatorService { export class MiningCalculatorService {
// 总积分股数量: 100.02B // 总积分股数量: 100.02亿 (SHARE_POOL_A: 100亿 + SHARE_POOL_B: 200万)
static readonly TOTAL_SHARES = new ShareAmount('100020000000'); static readonly TOTAL_SHARES = new ShareAmount('10002000000');
// 初始分配池: 200M // 初始分配池: 200M
static readonly INITIAL_DISTRIBUTION_POOL = new ShareAmount('200000000'); static readonly INITIAL_DISTRIBUTION_POOL = new ShareAmount('200000000');

View File

@ -27,7 +27,8 @@ enum SystemAccountType {
// 池账户类型 // 池账户类型
enum PoolAccountType { enum PoolAccountType {
SHARE_POOL // 积分股池 - 总股池 SHARE_POOL_A // 积分股池A - 初始100亿
SHARE_POOL_B // 积分股池B - 初始200万
BLACK_HOLE_POOL // 黑洞积分股池 - 销毁池 BLACK_HOLE_POOL // 黑洞积分股池 - 销毁池
CIRCULATION_POOL // 流通积分股池 - 交易流通池 CIRCULATION_POOL // 流通积分股池 - 交易流通池
} }

View File

@ -62,19 +62,28 @@ async function main() {
} }
} }
// 2. 初始化池账户(积分股池、黑洞池、流通池) // 2. 初始化池账户积分股池A/B、黑洞池、流通池
// 积分股池A: 100亿 (10,000,000,000) - 用于销毁
// 积分股池B: 200万 (2,000,000) - 用于挖矿分配
// 总计: 100.02亿 (10,002,000,000)
const poolAccounts = [ const poolAccounts = [
{ {
poolType: 'SHARE_POOL', poolType: 'SHARE_POOL_A',
name: '积分股池', name: '积分股池A',
balance: new Decimal('100000000'), // 1亿初始发行量 balance: new Decimal('10000000000'), // 100亿初始发行量
description: '挖矿奖励的来源池,总发行量', description: '销毁池来源初始100亿',
},
{
poolType: 'SHARE_POOL_B',
name: '积分股池B',
balance: new Decimal('2000000'), // 200万初始发行量
description: '挖矿分配池初始200万',
}, },
{ {
poolType: 'BLACK_HOLE_POOL', poolType: 'BLACK_HOLE_POOL',
name: '黑洞池', name: '黑洞池',
balance: new Decimal('0'), balance: new Decimal('0'),
targetBurn: new Decimal('50000000'), // 目标销毁5000万 targetBurn: new Decimal('10000000000'), // 目标销毁100亿
description: '销毁的积分股,用于减少流通量', description: '销毁的积分股,用于减少流通量',
}, },
{ {

View File

@ -6,7 +6,8 @@ import { PoolAccountType, TransactionType } from '@prisma/client';
import Decimal from 'decimal.js'; import Decimal from 'decimal.js';
class InitializePoolsDto { class InitializePoolsDto {
sharePool: { name: string; initialBalance?: string }; sharePoolA: { name: string; initialBalance?: string };
sharePoolB: { name: string; initialBalance?: string };
blackHolePool: { name: string; targetBurn: string }; blackHolePool: { name: string; targetBurn: string };
circulationPool: { name: string; initialBalance?: string }; circulationPool: { name: string; initialBalance?: string };
} }
@ -78,10 +79,16 @@ export class PoolAccountController {
@ApiResponse({ status: 201, description: '池账户初始化成功' }) @ApiResponse({ status: 201, description: '池账户初始化成功' })
async initialize(@Body() dto: InitializePoolsDto) { async initialize(@Body() dto: InitializePoolsDto) {
return this.poolAccountService.initializePools({ return this.poolAccountService.initializePools({
sharePool: { sharePoolA: {
name: dto.sharePool.name, name: dto.sharePoolA.name,
initialBalance: dto.sharePool.initialBalance initialBalance: dto.sharePoolA.initialBalance
? new Decimal(dto.sharePool.initialBalance) ? new Decimal(dto.sharePoolA.initialBalance)
: undefined,
},
sharePoolB: {
name: dto.sharePoolB.name,
initialBalance: dto.sharePoolB.initialBalance
? new Decimal(dto.sharePoolB.initialBalance)
: undefined, : undefined,
}, },
blackHolePool: { blackHolePool: {
@ -97,6 +104,14 @@ export class PoolAccountController {
}); });
} }
@Get('share-pool-balance')
@Public()
@ApiOperation({ summary: '获取积分股池总余量' })
@ApiResponse({ status: 200, description: '积分股池A+B的总余量' })
async getSharePoolTotalBalance() {
return this.poolAccountService.getSharePoolTotalBalance();
}
@Post('burn') @Post('burn')
@AdminOnly() @AdminOnly()
@ApiOperation({ summary: '销毁到黑洞池' }) @ApiOperation({ summary: '销毁到黑洞池' })

View File

@ -6,7 +6,11 @@ import Decimal from 'decimal.js';
import { DomainException } from '../../shared/filters/domain-exception.filter'; import { DomainException } from '../../shared/filters/domain-exception.filter';
export interface InitializePoolsInput { export interface InitializePoolsInput {
sharePool: { sharePoolA: {
name: string;
initialBalance?: Decimal;
};
sharePoolB: {
name: string; name: string;
initialBalance?: Decimal; initialBalance?: Decimal;
}; };
@ -30,27 +34,45 @@ export class PoolAccountService {
) {} ) {}
/** /**
* *
*/ */
async initializePools(input: InitializePoolsInput): Promise<PoolAccount[]> { async initializePools(input: InitializePoolsInput): Promise<PoolAccount[]> {
const pools: PoolAccount[] = []; const pools: PoolAccount[] = [];
// 积分股池 // 积分股池A - 初始100亿
const sharePool = await this.poolAccountRepo.create({ const sharePoolA = await this.poolAccountRepo.create({
poolType: 'SHARE_POOL', poolType: 'SHARE_POOL_A',
name: input.sharePool.name, name: input.sharePoolA.name,
}); });
pools.push(sharePool); pools.push(sharePoolA);
// 如果有初始余额,注入资金 if (input.sharePoolA.initialBalance?.greaterThan(0)) {
if (input.sharePool.initialBalance?.greaterThan(0)) {
await this.poolAccountRepo.updateBalanceWithTransaction( await this.poolAccountRepo.updateBalanceWithTransaction(
'SHARE_POOL', 'SHARE_POOL_A',
input.sharePool.initialBalance, input.sharePoolA.initialBalance,
{ {
transactionType: 'INITIAL_INJECT', transactionType: 'INITIAL_INJECT',
counterpartyType: 'EXTERNAL', counterpartyType: 'EXTERNAL',
memo: `初始注入, 数量${input.sharePool.initialBalance.toFixed(8)}`, memo: `初始注入积分股池A, 数量${input.sharePoolA.initialBalance.toFixed(8)}`,
},
);
}
// 积分股池B - 初始200万
const sharePoolB = await this.poolAccountRepo.create({
poolType: 'SHARE_POOL_B',
name: input.sharePoolB.name,
});
pools.push(sharePoolB);
if (input.sharePoolB.initialBalance?.greaterThan(0)) {
await this.poolAccountRepo.updateBalanceWithTransaction(
'SHARE_POOL_B',
input.sharePoolB.initialBalance,
{
transactionType: 'INITIAL_INJECT',
counterpartyType: 'EXTERNAL',
memo: `初始注入积分股池B, 数量${input.sharePoolB.initialBalance.toFixed(8)}`,
}, },
); );
} }
@ -93,7 +115,8 @@ export class PoolAccountService {
type: p.poolType, type: p.poolType,
name: p.name, name: p.name,
})), })),
sharePoolInitialBalance: input.sharePool.initialBalance?.toString() || '0', sharePoolAInitialBalance: input.sharePoolA.initialBalance?.toString() || '0',
sharePoolBInitialBalance: input.sharePoolB.initialBalance?.toString() || '0',
blackHoleTargetBurn: input.blackHolePool.targetBurn.toString(), blackHoleTargetBurn: input.blackHolePool.targetBurn.toString(),
circulationPoolInitialBalance: input.circulationPool.initialBalance?.toString() || '0', circulationPoolInitialBalance: input.circulationPool.initialBalance?.toString() || '0',
initializedAt: new Date().toISOString(), initializedAt: new Date().toISOString(),
@ -106,6 +129,7 @@ export class PoolAccountService {
/** /**
* *
* SHARE_POOL_B (200)
*/ */
async distributeMiningReward( async distributeMiningReward(
toAccountSeq: string, toAccountSeq: string,
@ -117,10 +141,12 @@ export class PoolAccountService {
referenceId?: string; referenceId?: string;
}, },
): Promise<void> { ): Promise<void> {
const sourcePool: PoolAccountType = 'SHARE_POOL_B';
const memo = `挖矿分配给用户[${toAccountSeq}], 算力占比${miningInfo.contributionRatio.mul(100).toFixed(4)}%, 分钟${miningInfo.miningMinute.toISOString()}`; const memo = `挖矿分配给用户[${toAccountSeq}], 算力占比${miningInfo.contributionRatio.mul(100).toFixed(4)}%, 分钟${miningInfo.miningMinute.toISOString()}`;
await this.poolAccountRepo.updateBalanceWithTransaction( await this.poolAccountRepo.updateBalanceWithTransaction(
'SHARE_POOL', sourcePool,
amount.negated(), amount.negated(),
{ {
transactionType: 'MINING_DISTRIBUTE', transactionType: 'MINING_DISTRIBUTE',
@ -139,7 +165,7 @@ export class PoolAccountService {
await this.outboxRepo.create({ await this.outboxRepo.create({
aggregateType: 'PoolAccount', aggregateType: 'PoolAccount',
aggregateId: 'SHARE_POOL', aggregateId: sourcePool,
eventType: 'MINING_DISTRIBUTED', eventType: 'MINING_DISTRIBUTED',
payload: { payload: {
toAccountSeq, toAccountSeq,
@ -151,6 +177,30 @@ export class PoolAccountService {
}); });
} }
/**
* (SHARE_POOL_A + SHARE_POOL_B)
*/
async getSharePoolTotalBalance(): Promise<{
poolA: string;
poolB: string;
total: string;
}> {
const [poolA, poolB] = await Promise.all([
this.poolAccountRepo.findByType('SHARE_POOL_A'),
this.poolAccountRepo.findByType('SHARE_POOL_B'),
]);
const balanceA = poolA ? new Decimal(poolA.balance.toString()) : new Decimal(0);
const balanceB = poolB ? new Decimal(poolB.balance.toString()) : new Decimal(0);
const total = balanceA.plus(balanceB);
return {
poolA: balanceA.toFixed(8),
poolB: balanceB.toFixed(8),
total: total.toFixed(8),
};
}
/** /**
* *
*/ */

View File

@ -12,8 +12,8 @@ datasource db {
// 交易全局配置 // 交易全局配置
model TradingConfig { model TradingConfig {
id String @id @default(uuid()) id String @id @default(uuid())
// 总积分股数量: 100.02B // 总积分股数量: 100.02亿
totalShares Decimal @default(100020000000) @map("total_shares") @db.Decimal(30, 8) totalShares Decimal @default(10002000000) @map("total_shares") @db.Decimal(30, 8)
// 目标销毁量: 100亿 (4年销毁完) // 目标销毁量: 100亿 (4年销毁完)
burnTarget Decimal @default(10000000000) @map("burn_target") @db.Decimal(30, 8) burnTarget Decimal @default(10000000000) @map("burn_target") @db.Decimal(30, 8)
// 销毁周期: 4年 (分钟数) 365*4*1440 = 2102400 // 销毁周期: 4年 (分钟数) 365*4*1440 = 2102400

View File

@ -66,7 +66,7 @@ async function main() {
if (!existingConfig) { if (!existingConfig) {
await prisma.tradingConfig.create({ await prisma.tradingConfig.create({
data: { data: {
totalShares: new Decimal('100020000000'), // 100.02B 总积分股 totalShares: new Decimal('10002000000'), // 100.02亿 总积分股
burnTarget: new Decimal('10000000000'), // 100亿目标销毁量 burnTarget: new Decimal('10000000000'), // 100亿目标销毁量
burnPeriodMinutes: 2102400, // 4年 = 365*4*1440 分钟 burnPeriodMinutes: 2102400, // 4年 = 365*4*1440 分钟
minuteBurnRate: new Decimal('4756.468797564687'), // 每分钟销毁率 minuteBurnRate: new Decimal('4756.468797564687'), // 每分钟销毁率
@ -93,17 +93,17 @@ async function main() {
console.log('Black hole account already exists'); console.log('Black hole account already exists');
} }
// 4. 初始化积分股池 // 4. 初始化积分股池(绿积分池)
const existingSharePool = await prisma.sharePool.findFirst(); const existingSharePool = await prisma.sharePool.findFirst();
if (!existingSharePool) { if (!existingSharePool) {
await prisma.sharePool.create({ await prisma.sharePool.create({
data: { data: {
greenPoints: new Decimal(0), // 初始绿积分为 0 greenPoints: new Decimal(5760), // 初始绿积分为 5760
totalInflow: new Decimal(0), totalInflow: new Decimal(5760),
totalOutflow: new Decimal(0), totalOutflow: new Decimal(0),
}, },
}); });
console.log('Created share pool'); console.log('Created share pool with initial green points: 5760');
} else { } else {
console.log('Share pool already exists'); console.log('Share pool already exists');
} }

View File

@ -14,8 +14,8 @@ import { Money } from '../value-objects/money.vo';
* 7. = ÷ 24 ÷ 60 ÷ 60 * 7. = ÷ 24 ÷ 60 ÷ 60
*/ */
export class TradingCalculatorService { export class TradingCalculatorService {
// 总积分股数量: 100.02B // 总积分股数量: 100.02亿 (SHARE_POOL_A: 100亿 + SHARE_POOL_B: 200万)
static readonly TOTAL_SHARES = new Decimal('100020000000'); static readonly TOTAL_SHARES = new Decimal('10002000000');
// 目标销毁量: 100亿 (4年销毁完) // 目标销毁量: 100亿 (4年销毁完)
static readonly BURN_TARGET = new Decimal('10000000000'); static readonly BURN_TARGET = new Decimal('10000000000');

View File

@ -35,7 +35,7 @@ export class TradingConfigRepository {
const record = await this.prisma.tradingConfig.create({ const record = await this.prisma.tradingConfig.create({
data: { data: {
totalShares: new Decimal('100020000000'), totalShares: new Decimal('10002000000'), // 100.02亿
burnTarget: new Decimal('10000000000'), burnTarget: new Decimal('10000000000'),
burnPeriodMinutes: 2102400, // 365 * 4 * 1440 burnPeriodMinutes: 2102400, // 365 * 4 * 1440
minuteBurnRate: new Decimal('4756.468797564687'), minuteBurnRate: new Decimal('4756.468797564687'),

View File

@ -59,4 +59,7 @@ class ApiEndpoints {
// Planting Ledger (Kong路由: /api/v2/contribution) // Planting Ledger (Kong路由: /api/v2/contribution)
static String plantingLedger(String accountSequence) => static String plantingLedger(String accountSequence) =>
'/api/v2/contribution/accounts/$accountSequence/planting-ledger'; '/api/v2/contribution/accounts/$accountSequence/planting-ledger';
// Mining Wallet Service 2.0 (Kong路由: /api/v2/mining-wallet)
static const String sharePoolBalance = '/api/v2/mining-wallet/pool-accounts/share-pool-balance';
} }

View File

@ -5,10 +5,8 @@ import '../../../core/constants/app_colors.dart';
import '../../../core/router/routes.dart'; import '../../../core/router/routes.dart';
import '../../../core/utils/format_utils.dart'; import '../../../core/utils/format_utils.dart';
import '../../../domain/entities/contribution.dart'; import '../../../domain/entities/contribution.dart';
import '../../../domain/entities/market_overview.dart';
import '../../providers/user_providers.dart'; import '../../providers/user_providers.dart';
import '../../providers/contribution_providers.dart'; import '../../providers/contribution_providers.dart';
import '../../providers/trading_providers.dart';
import '../../widgets/shimmer_loading.dart'; import '../../widgets/shimmer_loading.dart';
class ContributionPage extends ConsumerWidget { class ContributionPage extends ConsumerWidget {
@ -30,8 +28,8 @@ class ContributionPage extends ConsumerWidget {
// //
final estimatedEarnings = ref.watch(estimatedEarningsProvider(accountSequence)); final estimatedEarnings = ref.watch(estimatedEarningsProvider(accountSequence));
final statsAsync = ref.watch(contributionStatsProvider); final statsAsync = ref.watch(contributionStatsProvider);
// //
final marketAsync = ref.watch(marketOverviewProvider); final sharePoolAsync = ref.watch(sharePoolBalanceProvider);
// Extract loading state and data from AsyncValue // Extract loading state and data from AsyncValue
final isLoading = contributionAsync.isLoading; final isLoading = contributionAsync.isLoading;
@ -39,8 +37,8 @@ class ContributionPage extends ConsumerWidget {
final hasError = contributionAsync.hasError; final hasError = contributionAsync.hasError;
final error = contributionAsync.error; final error = contributionAsync.error;
final isStatsLoading = statsAsync.isLoading; final isStatsLoading = statsAsync.isLoading;
final isMarketLoading = marketAsync.isLoading; final isSharePoolLoading = sharePoolAsync.isLoading;
final market = marketAsync.valueOrNull; final sharePoolBalance = sharePoolAsync.valueOrNull;
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFFF5F5F5), backgroundColor: const Color(0xFFF5F5F5),
@ -50,7 +48,7 @@ class ContributionPage extends ConsumerWidget {
onRefresh: () async { onRefresh: () async {
ref.invalidate(contributionProvider(accountSequence)); ref.invalidate(contributionProvider(accountSequence));
ref.invalidate(contributionStatsProvider); ref.invalidate(contributionStatsProvider);
ref.invalidate(marketOverviewProvider); ref.invalidate(sharePoolBalanceProvider);
}, },
child: hasError && contribution == null child: hasError && contribution == null
? Center( ? Center(
@ -78,7 +76,7 @@ class ContributionPage extends ConsumerWidget {
sliver: SliverList( sliver: SliverList(
delegate: SliverChildListDelegate([ delegate: SliverChildListDelegate([
// //
_buildTotalContributionCard(ref, contribution, isLoading, market, isMarketLoading), _buildTotalContributionCard(ref, contribution, isLoading, sharePoolBalance, isSharePoolLoading),
const SizedBox(height: 16), const SizedBox(height: 16),
// //
_buildThreeColumnStats(ref, contribution, isLoading), _buildThreeColumnStats(ref, contribution, isLoading),
@ -167,8 +165,8 @@ class ContributionPage extends ConsumerWidget {
WidgetRef ref, WidgetRef ref,
Contribution? contribution, Contribution? contribution,
bool isLoading, bool isLoading,
MarketOverview? market, SharePoolBalance? sharePoolBalance,
bool isMarketLoading, bool isSharePoolLoading,
) { ) {
final total = contribution?.totalContribution ?? '0'; final total = contribution?.totalContribution ?? '0';
final hideAmounts = ref.watch(hideAmountsProvider); final hideAmounts = ref.watch(hideAmountsProvider);
@ -220,13 +218,13 @@ class ContributionPage extends ConsumerWidget {
'积分股池实时余量: ', '积分股池实时余量: ',
style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.9)), style: TextStyle(fontSize: 12, color: _grayText.withOpacity(0.9)),
), ),
isMarketLoading isSharePoolLoading
? const ShimmerText( ? const ShimmerText(
placeholder: '----', placeholder: '----',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange), style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
) )
: Text( : Text(
hideAmounts ? '******' : formatAmount(market?.greenPoints ?? '0'), hideAmounts ? '******' : formatAmount(sharePoolBalance?.total ?? '0'),
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange), style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: _orange),
), ),
], ],

View File

@ -6,6 +6,8 @@ import '../../domain/entities/contribution_stats.dart';
import '../../domain/usecases/contribution/get_user_contribution.dart'; import '../../domain/usecases/contribution/get_user_contribution.dart';
import '../../domain/repositories/contribution_repository.dart'; import '../../domain/repositories/contribution_repository.dart';
import '../../core/di/injection.dart'; import '../../core/di/injection.dart';
import '../../core/network/api_client.dart';
import '../../core/network/api_endpoints.dart';
final getUserContributionUseCaseProvider = Provider<GetUserContribution>((ref) { final getUserContributionUseCaseProvider = Provider<GetUserContribution>((ref) {
return getIt<GetUserContribution>(); return getIt<GetUserContribution>();
@ -176,3 +178,38 @@ final estimatedEarningsProvider = Provider.family<EstimatedEarnings, String>((re
/// ///
final hideAmountsProvider = StateProvider<bool>((ref) => false); final hideAmountsProvider = StateProvider<bool>((ref) => false);
///
class SharePoolBalance {
final String poolA;
final String poolB;
final String total;
const SharePoolBalance({
required this.poolA,
required this.poolB,
required this.total,
});
factory SharePoolBalance.fromJson(Map<String, dynamic> json) {
return SharePoolBalance(
poolA: json['poolA'] ?? '0',
poolB: json['poolB'] ?? '0',
total: json['total'] ?? '0',
);
}
static const zero = SharePoolBalance(poolA: '0', poolB: '0', total: '0');
}
/// Provider
final sharePoolBalanceProvider = FutureProvider<SharePoolBalance>((ref) async {
final client = getIt<ApiClient>();
try {
final response = await client.get(ApiEndpoints.sharePoolBalance);
return SharePoolBalance.fromJson(response.data as Map<String, dynamic>);
} catch (e) {
return SharePoolBalance.zero;
}
});