Skip to content

Commit 2cfc087

Browse files
authored
feat(packages): co-staking service (#247)
* feat(packages): co-staking service * chore(packages): remove unused * chore(packages): minimize ubbn baby sat btc conversions * chore(packages): move to baby.ts * chore(packages): format * chore(packages): format simple-staking
1 parent 6332def commit 2cfc087

File tree

13 files changed

+539
-87
lines changed

13 files changed

+539
-87
lines changed

packages/babylon-proto-ts/src/lib/lcd/baby.ts

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import type {
2-
DelegationDelegatorReward
3-
} from "cosmjs-types/cosmos/distribution/v1beta1/distribution";
1+
import type { DelegationDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/distribution";
42
import type {
53
DelegationResponse,
64
Validator,
@@ -19,7 +17,7 @@ const createBabylonClient = ({ request }: Dependencies) => ({
1917
return await fetchAllPages(
2018
request,
2119
`/cosmos/staking/v1beta1/delegations/${address}`,
22-
"delegationResponses"
20+
"delegationResponses",
2321
);
2422
} catch (error) {
2523
throw new Error(`Failed to fetch delegations for ${address}`, {
@@ -46,7 +44,11 @@ const createBabylonClient = ({ request }: Dependencies) => ({
4644

4745
async getValidators(): Promise<Validator[]> {
4846
try {
49-
return await fetchAllPages(request, "/cosmos/staking/v1beta1/validators", "validators");
47+
return await fetchAllPages(
48+
request,
49+
"/cosmos/staking/v1beta1/validators",
50+
"validators",
51+
);
5052
} catch (error) {
5153
throw new Error(`Failed to fetch validators`, {
5254
cause: error,
@@ -92,31 +94,100 @@ const createBabylonClient = ({ request }: Dependencies) => ({
9294
const btcStakingPortion = Number(params?.btcStakingPortion ?? 0);
9395
const fpPortion = Number(params?.fpPortion ?? 0);
9496
return {
95-
btcStakingPortion: Number.isFinite(btcStakingPortion) ? btcStakingPortion : 0,
97+
btcStakingPortion: Number.isFinite(btcStakingPortion)
98+
? btcStakingPortion
99+
: 0,
96100
fpPortion: Number.isFinite(fpPortion) ? fpPortion : 0,
97101
};
98102
},
99103

100104
async getCostakingParams(): Promise<{
101105
costakingPortion: number;
102106
validatorsPortion: number;
107+
scoreRatioBtcByBaby: string;
103108
}> {
104109
const response = await request("/babylon/costaking/v1/params");
105110
const params = response?.params ?? response;
106-
const costakingPortion = Number(params?.costakingPortion ?? 0);
107-
const validatorsPortion = Number(params?.validatorsPortion ?? 0);
111+
const costakingPortion = Number(
112+
params?.costakingPortion ?? params?.costaking_portion ?? 0,
113+
);
114+
const validatorsPortion = Number(
115+
params?.validatorsPortion ?? params?.validators_portion ?? 0,
116+
);
117+
const scoreRatioBtcByBaby =
118+
params?.scoreRatioBtcByBaby ?? params?.score_ratio_btc_by_baby ?? "50";
108119
return {
109-
costakingPortion: Number.isFinite(costakingPortion) ? costakingPortion : 0,
110-
validatorsPortion: Number.isFinite(validatorsPortion) ? validatorsPortion : 0,
120+
costakingPortion: Number.isFinite(costakingPortion)
121+
? costakingPortion
122+
: 0,
123+
validatorsPortion: Number.isFinite(validatorsPortion)
124+
? validatorsPortion
125+
: 0,
126+
scoreRatioBtcByBaby: scoreRatioBtcByBaby,
111127
};
112128
},
113129

114-
async getAnnualProvisions(): Promise<number> {
130+
async getCoStakerRewardsTracker(costakerAddress: string): Promise<{
131+
startPeriodCumulativeReward: number;
132+
activeSatoshis: string;
133+
activeBaby: string;
134+
totalScore: string;
135+
} | null> {
136+
if (!costakerAddress) {
137+
return null;
138+
}
139+
115140
try {
116141
const response = await request(
117-
"/cosmos/mint/v1beta1/annual_provisions",
142+
`/babylon/costaking/v1/costakers/${costakerAddress}/rewards_tracker`,
143+
);
144+
145+
return {
146+
startPeriodCumulativeReward:
147+
response?.start_period_cumulative_reward ?? 0,
148+
activeSatoshis: response?.active_satoshis ?? "0",
149+
activeBaby: response?.active_baby ?? "0",
150+
totalScore: response?.total_score ?? "0",
151+
};
152+
} catch (error: any) {
153+
// Return null for 404 errors (user has not co-staked yet)
154+
if (error?.message?.includes("404") || error?.status === 404) {
155+
return null;
156+
}
157+
throw new Error(
158+
`Failed to fetch co-staker rewards tracker for ${costakerAddress}`,
159+
{
160+
cause: error,
161+
},
118162
);
119-
const annualProvisions = response?.annualProvisions ?? response?.annual_provisions ?? response;
163+
}
164+
},
165+
166+
async getCurrentCoStakingRewards(): Promise<{
167+
rewards: Array<{ denom: string; amount: string }>;
168+
period: number;
169+
totalScore: string;
170+
}> {
171+
try {
172+
const response = await request("/babylon/costaking/v1/current_rewards");
173+
174+
return {
175+
rewards: response?.rewards ?? [],
176+
period: response?.period ?? 0,
177+
totalScore: response?.total_score ?? "0",
178+
};
179+
} catch (error) {
180+
throw new Error("Failed to fetch current co-staking rewards", {
181+
cause: error,
182+
});
183+
}
184+
},
185+
186+
async getAnnualProvisions(): Promise<number> {
187+
try {
188+
const response = await request("/cosmos/mint/v1beta1/annual_provisions");
189+
const annualProvisions =
190+
response?.annualProvisions ?? response?.annual_provisions ?? response;
120191
const result = Number(annualProvisions);
121192
return result;
122193
} catch (error) {
@@ -126,13 +197,11 @@ const createBabylonClient = ({ request }: Dependencies) => ({
126197
}
127198
},
128199

129-
130200
async getSupply(denom: string = "ubbn"): Promise<bigint> {
131201
try {
132-
const response = await request(
133-
"/cosmos/bank/v1beta1/supply/by_denom",
134-
{ denom },
135-
);
202+
const response = await request("/cosmos/bank/v1beta1/supply/by_denom", {
203+
denom,
204+
});
136205
const amount = response?.amount?.amount ?? 0;
137206
return BigInt(amount);
138207
} catch (error: any) {
@@ -144,9 +213,9 @@ const createBabylonClient = ({ request }: Dependencies) => ({
144213

145214
async getCurrentEpoch() {
146215
try {
147-
const {
148-
current_epoch, epoch_boundary
149-
} = await request("/babylon/epoching/v1/current_epoch");
216+
const { current_epoch, epoch_boundary } = await request(
217+
"/babylon/epoching/v1/current_epoch",
218+
);
150219

151220
return {
152221
epochBoundary: parseInt(epoch_boundary, 10),

packages/babylon-proto-ts/src/lib/rpc/btc.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type{ Coin } from "@cosmjs/stargate";
1+
import type { Coin } from "@cosmjs/stargate";
22

33
import { REWARD_GAUGE_KEY_BTC_DELEGATION } from "../../constants";
44
import * as btclightclientquery from "../../generated/babylon/btclightclient/v1/query";
@@ -67,7 +67,7 @@ const createBTCClient = ({ incentive, btcLight }: Dependencies) => ({
6767
cause: error,
6868
});
6969
}
70-
}
71-
})
70+
},
71+
});
7272

73-
export default createBTCClient;
73+
export default createBTCClient;

packages/babylon-proto-ts/src/lib/txs/baby.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ export interface StakeParams {
1616
amount: bigint;
1717
}
1818

19-
const createStakeMsg = ({ delegatorAddress, validatorAddress, amount }: StakeParams) => {
19+
const createStakeMsg = ({
20+
delegatorAddress,
21+
validatorAddress,
22+
amount,
23+
}: StakeParams) => {
2024
const wrappedDelegateMsg = epochingtx.MsgWrappedDelegate.fromPartial({
2125
msg: {
2226
delegatorAddress,
2327
validatorAddress,
2428
amount: {
25-
denom: 'ubbn',
26-
amount: amount.toString()
27-
}
29+
denom: "ubbn",
30+
amount: amount.toString(),
31+
},
2832
},
2933
});
3034

@@ -47,16 +51,20 @@ export interface UnstakeParams {
4751
amount: bigint;
4852
}
4953

50-
const createUnstakeMsg = ({ delegatorAddress, validatorAddress, amount }: UnstakeParams) => {
51-
const wrappedUndelegateMsg = epochingtx.MsgWrappedUndelegate.fromPartial({
54+
const createUnstakeMsg = ({
55+
delegatorAddress,
56+
validatorAddress,
57+
amount,
58+
}: UnstakeParams) => {
59+
const wrappedUndelegateMsg = epochingtx.MsgWrappedUndelegate.fromPartial({
5260
msg: {
5361
delegatorAddress,
5462
validatorAddress,
5563
amount: {
56-
denom: 'ubbn',
57-
amount: amount.toString()
58-
}
59-
}
64+
denom: "ubbn",
65+
amount: amount.toString(),
66+
},
67+
},
6068
});
6169

6270
return {

packages/babylon-proto-ts/src/lib/txs/btc.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@ const createClaimRewardMsg = ({ address }: ClaimRewardParams) => {
2929
* @param params - The parameters for the stake expansion
3030
* @returns The BTC stake expansion message
3131
*/
32-
const createExpandMsg = (
33-
params: Partial<btcstakingtx.MsgBtcStakeExpand>,
34-
) => {
32+
const createExpandMsg = (params: Partial<btcstakingtx.MsgBtcStakeExpand>) => {
3533
const stakeExpandMsg = btcstakingtx.MsgBtcStakeExpand.fromPartial(params);
3634

3735
return {
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import btc from './btc';
2-
import baby from './baby';
1+
import baby from "./baby";
2+
import btc from "./btc";
33

44
export default {
55
baby,
6-
btc
7-
}
6+
btc,
7+
};

packages/babylon-proto-ts/src/lib/utils/baby.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ export function babyToUbbn(bbn: number): bigint {
1717
*/
1818
export function ubbnToBaby(ubbn: bigint): number {
1919
return Number(ubbn) / BABY_IN_UBBN;
20-
}
20+
}

packages/babylon-proto-ts/src/lib/utils/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import { createAminoTypes } from "./amino";
2+
import { babyToUbbn, ubbnToBaby } from "./baby";
23
import { normalizeCosmjsAmount, normalizeRewardResponse } from "./normalize";
4+
import { buildPaginationParams, fetchAllPages } from "./pagination";
35
import { createRegistry } from "./registry";
4-
import { babyToUbbn, ubbnToBaby } from './baby';
5-
import {
6-
fetchAllPages,
7-
buildPaginationParams
8-
} from "./pagination";
96

107
export default {
118
ubbnToBaby,

packages/babylon-proto-ts/src/lib/utils/pagination.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ export interface PaginatedResult<T> {
1818
}
1919

2020
// Helper to convert pagination options to query parameters
21-
export function buildPaginationParams(options: PaginationOptions = {}): Record<string, string> {
21+
export function buildPaginationParams(
22+
options: PaginationOptions = {},
23+
): Record<string, string> {
2224
const params: Record<string, string> = {};
23-
25+
2426
if (options.limit !== undefined) {
25-
params['pagination.limit'] = options.limit.toString();
27+
params["pagination.limit"] = options.limit.toString();
2628
}
27-
28-
if (options.key !== undefined && options.key !== '') {
29-
params['pagination.key'] = options.key;
29+
30+
if (options.key !== undefined && options.key !== "") {
31+
params["pagination.key"] = options.key;
3032
}
31-
33+
3234
return params;
3335
}
3436

@@ -37,34 +39,34 @@ export async function fetchAllPages<T>(
3739
request: RequestFn,
3840
endpoint: string,
3941
dataKey: string,
40-
options: PaginationOptions = {}
42+
options: PaginationOptions = {},
4143
): Promise<T[]> {
4244
const allData: T[] = [];
4345
let nextKey: string | null = options.key || null;
4446
const limit = options.limit || DEFAULT_PAGINATION_LIMIT;
45-
47+
4648
do {
4749
const params = buildPaginationParams({
4850
...options,
4951
limit,
5052
key: nextKey || undefined,
5153
});
52-
54+
5355
const response = await request(endpoint, params);
5456
const data = response[dataKey];
55-
57+
5658
if (data && Array.isArray(data)) {
5759
allData.push(...data);
5860
}
5961

6062
const newNextKey = response.pagination?.nextKey;
61-
nextKey = newNextKey && newNextKey !== '' ? newNextKey : null;
63+
nextKey = newNextKey && newNextKey !== "" ? newNextKey : null;
6264

63-
if (data && data.length === 0 && nextKey === null) {
65+
if (data && data.length === 0 && nextKey === null) {
6466
break;
65-
}
67+
}
6668
} while (nextKey !== null);
67-
69+
6870
return allData;
6971
}
7072

@@ -73,13 +75,13 @@ export async function fetchPage<T>(
7375
request: RequestFn,
7476
endpoint: string,
7577
dataKey: string,
76-
options: PaginationOptions = {}
78+
options: PaginationOptions = {},
7779
): Promise<PaginatedResult<T>> {
7880
const params = buildPaginationParams(options);
79-
81+
8082
const response = await request(endpoint, params);
8183
const data = response[dataKey];
82-
84+
8385
return {
8486
data: data || [],
8587
pagination: {

0 commit comments

Comments
 (0)