forked from MergeFi/backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaintenance-pool.service.ts
More file actions
112 lines (101 loc) · 3.32 KB
/
Copy pathmaintenance-pool.service.ts
File metadata and controls
112 lines (101 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import {
BadRequestException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { MaintenancePool } from '../common/entities';
import { MaintenancePoolStatus } from '../common/enums';
import { EscrowService } from '../escrow/escrow.service';
import { CreatePoolDto } from './dto/create-pool.dto';
/**
* Recurring maintenance pool: sponsors make monthly deposits into a shared
* escrow; maintainers assign rewards out of the running balance for
* maintenance-type work (dependency bumps, docs, cleanup) without needing to
* create a one-off bounty + individual escrow each time.
*/
@Injectable()
export class MaintenancePoolService {
constructor(
@InjectRepository(MaintenancePool)
private readonly poolRepo: Repository<MaintenancePool>,
private readonly escrowService: EscrowService,
) {}
async create(dto: CreatePoolDto): Promise<MaintenancePool> {
const pool = this.poolRepo.create({
name: dto.name,
repositoryId: dto.repositoryId ?? null,
createdById: dto.createdById ?? null,
asset: dto.asset,
status: MaintenancePoolStatus.ACTIVE,
});
return this.poolRepo.save(pool);
}
async findOne(id: string): Promise<MaintenancePool> {
const pool = await this.poolRepo.findOne({ where: { id } });
if (!pool) throw new NotFoundException(`Maintenance pool ${id} not found`);
return pool;
}
/** Sponsor makes a (typically monthly) deposit, topping up the pool's on-chain balance. */
async deposit(
id: string,
amount: string,
funderAddress: string,
): Promise<MaintenancePool> {
const pool = await this.findOne(id);
if (pool.status !== MaintenancePoolStatus.ACTIVE) {
throw new BadRequestException(`Pool ${id} is not ACTIVE`);
}
if (!pool.escrowId) {
const escrow = await this.escrowService.fund({
amount,
asset: pool.asset,
funderAddress,
maintenancePoolId: pool.id,
});
pool.escrow = escrow;
pool.escrowId = escrow.id;
} else {
// Subsequent deposits top up the existing on-chain escrow balance.
await this.escrowService.fund({
amount,
asset: pool.asset,
funderAddress,
maintenancePoolId: pool.id,
});
}
pool.balance = (Number(pool.balance) + Number(amount)).toFixed(7);
pool.monthlyDeposit = amount;
return this.poolRepo.save(pool);
}
/** Maintainer assigns a reward from the pool's balance for completed maintenance work. */
async assignReward(
id: string,
amount: string,
recipientAddress: string,
recipientId?: string,
) {
const pool = await this.findOne(id);
if (!pool.escrowId) {
throw new BadRequestException(`Pool ${id} has no funded escrow yet`);
}
if (Number(amount) > Number(pool.balance)) {
throw new BadRequestException(
`Requested reward ${amount} exceeds pool balance ${pool.balance}`,
);
}
const payment = await this.escrowService.releasePartial(
pool.escrowId,
amount,
recipientAddress,
recipientId,
);
pool.balance = (Number(pool.balance) - Number(amount)).toFixed(7);
await this.poolRepo.save(pool);
return payment;
}
async list(): Promise<MaintenancePool[]> {
return this.poolRepo.find();
}
}