diff --git a/packages/xchain-midgard-query/CHANGELOG.md b/packages/xchain-midgard-query/CHANGELOG.md index 713d7cf0a..bb3fb8028 100644 --- a/packages/xchain-midgard-query/CHANGELOG.md +++ b/packages/xchain-midgard-query/CHANGELOG.md @@ -1,3 +1,9 @@ +# v0.1.2 (2023-09-18) + +## Update + +- Function getTHORNameReverseLookup + # v0.1.1 (2023-09-10) ## Update diff --git a/packages/xchain-midgard-query/__e2e__/thorchain-midgard-query-e2e.ts b/packages/xchain-midgard-query/__e2e__/thorchain-midgard-query-e2e.ts index 697a44a47..ba6719901 100644 --- a/packages/xchain-midgard-query/__e2e__/thorchain-midgard-query-e2e.ts +++ b/packages/xchain-midgard-query/__e2e__/thorchain-midgard-query-e2e.ts @@ -63,4 +63,16 @@ describe('Midgard-query liquidity action end to end Tests', () => { console.log('thorname', thorname) printThorname(thorname) }) + it(`Should get thornames by alias address`, async () => { + const thornames = await midgardQuery.midgardCache.midgard.getTHORNameReverseLookup( + 'bc1qy6hm644lr8ezl37wn0x59fzd5ps4k4hpufw0dp', + ) + console.log('thornames', thornames) + }) + it(`Try get thornames by alias address`, async () => { + const thornames = await midgardQuery.midgardCache.midgard.getTHORNameReverseLookup( + 'thor138yxnksuhm37j9qqcugt3xzh8cykgvmmtgt63u', + ) + console.log('thornames', thornames) + }) }) diff --git a/packages/xchain-midgard-query/package.json b/packages/xchain-midgard-query/package.json index 3fda6bc9d..3f722cf78 100644 --- a/packages/xchain-midgard-query/package.json +++ b/packages/xchain-midgard-query/package.json @@ -1,6 +1,6 @@ { "name": "@xchainjs/xchain-midgard-query", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "description": "Module that is responsible for get data from Midgard API", "keywords": [ diff --git a/packages/xchain-midgard-query/src/utils/midgard.ts b/packages/xchain-midgard-query/src/utils/midgard.ts index a2da029fe..dfe9b3703 100644 --- a/packages/xchain-midgard-query/src/utils/midgard.ts +++ b/packages/xchain-midgard-query/src/utils/midgard.ts @@ -1,5 +1,12 @@ import { Network } from '@xchainjs/xchain-client' -import { Configuration, MidgardApi, PoolDetail, SaverDetails, THORNameDetails } from '@xchainjs/xchain-midgard' +import { + Configuration, + MidgardApi, + PoolDetail, + ReverseTHORNames, + SaverDetails, + THORNameDetails, +} from '@xchainjs/xchain-midgard' import axios from 'axios' import axiosRetry from 'axios-retry' @@ -84,4 +91,23 @@ export class Midgard { } throw Error(`Midgard not responding`) } + + public async getTHORNameReverseLookup(address: string): Promise { + for (const api of this.midgardApis) { + try { + const resp = await api.getTHORNamesByAddress(address) + if (resp.status == 404) { + return [] + } else if (resp.status == 200) { + return resp.data + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if (e.response.status == 404) { + return [] + } + } + } + throw Error(`Midgard not responding`) + } } diff --git a/packages/xchain-thorchain-amm/CHANGELOG.md b/packages/xchain-thorchain-amm/CHANGELOG.md index d70913dd4..35ac9b4d5 100644 --- a/packages/xchain-thorchain-amm/CHANGELOG.md +++ b/packages/xchain-thorchain-amm/CHANGELOG.md @@ -1,3 +1,9 @@ +# v0.7.1 (2023-09-18) + +## Update + +- Add functions getThornamesByAddress, registerThorname and updateThorname + # v0.7.0 (2023-09-10) ## Update diff --git a/packages/xchain-thorchain-amm/__e2e__/thorchain-thornames.e2e.ts b/packages/xchain-thorchain-amm/__e2e__/thorchain-thornames.e2e.ts new file mode 100644 index 000000000..d8d3d1f98 --- /dev/null +++ b/packages/xchain-thorchain-amm/__e2e__/thorchain-thornames.e2e.ts @@ -0,0 +1,13 @@ +import { ThorchainQuery } from '@xchainjs/xchain-thorchain-query' + +import { ThorchainAMM } from '../src/thorchain-amm' + +const thorchainQueryMainnet = new ThorchainQuery() +const mainetThorchainAmm = new ThorchainAMM(thorchainQueryMainnet) + +describe('Thorchain-amm thornames', () => { + it(`Should get all thornames by address`, async () => { + const thornames = await mainetThorchainAmm.getThornamesByAddress('0xc50531811f3d8161a2b53349974ae4c7c6d3bfba') + console.log('thornames', thornames) + }) +}) diff --git a/packages/xchain-thorchain-amm/__e2e__/wallet.e2e.ts b/packages/xchain-thorchain-amm/__e2e__/wallet.e2e.ts index c0bed66bf..da6bb5b92 100644 --- a/packages/xchain-thorchain-amm/__e2e__/wallet.e2e.ts +++ b/packages/xchain-thorchain-amm/__e2e__/wallet.e2e.ts @@ -1,6 +1,15 @@ import cosmosclient from '@cosmos-client/core' import { Network } from '@xchainjs/xchain-client' -import { ThorchainCache, ThorchainQuery, Thornode } from '@xchainjs/xchain-thorchain-query' +import { + AssetBTC, + AssetETH, + BCHChain, + BTCChain, + ETHChain, + ThorchainCache, + ThorchainQuery, + Thornode, +} from '@xchainjs/xchain-thorchain-query' import { baseToAsset, formatAssetAmountCurrency, register9Rheader } from '@xchainjs/xchain-util' import axios from 'axios' @@ -42,4 +51,66 @@ describe('xchain-swap wallet Tests', () => { console.error(e) } }) + it(`Register thorname`, async () => { + try { + await mainnetWallet.registerThorname({ + thorname: 'hippocampus', + chain: BTCChain, + preferredAsset: AssetBTC, + expirity: new Date(2024, 8, 11, 14, 30, 0, 0), + }) + } catch (e) { + console.error(e) + } + }) + + it(`Update thorname`, async () => { + try { + await mainnetWallet.updateThorname({ + thorname: 'hippo', + chain: BCHChain, + chainAddress: 'qz53fqdfjqwefhff9xf3dmq45g3l7jydyu6d990e76', + }) + } catch (e) { + console.error(e) + } + }) + + it(`Update thorname with expirity`, async () => { + try { + await mainnetWallet.updateThorname({ + thorname: 'hippo', + chain: BCHChain, + chainAddress: 'qz53fqdfjqwefhff9xf3dmq45g3l7jydyu6d990e76', + expirity: new Date(2024, 9, 11, 14, 30, 0, 0), + }) + } catch (e) { + console.error(e) + } + }) + + it(`Update thorname prefered asset`, async () => { + try { + const hash = await mainnetWallet.updateThorname({ + thorname: 'hippo', + chain: ETHChain, + preferredAsset: AssetETH, + }) + console.log('hash', hash) + } catch (e) { + console.error(e) + } + }) + + it(`Try update thorname is not yours`, async () => { + try { + await mainnetWallet.updateThorname({ + thorname: 'dx', + chain: ETHChain, + chainAddress: '0xc50531811f3d8161a2b53349974ae4c7c6d3bfba', + }) + } catch (e) { + console.error(e) + } + }) }) diff --git a/packages/xchain-thorchain-amm/package.json b/packages/xchain-thorchain-amm/package.json index aab3411e4..5374833ef 100644 --- a/packages/xchain-thorchain-amm/package.json +++ b/packages/xchain-thorchain-amm/package.json @@ -1,6 +1,6 @@ { "name": "@xchainjs/xchain-thorchain-amm", - "version": "0.7.0", + "version": "0.7.1", "description": "module that exposes estimating & swappping cryptocurrency assets on thorchain", "keywords": [ "THORChain", @@ -55,7 +55,7 @@ "@xchainjs/xchain-midgard": "^0.5.1", "@xchainjs/xchain-thorchain": "^0.28.6", "@xchainjs/xchain-thornode": "^0.3.6", - "@xchainjs/xchain-thorchain-query": "^0.6.0", + "@xchainjs/xchain-thorchain-query": "^0.6.1", "@xchainjs/xchain-util": "^0.13.1", "@xchainjs/xchain-utxo-providers": "^0.2.2", "axios": "^1.3.6", @@ -91,7 +91,7 @@ "@xchainjs/xchain-midgard": "^0.5.1", "@xchainjs/xchain-thorchain": "^0.28.6", "@xchainjs/xchain-thornode": "^0.3.6", - "@xchainjs/xchain-thorchain-query": "^0.6.0", + "@xchainjs/xchain-thorchain-query": "^0.6.1", "@xchainjs/xchain-util": "^0.13.1", "@xchainjs/xchain-utxo-providers": "^0.2.2", "axios": "^1.3.6", diff --git a/packages/xchain-thorchain-amm/src/thorchain-amm.ts b/packages/xchain-thorchain-amm/src/thorchain-amm.ts index e227a0180..4285de973 100644 --- a/packages/xchain-thorchain-amm/src/thorchain-amm.ts +++ b/packages/xchain-thorchain-amm/src/thorchain-amm.ts @@ -268,4 +268,13 @@ export class ThorchainAMM { toAddress: `${withdrawLoan.inboundAddress}`, }) } + + /** + * Get all Thornames and its data associated owned by an address + * @param address - address + * @returns thornames data + */ + public async getThornamesByAddress(address: string) { + return this.thorchainQuery.thorchainCache.midgardQuery.midgardCache.midgard.getTHORNameReverseLookup(address) + } } diff --git a/packages/xchain-thorchain-amm/src/types.ts b/packages/xchain-thorchain-amm/src/types.ts index dd7243f4a..02b601737 100644 --- a/packages/xchain-thorchain-amm/src/types.ts +++ b/packages/xchain-thorchain-amm/src/types.ts @@ -64,3 +64,21 @@ export type LoanCloseParams = { amount: CryptoAmount toAddress: Address } + +export type RegisterThornameParams = { + thorname: string + owner?: string + chain?: string + chainAddress?: string + preferredAsset?: Asset + expirity?: Date +} + +export type UpdateThornameParams = { + thorname: string + owner?: string + chain?: string + chainAddress?: string + preferredAsset?: Asset + expirity?: Date +} diff --git a/packages/xchain-thorchain-amm/src/wallet.ts b/packages/xchain-thorchain-amm/src/wallet.ts index 0c380d6e7..ac5c4abf0 100644 --- a/packages/xchain-thorchain-amm/src/wallet.ts +++ b/packages/xchain-thorchain-amm/src/wallet.ts @@ -1,6 +1,6 @@ import { Client as AvaxClient, defaultAvaxParams } from '@xchainjs/xchain-avax' import { Client as BnbClient } from '@xchainjs/xchain-binance' -import { Client as BtcClient } from '@xchainjs/xchain-bitcoin' +import { BTCChain, Client as BtcClient } from '@xchainjs/xchain-bitcoin' import { Client as BchClient } from '@xchainjs/xchain-bitcoincash' import { Client as BscClient, defaultBscParams } from '@xchainjs/xchain-bsc' import { FeeOption, Network, XChainClient } from '@xchainjs/xchain-client' @@ -11,7 +11,7 @@ import { Client as LtcClient } from '@xchainjs/xchain-litecoin' import { Client as MayaClient } from '@xchainjs/xchain-mayachain' import { Client as ThorClient, THORChain, ThorchainClient } from '@xchainjs/xchain-thorchain' import { CryptoAmount, ThorchainQuery } from '@xchainjs/xchain-thorchain-query' -import { Address, Asset } from '@xchainjs/xchain-util' +import { Address, Asset, assetFromString } from '@xchainjs/xchain-util' import { AddLiquidity, @@ -19,7 +19,9 @@ import { ExecuteSwap, LoanCloseParams, LoanOpenParams, + RegisterThornameParams, TxSubmitted, + UpdateThornameParams, WithdrawLiquidity, } from './types' import { EvmHelper } from './utils/evm-helper' @@ -414,6 +416,88 @@ export class Wallet { } } + /** + * Register a THORName with a default expirity of one year. By default chain and chainAddress is getting from wallet instance and is BTC. + * By default owner is getting from wallet + * @param thorname - Name to register + * @param chain - Chain to add alias + * @param chainAddress - Address to add to chain alias + * @param owner - Owner address (rune address) + * @param preferredAsset - referred asset + * @param expirity - expirity of the domain in MILLISECONDS + * @param isUpdate - true only if the domain is already register and you want to update its data + * @returns memo and value of deposit + */ + async registerThorname(params: RegisterThornameParams) { + const chainClient = this.clients[params.chain || BTCChain] + const thorClient = this.clients.THOR + + if (!chainClient || !thorClient) { + throw Error('Can not find a wallet client') + } + + const thornameEstimation = await this.thorchainQuery.estimateThorname({ + ...params, + chain: params.chain || BTCChain, + chainAddress: params.chainAddress || chainClient.getAddress(), + owner: params.owner || thorClient.getAddress(), + }) + + const castedThorClient = thorClient as unknown as ThorchainClient + const result = await castedThorClient.deposit({ + asset: thornameEstimation.value.asset, + amount: thornameEstimation.value.baseAmount, + memo: thornameEstimation.memo, + }) + + return result + } + + /** + * Register a THORName with a default expirity of one year. By default chain and chainAddress is getting from wallet instance and is BTC. + * By default owner is getting from wallet + * @param thorname - Name to register + * @param chain - Chain to add alias + * @param chainAddress - Address to add to chain alias + * @param owner - Owner address (rune address) + * @param preferredAsset - referred asset + * @param expirity - expirity of the domain in MILLISECONDS + * @param isUpdate - true only if the domain is already register and you want to update its data + * @returns memo and value of deposit + */ + async updateThorname(params: UpdateThornameParams) { + const chainClient = this.clients[params.chain || BTCChain] + const thorClient = this.clients.THOR + + if (!chainClient || !thorClient) { + throw Error('Can not find a wallet client') + } + + const thornameDetail = await this.thorchainQuery.getThornameDetails(params.thorname) + + if (thornameDetail?.owner !== thorClient.getAddress()) { + throw Error('You cannot update a domain that is not yours') + } + + const thornameEstimation = await this.thorchainQuery.estimateThorname({ + ...params, + chain: params.chain || BTCChain, + isUpdate: true, + preferredAsset: params.preferredAsset || assetFromString(thornameDetail.preferredAsset), + chainAddress: params.chainAddress || chainClient.getAddress(), + }) + + const castedThorClient = thorClient as unknown as ThorchainClient + + const result = await castedThorClient.deposit({ + asset: thornameEstimation.value.asset, + amount: thornameEstimation.value.baseAmount, + memo: thornameEstimation.memo, + }) + + return result + } + /** Function handles liquidity add for all non rune assets * * @param params - parameters for add liquidity diff --git a/packages/xchain-thorchain-query/CHANGELOG.md b/packages/xchain-thorchain-query/CHANGELOG.md index 9861ef9ab..9622426c2 100644 --- a/packages/xchain-thorchain-query/CHANGELOG.md +++ b/packages/xchain-thorchain-query/CHANGELOG.md @@ -1,3 +1,9 @@ +# v0.6.1 (2023-09-18) + +## Update + +- New functions estimateThorname and getThornameDetails + # v0.6.0 (2023-09-10) ## Update diff --git a/packages/xchain-thorchain-query/__e2e__/thorchain-thorname.e2e.ts b/packages/xchain-thorchain-query/__e2e__/thorchain-thorname.e2e.ts new file mode 100644 index 000000000..aa5a694fc --- /dev/null +++ b/packages/xchain-thorchain-query/__e2e__/thorchain-thorname.e2e.ts @@ -0,0 +1,74 @@ +import { ThorchainCache } from '../src/thorchain-cache' +import { ThorchainQuery } from '../src/thorchain-query' +import { AssetBTC, BTCChain } from '../src/utils' + +const thorchainCache = new ThorchainCache() +const thorchainQuery = new ThorchainQuery(thorchainCache) + +const btcAddress = 'bc1q3q6gfcg2n4c7hdzjsvpq5rp9rfv5t59t5myz5v' +const owner = 'thor1tqpyn3athvuj8dj7nu5fp0xm76ut86sjcl3pqu' + +// Test User Functions - single and double swap using mock pool data +describe('Thorchain-query thorname Integration Tests', () => { + it('should fetch thorname details', async () => { + const thorname = await thorchainQuery.getThornameDetails('swapper') + console.log(thorname) + }) + it('Estimate update thorname with expirity', async () => { + const thorname = await thorchainQuery.estimateThorname({ + thorname: 'hippo', + isUpdate: true, + chain: BTCChain, + chainAddress: btcAddress, + owner: owner, + preferredAsset: AssetBTC, + expirity: new Date(2024, 9, 11, 14, 30, 0, 0), + }) + console.log(thorname.value.baseAmount.amount().toString()) + console.log(thorname.memo) + }) + it('Estimate update thorname without expirity', async () => { + const thorname = await thorchainQuery.estimateThorname({ + thorname: 'dx', + isUpdate: true, + chain: BTCChain, + chainAddress: btcAddress, + preferredAsset: AssetBTC, + }) + console.log(thorname.value.baseAmount.amount().toString()) + console.log(thorname.memo) + }) + it('Estimate not registered thorname without expirity', async () => { + const thorname = await thorchainQuery.estimateThorname({ + thorname: 'hippo-2', + chain: BTCChain, + chainAddress: btcAddress, + owner: owner, + preferredAsset: AssetBTC, + }) + console.log(thorname.value.baseAmount.amount().toString()) + console.log(thorname.memo) + }) + it('Estimate register not prefered assets with expirity', async () => { + const thorname = await thorchainQuery.estimateThorname({ + thorname: 'hippo-2', + chain: BTCChain, + chainAddress: btcAddress, + owner: owner, + expirity: new Date(2024, 8, 11, 14, 30, 0, 0), + }) + console.log(thorname.value.baseAmount.amount().toString()) + console.log(thorname.memo) + }) + it('Try to estimate already registered thorname', async () => { + const thorname = await thorchainQuery.estimateThorname({ + thorname: 'dx', + chain: BTCChain, + chainAddress: btcAddress, + owner: owner, + preferredAsset: AssetBTC, + expirity: new Date(2024, 8, 11, 14, 30, 0, 0), + }) + console.log(thorname.value.baseAmount.amount().toString()) + }) +}) diff --git a/packages/xchain-thorchain-query/package.json b/packages/xchain-thorchain-query/package.json index 4a131df74..a35cb0973 100644 --- a/packages/xchain-thorchain-query/package.json +++ b/packages/xchain-thorchain-query/package.json @@ -1,6 +1,6 @@ { "name": "@xchainjs/xchain-thorchain-query", - "version": "0.6.0", + "version": "0.6.1", "license": "MIT", "description": "Thorchain query module that is resposible for estimating swap calculations and add/remove liquidity for thorchain ", "keywords": [ @@ -35,7 +35,7 @@ "devDependencies": { "@xchainjs/xchain-client": "^0.14.2", "@xchainjs/xchain-thornode": "^0.3.6", - "@xchainjs/xchain-midgard-query": "^0.1.1", + "@xchainjs/xchain-midgard-query": "^0.1.2", "@xchainjs/xchain-util": "^0.13.1", "axios": "^1.3.6", "axios-retry": "^3.2.5", @@ -44,7 +44,7 @@ "peerDependencies": { "@xchainjs/xchain-client": "^0.14.2", "@xchainjs/xchain-thornode": "^0.3.6", - "@xchainjs/xchain-midgard-query": "^0.1.1", + "@xchainjs/xchain-midgard-query": "^0.1.2", "@xchainjs/xchain-util": "^0.13.1", "axios": "^1.3.6", "axios-retry": "^3.2.5", diff --git a/packages/xchain-thorchain-query/src/index.ts b/packages/xchain-thorchain-query/src/index.ts index 8583ca300..481dd8892 100644 --- a/packages/xchain-thorchain-query/src/index.ts +++ b/packages/xchain-thorchain-query/src/index.ts @@ -6,3 +6,4 @@ export * from './crypto-amount' export * from './chain-defaults' export * from './types' export * from './utils' +export * from './chain-defaults' diff --git a/packages/xchain-thorchain-query/src/thorchain-cache.ts b/packages/xchain-thorchain-query/src/thorchain-cache.ts index 5eeb90d69..3e935e700 100644 --- a/packages/xchain-thorchain-query/src/thorchain-cache.ts +++ b/packages/xchain-thorchain-query/src/thorchain-cache.ts @@ -27,10 +27,9 @@ export class ThorchainCache { /** * Constructor to create a ThorchainCache * - * @param midgard - an instance of the midgard API (could be pointing to stagenet,testnet,mainnet) + * @param thornode - an instance of the thornode API (could be pointing to stagenet,testnet,mainnet) * @param midgardQuery - an instance of the midgard query class (could be pointing to stagenet,testnet,mainnet) * @param expirePoolCacheMillis - how long should the pools be cached before expiry - * @param expireAsgardCacheMillis - how long should the inboundAsgard Addresses be cached before expiry * @param expireInboundDetailsCacheMillis - how long should the InboundDetails be cached before expiry * @param expireNetworkValuesCacheMillis - how long should the Mimir/Constants be cached before expiry * @returns ThorchainCache diff --git a/packages/xchain-thorchain-query/src/thorchain-query.ts b/packages/xchain-thorchain-query/src/thorchain-query.ts index 60e4bdb9a..490a33172 100644 --- a/packages/xchain-thorchain-query/src/thorchain-query.ts +++ b/packages/xchain-thorchain-query/src/thorchain-query.ts @@ -1,5 +1,6 @@ -import { LastBlock } from '@xchainjs/xchain-thornode' +import { LastBlock, Thorname } from '@xchainjs/xchain-thornode' import { + Address, Asset, Chain, assetAmount, @@ -30,9 +31,12 @@ import { PoolRatios, PostionDepositValue, QuoteSwapParams, + QuoteThornameParams, SaverFees, SaversPosition, SaversWithdraw, + ThornameAlias, + ThornameDetails, TotalFees, TxDetails, UnitData, @@ -181,25 +185,6 @@ export class ThorchainQuery { return txDetails } - // /** - // * This is no longer used - // * @param params - swap object - // * @returns - constructed memo string - // */ - // private constructSwapMemo(memo: string, interfaceID: string): string { - // const memoPart = memo.split(':') - // if (memoPart.length > 3) { - // memoPart[3] = - // memoPart[3].length >= 3 ? memoPart[3].substring(0, memoPart[3].length - 3).concat(interfaceID) : interfaceID - // let outmemo = '' - // for (let i = 0; i < memoPart.length; i++) { - // outmemo = outmemo.concat(`${memoPart[i]}:`) - // } - // return outmemo.substring(0, outmemo.length - 1) - // } - // return memo - // } - /** * Works out how long an outbound Tx will be held by THORChain before sending. * @@ -603,7 +588,6 @@ export class ThorchainQuery { // request param amount should always be in 1e8 which is why we pass in adjusted decimals if chain decimals != 8 const newAddAmount = addAmount.baseAmount.decimal != 8 ? getBaseAmountWithDiffDecimals(addAmount, 8) : addAmount.baseAmount.amount() - // Fetch quote const depositQuote = await this.thorchainCache.thornode.getSaversDepositQuote( assetToString(addAmount.asset), @@ -1011,4 +995,105 @@ export class ThorchainQuery { return loanCloseQuote } + + /** + * + * @param thorname - input param + * @returns retrieves details for a thorname + */ + public async getThornameDetails(thorname: string, height?: number): Promise { + const errors: string[] = [] + + const thornameResp = await this.thorchainCache.thornode.getThornameDetails(thorname, height) + const thornameRawData = thornameResp as unknown as Thorname // TODO: Until integrate THORNode PR + const response: { error?: string } = JSON.parse(JSON.stringify(thorname)) + if (response.error) errors.push(`Thornode request quote failed: ${response.error}`) + if (errors.length > 0) { + const errorResp: ThornameDetails = { + name: '', + expireBlockHeight: 0, + owner: '', + preferredAsset: '', + affiliateCollectorRune: '', + aliases: [], + error: errors, + } + return errorResp + } + + const thornameAliases: ThornameAlias[] = thornameRawData.aliases.map((alias) => ({ + chain: alias.chain as Chain, + address: alias.address as Address, + })) + + const thornameDetails: ThornameDetails = { + name: thornameRawData.name || '', + expireBlockHeight: thornameRawData.expire_block_height || 0, + owner: thornameRawData.owner || '', + preferredAsset: thornameRawData.preferred_asset, + affiliateCollectorRune: thornameRawData.affiliate_collector_rune || '', + aliases: thornameAliases, + } + + return thornameDetails // Return the array + } + + /** + * Generate the memo and estimate the cost of register or update a THORName + * @param thorname - Name to register + * @param chain - Chain to update / register + * @param chainAddress - Address to add to chain alias + * @param owner - Owner address (rune address) + * @param preferredAsset - referred asset + * @param expirity - expirity of the domain in MILLISECONDS + * @param isUpdate - true only if the domain is already register and you want to update its data + * @returns memo and value of deposit + */ + public async estimateThorname(params: QuoteThornameParams) { + // CHECK IF ALREADY EXISTS + const thornameDetails = (await this.thorchainCache.thornode.getThornameDetails( + params.thorname, + )) as unknown as Thorname // TODO: Until integrate THORNode PR + + if (thornameDetails && !params.isUpdate) { + throw Error('Thorname already reistered') + } + + const blockData = await this.thorchainCache.thornode.getLastBlock() + const currentThorchainHeight = blockData[0].thorchain + const currentHeightForExpirity = params.isUpdate + ? (thornameDetails?.expire_block_height as number) + : currentThorchainHeight + + // DEFAULT EXPIRITY + let numberOfBlocksToAddToExpirity = params.isUpdate ? 0 : 5259600 // One year by default + + // COMPUTE EXPIRITY HEIGHT + if (params.expirity) { + const currentTimestamp = Math.floor(Date.now() / 1000) + const expirityTimestamp = Math.floor(params.expirity.getTime() / 1000) + const numberOfSecondsToExpire = expirityTimestamp - currentTimestamp + const numberOfBlocks = Math.round(numberOfSecondsToExpire / 6) + const newHeightExpirity = currentThorchainHeight + numberOfBlocks + numberOfBlocksToAddToExpirity = thornameDetails?.expire_block_height + ? newHeightExpirity - thornameDetails?.expire_block_height + : numberOfBlocks + } + // COMPUTE VALUE + const constantsDetails = await this.thorchainCache.thornode.getTcConstants() + const oneTimeFee = params.isUpdate ? baseAmount(0) : baseAmount(constantsDetails['TNSRegisterFee']) + const totalFeePerBlock = baseAmount(constantsDetails['TNSFeePerBlock']).times( + numberOfBlocksToAddToExpirity > 0 ? numberOfBlocksToAddToExpirity : 0, + ) + const totalCost = new CryptoAmount(oneTimeFee.plus(totalFeePerBlock), AssetRuneNative) + const thornameMemo = `~:${params.thorname}:${params.chain}:${params.chainAddress}:${ + params.owner ? params.owner : '' + }:${params.preferredAsset ? assetToString(params.preferredAsset) : ''}:${ + currentHeightForExpirity + numberOfBlocksToAddToExpirity + }` + return { + memo: thornameMemo, + value: totalCost, + } + } } diff --git a/packages/xchain-thorchain-query/src/types.ts b/packages/xchain-thorchain-query/src/types.ts index edf5c9eea..9448521b9 100644 --- a/packages/xchain-thorchain-query/src/types.ts +++ b/packages/xchain-thorchain-query/src/types.ts @@ -325,3 +325,28 @@ export type BlockInformation = { outboundDelayBlocks?: number outbondDelaySeconds?: number } + +export type ThornameDetails = { + name: string + expireBlockHeight: number + owner: string + preferredAsset: string + affiliateCollectorRune: string + aliases: ThornameAlias[] + error?: string[] +} + +export type ThornameAlias = { + chain: Chain + address: Address +} + +export type QuoteThornameParams = { + thorname: string + chain: string + chainAddress: string + owner?: string + preferredAsset?: Asset | null + expirity?: Date + isUpdate?: boolean +} diff --git a/packages/xchain-thorchain-query/src/utils/thornode.ts b/packages/xchain-thorchain-query/src/utils/thornode.ts index bbb63db39..afbe4ce2b 100644 --- a/packages/xchain-thorchain-query/src/utils/thornode.ts +++ b/packages/xchain-thorchain-query/src/utils/thornode.ts @@ -21,6 +21,8 @@ import { Saver, SaversApi, SaversResponse, + ThornameResponse, + ThornamesApi, TransactionsApi, TxDetailsResponse, TxOutItem, @@ -62,6 +64,7 @@ export class Thornode { private saversApi: SaversApi[] private quoteApi: QuoteApi[] private mimirApi: MimirApi[] + private thornamesApi: ThornamesApi[] constructor(network: Network = Network.Mainnet, config?: ThornodeConfig) { this.network = network @@ -79,6 +82,9 @@ export class Thornode { this.saversApi = this.config.thornodeBaseUrls.map((url) => new SaversApi(new Configuration({ basePath: url }))) this.quoteApi = this.config.thornodeBaseUrls.map((url) => new QuoteApi(new Configuration({ basePath: url }))) this.mimirApi = this.config.thornodeBaseUrls.map((url) => new MimirApi(new Configuration({ basePath: url }))) + this.thornamesApi = this.config.thornodeBaseUrls.map( + (url) => new ThornamesApi(new Configuration({ basePath: url })), + ) } /** @@ -493,4 +499,19 @@ export class Thornode { } throw new Error(`THORNode is not responding`) } + + async getThornameDetails(thorname: string, height?: number): Promise { + for (const api of this.thornamesApi) { + try { + const resp = (await api.thorname(thorname, height)).data + return resp + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + if (e.response.status == 404) { + return undefined + } + } + } + throw new Error(`THORNode is not responding`) + } }