diff --git a/.changeset/itchy-jobs-end.md b/.changeset/itchy-jobs-end.md new file mode 100644 index 0000000..cdb55df --- /dev/null +++ b/.changeset/itchy-jobs-end.md @@ -0,0 +1,5 @@ +--- +"@shadeprotocol/shadejs": patch +--- + +update the reward token staking response type since we had a duplicate type diff --git a/.changeset/many-worms-smash.md b/.changeset/many-worms-smash.md new file mode 100644 index 0000000..2fa18a4 --- /dev/null +++ b/.changeset/many-worms-smash.md @@ -0,0 +1,5 @@ +--- +"@shadeprotocol/shadejs": patch +--- + +Batch Pair Config Queries diff --git a/.changeset/shaggy-tables-obey.md b/.changeset/shaggy-tables-obey.md new file mode 100644 index 0000000..02ba4cb --- /dev/null +++ b/.changeset/shaggy-tables-obey.md @@ -0,0 +1,5 @@ +--- +"@shadeprotocol/shadejs": patch +--- + +fix duplicate type exports for contract data diff --git a/docs/queries/swap.md b/docs/queries/swap.md index 3f889c1..8d3a524 100644 --- a/docs/queries/swap.md +++ b/docs/queries/swap.md @@ -194,7 +194,7 @@ console.log(output) ``` ## Pair Config -query the configuration of the pair +query the configuration of a single pair **input** @@ -281,14 +281,142 @@ console.log(output) }; ``` -## Pairs Info -Query the info for multiple pairs +## Pairs Config +Query the pair config for multiple pairs ::: info This query uses a smart contract batch query router to allow you to query many pairs in a single http request. This is a highly efficient method of interacting with the chain an minimizes the load on the LCD endpoint. ::: **input** +```ts +async function batchQueryPairsConfig({ + queryRouterContractAddress, + queryRouterCodeHash, + lcdEndpoint, + chainId, + pairsContracts, +}:{ + queryRouterContractAddress: string, + queryRouterCodeHash?: string, + lcdEndpoint?: string, + chainId?: string, + pairsContracts: Contract[] +}): Promise +``` + +**output** + +```ts +type BatchPairsConfig = BatchPairConfig[] + +type BatchPairConfig = { + pairContractAddress: string, + pairConfig: PairConfig, +} + +type CustomFee = { + daoFee: number, + lpFee: number, +} + +type PairConfig = { + factoryContract: Contract | null, + lpTokenContract: Contract | null, + stakingContract: Contract | null, + token0Contract: Contract, + token1Contract: Contract, + isStable: boolean, + fee: CustomFee | null, +} + +``` + +**example use** + +```ts +const output = await batchQueryPairsConfig({ + queryRouterContractAddress: '[QUERY_ROUTER_CONTRACT_ADDRESS]', + queryRouterCodeHash: '[QUERY_ROUTER_CODE_HASH]', + pairsContracts: [{ + address: '[PAIR_1_ADDRESS]', + codeHash: '[PAIR_1_CODE_HASH]', + }, + { + address: '[PAIR_2_ADDRESS]', + codeHash: '[PAIR_2_CODE_HASH]', + }] +}) +console.log(output) +``` +***console*** +```md +[ + { + "pairContractAddress": "secret1qyt4l47yq3x43ezle4nwlh5q0sn6f9sesat7ap", + "pairConfig": { + "factoryContract": { + "address": "secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp", + "codeHash": "2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e" + }, + "lpTokenContract": { + "address": "secret10egcg03euavu336fzed87m4zdx8jkgzzz7zgmh", + "codeHash": "b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b" + }, + "stakingContract": { + "address": "secret1vgtmfvzdn7ztn7kcrqd7p6f2z97wvauavp3udh", + "codeHash": "a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49" + }, + "token0Contract": { + "address": "secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm", + "codeHash": "638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e" + }, + "token1Contract": { + "address": "secret1k6u0cy4feepm6pehnz804zmwakuwdapm69tuc4", + "codeHash": "f6be719b3c6feb498d3554ca0398eb6b7e7db262acb33f84a8f12106da6bbb09" + }, + "isStable": false, + "fee": null + } + }, + { + "pairContractAddress": "secret1l34fyc9g23fnlk896693nw57phevnyha7pt6gj", + "pairConfig": { + "factoryContract": { + "address": "secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp", + "codeHash": "2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e" + }, + "lpTokenContract": { + "address": "secret1zw9gwj6kx7vd3xax7wf45y6dmawkj3pd3dk7wt", + "codeHash": "b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b" + }, + "stakingContract": { + "address": "secret13j4n5gj8857h2j4cnempdkfygrw9snasx4yzw2", + "codeHash": "a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49" + }, + "token0Contract": { + "address": "secret1fl449muk5yq8dlad7a22nje4p5d2pnsgymhjfd", + "codeHash": "638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e" + }, + "token1Contract": { + "address": "secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm", + "codeHash": "638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e" + }, + "isStable": false, + "fee": { + "lpFee": 0, + "daoFee": 0.003 + } + } + } +] +``` + +## Pairs Info +Query the info for multiple pairs + +**input** + ```ts async function batchQueryPairsInfo({ queryRouterContractAddress, diff --git a/src/contracts/services/swap.test.ts b/src/contracts/services/swap.test.ts index afcdaf0..91a0821 100644 --- a/src/contracts/services/swap.test.ts +++ b/src/contracts/services/swap.test.ts @@ -19,10 +19,13 @@ import { parseSwapResponse, parseBatchQueryPairInfoResponse, parseBatchQueryStakingInfoResponse, + parseBatchQueryPairConfigResponse, batchQueryPairsInfo, batchQueryPairsInfo$, batchQueryStakingInfo, batchQueryStakingInfo$, + batchQueryPairsConfig, + batchQueryPairsConfig$, } from '~/contracts/services/swap'; import factoryConfigResponse from '~/test/mocks/swap/factoryConfig.json'; import factoryPairsResponse from '~/test/mocks/swap/factoryPairs.json'; @@ -49,6 +52,8 @@ import { pairsInfoResponseUnparsed } from '~/test/mocks/swap/batchPairsInfoRespo import { pairsInfoParsed } from '~/test/mocks/swap/batchPairsInfoParsed'; import { batchStakingConfigUnparsed } from '~/test/mocks/swap/batchStakingConfigUnparsed'; import { batchStakingConfigParsed } from '~/test/mocks/swap/batchStakingConfigParsed'; +import { batchPairsConfigResponseUnparsed } from '~/test/mocks/swap/batchPairsConfigResponseUnparsed'; +import { batchPairsConfigParsed } from '~/test/mocks/swap/batchPairsConfigParsed'; const sendSecretClientContractQuery$ = vi.hoisted(() => vi.fn()); const batchQuery$ = vi.hoisted(() => vi.fn()); @@ -121,6 +126,12 @@ test('it can parse the batch staking response', () => { )).toStrictEqual(batchStakingConfigParsed); }); +test('it can parse the batch pair config response', () => { + expect(parseBatchQueryPairConfigResponse( + batchPairsConfigResponseUnparsed, + )).toStrictEqual(batchPairsConfigParsed); +}); + test('it can call the query factory config service', async () => { // observables function sendSecretClientContractQuery$.mockReturnValueOnce(of(factoryConfigResponse)); @@ -354,3 +365,61 @@ test('it can call the batch staking info query service', async () => { }); expect(response).toStrictEqual(batchStakingConfigParsed); }); + +test('it can call the batch pair config query service', async () => { + const input = { + queryRouterContractAddress: 'CONTRACT_ADDRESS', + queryRouterCodeHash: 'CODE_HASH', + lcdEndpoint: 'LCD_ENDPOINT', + chainId: 'CHAIN_ID', + pairsContracts: [{ + address: 'PAIR_ADDRESS', + codeHash: 'PAIR_CODE_HASH', + }], + }; + + // observables function + batchQuery$.mockReturnValueOnce(of(batchPairsConfigResponseUnparsed)); + let output; + batchQueryPairsConfig$(input).subscribe({ + next: (response) => { + output = response; + }, + }); + + expect(batchQuery$).toHaveBeenCalledWith({ + contractAddress: input.queryRouterContractAddress, + codeHash: input.queryRouterCodeHash, + lcdEndpoint: input.lcdEndpoint, + chainId: input.chainId, + queries: [{ + id: input.pairsContracts[0].address, + contract: { + address: input.pairsContracts[0].address, + codeHash: input.pairsContracts[0].codeHash, + }, + queryMsg: 'PAIR_CONFIG_MSG', + }], + }); + + expect(output).toStrictEqual(batchPairsConfigParsed); + + // async/await function + batchQuery$.mockReturnValueOnce(of(batchPairsConfigResponseUnparsed)); + const response = await batchQueryPairsConfig(input); + expect(batchQuery$).toHaveBeenCalledWith({ + contractAddress: input.queryRouterContractAddress, + codeHash: input.queryRouterCodeHash, + lcdEndpoint: input.lcdEndpoint, + chainId: input.chainId, + queries: [{ + id: input.pairsContracts[0].address, + contract: { + address: input.pairsContracts[0].address, + codeHash: input.pairsContracts[0].codeHash, + }, + queryMsg: 'PAIR_CONFIG_MSG', + }], + }); + expect(response).toStrictEqual(batchPairsConfigParsed); +}); diff --git a/src/contracts/services/swap.ts b/src/contracts/services/swap.ts index 829a9a6..fa376ab 100644 --- a/src/contracts/services/swap.ts +++ b/src/contracts/services/swap.ts @@ -15,6 +15,7 @@ import { } from '~/types/contracts/swap/response'; import { BatchPairsInfo, + BatchPairsConfig, BatchStakingInfo, FactoryConfig, FactoryPairs, @@ -148,7 +149,7 @@ function parsePairConfig(response: PairConfigResponse): PairConfig { isStable: pair[2], fee: customFee ? { lpFee: customFee.lp_fee.nom / customFee.lp_fee.denom, - daoFee: customFee.lp_fee.nom / customFee.lp_fee.denom, + daoFee: customFee.shade_dao_fee.nom / customFee.shade_dao_fee.denom, } : null, }; } @@ -233,6 +234,16 @@ const parseBatchQueryPairInfoResponse = ( pairInfo: parsePairInfo(item.response), })); +/** + * parses the pair config response from a batch query of + * multiple pair contracts + */ +const parseBatchQueryPairConfigResponse = ( + response: BatchQueryParsedResponse, +): BatchPairsConfig => response.map((item) => ({ + pairContractAddress: item.id as string, + pairConfig: parsePairConfig(item.response), +})); /** * parses the single staking info response */ @@ -487,7 +498,7 @@ async function queryPairConfig({ } /** - * query the pair info for multiple pools at one time + * query the info for multiple pairs at one time */ function batchQueryPairsInfo$({ queryRouterContractAddress, @@ -523,7 +534,7 @@ function batchQueryPairsInfo$({ } /** - * query the pair info for multiple pools at one time + * query the info for multiple pairs at one time */ async function batchQueryPairsInfo({ queryRouterContractAddress, @@ -547,6 +558,67 @@ async function batchQueryPairsInfo({ })); } +/** + * query the config for multiple pairs at one time + */ +function batchQueryPairsConfig$({ + queryRouterContractAddress, + queryRouterCodeHash, + lcdEndpoint, + chainId, + pairsContracts, +}:{ + queryRouterContractAddress: string, + queryRouterCodeHash?: string, + lcdEndpoint?: string, + chainId?: string, + pairsContracts: Contract[] +}) { + const queries:BatchQuery[] = pairsContracts.map((contract) => ({ + id: contract.address, + contract: { + address: contract.address, + codeHash: contract.codeHash, + }, + queryMsg: msgQueryPairConfig(), + })); + return batchQuery$({ + contractAddress: queryRouterContractAddress, + codeHash: queryRouterCodeHash, + lcdEndpoint, + chainId, + queries, + }).pipe( + map(parseBatchQueryPairConfigResponse), + first(), + ); +} + +/** + * query the config for multiple pairs at one time + */ +async function batchQueryPairsConfig({ + queryRouterContractAddress, + queryRouterCodeHash, + lcdEndpoint, + chainId, + pairsContracts, +}:{ + queryRouterContractAddress: string, + queryRouterCodeHash?: string, + lcdEndpoint?: string, + chainId?: string, + pairsContracts: Contract[] +}) { + return lastValueFrom(batchQueryPairsConfig$({ + queryRouterContractAddress, + queryRouterCodeHash, + lcdEndpoint, + chainId, + pairsContracts, + })); +} + /** * query the staking info for multiple staking contracts at one time */ @@ -622,9 +694,12 @@ export { queryPairConfig, batchQueryPairsInfo$, batchQueryPairsInfo, + batchQueryPairsConfig$, + batchQueryPairsConfig, batchQueryStakingInfo$, batchQueryStakingInfo, parseSwapResponse, parseBatchQueryPairInfoResponse, parseBatchQueryStakingInfoResponse, + parseBatchQueryPairConfigResponse, }; diff --git a/src/test/mocks/swap/batchPairsConfigParsed.ts b/src/test/mocks/swap/batchPairsConfigParsed.ts new file mode 100644 index 0000000..2233bad --- /dev/null +++ b/src/test/mocks/swap/batchPairsConfigParsed.ts @@ -0,0 +1,63 @@ +const batchPairsConfigParsed = [ + { + pairContractAddress: 'secret1qyt4l47yq3x43ezle4nwlh5q0sn6f9sesat7ap', + pairConfig: { + factoryContract: { + address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp', + codeHash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e', + }, + lpTokenContract: { + address: 'secret10egcg03euavu336fzed87m4zdx8jkgzzz7zgmh', + codeHash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b', + }, + stakingContract: { + address: 'secret1vgtmfvzdn7ztn7kcrqd7p6f2z97wvauavp3udh', + codeHash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49', + }, + token0Contract: { + address: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm', + codeHash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + token1Contract: { + address: 'secret1k6u0cy4feepm6pehnz804zmwakuwdapm69tuc4', + codeHash: 'f6be719b3c6feb498d3554ca0398eb6b7e7db262acb33f84a8f12106da6bbb09', + }, + isStable: false, + fee: null, + }, + }, + { + pairContractAddress: 'secret1l34fyc9g23fnlk896693nw57phevnyha7pt6gj', + pairConfig: { + factoryContract: { + address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp', + codeHash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e', + }, + lpTokenContract: { + address: 'secret1zw9gwj6kx7vd3xax7wf45y6dmawkj3pd3dk7wt', + codeHash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b', + }, + stakingContract: { + address: 'secret13j4n5gj8857h2j4cnempdkfygrw9snasx4yzw2', + codeHash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49', + }, + token0Contract: { + address: 'secret1fl449muk5yq8dlad7a22nje4p5d2pnsgymhjfd', + codeHash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + token1Contract: { + address: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm', + codeHash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + isStable: false, + fee: { + lpFee: 0, + daoFee: 0.003, + }, + }, + }, +]; + +export { + batchPairsConfigParsed, +}; diff --git a/src/test/mocks/swap/batchPairsConfigResponseUnparsed.ts b/src/test/mocks/swap/batchPairsConfigResponseUnparsed.ts new file mode 100644 index 0000000..f6bcafa --- /dev/null +++ b/src/test/mocks/swap/batchPairsConfigResponseUnparsed.ts @@ -0,0 +1,85 @@ +const batchPairsConfigResponseUnparsed = [ + { + id: 'secret1qyt4l47yq3x43ezle4nwlh5q0sn6f9sesat7ap', + response: { + get_config: { + factory_contract: { + address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp', + code_hash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e', + }, + lp_token: { + address: 'secret10egcg03euavu336fzed87m4zdx8jkgzzz7zgmh', + code_hash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b', + }, + staking_contract: { + address: 'secret1vgtmfvzdn7ztn7kcrqd7p6f2z97wvauavp3udh', + code_hash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49', + }, + pair: [ + { + custom_token: { + contract_addr: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm', + token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + }, + { + custom_token: { + contract_addr: 'secret1k6u0cy4feepm6pehnz804zmwakuwdapm69tuc4', + token_code_hash: 'f6be719b3c6feb498d3554ca0398eb6b7e7db262acb33f84a8f12106da6bbb09', + }, + }, + false, + ], + custom_fee: null, + }, + }, + }, + { + id: 'secret1l34fyc9g23fnlk896693nw57phevnyha7pt6gj', + response: { + get_config: { + factory_contract: { + address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp', + code_hash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e', + }, + lp_token: { + address: 'secret1zw9gwj6kx7vd3xax7wf45y6dmawkj3pd3dk7wt', + code_hash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b', + }, + staking_contract: { + address: 'secret13j4n5gj8857h2j4cnempdkfygrw9snasx4yzw2', + code_hash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49', + }, + pair: [ + { + custom_token: { + contract_addr: 'secret1fl449muk5yq8dlad7a22nje4p5d2pnsgymhjfd', + token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + }, + { + custom_token: { + contract_addr: 'secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm', + token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e', + }, + }, + false, + ], + custom_fee: { + shade_dao_fee: { + nom: 30, + denom: 10000, + }, + lp_fee: { + nom: 0, + denom: 10000, + }, + }, + }, + }, + }, +]; + +export { + batchPairsConfigResponseUnparsed, +}; diff --git a/src/types/contracts/shared/index.ts b/src/types/contracts/shared/index.ts index 422f555..23e9bc9 100644 --- a/src/types/contracts/shared/index.ts +++ b/src/types/contracts/shared/index.ts @@ -3,6 +3,11 @@ type Contract = { codeHash: string, }; +type ContractData = { + address: string, + code_hash:string, +} export type { Contract, + ContractData, }; diff --git a/src/types/contracts/swap/model.ts b/src/types/contracts/swap/model.ts index 7885e56..a2b2015 100644 --- a/src/types/contracts/swap/model.ts +++ b/src/types/contracts/swap/model.ts @@ -96,6 +96,13 @@ type BatchPairInfo = { type BatchPairsInfo = BatchPairInfo[] +type BatchPairConfig = { + pairContractAddress: string, + pairConfig: PairConfig, +} + +type BatchPairsConfig = BatchPairConfig[] + type RewardTokenInfo = { token: Contract, rewardPerSecond: string, @@ -138,6 +145,7 @@ type ParsedSwapResponse = { export type { FactoryPairs, PairConfig, + BatchPairsConfig, FactoryConfig, PairInfo, BatchPairsInfo, diff --git a/src/types/contracts/swap/response.ts b/src/types/contracts/swap/response.ts index 53be769..e398f94 100644 --- a/src/types/contracts/swap/response.ts +++ b/src/types/contracts/swap/response.ts @@ -1,3 +1,5 @@ +import { ContractData } from '~/types/contracts/shared'; + type CustomToken = { custom_token:{ contract_addr: string, @@ -17,10 +19,6 @@ type ContractInstantiationInfo = { code_hash: string, id: number, } -type Contract = { - address: string, - code_hash:string, -} type Fee = { nom: number, @@ -32,7 +30,7 @@ type AMMSettings = { shade_dao_fee: Fee, stable_lp_fee: Fee, stable_shade_dao_fee: Fee, - shade_dao_address: Contract, + shade_dao_address: ContractData, } type FactoryConfigResponse = { @@ -40,8 +38,8 @@ type FactoryConfigResponse = { pair_contract: ContractInstantiationInfo, amm_settings: AMMSettings, lp_token_contract: ContractInstantiationInfo, - authenticator: Contract | null, - admin_auth: Contract, + authenticator: ContractData | null, + admin_auth: ContractData, } } @@ -58,9 +56,9 @@ type CustomFee = { type PairConfigResponse = { get_config:{ - factory_contract: Contract | null, - lp_token: Contract | null, - staking_contract: Contract | null + factory_contract: ContractData | null, + lp_token: ContractData | null, + staking_contract: ContractData | null pair: TokenPair, custom_fee: CustomFee | null } @@ -82,7 +80,7 @@ type StableInfo = { a: string, gamma1: string, gamma2: string, - oracle: Contract, + oracle: ContractData, min_trade_size_x_for_y: string, min_trade_size_y_for_x: string, max_price_impact_allowed: string @@ -95,8 +93,8 @@ type StableInfo = { type PairInfoResponse = { get_pair_info:{ - liquidity_token: Contract, - factory: Contract | null, + liquidity_token: ContractData, + factory: ContractData | null, pair: TokenPair, amount_0: string, amount_1: string, @@ -113,8 +111,8 @@ type PairInfoResponse = { } } -type RewardTokenInfo = { - token: Contract, +type RewardTokenInfoResponse = { + token: ContractData, decimals: number, reward_per_second: string, reward_per_staked_token: string, @@ -123,12 +121,12 @@ type RewardTokenInfo = { } type StakingConfigResponse = { - lp_token: Contract, + lp_token: ContractData, amm_pair: string, - admin_auth: Contract, - query_auth: Contract | null, + admin_auth: ContractData, + query_auth: ContractData | null, total_amount_staked: string, - reward_tokens: RewardTokenInfo[], + reward_tokens: RewardTokenInfoResponse[], } export type {