diff --git a/.mocharc.json b/.mocharc.json index 17d3461..c9423b6 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -1,5 +1,5 @@ { "exit": true, "recursive": true, - "timeout": 60000 + "timeout": 120000 } diff --git a/src/__tests__/acceptance/staking-contracts-service.acceptance.ts b/src/__tests__/acceptance/staking-contracts-service.acceptance.ts index b2bfd82..7033d2f 100644 --- a/src/__tests__/acceptance/staking-contracts-service.acceptance.ts +++ b/src/__tests__/acceptance/staking-contracts-service.acceptance.ts @@ -32,6 +32,18 @@ describe('Staking contracts service', () => { }); }); + it.skip('gets staked token ids by asset type', async () => { + const contracts = service.stakingContracts; + await pMap(contracts, async contract => { + const staked = await service.getStakedTokenIdsByAssetType( + '0x9abbf7218c65c4d22c8483b5d6be93075a3c159c', + 1, + contract.supportedAssets[0].asset, + ); + expect(staked).to.be.Array(); + }); + }); + it('gets staked token balances', async () => { const contracts = service.stakingContracts; await pMap(contracts, async contract => { diff --git a/src/adapters/ririsu.adapter.ts b/src/adapters/ririsu.adapter.ts index dbd5d59..0e44110 100644 --- a/src/adapters/ririsu.adapter.ts +++ b/src/adapters/ririsu.adapter.ts @@ -40,12 +40,18 @@ export class RirisuStakingContractAdapter extends BaseStakingContractAdapter { }, ]; - getStakedTokenIds(owner: string, assetType = 'Ririsu'): Promise { + async getStakedTokenIds( + owner: string, + assetType = 'Ririsu', + ): Promise { + const asset = this.getStakingAsset(assetType); + if (asset == null) return []; + const contract = RirisuStaking__factory.connect( this.contractAddress, this.provider, ); - if (assetType.toLowerCase() === 'sanctum') { + if (asset.name?.toLowerCase() === 'sanctum') { return contract.stakedSanctums(owner); } return contract.stakedRiris(owner); diff --git a/src/services/staking-contracts.service.ts b/src/services/staking-contracts.service.ts index 51aeb40..f55dcff 100644 --- a/src/services/staking-contracts.service.ts +++ b/src/services/staking-contracts.service.ts @@ -9,14 +9,14 @@ import { AssetType, AssetTypeParams, } from '@collabland/chain'; -import {debugFactory} from '@collabland/common'; +import {debugFactory, pMap} from '@collabland/common'; import { BindingScope, ContextTags, extensions, injectable, } from '@loopback/core'; -import {utils} from 'ethers'; +import {BigNumber, utils} from 'ethers'; import { STAKING_ADAPTERS_EXTENSION_POINT, STAKING_CONTRACTS_SERVICE, @@ -107,6 +107,60 @@ export class StakingContractsService { return adapter; } + private async getAdaptersByAssetType(chainId = 1, assetName: string) { + const adapters = this.adapters.filter(a => a.chainId ?? 1 === chainId); + const list: StackingContractAdapter[] = []; + for (const a of adapters) { + const supported = await a.isAssetSupported(assetName); + if (supported) { + list.push(a); + } + } + return list; + } + + /** + * Get staked token ids for the given asset type + * @param owner - Owner address + * @param chainId - Chain id + * @param assetName - Asset name of the original token + * @returns + */ + async getStakedTokenIdsByAssetType( + owner: string, + chainId = 1, + assetName: string, + ) { + const adapters = await this.getAdaptersByAssetType(chainId, assetName); + const ids = await pMap(adapters, a => { + return a.getStakedTokenIds(owner, assetName); + }); + return ids.flat(); + } + + /** + * Get staked token balance for the given asset type + * @param owner - Owner address + * @param chainId - Chain id + * @param assetName - Asset name of the original token + * @returns + */ + async getStakedBalanceByAssetType( + owner: string, + chainId = 1, + assetName: string, + ) { + const adapters = await this.getAdaptersByAssetType(chainId, assetName); + const balances = await pMap(adapters, a => { + return a.getStakedTokenBalance(owner, assetName); + }); + let total = BigNumber.from(0); + for (const bal of balances) { + total = total.add(bal); + } + return total; + } + /** * Get a list of token ids staked by the owner * @param owner - Owner's wallet address diff --git a/src/staking.ts b/src/staking.ts index 52ba776..6cdac73 100644 --- a/src/staking.ts +++ b/src/staking.ts @@ -36,6 +36,12 @@ export interface StackingContractAdapter { */ supportedAssets: StakingAsset[]; + /** + * Check if the given asset name is supported by this staking contract + * @param asset - Asset name such as `ERC721:0x...` + */ + isAssetSupported(assetName: string): Promise; + /** * Get asset type that can be staked to the contract * @param assetName - Name of the asset if the staking contract allows multiple @@ -78,6 +84,8 @@ const defaultEthersProviderService = { export abstract class BaseStakingContractAdapter implements StackingContractAdapter { + contractName?: string | undefined; + @inject(ETHERS_PROVIDER_SERVICE, {optional: true}) providerService: EthersProviderService = defaultEthersProviderService; @@ -94,14 +102,28 @@ export abstract class BaseStakingContractAdapter return this.provider; } + async isAssetSupported(assetName: string): Promise { + return this.supportedAssets.some( + a => a.asset.toLowerCase() === assetName.toLowerCase(), + ); + } + + getStakingAsset(nameOrAssetType?: string) { + const assetType = nameOrAssetType?.toLowerCase(); + let asset = this.supportedAssets.find( + a => + a.name?.toLowerCase() === assetType || + a.asset.toLowerCase() === assetType, + ); + if (asset != null) return asset; + // Find the asset that doesn't have a name + asset = this.supportedAssets.find(a => a.name == null); + if (asset != null) return asset; + return this.supportedAssets[0]; + } + getStakingAssetType(name?: string) { - let asset = this.supportedAssets.find(a => a.name === name); - if (asset == null) { - asset = this.supportedAssets.find(a => a.name == null); - if (asset == null) { - asset = this.supportedAssets[0]; - } - } + const asset = this.getStakingAsset(name); if (asset == null) return undefined; return new AssetType({ chainId: {