diff --git a/package-lock.json b/package-lock.json index 6c466615..cee82ca5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@terra-money/feather.js", - "version": "2.1.0-beta.4", + "version": "3.0.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@terra-money/feather.js", - "version": "2.1.0-beta.4", + "version": "3.0.0-beta.1", "license": "MIT", "dependencies": { "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^5.1.0-beta.3", + "@terra-money/terra.proto": "5.3.0-beta.0", "assert": "^2.0.0", "axios": "^0.27.2", "bech32": "^2.0.0", @@ -1291,9 +1291,9 @@ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/@terra-money/terra.proto": { - "version": "5.1.0-beta.3", - "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-5.1.0-beta.3.tgz", - "integrity": "sha512-y69nixzKxV5AK/4Kgza/eF+0Ez++xpbnse5LrYI7ar43OMWP59gxeASeTZPLsbx8bG5CsqCtcRT/klFtwsDzXA==", + "version": "5.3.0-beta.0", + "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-5.3.0-beta.0.tgz", + "integrity": "sha512-pRy8RScTbhHuSZsuevQBGdVAcFaMTriQeCrT1TOJ2/cMJLGnBJ/43wQN/0KS+4tg2UyYpudZzBhARp+0nvbs1Q==", "dependencies": { "@improbable-eng/grpc-web": "^0.14.1", "browser-headers": "^0.4.1", diff --git a/package.json b/package.json index e7da93ea..b8c71a9a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@terra-money/feather.js", - "version": "2.1.0-beta.4", - "description": "TypeScript SDK for Terra and Cosmos chains", + "version": "3.0.0-beta.1", + "description": "The JavaScript SDK for Terra and Feather chains", "license": "MIT", "author": "Terraform Labs, PTE.", "keywords": [ @@ -86,7 +86,7 @@ }, "dependencies": { "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", - "@terra-money/terra.proto": "^5.1.0-beta.3", + "@terra-money/terra.proto": "5.3.0-beta.0", "assert": "^2.0.0", "axios": "^0.27.2", "bech32": "^2.0.0", diff --git a/src/client/lcd/api/AllianceAPI.spec.ts b/src/client/lcd/api/AllianceAPI.spec.ts new file mode 100644 index 00000000..eb40b6d3 --- /dev/null +++ b/src/client/lcd/api/AllianceAPI.spec.ts @@ -0,0 +1,143 @@ +import { AllianceAPI } from './AllianceAPI'; +import { LCDClient } from '../LCDClient'; +import { Dec } from '../../../core'; +import { RewardWeightRange } from '../../../core/alliance'; + +const lcd = LCDClient.fromDefaultConfig('testnet'); +const alliance = new AllianceAPI(lcd); + +describe('AllianceAPI', () => { + it('params', async () => { + const params = await alliance.params('pisco-1'); + + expect(params.rewardDelayTime).toBeDefined(); + expect(params.takeRateClaimInterval).toBeDefined(); + expect(params.lastTakeRateClaimTime.getDate()).toBeLessThanOrEqual( + Date.now() + ); + }); + + describe('alliance assets', () => { + it('query an alliance by denom', async () => { + const res = await alliance.queryAlliance( + 'pisco-1', + 'factory/terra1zdpgj8am5nqqvht927k3etljyl6a52kwqup0je/utest766e' + ); + + expect(res).toBeDefined(); + expect(res.denom).toBe( + 'factory/terra1zdpgj8am5nqqvht927k3etljyl6a52kwqup0je/utest766e' + ); + expect(res.rewardWeight.eq(new Dec(0.01))).toBeTruthy(); + expect(res.takeRate.equals(new Dec(0))).toBeTruthy(); + expect(res.totalTokens.greaterThan(new Dec(1))).toBeTruthy(); + expect(res.totalValidatorShares.greaterThan(new Dec(1))).toBeTruthy; + expect(res.rewardStartTime.getTime()).toBeLessThanOrEqual(Date.now()); + + expect(res.rewardChangeRate.equals(new Dec('1'))).toBeTruthy(); + expect(res.rewardChangeInterval).toStrictEqual('6000s'); + expect(res.lastRewardChangeTime.getTime()).toBeLessThanOrEqual( + Date.now() + ); + expect(res.rewardWeightRange).toStrictEqual( + new RewardWeightRange(new Dec(0), new Dec(1)) + ); + expect(res.isInitialized).toBeTruthy(); + }); + + it('query all alliances', async () => { + const res = await alliance.queryAlliances('pisco-1'); + expect(res.pagination).toBeDefined(); + expect(res.alliances.length).toBeGreaterThan(0); + + const allianceAsset = res.alliances[0]; + expect(allianceAsset).toBeDefined(); + expect(allianceAsset.denom).toBe( + 'factory/terra1zdpgj8am5nqqvht927k3etljyl6a52kwqup0je/utest766e' + ); + expect(allianceAsset.rewardWeight.eq(new Dec(0.01))).toBeTruthy(); + expect(allianceAsset.takeRate.equals(new Dec(0))).toBeTruthy(); + expect(allianceAsset.totalTokens.greaterThan(new Dec(1))).toBeTruthy(); + expect(allianceAsset.totalValidatorShares.greaterThan(new Dec(1))) + .toBeTruthy; + expect(allianceAsset.rewardStartTime.getTime()).toBeLessThanOrEqual( + Date.now() + ); + + expect(allianceAsset.rewardChangeRate.equals(new Dec('1'))).toBeTruthy(); + expect(allianceAsset.rewardChangeInterval).toStrictEqual('6000s'); + expect(allianceAsset.lastRewardChangeTime.getTime()).toBeLessThanOrEqual( + Date.now() + ); + expect(allianceAsset.rewardWeightRange).toStrictEqual( + new RewardWeightRange(new Dec(0), new Dec(1)) + ); + expect(allianceAsset.isInitialized).toBeTruthy(); + }); + }); + + describe('delegations', () => { + it('query all', async () => { + const res = await alliance.queryAllianceDelegations('pisco-1'); + expect(res.delegations.length).toBeGreaterThan(10); + expect(res.pagination).toBeDefined(); + }); + + it('query all by delegator', async () => { + const res = await alliance.queryAllianceDelegations( + 'pisco-1', + 'terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43' + ); + expect(res.delegations.length).toBeGreaterThan(0); + expect(res.pagination).toBeDefined(); + }); + + it('query all by delegator and validator', async () => { + const res = await alliance.queryAllianceDelegations( + 'pisco-1', + 'terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43', + 'terravaloper1zdpgj8am5nqqvht927k3etljyl6a52kwqndjz2' + ); + expect(res.delegations.length).toBeGreaterThan(0); + expect(res.pagination).toBeDefined(); + }); + + it('query all by delegator, validator and denom', async () => { + const res = await alliance.queryAllianceDelegations( + 'pisco-1', + 'terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43', + 'terravaloper1zdpgj8am5nqqvht927k3etljyl6a52kwqndjz2', + 'factory/terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43/ualliance' + ); + expect(res.delegations.length).toBeGreaterThan(0); + expect(res.pagination).toBeDefined(); + }); + + it('with missing incremental params', async () => { + await alliance + .queryAllianceDelegations( + 'pisco-1', + undefined, + 'terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43' + ) + .catch((e: Error) => { + expect(e.message).toStrictEqual( + 'DELEGATOR ADDRESS must be provided when VALIDATOR ADDRESS is provided!!' + ); + }); + + await alliance + .queryAllianceDelegations( + 'pisco-1', + 'terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43', + undefined, + 'factory/terra1eaxcahzxp0x8wqejqjlqaey53tp06l728qad6z395lyzgl026qkq20xj43/ualliance' + ) + .catch((e: Error) => { + expect(e.message).toStrictEqual( + 'VALIDATOR ADDRESS must be provided when ALLIANCE DENOM is provided!!' + ); + }); + }); + }); +}); diff --git a/src/client/lcd/api/AllianceAPI.ts b/src/client/lcd/api/AllianceAPI.ts index 7933415e..ea42f327 100644 --- a/src/client/lcd/api/AllianceAPI.ts +++ b/src/client/lcd/api/AllianceAPI.ts @@ -1,117 +1,23 @@ import { BaseAPI } from './BaseAPI'; -import { Coins } from '../../../core'; +import { + AllianceParams, + AllianceAsset, + AllianceDelegation, + AllianceRedelegation, + AllianceUnbonding, + AllianceValidator, +} from '../../../core/alliance'; +import { AccAddress, Coins, ValAddress } from '../../../core'; import { APIParams, Pagination, PaginationOptions } from '../APIRequester'; import { LCDClient } from '../LCDClient'; -export interface AllianceParams { - reward_delay_time?: string; - - /** Time interval between consecutive applications of `take_rate` */ - take_rate_claim_interval?: string; - - /** - * Last application of `take_rate` on assets - * @format date-time from golang - */ - last_take_rate_claim_time?: string; -} - -export interface AllianceValidator { - validator_addr?: string; - total_delegation_shares?: V1Beta1DecCoin[]; - validator_shares?: V1Beta1DecCoin[]; - total_staked?: V1Beta1DecCoin[]; -} - -export interface V1Beta1DecCoin { - denom?: string; - amount?: string; -} - -/** -* DelegationResponse is equivalent to Delegation except that it contains a -balance in addition to shares which is more suitable for client responses. -*/ -export interface AllianceDelegationResponse { - delegation: AllianceDelegation; - - /** - * Coin defines a token with a denomination and an amount. - * - * NOTE: The amount field is an Int which implements the custom method - * signatures required by gogoproto. - */ - balance: { - denom: string; - amount: string; - }; -} - -export interface AllianceRewardHistory { - denom: string; - index: string; -} - -export interface AllianceDelegation { - /** delegator_address is the bech32-encoded address of the delegator. */ - delegator_address: string; - - /** validator_address is the bech32-encoded address of the validator. */ - validator_address: string; - - /** denom of token staked */ - denom: string; - - /** shares define the delegation shares received. */ - shares: string; - reward_history: AllianceRewardHistory[]; - - /** @format uint64 */ - last_reward_claim_height: string; -} -export interface RewardWeightRange { - min: string; - max: string; -} -export interface AllianceAsset { - /** Denom of the asset. It could either be a native token or an IBC token */ - denom: string; - - /** - * The reward weight specifies the ratio of rewards that will be given to each alliance asset - * It does not need to sum to 1. rate = weight / total_weight - * Native asset is always assumed to have a weight of 1.s - */ - reward_weight: string; - - /** - * A positive take rate is used for liquid staking derivatives. It defines an rate that is applied per take_rate_interval - * that will be redirected to the distribution rewards pool - */ - take_rate: string; - total_tokens: string; - total_validator_shares: string; - - /** @format date-time */ - reward_start_time: string; - reward_change_rate: string; - reward_change_interval: string; - - /** @format date-time */ - last_reward_change_time: string; - /** set a bound of weight range to limit how much reward weights can scale. */ - reward_weight_range?: RewardWeightRange; - /** flag to check if an asset has completed the initialization process after the reward delay */ - is_initialized: boolean; -} - export class AllianceAPI extends BaseAPI { constructor(public lcd: LCDClient) { super(lcd.apiRequesters, lcd.config); } /** - * Query the alliance module params + * Query the alliance module params. * * @tags Query * @name params @@ -121,187 +27,291 @@ export class AllianceAPI extends BaseAPI { public async params( chainId: string, params: Partial = {} - ) { - return this.getReqFromChainID(chainId).get<{ params: AllianceParams }>( - `/terra/alliances/params`, - params - ); + ): Promise { + const res = await this.getReqFromChainID(chainId).get<{ + params: AllianceParams.Data; + }>(`/terra/alliances/params`, params); + + return AllianceParams.fromData(res.params); } /** - * Query all available alliances with pagination + * Query all available alliances with pagination. * * @tags Query * @name alliances * @summary Query paginated alliances * @request GET:/terra/alliances */ - public async alliances( + public async queryAlliances( chainID: string, params: Partial = {} - ) { - return this.getReqFromChainID(chainID).get<{ + ): Promise<{ pagination: Pagination; alliances: AllianceAsset[] }> { + const res = await this.getReqFromChainID(chainID).get<{ pagination: Pagination; - alliances: AllianceAsset[]; + alliances: AllianceAsset.Data[]; }>(`/terra/alliances`, params); + + return { + pagination: res.pagination, + alliances: res.alliances.map(a => AllianceAsset.fromData(a)), + }; } /** - * Query the alliance by denom where denom can be either the - * ibc prefixed hash or any other native asset alliance denom + * Query the alliance by denom where denom will be encoded to URI component + * where "/" will be replaced by "%2F" and will allow querying for alliance + * assets with "/" or other special characters in their denom. * * @tags Query - * @name alliance + * @name queryAlliance * @summary Query the alliance by denom * @request GET:/terra/alliances/{denom} */ - public async alliance( + public async queryAlliance( chainId: string, denom: string, params: Partial = {} - ) { - return this.getReqFromChainID(chainId).get<{ - alliance: AllianceAsset; - pagination: Pagination; - }>(`/terra/alliances/${denom}`, params); + ): Promise { + const encodedDenom = encodeURIComponent(encodeURIComponent(denom)); + const res = await this.getReqFromChainID(chainId).get<{ + alliance: AllianceAsset.Data; + }>(`/terra/alliances/${encodedDenom}`, params); + + return AllianceAsset.fromData(res.alliance); } /** - * Query all paginated alliance delegations + * Query all paginated alliance delegations with **OPTIONAL** delAddr, valAddr and denom parameters + * **BUT** dependent on each previous value. Which means that you cannot use this method to query + * the validator's delegations without providing the delegator's address. The denom in the query will be + * URL encoded to allow querying for alliance assets with "/" or other special characters in their denom. * + * - When no values are provided, this query returns all delegations. + * - When **delAddr** is provided, this query returns the delegations for the provided address. + * - When **delAddr** and **valAddr** are provided, this query returns the delegations for the specified address and validator. + * - When **delAddr**, **valAddr** and **denom** are provided, this query returns the delegations for the specified address, validator and denom. + * ¡WARNING!: for efficiency reasons, provide all specified parameters, otherwise the query will be slower. * @tags Query - * @name alliancesDelegations + * @name queryAllianceDelegations * @summary Query all paginated alliance delegations - * @request GET:/terra/alliances/delegations + * @request GET:/terra/alliances/delegations or + * GET:/terra/alliances/delegations/{delAddr} or + * GET:/terra/alliances/delegations/{delAddr}/{valAddr} or + * GET:/terra/alliances/delegations/{delAddr}/{valAddr}/{denom} */ - public async alliancesDelegations( + public async queryAllianceDelegations( chainID: string, + delAddr?: AccAddress, + valAddr?: ValAddress, + denom?: string, params: Partial = {} - ) { - return this.getReqFromChainID(chainID).get<{ - delegations: AllianceDelegationResponse[]; - pagination: Pagination; - }>(`/terra/alliances/delegations`, params); - } + ): Promise<{ + delegations: AllianceDelegation[]; + balance: Coins; + pagination: Pagination; + }> { + let url = `/terra/alliances/delegations`; + if (delAddr) { + url += `/${delAddr}`; + } + if (valAddr) { + if (!delAddr) { + throw new Error( + 'A DELEGATOR ADDRESS must be provided if a VALIDATOR ADDRESS is provided!!' + ); + } - /** - * Query all paginated alliance delegations for a specific delegator address - * - * @tags Query - * @name alliancesDelegation - * @summary Query all paginated alliance delegations - * @request GET:/terra/alliances/delegations/{delegatorAddr} - */ - public async alliancesDelegation( - delegatorAddr: string, - params: Partial = {} - ) { - return this.getReqFromAddress(delegatorAddr).get<{ - delegations: AllianceDelegationResponse[]; - pagination: Pagination; - }>(`/terra/alliances/delegations/${delegatorAddr}`, params); + url += `/${valAddr}`; + } + if (denom) { + if (!valAddr) { + throw new Error( + 'A VALIDATOR ADDRESS must be provided if an ALLIANCE DENOM is provided!!' + ); + } + url += `/${encodeURIComponent(encodeURIComponent(denom))}`; + } + + // If all parameters are provided, the response will be a single delegation. + // In order to fit the return type, an array of delegations is returned. + if (delAddr && valAddr && denom) { + const res = await this.getReqFromChainID(chainID).get<{ + delegation: AllianceDelegation.Data; + balance: Coins.Data; + }>(url, params); + + return { + pagination: { + next_key: null, + total: 1, + }, + delegations: [AllianceDelegation.fromData(res.delegation)], + balance: Coins.fromData(res.balance), + }; + } else { + const res = await this.getReqFromChainID(chainID).get<{ + delegations: AllianceDelegation.Data[]; + balance: Coins.Data; + pagination: Pagination; + }>(url, params); + + return { + pagination: res.pagination, + delegations: res.delegations.map(d => AllianceDelegation.fromData(d)), + balance: Coins.fromData(res.balance), + }; + } } /** - * Query all paginated alliance delegations for a delegator addr and validator_addr + * Query paginated redelegations by delAddr. Optionally, you can also provide + * the denom parameter which will improve the query response time. The denom in + * the query will be URL encoded to allow querying for alliance assets with "/" + * or other special characters in their denom. * * @tags Query - * @name alliancesDelegationByValidator - * @summary Query all paginated alliance delegations for a delegator addr and validator_addr - * @request GET:/terra/alliances/delegations/{delegator_addr}/{validator_addr} + * @name queryAllianceRedelegations + * @summary Query for redelegations by delegator addr and denom + * @request GET:/terra/alliances/redelegations/{delAddr} or + * GET:/terra/alliances/redelegations/{denom}/{delAddr} */ - public async alliancesDelegationByValidator( - delegatorAddr: string, - validatorAddr: string, + public async queryAllianceRedelegations( + delAddr: string, + denom: string, params: Partial = {} ) { - return this.getReqFromAddress(delegatorAddr).get<{ - delegations: AllianceDelegationResponse[]; + const url = denom + ? `/terra/alliances/redelegations/${encodeURIComponent( + encodeURIComponent(denom) + )}/${delAddr}` + : `/terra/alliances/redelegations/${delAddr}`; + + const res = await this.getReqFromAddress(delAddr).get<{ + redelegations: AllianceRedelegation.Data[]; pagination: Pagination; - }>( - `/terra/alliances/delegations/${delegatorAddr}/${validatorAddr}`, - params - ); + }>(url, params); + + return { + redelegations: res.redelegations.map(r => + AllianceRedelegation.fromData(r) + ), + pagination: res.pagination, + }; } /** - * Query a delegation to an alliance by delegator addr, validator_addr and denom - * the denom can be both the ibc prefixed denom or any other alliance denom. + * Query paginated rewards by delAddr, valAddr and alliance denom. The denom in + * the query will be URL encoded to allow querying for alliance assets with "/" + * or other special characters in their denom. * * @tags Query - * @name allianceDelegation - * @summary Query a delegation to an alliance by delegator addr, validator_addr and denom - * @request GET:/terra/alliances/delegations/{delegator_addr}/{validator_addr}/{denom} + * @name queryAllianceRewards + * @summary Query alliance rewards by delegator addr, validator_addr and denom + * @request GET:/terra/alliances/rewards/{delAddr}/{valAddr}/{denom} */ - public async allianceDelegation( - delegatorAddr: string, - validatorAddr: string, + public async queryAllianceRewards( + delAddr: string, + valAddr: string, denom: string, params: Partial = {} ) { - return this.getReqFromAddress(delegatorAddr).get<{ - delegation: AllianceDelegationResponse[]; - pagination: Pagination; - }>( - `/terra/alliances/delegations/${delegatorAddr}/${validatorAddr}/${denom}`, - params - ); + const url = `/terra/alliances/rewards/${encodeURIComponent( + encodeURIComponent(denom) + )}/${valAddr}/${delAddr}`; + + const res = await this.getReqFromAddress(delAddr).get<{ + rewards: Coins.Data; + }>(url, params); + + return Coins.fromData(res.rewards); } /** - * Query for rewards by delegator addr, validator_addr and denom - * where denom can be either the ibc prefixed hash or any other native asset alliance denom + * Query alliances unbondings by delAddr where denom and valAddr are optional parameters, + * that valAddr depend on the denom. When all values are provided the query will be faster, + * Any denom specified in this query will be URL encoded to allow querying for alliance assets + * with "/" or other special characters in their denom. + * + * - When **delAddr** is provided, this query returns the unbondings for the provided address. + * - When **denom** and **delAddr** are provided, this query returns the unbondings for the + * specified address and denom. + * - When **delAddr**, **valAddr** and **denom** are provided, this query returns the unbondings + * for the specified address, validator and denom. * * @tags Query - * @name delegatorRewards - * @summary Query for rewards by delegator addr, validator_addr and denom - * @request GET:/terra/alliances/params + * @name queryAllianceUnbondings + * @summary Query alliance unbondings by delegator addr, validator_addr and denom + * @request GET:/terra/alliances/unbondings/{delAddr} + * GET:/terra/alliances/unbondings/{denom}/{delAddr} + * GET:/terra/alliances/unbondings/{denom}/{delAddr}/{valAddr} */ - public async delegatorRewards( - delegatorAddr: string, - validatorAddr: string, - denom: string, + public async queryAllianceUnbondings( + delAddr: string, + denom?: string, + valAddr?: string, params: Partial = {} ) { - return this.getReqFromAddress(delegatorAddr).get<{ rewards: Coins }>( - `/terra/alliances/rewards/${delegatorAddr}/${validatorAddr}/${denom}`, - params - ); + let url = '/terra/alliances/unbondings'; + + // Since the url is different when denom is provided, the url will be built + // based on the parameters provided + if (denom && valAddr) { + url += `/${encodeURIComponent( + encodeURIComponent(denom) + )}/${delAddr}/${valAddr}`; + } else if (denom) { + url += `/${encodeURIComponent(encodeURIComponent(denom))}/${delAddr}`; + } else { + url += `/${delAddr}`; + } + + const res = await this.getReqFromAddress(delAddr).get<{ + unbondings: AllianceUnbonding.Data[]; + }>(url, params); + + return res.unbondings.map(e => AllianceUnbonding.fromData(e)); } /** - * Query all validators that has alliance assets delegated to them + * Query all validators that have at least one user delegation. You can optionally provide valAddr + * to query a single validator. Providing the validatorAddr will deliver a faster response. + * This query returns data about the delegations shares, validator shares, + * and total staked tokens. * * @tags Query - * @name allianceValidators + * @name queryAllianceValidators * @summary Query all paginated alliance validators - * @request GET:/terra/alliances/validators + * @request GET:/terra/alliances/validators or + * GET:/terra/alliances/validators/{valAddr} */ - public async alliancesByValidators( + public async queryAllianceValidators( chainID: string, + valAddr?: ValAddress, params: Partial = {} ) { - return this.getReqFromChainID(chainID).get<{ - validators: AllianceValidator; - pagination: Pagination; - }>(`/terra/alliances/validators`, params); - } + if (valAddr) { + const res = await this.getReqFromChainID( + chainID + ).get(`/terra/alliances/validators/${valAddr}`); - /** - * Query an alliance validator that has alliance assets delegated to it - * - * @tags Query - * @name allianceValidators - * @summary Query alliance validator - * @request GET:/terra/alliances/validators/{validatorAddr} - */ - public async alliancesByValidator( - validatorAddr: string, - params: Partial = {} - ) { - return this.getReqFromAddress(validatorAddr).get( - `/terra/alliances/validators/${validatorAddr}`, - params - ); + return { + validators: [AllianceValidator.fromData(res)], + pagination: { + next_key: null, + total: 1, + }, + }; + } else { + const res = await this.getReqFromChainID(chainID).get<{ + validators: AllianceValidator.Data[]; + pagination: Pagination; + }>(`/terra/alliances/validators`, params); + + return { + validators: res.validators.map(v => AllianceValidator.fromData(v)), + pagination: res.pagination, + }; + } } } diff --git a/src/core/alliance/index.ts b/src/core/alliance/index.ts index 92629159..7fed6406 100644 --- a/src/core/alliance/index.ts +++ b/src/core/alliance/index.ts @@ -10,6 +10,7 @@ import { MsgUpdateAlliance, } from './proposals'; +export * from './models'; export * from './msgs'; export * from './proposals'; diff --git a/src/core/alliance/models/AllianceAsset.ts b/src/core/alliance/models/AllianceAsset.ts new file mode 100644 index 00000000..836b0c31 --- /dev/null +++ b/src/core/alliance/models/AllianceAsset.ts @@ -0,0 +1,109 @@ +import { RewardWeightRange } from './RewardWeightRange'; +import { Dec } from '../../../core/numeric'; + +export class AllianceAsset { + constructor( + /** Denom of the asset. It could either be a native token or an IBC token */ + public denom: string, + /** The reward weight specifies the ratio of rewards that will be given to each alliance asset + It does not need to sum to 1. rate = weight / total_weight. Native staking asset is always assumed to have a weight of 1. */ + public rewardWeight: Dec, + /** A positive take rate is a "tax" used for liquid staking derivatives. It defines a rate that is + applied per take_rate_interval that will be redirected to the distribution rewards pool */ + public takeRate: Dec, + // The total amount of tokens that are bonded to the alliance + public totalTokens: Dec, + // The total amount of validator shares that are bonded to the alliance + public totalValidatorShares: Dec, + // The time when the reward distribution starts + public rewardStartTime: Date, + // The rate at which the reward changes + public rewardChangeRate: Dec, + // The interval at which the reward changes when rewardWeightRange and rewardChangeRate are set + public rewardChangeInterval: string, + // The time when the last reward change occurred + public lastRewardChangeTime: Date, + /** set a bound of weight range to limit how much reward weights can scale. */ + public rewardWeightRange: RewardWeightRange, + /** flag to check if an asset has completed the initialization process after the reward delay */ + public isInitialized: boolean + ) {} + + public static fromData(data: AllianceAsset.Data, _?: boolean): AllianceAsset { + _; + const { + denom, + reward_weight, + take_rate, + total_tokens, + total_validator_shares, + reward_start_time, + reward_change_rate, + reward_change_interval, + last_reward_change_time, + reward_weight_range, + is_initialized, + } = data; + + return new AllianceAsset( + denom, + new Dec(reward_weight), + new Dec(take_rate), + new Dec(total_tokens), + new Dec(total_validator_shares), + new Date(reward_start_time), + new Dec(reward_change_rate), + reward_change_interval, + new Date(last_reward_change_time), + RewardWeightRange.fromData(reward_weight_range), + is_initialized + ); + } + + public toData(_?: boolean): AllianceAsset.Data { + _; + const { + denom, + rewardWeight, + takeRate, + totalTokens, + totalValidatorShares, + rewardStartTime, + rewardChangeRate, + rewardChangeInterval, + lastRewardChangeTime, + rewardWeightRange, + isInitialized, + } = this; + + return { + denom: denom, + reward_weight: rewardWeight.toString(), + take_rate: takeRate.toString(), + total_tokens: totalTokens.toString(), + total_validator_shares: totalValidatorShares.toString(), + reward_start_time: rewardStartTime.toString(), + reward_change_rate: rewardChangeRate.toString(), + reward_change_interval: rewardChangeInterval, + last_reward_change_time: lastRewardChangeTime.toString(), + reward_weight_range: rewardWeightRange.toData(), + is_initialized: isInitialized, + }; + } +} + +export namespace AllianceAsset { + export interface Data { + denom: string; + reward_weight: string; + take_rate: string; + total_tokens: string; + total_validator_shares: string; + reward_start_time: string; + reward_change_rate: string; + reward_change_interval: string; + last_reward_change_time: string; + reward_weight_range: RewardWeightRange.Data; + is_initialized: boolean; + } +} diff --git a/src/core/alliance/models/AllianceDelegation.ts b/src/core/alliance/models/AllianceDelegation.ts new file mode 100644 index 00000000..5d460fc1 --- /dev/null +++ b/src/core/alliance/models/AllianceDelegation.ts @@ -0,0 +1,80 @@ +import { Dec } from '../../../core/numeric'; +import { AccAddress, ValAddress } from 'core/bech32'; +import { RewardHistory } from './RewardHistory'; +import Long from 'long'; +import { Coin } from '../../../core/Coin'; + +export class AllianceDelegation { + constructor( + /** delegator_address is the bech32-encoded address of the delegator. */ + public delegatorAddress: AccAddress, + /** validator_address is the bech32-encoded address of the validator. */ + public validatorAddress: ValAddress, + /** denom of token staked */ + public denom: string, + /** shares define the Alliancedelegation shares received. */ + public shares: Dec, + public rewardHistory: RewardHistory[], + public lastRewardClaimHeight: Long + ) {} + + public static fromData( + data: AllianceDelegation.Data, + _?: boolean + ): AllianceDelegation { + _; + const { + delegator_address, + validator_address, + denom, + shares, + reward_history, + last_reward_claim_height, + } = data; + + return new AllianceDelegation( + delegator_address, + validator_address, + denom, + new Dec(shares), + reward_history?.map(r => RewardHistory.fromData(r)), + last_reward_claim_height + ); + } + + public toData(_?: boolean): AllianceDelegation.Data { + _; + const { + delegatorAddress, + validatorAddress, + denom, + shares, + rewardHistory, + lastRewardClaimHeight, + } = this; + + return { + delegator_address: delegatorAddress, + validator_address: validatorAddress, + denom: denom, + shares: shares.toString(), + reward_history: rewardHistory, + last_reward_claim_height: lastRewardClaimHeight, + }; + } +} + +export namespace AllianceDelegation { + export interface Data { + /** delegator_address is the bech32-encoded address of the delegator. */ + delegator_address: AccAddress; + /** validator_address is the bech32-encoded address of the validator. */ + validator_address: ValAddress; + /** denom of token staked */ + denom: string; + /** shares define the Alliancedelegation shares received. */ + shares: string; + reward_history: RewardHistory.Data[]; + last_reward_claim_height: Long; + } +} diff --git a/src/core/alliance/models/AllianceRedelegation.ts b/src/core/alliance/models/AllianceRedelegation.ts new file mode 100644 index 00000000..38d422f1 --- /dev/null +++ b/src/core/alliance/models/AllianceRedelegation.ts @@ -0,0 +1,63 @@ +import { AccAddress, ValAddress } from 'core/bech32'; +import { Coin } from '../../../core/Coin'; + +export class AllianceRedelegation { + constructor( + public delegatorAddress: AccAddress, + public srcValidatorAddress: ValAddress, + public dstValidatorAddress: ValAddress, + public balance: Coin, + public completionTime: Date + ) {} + + public static fromData( + data: AllianceRedelegation.Data, + _?: boolean + ): AllianceRedelegation { + _; + const { + delegator_address, + src_validator_address, + dst_validator_address, + balance, + completion_time, + } = data; + + return new AllianceRedelegation( + delegator_address, + src_validator_address, + dst_validator_address, + Coin.fromData(balance), + new Date(completion_time) + ); + } + + public toData(_?: boolean): AllianceRedelegation.Data { + _; + const { + delegatorAddress, + srcValidatorAddress, + dstValidatorAddress, + balance, + completionTime, + } = this; + + return { + delegator_address: delegatorAddress, + src_validator_address: srcValidatorAddress, + dst_validator_address: dstValidatorAddress, + balance: balance.toData(), + completion_time: completionTime.toString(), + }; + } +} + +export namespace AllianceRedelegation { + export interface Data { + delegator_address: string; + src_validator_address: string; + dst_validator_address: string; + balance: Coin.Data; + completion_time: string; + } +} diff --git a/src/core/alliance/models/AllianceUnbondings.ts b/src/core/alliance/models/AllianceUnbondings.ts new file mode 100644 index 00000000..491a52a0 --- /dev/null +++ b/src/core/alliance/models/AllianceUnbondings.ts @@ -0,0 +1,46 @@ +import { ValAddress } from 'core/bech32'; + +export class AllianceUnbonding { + constructor( + public validatorAddr: ValAddress, + public completionTime: Date, + public amount: number, + public denom: string + ) {} + + public static fromData( + data: AllianceUnbonding.Data, + _?: boolean + ): AllianceUnbonding { + _; + const { validator_addr, completion_time, amount, denom } = data; + + return new AllianceUnbonding( + validator_addr, + new Date(completion_time), + parseInt(amount), + denom + ); + } + + public toData(_?: boolean): AllianceUnbonding.Data { + _; + const { validatorAddr, completionTime, amount, denom } = this; + + return { + validator_addr: validatorAddr, + completion_time: completionTime.toString(), + amount: amount.toString(), + denom: denom, + }; + } +} + +export namespace AllianceUnbonding { + export interface Data { + validator_addr: string; + completion_time: string; + amount: string; + denom: string; + } +} diff --git a/src/core/alliance/models/AllianceValidator.ts b/src/core/alliance/models/AllianceValidator.ts new file mode 100644 index 00000000..e0671336 --- /dev/null +++ b/src/core/alliance/models/AllianceValidator.ts @@ -0,0 +1,58 @@ +import { ValAddress } from 'core/bech32'; +import { Coin } from '../../../core/Coin'; +import { AllianceValidatorAmount } from './AllianceValidatorAmount'; + +export class AllianceValidator { + constructor( + public validatorAddr: ValAddress, + public totalDelegationShares: Array, + public validatorShares: Array, + public totalStaked: Array + ) {} + + public static fromData( + data: AllianceValidator.Data, + _?: boolean + ): AllianceValidator { + _; + const { + validator_addr, + total_delegation_shares, + validator_shares, + total_staked, + } = data; + + return new AllianceValidator( + validator_addr, + total_delegation_shares.map(a => AllianceValidatorAmount.fromData(a)), + validator_shares.map(a => AllianceValidatorAmount.fromData(a)), + total_staked.map(a => AllianceValidatorAmount.fromData(a)) + ); + } + + public toData(_?: boolean): AllianceValidator.Data { + _; + const { + validatorAddr, + totalDelegationShares, + validatorShares, + totalStaked, + } = this; + + return { + validator_addr: validatorAddr, + total_delegation_shares: totalDelegationShares.map(a => a.toData()), + validator_shares: validatorShares.map(a => a.toData()), + total_staked: totalStaked.map(a => a.toData()), + }; + } +} + +export namespace AllianceValidator { + export interface Data { + validator_addr: string; + total_delegation_shares: Array; + validator_shares: Array; + total_staked: Array; + } +} diff --git a/src/core/alliance/models/AllianceValidatorAmount.ts b/src/core/alliance/models/AllianceValidatorAmount.ts new file mode 100644 index 00000000..c0e5cbe2 --- /dev/null +++ b/src/core/alliance/models/AllianceValidatorAmount.ts @@ -0,0 +1,29 @@ +import { Dec } from '../../../core/numeric'; + +export class AllianceValidatorAmount { + constructor(public denom: string, public amount: Dec) {} + + public static fromData( + proto: AllianceValidatorAmount.Data, + _?: boolean + ): AllianceValidatorAmount { + _; + const { denom, amount } = proto; + + return new AllianceValidatorAmount(denom, new Dec(amount)); + } + + public toData(_?: boolean): AllianceValidatorAmount.Data { + _; + const { denom, amount } = this; + + return { denom, amount: amount.toString() }; + } +} + +export namespace AllianceValidatorAmount { + export interface Data { + denom: string; + amount: string; + } +} diff --git a/src/core/alliance/models/Params.ts b/src/core/alliance/models/Params.ts new file mode 100644 index 00000000..113994cb --- /dev/null +++ b/src/core/alliance/models/Params.ts @@ -0,0 +1,49 @@ +export class AllianceParams { + // Model used to parse Alliance's module params from + // the plain model to the corresponding interfaces. + // rewardDelayTimee e.g. "604800s" + // takeRateClaimInterval e.g. "300s" + constructor( + public rewardDelayTime: string, + public takeRateClaimInterval: string, + public lastTakeRateClaimTime: Date + ) {} + + public static fromData( + data: AllianceParams.Data, + _?: boolean + ): AllianceParams { + _; + const { + reward_delay_time, + take_rate_claim_interval, + last_take_rate_claim_time, + } = data; + + return new AllianceParams( + reward_delay_time, + take_rate_claim_interval, + new Date(last_take_rate_claim_time) + ); + } + + public toData(_?: boolean): AllianceParams.Data { + _; + const { rewardDelayTime, lastTakeRateClaimTime, takeRateClaimInterval } = + this; + + return { + reward_delay_time: rewardDelayTime.toString(), + last_take_rate_claim_time: lastTakeRateClaimTime.toString(), + take_rate_claim_interval: takeRateClaimInterval.toString(), + }; + } +} + +export namespace AllianceParams { + export interface Data { + reward_delay_time: string; + take_rate_claim_interval: string; + last_take_rate_claim_time: string; + } +} diff --git a/src/core/alliance/models/RewardHistory.ts b/src/core/alliance/models/RewardHistory.ts new file mode 100644 index 00000000..ed2cd06f --- /dev/null +++ b/src/core/alliance/models/RewardHistory.ts @@ -0,0 +1,32 @@ +export class RewardHistory { + constructor( + public denom: string, + public index: string, + public alliance: string + ) {} + + public static fromData( + proto: RewardHistory.Data, + _?: boolean + ): RewardHistory { + _; + const { denom, index, alliance } = proto; + + return new RewardHistory(denom, index, alliance); + } + + public toData(_?: boolean): RewardHistory.Data { + _; + const { denom, index, alliance } = this; + + return { denom, index, alliance }; + } +} + +export namespace RewardHistory { + export interface Data { + denom: string; + index: string; + alliance: string; + } +} diff --git a/src/core/alliance/models/RewardWeightRange.ts b/src/core/alliance/models/RewardWeightRange.ts new file mode 100644 index 00000000..e9adbbaa --- /dev/null +++ b/src/core/alliance/models/RewardWeightRange.ts @@ -0,0 +1,32 @@ +import { Dec } from '../../../core/numeric'; + +export class RewardWeightRange { + constructor(public min: Dec, public max: Dec) {} + + public static fromData( + proto: RewardWeightRange.Data, + _?: boolean + ): RewardWeightRange { + _; + const { min, max } = proto; + + return new RewardWeightRange(new Dec(min), new Dec(max)); + } + + public toData(_?: boolean): RewardWeightRange.Data { + _; + const { min, max } = this; + + return { + min: min.toString(), + max: max.toString(), + }; + } +} + +export namespace RewardWeightRange { + export interface Data { + min: string; + max: string; + } +} diff --git a/src/core/alliance/models/index.ts b/src/core/alliance/models/index.ts new file mode 100644 index 00000000..bed31069 --- /dev/null +++ b/src/core/alliance/models/index.ts @@ -0,0 +1,9 @@ +export * from './AllianceAsset'; +export * from './AllianceDelegation'; +export * from './AllianceRedelegation'; +export * from './AllianceUnbondings'; +export * from './AllianceValidator'; +export * from './AllianceValidatorAmount'; +export * from './Params'; +export * from './RewardHistory'; +export * from './RewardWeightRange';