diff --git a/default.yaml b/default.yaml index f6afb64b..5f2099e0 100644 --- a/default.yaml +++ b/default.yaml @@ -24,6 +24,11 @@ rebalancing: minOnchain: 10000000 # 0.1 fees: - BASE_FEE: 0.0005 - IMMEDIATE_CONVERSION_SPREAD: 0.0005 - DELAYED_CONVERSION_SPREAD: 0.0010 + BASE_FEE: 0.0003 + IMMEDIATE_CONVERSION_SPREAD: 0.0001 + DELAYED_CONVERSION_SPREAD: 0.0002 + +withdrawal: + MIN_ON_CHAIN_WITHDRAWAL_AMOUNT: 0.001 + MIN_ON_CHAIN_WITHDRAWAL_FEE: 0.0002 + MAX_ON_CHAIN_WITHDRAWAL_FEE: 0.0004 diff --git a/src/DealerRemoteWallet.ts b/src/DealerRemoteWallet.ts index dda19703..29ebe4dc 100644 --- a/src/DealerRemoteWallet.ts +++ b/src/DealerRemoteWallet.ts @@ -44,7 +44,7 @@ const IN_MEMORY_CACHE_CONFIG = { const WALLETS = gql` query wallets { - wallet { + wallets { id balance walletCurrency @@ -146,6 +146,27 @@ export class DealerRemoteWallet implements GaloyWallet { } } + public async getWalletOnChainTransactionFee( + address: string, + btcAmountInSats: number, + ): Promise> { + const logger = this.logger.child({ method: "getWalletOnChainTransactionFee()" }) + try { + const fee = 0 + logger.debug( + { address, btcAmountInSats, result: fee }, + "{GET_ONCHAIN_FEE} query to galoy graphql api successful with {result}", + ) + return { ok: true, value: fee } + } catch (error) { + logger.error( + { error }, + "{GET_ONCHAIN_FEE} query to galoy graphql api failed with {error}", + ) + return { ok: false, error } + } + } + public async payOnChain( address: string, btcAmountInSats: number, diff --git a/src/DealerRemoteWalletV2.ts b/src/DealerRemoteWalletV2.ts index f6e56931..dc2cdf7e 100644 --- a/src/DealerRemoteWalletV2.ts +++ b/src/DealerRemoteWalletV2.ts @@ -145,6 +145,40 @@ export class DealerRemoteWalletV2 implements GaloyWallet { } } + public async getWalletOnChainTransactionFee( + address: string, + btcAmountInSats: number, + ): Promise> { + const logger = this.logger.child({ method: "getWalletOnChainTransactionFee()" }) + try { + const walletsResult = await this.getWalletsBalances() + if (!walletsResult.ok) { + return walletsResult + } + const variables = { + walletId: walletsResult.value.btcWalletId, + address: address, + amount: btcAmountInSats, + } + const result = await this.client.query({ + query: QUERIES.onChainTxFee, + variables: variables, + }) + logger.debug( + { query: QUERIES.onChainTxFee, variables, result }, + "{query} with {variables} to galoy graphql api successful with {result}", + ) + const btcAddress = result.data?.onChainTxFee?.amount ?? undefined + return { ok: true, value: btcAddress } + } catch (error) { + logger.error( + { query: QUERIES.onChainTxFee, address, btcAmountInSats, error }, + "{query} to galoy graphql api failed with {error}", + ) + return { ok: false, error } + } + } + public async payOnChain( address: string, btcAmountInSats: number, @@ -161,7 +195,6 @@ export class DealerRemoteWalletV2 implements GaloyWallet { address: address, amount: btcAmountInSats, memo: memo, - targetConfirmations: 1, walletId: walletsResult.value.btcWalletId, }, } diff --git a/src/DealerSimulatedWallet.ts b/src/DealerSimulatedWallet.ts index 7011621f..feffe1a2 100644 --- a/src/DealerSimulatedWallet.ts +++ b/src/DealerSimulatedWallet.ts @@ -44,7 +44,7 @@ const IN_MEMORY_CACHE_CONFIG = { const WALLETS = gql` query wallets { - wallet @client { + wallets @client { id balance walletCurrency @@ -146,6 +146,27 @@ export class DealerSimulatedWallet implements GaloyWallet { } } + public async getWalletOnChainTransactionFee( + address: string, + btcAmountInSats: number, + ): Promise> { + const logger = this.logger.child({ method: "getWalletOnChainTransactionFee()" }) + try { + const fee = 0 + logger.debug( + { address, btcAmountInSats, result: fee }, + "{GET_ONCHAIN_FEE} query to galoy graphql api successful with {result}", + ) + return { ok: true, value: fee } + } catch (error) { + logger.error( + { error }, + "{GET_ONCHAIN_FEE} query to galoy graphql api failed with {error}", + ) + return { ok: false, error } + } + } + public async payOnChain( address: string, btcAmountInSats: number, diff --git a/src/GaloyWalletTypes.ts b/src/GaloyWalletTypes.ts index 41880b94..8f98d073 100644 --- a/src/GaloyWalletTypes.ts +++ b/src/GaloyWalletTypes.ts @@ -14,6 +14,10 @@ export interface GaloyWallet { getBtcWalletBalance(): Promise> getWalletOnChainDepositAddress(): Promise> + getWalletOnChainTransactionFee( + address: string, + btcAmountInSats: number, + ): Promise> payOnChain( address: string, diff --git a/src/OkexExchange.ts b/src/OkexExchange.ts index 395fd7e5..6f856ece 100644 --- a/src/OkexExchange.ts +++ b/src/OkexExchange.ts @@ -227,6 +227,31 @@ export class OkexExchange extends ExchangeBase { try { const config = this.exchangeConfig as OkexExchangeConfiguration + try { + const currencies = await this.exchange.privateGetAssetCurrencies() + const btcCurrency = currencies?.data?.find( + (currency) => currency?.chain === "BTC-Bitcoin", + ) + if (btcCurrency) { + this.logger.debug( + { config, response: btcCurrency }, + "exchange.privateGetAssetCurrencies() returned: {response}", + ) + if (btcCurrency?.minWd) config.minOnChainWithdrawalAmount = btcCurrency?.minWd + if (btcCurrency?.minFee) config.minOnChainWithdrawalFee = btcCurrency?.minFee + if (btcCurrency?.maxFee) config.maxOnChainWithdrawalFee = btcCurrency?.maxFee + this.logger.debug( + { config, response: btcCurrency }, + "ExchangeConfiguration after update: {config}", + ) + } + } catch (error) { + this.logger.error( + { error }, + "exchange.privateGetAssetCurrencies() failed: {error}", + ) + } + const args1 = { posMode: config.positionMode } const positionResponse = await this.exchange.privatePostAccountSetPositionMode( args1, diff --git a/src/OkexExchangeConfiguration.ts b/src/OkexExchangeConfiguration.ts index fad80929..a9ae35b8 100644 --- a/src/OkexExchangeConfiguration.ts +++ b/src/OkexExchangeConfiguration.ts @@ -45,6 +45,7 @@ export enum DestinationAddressType { } const hedgingBounds = yamlConfig.hedging +const withdrawalFees = yamlConfig.withdrawal export class OkexExchangeConfiguration implements ExchangeConfiguration { exchangeId: SupportedExchange @@ -53,6 +54,9 @@ export class OkexExchangeConfiguration implements ExchangeConfiguration { positionMode: PositionMode marginMode: MarginMode leverage: number + minOnChainWithdrawalAmount: number + minOnChainWithdrawalFee: number + maxOnChainWithdrawalFee: number constructor() { this.exchangeId = SupportedExchange.OKEX5 @@ -61,6 +65,9 @@ export class OkexExchangeConfiguration implements ExchangeConfiguration { this.positionMode = PositionMode.Net this.marginMode = MarginMode.Cross this.leverage = hedgingBounds.HIGH_BOUND_LEVERAGE + this.minOnChainWithdrawalAmount = withdrawalFees.MIN_ON_CHAIN_WITHDRAWAL_AMOUNT + this.minOnChainWithdrawalFee = withdrawalFees.MIN_ON_CHAIN_WITHDRAWAL_FEE + this.maxOnChainWithdrawalFee = withdrawalFees.MAX_ON_CHAIN_WITHDRAWAL_FEE if (process.env["NETWORK"] === "testnet") { this.headers["x-simulated-trading"] = 1 diff --git a/src/OkexPerpetualSwapStrategy.ts b/src/OkexPerpetualSwapStrategy.ts index aa383897..c47a2fde 100644 --- a/src/OkexPerpetualSwapStrategy.ts +++ b/src/OkexPerpetualSwapStrategy.ts @@ -432,13 +432,16 @@ export class OkexPerpetualSwapStrategy implements HedgingStrategy { await database.internalTransfers.insert(intTransferRecord) // then initiate the withdrawal which by default uses the funding account + const config = this.exchangeConfig as OkexExchangeConfiguration + const averageFee = + (config.minOnChainWithdrawalFee + config.maxOnChainWithdrawalFee) / 2 const withdrawArgs = { currency: TradeCurrency.BTC, quantity: transferSizeInBtc, address: withdrawOnChainAddress, params: { - fee: "0", // probably need to fetch from galoy wallet api - dest: DestinationAddressType.OKEx, // TODO: fix this to external or get from api also + fee: averageFee, + dest: DestinationAddressType.External, pwd: this.exchange.fundingPassword, }, } diff --git a/src/servers/graphql/WalletGraphqlServer.ts b/src/servers/graphql/WalletGraphqlServer.ts index 4938ffc9..3181c7b5 100644 --- a/src/servers/graphql/WalletGraphqlServer.ts +++ b/src/servers/graphql/WalletGraphqlServer.ts @@ -53,11 +53,14 @@ export async function startApolloServer() { BTC } + # An amount (of a currency) that can be negative (i.g. in a transaction) + scalar SignedAmount + # ?: Account? Multiple wallets type Wallet { id: ID! walletCurrency: Currency! - balance: Int! + balance: SignedAmount! } type LastOnChainAddress { @@ -68,7 +71,7 @@ export async function startApolloServer() { # clients can execute, along with the return type for each. In this # case, the "books" query returns an array of zero or more Books (defined above). type Query { - wallet: [Wallet] + wallets: [Wallet] getLastOnChainAddress: LastOnChainAddress } @@ -103,7 +106,7 @@ export async function startApolloServer() { const resolvers = { Query: { - wallet: async (_, __, { logger }) => { + wallets: async (_, __, { logger }) => { const result = await database.graphql.getWallet() logger.debug({ result }, "wallet: database.getWallet() returned: {result}") if (result.ok && result.value && result.value.jsonData) {