From fb76bd34c94bf114d447564a2161290b608316dc Mon Sep 17 00:00:00 2001 From: hotequil Date: Mon, 11 Nov 2024 19:07:17 -0300 Subject: [PATCH] CU-86a5jgc40-BS Swap - Add refund in SwapStatus type and separate getStatus method --- .../CU-86a5jgc40_2024-11-11-17-59.json | 10 ++++ .../CU-86a5jgc40_2024-11-11-17-59.json | 10 ++++ packages/blockchain-service/src/interfaces.ts | 10 +++- .../src/__tests__/SimpleSwapService.spec.ts | 28 ++++----- packages/bs-swap/src/apis/SimpleSwapApi.ts | 26 +++++---- .../src/helpers/SimpleSwapServiceHelper.ts | 34 +++++++++++ .../bs-swap/src/services/SimpleSwapService.ts | 57 +++++-------------- packages/bs-swap/src/types/simpleSwap.ts | 4 +- 8 files changed, 106 insertions(+), 73 deletions(-) create mode 100644 common/changes/@cityofzion/blockchain-service/CU-86a5jgc40_2024-11-11-17-59.json create mode 100644 common/changes/@cityofzion/bs-swap/CU-86a5jgc40_2024-11-11-17-59.json create mode 100644 packages/bs-swap/src/helpers/SimpleSwapServiceHelper.ts diff --git a/common/changes/@cityofzion/blockchain-service/CU-86a5jgc40_2024-11-11-17-59.json b/common/changes/@cityofzion/blockchain-service/CU-86a5jgc40_2024-11-11-17-59.json new file mode 100644 index 0000000..0e0e3bc --- /dev/null +++ b/common/changes/@cityofzion/blockchain-service/CU-86a5jgc40_2024-11-11-17-59.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/blockchain-service", + "comment": "Add refund in SwapStatus type and separate getStatus method", + "type": "minor" + } + ], + "packageName": "@cityofzion/blockchain-service" +} \ No newline at end of file diff --git a/common/changes/@cityofzion/bs-swap/CU-86a5jgc40_2024-11-11-17-59.json b/common/changes/@cityofzion/bs-swap/CU-86a5jgc40_2024-11-11-17-59.json new file mode 100644 index 0000000..df2295a --- /dev/null +++ b/common/changes/@cityofzion/bs-swap/CU-86a5jgc40_2024-11-11-17-59.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@cityofzion/bs-swap", + "comment": "Add refund in SwapStatus type and separate getStatus method", + "type": "minor" + } + ], + "packageName": "@cityofzion/bs-swap" +} \ No newline at end of file diff --git a/packages/blockchain-service/src/interfaces.ts b/packages/blockchain-service/src/interfaces.ts index 582c5b3..b9ecb61 100644 --- a/packages/blockchain-service/src/interfaces.ts +++ b/packages/blockchain-service/src/interfaces.ts @@ -286,8 +286,13 @@ export type SwapServiceSwapResult = { } export type SwapServiceStatusResponse = { - status: 'finished' | 'confirming' | 'exchanging' | 'failed' - transactionHashes: string[] + status: 'finished' | 'confirming' | 'exchanging' | 'failed' | 'refunded' + txFrom?: string + txTo?: string +} + +export interface SwapServiceHelper { + getStatus(id: string): Promise } export interface SwapService { @@ -300,5 +305,4 @@ export interface SwapService { setAddressToReceive(address: string | null): Promise swap(): Promise calculateFee(): Promise - getStatus(): Promise } diff --git a/packages/bs-swap/src/__tests__/SimpleSwapService.spec.ts b/packages/bs-swap/src/__tests__/SimpleSwapService.spec.ts index e01eb54..a53e636 100644 --- a/packages/bs-swap/src/__tests__/SimpleSwapService.spec.ts +++ b/packages/bs-swap/src/__tests__/SimpleSwapService.spec.ts @@ -19,7 +19,7 @@ let amountToUse: SwapServiceLoadableValue let amountToUseMinMax: SwapServiceLoadableValue let amountToReceive: SwapServiceLoadableValue let addressToReceive: SwapServiceValidateValue -let accountToUse: SwapServiceLoadableValue> +let accountToUse: SwapServiceValidateValue> describe('SimpleSwapService', () => { beforeEach(async () => { @@ -31,7 +31,7 @@ describe('SimpleSwapService', () => { amountToUseMinMax = { loading: false, value: null } amountToReceive = { loading: false, value: null } addressToReceive = { loading: false, value: null, valid: null } - accountToUse = { loading: false, value: null } + accountToUse = { loading: false, value: null, valid: null } blockchainServicesByName = { neo3: new BSNeo3('neo3'), @@ -110,7 +110,7 @@ describe('SimpleSwapService', () => { expect(availableTokensToUse).toEqual({ loading: false, value: expect.any(Array) }) expect(tokenToUse).toEqual({ loading: false, value: null }) - expect(accountToUse).toEqual({ loading: false, value: null }) + expect(accountToUse).toEqual({ loading: false, value: null, valid: null }) expect(amountToUse).toEqual({ loading: false, value: null }) expect(amountToReceive).toEqual({ loading: false, value: null }) expect(tokenToReceive).toEqual({ loading: false, value: null }) @@ -122,7 +122,7 @@ describe('SimpleSwapService', () => { it('Should be able to set the token to use', async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) @@ -147,7 +147,7 @@ describe('SimpleSwapService', () => { it('Should not be able to set the account to use if account blockchain is different of token to use blockchain', async () => { await simpleSwapService.init() - await simpleSwapService.setTokenToUse(availableTokensToUse.value![0]) + await simpleSwapService.setTokenToUse(availableTokensToUse.value![1]) const account = blockchainServicesByName.neo3.generateAccountFromKey(process.env.TEST_PRIVATE_KEY as string) account.blockchain = 'NONEXISTENT' as any @@ -159,7 +159,7 @@ describe('SimpleSwapService', () => { it('Should be able to set the account to use to null', async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) await simpleSwapService.setAccountToUse(null) @@ -177,7 +177,7 @@ describe('SimpleSwapService', () => { it('Should be able to set the account to use', async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) expect(accountToUse).toEqual({ loading: false, value: null, valid: null }) @@ -198,7 +198,7 @@ describe('SimpleSwapService', () => { it('Should be able to set the amount to use', async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) const account = blockchainServicesByName.neo3.generateAccountFromKey(process.env.TEST_PRIVATE_KEY as string) @@ -231,7 +231,7 @@ describe('SimpleSwapService', () => { it("Should not be able to set the token to receive if it's not in the available tokens to receive", async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) @@ -242,7 +242,7 @@ describe('SimpleSwapService', () => { it('Should be able to set the token to receive to null', async () => { await simpleSwapService.init() - const token = availableTokensToUse.value![0] + const token = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(token) const account = blockchainServicesByName.neo3.generateAccountFromKey(process.env.TEST_PRIVATE_KEY as string) @@ -263,13 +263,13 @@ describe('SimpleSwapService', () => { it('Should be able to set the token to receive', async () => { await simpleSwapService.init() - const tokenUse = availableTokensToUse.value![0] + const tokenUse = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(tokenUse) const account = blockchainServicesByName.neo3.generateAccountFromKey(process.env.TEST_PRIVATE_KEY as string) await simpleSwapService.setAccountToUse(account) - const tokenReceive = availableTokensToReceive.value![1] + const tokenReceive = availableTokensToReceive.value![0] await simpleSwapService.setTokenToReceive(tokenReceive) expect(tokenToUse).toEqual({ loading: false, value: tokenUse }) @@ -285,7 +285,7 @@ describe('SimpleSwapService', () => { it('Should be able to set an invalid address', async () => { await simpleSwapService.init() - const tokenUse = availableTokensToUse.value![0] + const tokenUse = availableTokensToUse.value![1] await simpleSwapService.setTokenToUse(tokenUse) const account = blockchainServicesByName.neo3.generateAccountFromKey(process.env.TEST_PRIVATE_KEY as string) @@ -328,5 +328,5 @@ describe('SimpleSwapService', () => { expect(amountToReceive).toEqual({ loading: false, value: expect.any(String) }) expect(addressToReceive).toEqual({ loading: false, value: account.address, valid: true }) expect(amountToUseMinMax).toEqual({ loading: false, value: expect.objectContaining({ min: expect.any(String) }) }) - }, 10000) + }, 20000) }) diff --git a/packages/bs-swap/src/apis/SimpleSwapApi.ts b/packages/bs-swap/src/apis/SimpleSwapApi.ts index 8017ca4..a74ed08 100644 --- a/packages/bs-swap/src/apis/SimpleSwapApi.ts +++ b/packages/bs-swap/src/apis/SimpleSwapApi.ts @@ -1,4 +1,3 @@ -import { BlockchainService } from '@cityofzion/blockchain-service' import axios, { AxiosInstance } from 'axios' import { SimpleSwapApiCreateExchangeResponse, @@ -15,23 +14,25 @@ import { export class SimpleSwapApi { #axios: AxiosInstance #allCurrenciesMap: Map> = new Map() - #chainsByServiceNameEntries: [BSName, string[]][] - #blockchainServicesByName: Record> - constructor({ apiKey, blockchainServicesByName, chainsByServiceName }: SimpleSwapServiceInitParams) { + constructor(apiKey: string) { this.#axios = axios.create({ baseURL: 'https://api.simpleswap.io/v3', headers: { 'X-API-KEY': apiKey } }) - this.#chainsByServiceNameEntries = Object.entries(chainsByServiceName) as [BSName, string[]][] - this.#blockchainServicesByName = blockchainServicesByName } - #getTokenFromCurrency(currency: SimpleSwapApiCurrencyResponse): SimpleSwapApiCurrency | undefined { + #getTokenFromCurrency( + currency: SimpleSwapApiCurrencyResponse, + options: Omit, 'apiKey'> + ): SimpleSwapApiCurrency | undefined { if (!currency.ticker || !currency.network || !currency.image || !currency.name || !currency.validationAddress) { return } - const chainsByServiceNameEntry = this.#chainsByServiceNameEntries.find(([_serviceName, chains]) => + const chainsByServiceNameEntries = Object.entries(options.chainsByServiceName) as [BSName, string[]][] + + const chainsByServiceNameEntry = chainsByServiceNameEntries.find(([_serviceName, chains]) => chains.includes(currency.network!) ) + let hash = currency.contractAddress ?? undefined let decimals: number | undefined let name = currency.name @@ -42,7 +43,7 @@ export class SimpleSwapApi { blockchain = chainsByServiceNameEntry[0] if (!hash) { - const token = this.#blockchainServicesByName[blockchain].tokens.find( + const token = options.blockchainServicesByName[blockchain].tokens.find( token => currency.ticker?.toLowerCase().startsWith(token.symbol.toLowerCase()) ) @@ -69,7 +70,9 @@ export class SimpleSwapApi { } } - async getCurrencies() { + async getCurrencies( + options: Omit, 'apiKey'> + ): Promise[]> { if (this.#allCurrenciesMap.size) { return Array.from(this.#allCurrenciesMap.values()) } @@ -79,7 +82,7 @@ export class SimpleSwapApi { const tokens: SimpleSwapApiCurrency[] = [] response.data.result.forEach(currency => { - const token = this.#getTokenFromCurrency(currency) + const token = this.#getTokenFromCurrency(currency, options) if (!token) return this.#allCurrenciesMap.set(`${token.ticker}:${token.network}`, token) @@ -128,6 +131,7 @@ export class SimpleSwapApi { amount, }, }) + return response.data.result.estimatedAmount } diff --git a/packages/bs-swap/src/helpers/SimpleSwapServiceHelper.ts b/packages/bs-swap/src/helpers/SimpleSwapServiceHelper.ts new file mode 100644 index 0000000..16700a4 --- /dev/null +++ b/packages/bs-swap/src/helpers/SimpleSwapServiceHelper.ts @@ -0,0 +1,34 @@ +import { SwapServiceHelper, SwapServiceStatusResponse } from '@cityofzion/blockchain-service' +import { SimpleSwapApi } from '../apis/SimpleSwapApi' + +export class SimpleSwapServiceHelper implements SwapServiceHelper { + #api: SimpleSwapApi + + constructor(apiKey: string) { + this.#api = new SimpleSwapApi(apiKey) + } + + async getStatus(id: string): Promise { + const response = await this.#api.getExchange(id) + + const statusBySimpleSwapStatus: Record = { + waiting: 'confirming', + confirming: 'confirming', + exchanging: 'exchanging', + sending: 'exchanging', + verifying: 'exchanging', + finished: 'finished', + expired: 'failed', + failed: 'failed', + refunded: 'refunded', + } + + const status = statusBySimpleSwapStatus[response.status] + + return { + status, + txFrom: response.txFrom, + txTo: response.txTo, + } + } +} diff --git a/packages/bs-swap/src/services/SimpleSwapService.ts b/packages/bs-swap/src/services/SimpleSwapService.ts index 7539c40..4de8796 100644 --- a/packages/bs-swap/src/services/SimpleSwapService.ts +++ b/packages/bs-swap/src/services/SimpleSwapService.ts @@ -6,7 +6,6 @@ import { SwapServiceEvents, SwapServiceLoadableValue, SwapServiceMinMaxAmount, - SwapServiceStatusResponse, SwapServiceSwapResult, SwapServiceToken, SwapServiceValidateValue, @@ -24,9 +23,7 @@ export class SimpleSwapService implements SwapSe #api: SimpleSwapApi #blockchainServicesByName: Record> - - #internalExchangeId: string | undefined = undefined - #internalTransactionHash: string | undefined = undefined + #chainsByServiceName: Partial> #internalAvailableTokensToUse: SwapServiceLoadableValue[]> = { loading: true, @@ -46,8 +43,9 @@ export class SimpleSwapService implements SwapSe constructor(params: SimpleSwapServiceInitParams) { this.eventEmitter = new EventEmitter() as TypedEmitter - this.#api = new SimpleSwapApi(params) + this.#api = new SimpleSwapApi(params.apiKey) this.#blockchainServicesByName = params.blockchainServicesByName + this.#chainsByServiceName = params.chainsByServiceName } get #availableTokensToUse(): SwapServiceLoadableValue[]> { @@ -176,12 +174,14 @@ export class SimpleSwapService implements SwapSe } if (shouldRecalculateAmountToReceive) { + const estimate = await this.#api.getEstimate( + this.#tokenToUse.value, + this.#tokenToReceive.value!, + this.#amountToUse.value! + ) + this.#amountToReceive = { - value: await this.#api.getEstimate( - this.#tokenToUse.value, - this.#tokenToReceive.value!, - this.#amountToUse.value! - ), + value: estimate, } } } @@ -194,7 +194,10 @@ export class SimpleSwapService implements SwapSe } async init() { - const tokens = await this.#api.getCurrencies() + const tokens = await this.#api.getCurrencies({ + blockchainServicesByName: this.#blockchainServicesByName, + chainsByServiceName: this.#chainsByServiceName, + }) this.#availableTokensToUse = { loading: false, value: tokens } } @@ -289,9 +292,6 @@ export class SimpleSwapService implements SwapSe ], }) - this.#internalExchangeId = id - this.#internalTransactionHash = transactionHash - return { id, // SimpleSwap always make 2 transactions @@ -331,33 +331,4 @@ export class SimpleSwapService implements SwapSe ], }) } - - async getStatus(): Promise { - if (!this.#internalExchangeId || !this.#internalTransactionHash) throw new Error('You need to execute a swap first') - - const response = await this.#api.getExchange(this.#internalExchangeId) - - const transactionHashes: string[] = [this.#internalTransactionHash] - - if (response.txTo) transactionHashes.push(response.txTo) - - const statusBySimpleSwapStatus: Record = { - waiting: 'confirming', - confirming: 'confirming', - exchanging: 'exchanging', - sending: 'exchanging', - verifying: 'exchanging', - finished: 'finished', - expired: 'failed', - failed: 'failed', - refunded: 'failed', - } - - const status = statusBySimpleSwapStatus[response.status] - - return { - status, - transactionHashes: transactionHashes, - } - } } diff --git a/packages/bs-swap/src/types/simpleSwap.ts b/packages/bs-swap/src/types/simpleSwap.ts index 83bfcbb..aa20293 100644 --- a/packages/bs-swap/src/types/simpleSwap.ts +++ b/packages/bs-swap/src/types/simpleSwap.ts @@ -52,7 +52,7 @@ export type SimpleSwapApiCreateExchangeResponse = { export type SimpleSwapApiGetExchangeResponse = { result: { status: string - txFrom: string - txTo: string + txFrom?: string + txTo?: string } }