Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/great-bats-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@galacticcouncil/sdk-next': patch
---

Wrapping extra fees for aave routes only after dryRun errors
1 change: 1 addition & 0 deletions packages/sdk-next/src/aave/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const AAVE_LENDING_POOL_ADDRESS =
'0xf3Ba4D1b50f78301BDD7EAEa9B67822A15FCA691';

export const AAVE_GAS_LIMIT = 1_000_000n;
export const WEIGHT_PER_GAS = 25_000n;
export const AAVE_ROUNDING_THRESHOLD = 5;
export const AAVE_UINT_256_MAX = BigInt(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
Expand Down
21 changes: 3 additions & 18 deletions packages/sdk-next/src/tx/OrderTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class OrderTxBuilder extends TxBuilder {
tradeRoute,
} = this.order;

let tx: Transaction = this.api.tx.DCA.schedule({
const tx: Transaction = this.api.tx.DCA.schedule({
schedule: {
owner: this.beneficiary,
period: tradePeriod,
Expand All @@ -97,11 +97,6 @@ export class OrderTxBuilder extends TxBuilder {
start_execution_block: undefined,
});

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}

return this.wrapTx('DcaSchedule', tx);
}

Expand All @@ -119,7 +114,7 @@ export class OrderTxBuilder extends TxBuilder {
const slippage = calc.getFraction(tradeAmountOut, this.slippagePct);
const minAmountOut = tradeAmountOut - slippage;

let tx: Transaction = this.api.tx.DCA.schedule({
const tx: Transaction = this.api.tx.DCA.schedule({
schedule: {
owner: this.beneficiary,
period: tradePeriod,
Expand All @@ -138,11 +133,6 @@ export class OrderTxBuilder extends TxBuilder {
start_execution_block: undefined,
});

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}

return this.wrapTx('DcaSchedule.twapSell', tx);
}

Expand All @@ -160,7 +150,7 @@ export class OrderTxBuilder extends TxBuilder {
const slippage = calc.getFraction(tradeAmountIn, this.slippagePct);
const maxAmountIn = tradeAmountIn + slippage;

let tx: Transaction = this.api.tx.DCA.schedule({
const tx: Transaction = this.api.tx.DCA.schedule({
schedule: {
owner: this.beneficiary,
period: tradePeriod,
Expand All @@ -179,11 +169,6 @@ export class OrderTxBuilder extends TxBuilder {
start_execution_block: undefined,
});

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}

return this.wrapTx('DcaSchedule.twapBuy', tx);
}
}
100 changes: 86 additions & 14 deletions packages/sdk-next/src/tx/TradeTxBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Trade, TradeRouteBuilder, TradeType } from '../sor';
import { AAVE_GAS_LIMIT, WEIGHT_PER_GAS } from '../aave';
import { PoolType } from '../pool';
import { Swap, Trade, TradeRouteBuilder, TradeType } from '../sor';
import { calc } from '../utils';

import { TxBuilder } from './TxBuilder';
import { Transaction, Tx } from './types';
import { DryRunResult, Transaction, Tx } from './types';
import { enumPath } from './utils';

export class TradeTxBuilder extends TxBuilder {
private _trade?: Trade;
Expand Down Expand Up @@ -90,10 +93,7 @@ export class TradeTxBuilder extends TxBuilder {
});
}

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}
tx = await this.applyExtraGas(this.beneficiary, tx, swaps);

return this.wrapTx('RouterBuy', tx);
}
Expand Down Expand Up @@ -128,10 +128,7 @@ export class TradeTxBuilder extends TxBuilder {
});
}

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}
tx = await this.applyExtraGas(this.beneficiary, tx, swaps);

return this.wrapTx('RouterSell', tx);
}
Expand All @@ -154,11 +151,86 @@ export class TradeTxBuilder extends TxBuilder {
route: TradeRouteBuilder.build(swaps) as any,
});

const hasDebt = await this.aaveUtils.hasBorrowPositions(this.beneficiary);
if (hasDebt) {
tx = await this.dispatchWithExtraGas(tx);
}
tx = await this.applyExtraGas(this.beneficiary, tx, swaps);

return this.wrapTx('RouterSellAll', tx);
}

private async applyExtraGas(
account: string,
tx: Transaction,
swaps: Swap[]
): Promise<Transaction> {
const hasAavePool = swaps.some((s) => s.pool === PoolType.Aave);
if (!hasAavePool) {
return tx;
}

const result = await this.dryRun(account, tx);
const error = this.getDryRunError(result);
if (!error || !error.includes('OutOfGas')) {
return tx;
}

return this.estimateExtraGas(account, tx);
}

private async estimateExtraGas(
account: string,
tx: Transaction
): Promise<Transaction> {
const wrappedBase = this.dispatchWithExtraGas(tx, 0n);
const wrappedMax = this.dispatchWithExtraGas(tx, AAVE_GAS_LIMIT);

const [baseInfo, maxResult] = await Promise.all([
wrappedBase.getPaymentInfo(account),
this.dryRun(account, wrappedMax),
]);

const error = this.getDryRunError(maxResult);
if (error) {
return wrappedMax;
}

const baseRefTime = BigInt(baseInfo.weight.ref_time);
const actualRefTime = this.getActualRefTime(maxResult);

if (actualRefTime <= baseRefTime) {
return wrappedMax;
}

const usedExtraWeight = actualRefTime - baseRefTime;
const extraGas = usedExtraWeight / WEIGHT_PER_GAS;
const bufferedGas = (extraGas * 110n) / 100n;
const clampedGas =
bufferedGas > AAVE_GAS_LIMIT ? AAVE_GAS_LIMIT : bufferedGas;

return this.dispatchWithExtraGas(tx, clampedGas);
}

private getDryRunError(result: DryRunResult): string | null {
if (!result.success) {
return 'DryRun call failed';
}

const execResult = result.value.execution_result;
if (!execResult.success) {
return enumPath(execResult.value.error.value);
}

return null;
}

private getActualRefTime(result: DryRunResult): bigint {
if (!result.success) {
return 0n;
}

const execResult = result.value.execution_result;
if (execResult.success) {
return execResult.value.actual_weight?.ref_time ?? 0n;
}

return execResult.value.post_info?.actual_weight?.ref_time ?? 0n;
}
}
38 changes: 19 additions & 19 deletions packages/sdk-next/src/tx/TxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Enum, PolkadotClient } from 'polkadot-api';

import { PublicClient } from 'viem';

import { AAVE_GAS_LIMIT, AaveUtils } from '../aave';
import { AAVE_GAS_LIMIT } from '../aave';
import { Papi } from '../api';
import { BalanceClient } from '../client';
import { EvmClient } from '../evm';
Expand All @@ -17,28 +17,40 @@ export abstract class TxBuilder extends Papi {
protected readonly evmClient: PublicClient;

protected readonly balance: BalanceClient;
protected readonly aaveUtils: AaveUtils;

constructor(client: PolkadotClient, evm: EvmClient) {
super(client);
this.evm = evm;
this.evmClient = evm.getWsProvider();
this.balance = new BalanceClient(client);
this.aaveUtils = new AaveUtils(evm);
}

protected wrapTx(name: string, tx: Transaction): Tx {
return {
name,
get: () => tx,
dryRun: (account: string) => this.dryRun(account, tx),
dryRun: async (account: string) => {
const result = await this.dryRun(account, tx);
if (result.success && !result.value.execution_result.success) {
const errorPath = enumPath(
result.value.execution_result.value.error.value
);
throw new Error('Dry run execution error!', {
cause: errorPath,
});
}
return result;
},
};
}

protected async dispatchWithExtraGas(tx: Transaction): Promise<Transaction> {
protected dispatchWithExtraGas(
tx: Transaction,
extraGas: bigint = AAVE_GAS_LIMIT
): Transaction {
return this.api.tx.Dispatcher.dispatch_with_extra_gas({
call: tx.decodedCall,
extra_gas: AAVE_GAS_LIMIT,
extra_gas: extraGas,
});
}

Expand All @@ -53,19 +65,7 @@ export abstract class TxBuilder extends Papi {
.getUnsafeApi()
.apis.DryRunApi.dry_run_call(origin, tx.decodedCall);

const result = dryRun as DryRunResult;
const error =
result.success && !result.value.execution_result.success
? result.value.execution_result.value.error
: null;

if (error) {
const errorPath = enumPath(error.value);
throw new Error('Dry run execution error!', {
cause: errorPath,
});
}
return result;
return dryRun as DryRunResult;
}

protected isDirectOmnipoolTrade(swaps: Swap[]): boolean {
Expand Down
Loading