Skip to content

Commit

Permalink
feat: batch shade staking query
Browse files Browse the repository at this point in the history
  • Loading branch information
AustinWoetzel committed Dec 4, 2024
1 parent dd758e3 commit ec0d62a
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-mirrors-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shadeprotocol/shadejs": patch
---

add batch query for shade staking
66 changes: 66 additions & 0 deletions src/contracts/services/shadeStaking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ import { of } from 'rxjs';
import stakingOpportunityResponse from '~/test/mocks/shadeStaking/stakingOpportunityResponse.json';
import { stakingOpportunityResponseParsed } from '~/test/mocks/shadeStaking/response';
import {
batchQueryShadeStakingOpportunity,
batchQueryShadeStakingOpportunity$,
parseStakingOpportunity,
queryShadeStakingOpportunity,
queryShadeStakingOpportunity$,
} from '~/contracts/services/shadeStaking';
import { StakingInfoServiceResponse } from '~/types/contracts/shadeStaking/index';
import { batchStakingInfoUnparsed } from '~/test/mocks/shadeStaking/batchStakingInfoUnparsed';
import { batchStakingInfoParsed } from '~/test/mocks/shadeStaking/batchStakingInfoParsed';

const sendSecretClientContractQuery$ = vi.hoisted(() => vi.fn());
const batchQuery$ = vi.hoisted(() => vi.fn());

beforeAll(() => {
vi.mock('~/contracts/definitions/shadeStaking', () => ({
Expand All @@ -28,6 +33,10 @@ beforeAll(() => {
vi.mock('~/client/services/clientServices', () => ({
sendSecretClientContractQuery$,
}));

vi.mock('~/contracts/services/batchQuery', () => ({
batchQuery$,
}));
});

test('it can parse the shade staking info', () => {
Expand Down Expand Up @@ -76,3 +85,60 @@ test('it can call the query shade staking info service', async () => {

expect(output2).toStrictEqual(stakingOpportunityResponseParsed);
});

test('it can call the batch shade staking info query service', async () => {
const input = {
queryRouterContractAddress: 'CONTRACT_ADDRESS',
queryRouterCodeHash: 'CODE_HASH',
lcdEndpoint: 'LCD_ENDPOINT',
chainId: 'CHAIN_ID',
stakingContracts: [{
address: 'STAKING_ADDRESS',
codeHash: 'STAKING_CODE_HASH',
}],
};
// observables function
batchQuery$.mockReturnValueOnce(of(batchStakingInfoUnparsed));
let output;
batchQueryShadeStakingOpportunity$(input).subscribe({
next: (response) => {
output = response;
},
});

expect(batchQuery$).toHaveBeenCalledWith({
contractAddress: input.queryRouterContractAddress,
codeHash: input.queryRouterCodeHash,
lcdEndpoint: input.lcdEndpoint,
chainId: input.chainId,
queries: [{
id: input.stakingContracts[0].address,
contract: {
address: input.stakingContracts[0].address,
codeHash: input.stakingContracts[0].codeHash,
},
queryMsg: 'STAKING_INFO_MSG',
}],
});

expect(output).toStrictEqual(batchStakingInfoParsed);

// async/await function
batchQuery$.mockReturnValueOnce(of(batchStakingInfoUnparsed));
const response = await batchQueryShadeStakingOpportunity(input);
expect(batchQuery$).toHaveBeenCalledWith({
contractAddress: input.queryRouterContractAddress,
codeHash: input.queryRouterCodeHash,
lcdEndpoint: input.lcdEndpoint,
chainId: input.chainId,
queries: [{
id: input.stakingContracts[0].address,
contract: {
address: input.stakingContracts[0].address,
codeHash: input.stakingContracts[0].codeHash,
},
queryMsg: 'STAKING_INFO_MSG',
}],
});
expect(response).toStrictEqual(batchStakingInfoParsed);
});
89 changes: 89 additions & 0 deletions src/contracts/services/shadeStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ import {
StakingInfoServiceResponse,
StakingRewardPoolServiceModel,
StakingInfoServiceModel,
BatchShadeStakingOpportunity,
} from '~/types/contracts/shadeStaking/index';
import {
BatchQueryParams,
BatchQueryParsedResponse,
Contract,
MinBlockHeightValidationOptions,
} from '~/types';
import { batchQuery$ } from './batchQuery';

// data returned from the contract in normalized form with
// 18 decimals, in addition to any decimals on the individual token
Expand Down Expand Up @@ -42,6 +50,18 @@ function parseStakingOpportunity(data: StakingInfoServiceResponse): StakingInfoS
};
}

/**
* parses the staking info reponse from a batch query of
* multiple staking contracts
*/
const parseBatchQueryShadeStakingOpportunityResponse = (
response: BatchQueryParsedResponse,
): BatchShadeStakingOpportunity => response.map((item) => ({
stakingContractAddress: item.id as string,
stakingInfo: parseStakingOpportunity(item.response),
blockHeight: item.blockHeight,
}));

/**
* query the staking info from the shade staking contract
*/
Expand Down Expand Up @@ -88,8 +108,77 @@ async function queryShadeStakingOpportunity({
}));
}

/**
* query the staking info for multiple staking contracts at one time
*/
function batchQueryShadeStakingOpportunity$({
queryRouterContractAddress,
queryRouterCodeHash,
lcdEndpoint,
chainId,
stakingContracts,
minBlockHeightValidationOptions,
}:{
queryRouterContractAddress: string,
queryRouterCodeHash?: string,
lcdEndpoint?: string,
chainId?: string,
stakingContracts: Contract[]
minBlockHeightValidationOptions?: MinBlockHeightValidationOptions,
}) {
const queries:BatchQueryParams[] = stakingContracts.map((contract) => ({
id: contract.address,
contract: {
address: contract.address,
codeHash: contract.codeHash,
},
queryMsg: msgQueryShadeStakingOpportunity(),
}));
return batchQuery$({
contractAddress: queryRouterContractAddress,
codeHash: queryRouterCodeHash,
lcdEndpoint,
chainId,
queries,
minBlockHeightValidationOptions,
}).pipe(
map(parseBatchQueryShadeStakingOpportunityResponse),
first(),
);
}

/**
* query the staking info for multiple staking contracts at one time
*/
async function batchQueryShadeStakingOpportunity({
queryRouterContractAddress,
queryRouterCodeHash,
lcdEndpoint,
chainId,
stakingContracts,
minBlockHeightValidationOptions,
}:{
queryRouterContractAddress: string,
queryRouterCodeHash?: string,
lcdEndpoint?: string,
chainId?: string,
stakingContracts: Contract[]
minBlockHeightValidationOptions?: MinBlockHeightValidationOptions,
}) {
return lastValueFrom(batchQueryShadeStakingOpportunity$({
queryRouterContractAddress,
queryRouterCodeHash,
lcdEndpoint,
chainId,
stakingContracts,
minBlockHeightValidationOptions,
}));
}

export {
parseStakingOpportunity,
queryShadeStakingOpportunity$,
queryShadeStakingOpportunity,
batchQueryShadeStakingOpportunity$,
batchQueryShadeStakingOpportunity,
};
42 changes: 42 additions & 0 deletions src/test/mocks/shadeStaking/batchStakingInfoParsed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const batchStakingInfoParsed = [
{
stakingContractAddress: 'secret17ue98qd2akjazu2w2r95cz06mh8pfl3v5hva4j',
stakingInfo: {
stakeTokenAddress: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
totalStakedRaw: '158473829064218',
unbondingPeriod: 604800,
rewardPools: [
{
id: '1',
amountRaw: '500000000000',
startDate: new Date('2023-06-27T19:00:00.000Z'),
endDate: new Date('2023-07-27T19:00:00.000Z'),
tokenAddress: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
rateRaw: '192901.234567901234567901',
},
],
},
blockHeight: 1,
},
{
stakingContractAddress: 'secret17ue98qd2akjazu2w2r95cz06mh8pfl3v5hva4j',
stakingInfo: {
stakeTokenAddress: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
totalStakedRaw: '258473829064218',
unbondingPeriod: 704800,
rewardPools: [
{
id: '1',
amountRaw: '600000000000',
startDate: new Date('2023-06-27T19:00:00.000Z'),
endDate: new Date('2023-07-27T19:00:00.000Z'),
tokenAddress: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
rateRaw: '292901.234567901234567901',
},
],
},
blockHeight: 1,
},
];

export { batchStakingInfoParsed };
60 changes: 60 additions & 0 deletions src/test/mocks/shadeStaking/batchStakingInfoUnparsed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const batchStakingInfoUnparsed = [
{
id: 'secret17ue98qd2akjazu2w2r95cz06mh8pfl3v5hva4j',
response: {
staking_info: {
info: {
stake_token: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
total_staked: '158473829064218',
unbond_period: '604800',
reward_pools: [
{
id: '1',
amount: '500000000000',
start: '1687892400',
end: '1690484400',
token: {
address: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
rate: '192901234567901234567901',
official: true,
},
],
},
},
},
blockHeight: 1,
},
{
id: 'secret17ue98qd2akjazu2w2r95cz06mh8pfl3v5hva4j',
response: {
staking_info: {
info: {
stake_token: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
total_staked: '258473829064218',
unbond_period: '704800',
reward_pools: [
{
id: '1',
amount: '600000000000',
start: '1687892400',
end: '1690484400',
token: {
address: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm',
code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
rate: '292901234567901234567901',
official: true,
},
],
},
},
},
blockHeight: 1,
},
];

export {
batchStakingInfoUnparsed,
};
9 changes: 9 additions & 0 deletions src/types/contracts/shadeStaking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,17 @@ type StakingInfoServiceModel = {
rewardPools: StakingRewardPoolServiceModel[],
}

type BatchSingleShadeStakingOpportunity = {
stakingContractAddress: string,
stakingInfo: StakingInfoServiceModel,
blockHeight: number,
}

type BatchShadeStakingOpportunity = BatchSingleShadeStakingOpportunity[]

export type {
StakingInfoServiceResponse,
StakingRewardPoolServiceModel,
StakingInfoServiceModel,
BatchShadeStakingOpportunity,
};

0 comments on commit ec0d62a

Please sign in to comment.