-
Notifications
You must be signed in to change notification settings - Fork 466
Conversation
8948fb8
to
61d4d0b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have full context on the client-side code but the Solidity LGTM! We should probably add some basic tests before merging. Esp since simbot is not currently part of the continuous integration tests, we'll want some kind of sanity checks in here to future proof the implementation.
e848669
to
f93cb28
Compare
packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice job with the multiple quotes per source!!
bytes calldata bridgeData | ||
) | ||
external | ||
freesGasTokensFromCollector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't needed and I should really roll back the change in Curve Bridge development
. We technically downgraded the CurveBridge address to the older non-burning equivalent when we shipped DFB.
We now either free from DFB or in the future FQT.
packages/asset-swapper/src/utils/market_operation_utils/constants.ts
Outdated
Show resolved
Hide resolved
case ERC20BridgeSource.CurveUsdcDaiUsdtTusd: | ||
case ERC20BridgeSource.CurveUsdcDaiUsdtBusd: | ||
case ERC20BridgeSource.CurveUsdcDaiUsdtSusd: | ||
case ERC20BridgeSource.Curve: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johnrjj FYI Sources might start coming back as Curve
instead of Curve_DAI_USDC
and UniswapV2
instead of UniswapV2_ETH
. Was the change made in matcha to split on _
and take the first element?
curveFillData.poolAddress, | ||
curveFillData.fromTokenIdx, | ||
curveFillData.toTokenIdx, | ||
1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One day I'll remove this version hack. Then Ren BTC Curve introduces a new "version" 😂
packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts
Show resolved
Hide resolved
packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts
Outdated
Show resolved
Hide resolved
8316c01
to
ce6b665
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really digging this refactor. Hopefully this leads to many more.
await testContract | ||
.bridgeTransferFrom(usdcAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData) | ||
.awaitTransactionSuccessAsync({ from: CHONKY_WETH_WALLET, gasPrice: 0 }, { shouldValidate: false }); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these tests check that the receiver
actually received something?
packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts
Outdated
Show resolved
Hide resolved
packages/asset-swapper/src/utils/market_operation_utils/curve_utils.ts
Outdated
Show resolved
Hide resolved
FakeBuyOpts, | ||
SourceQuoteOperation, | ||
UniswapV2FillData, | ||
} from './types'; | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting to feel like this needs to be a class so it can hold state like registry addresses and cached balancer pools and calls to some of these ops can be more legible. I think it'll clean up some of the tests as well.
packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts
Outdated
Show resolved
Hide resolved
packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts
Outdated
Show resolved
Hide resolved
packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts
Outdated
Show resolved
Hide resolved
9272410
to
181e67d
Compare
fb7fa55
to
0b0a52d
Compare
160b52b
to
818fac1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still going through tests
this._wethAddress, | ||
this._sampler.balancerPoolsCache, | ||
this._liquidityProviderRegistry, | ||
this._multiBridge, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this arg list getting ridic
def need to make DexOrderSampler ops a class during refactors
@@ -148,12 +145,12 @@ export function fillQuoteOrders( | |||
if (remainingInput.lte(0)) { | |||
break; | |||
} | |||
result.gas += getTotalGasUsedByFills(fo.order.fills, gasSchedule); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this calculation moved here? Isn't this wrong? We might not need to hit all the fills/sources.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, you're right
public async getPoolsForPairAsync(takerToken: string, makerToken: string): Promise<BalancerPool[]> { | ||
const key = JSON.stringify([takerToken, makerToken].sort()); | ||
const value = this._cache[key]; | ||
const minTimestamp = new Date().getTime() - this.cacheExpiryMs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const minTimestamp = new Date().getTime() - this.cacheExpiryMs; | |
const minTimestamp = Date.now() - this.cacheExpiryMs; |
const minTimestamp = new Date().getTime() - this.cacheExpiryMs; | ||
if (value.timestamp < minTimestamp) { | ||
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); | ||
const timestamp = new Date().getTime(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const timestamp = new Date().getTime(); | |
const timestamp = Date.now(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also seeing a relatively high revert rate.
Some of them are likely explained in the routing being very similar to the following:
Uniswap: 99.99%, Uniswap_V2: 6e-16%
Definitely not worth filling Uniswap_V2
for a tiny fraction of a $600 trade.
Below is what I'm seeing locally over MKR,USDC,DAI,WETH,WBTC
.
const value = this._cache[key]; | ||
const minTimestamp = Date.now() - this.cacheExpiryMs; | ||
if (value === undefined || value.timestamp < minTimestamp) { | ||
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we keep only the top 10 pools when sorted by in/out balances?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah probably –– I guess top 10 by out balances makes sense?
packages/asset-swapper/src/utils/market_operation_utils/balancer_utils.ts
Show resolved
Hide resolved
difference(FEE_QUOTE_SOURCES.concat(this._optionalSources()), _opts.excludedSources), | ||
makerToken, | ||
this._wethAddress, | ||
ONE_ETHER, | ||
this._wethAddress, | ||
this._sampler.balancerPoolsCache, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh this probably doesn't need to be a parameter and can be found out of global space?
Like BalancerPoolCache.getInstance().blah(
the others are parameters as they change on network or are provided as variables ALL the way down the stack. This feels less like that?
Maybe it's easier to test when it's injected in but I have a feeling we aren't testing this explicitly.
constructor( | ||
private readonly _samplerContract: IERC20BridgeSamplerContract, | ||
public balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(), | ||
) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment as above imo
public cacheExpiryMs: number = THIRTY_MINUTES_MS, | ||
) {} | ||
public async getPoolsForPairAsync(takerToken: string, makerToken: string): Promise<BalancerPool[]> { | ||
const key = JSON.stringify([takerToken, makerToken].sort()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const key = JSON.stringify([takerToken, makerToken].sort()); | |
const key = JSON.stringify([takerToken, makerToken]); |
is balanceIn
relative to the first argument in fetchPoolsForPair? And sorting these would result in an invalid rate in the opposite direction?
E.g
query DAI, WETH
, balanceIn
is DAI. Result is stored as DAI, WETH
.
query WETH, DAI
, balanceIn
is WETH. Result is already stored and retrieved from
DAI, WETH`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, you're right
timestamp: number; | ||
pools: BalancerPool[]; | ||
} | ||
const THIRTY_MINUTES_MS = 30 * 60 * 1000; // tslint:disable-line:custom-no-magic-numbers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO 30 mins is fine for a cache of existing pools. I.e a new pool added takes 30m till it is served by AssetSwapper.
30 mins is too long for rates within the pool and determining the route based on these values. Especially with sub 1% slippage.
I'd suggest we cache for at most a minute and move towards a model where the pools are discovered (cached) but queried on-demand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I do wonder if it'd be worth differentiating pool discovery vs pool state. idk if there would be a substantial difference in latency between querying the state of the cached pool addresses versus calling getPoolsWithTokens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I assume TheGraph is fairly quick, which is fine for pool discovery.
But ultimately I think we'd want to get the rates as live as possible to reduce our potential for revert.
3b63749
to
a2fa30f
Compare
@@ -34,4 +34,7 @@ if (isNode) { | |||
}; | |||
} | |||
|
|||
const orig = BigNumber.config; | |||
BigNumber.config = (_args?: any) => orig({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably document why we do this and also clobber BigNumber.set()
.
Also, maybe this is neater?
(orig => BigNumber.config = (...args: any) => orig({}))(BigNumber.config);
BigNumber.set = BigNumber.config;
const samplerOps = subOps.filter(op => op.source !== ERC20BridgeSource.Balancer); | ||
const nonSamplerOps = subOps.filter(op => op.source === ERC20BridgeSource.Balancer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this change just to reduce the load on the sampler contract?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the sampler was reverting because batchCall
didn't know what to do with empty bytes. we could change batchCall
instead to skip over if callDatas[i] == 0
, but 🤷
b.balanceOut.minus(a.balanceOut).toNumber(), | ||
); | ||
// tslint:disable-next-line:custom-no-magic-numbers | ||
return pools.length > 10 ? pools.slice(0, 10) : pools; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth putting 10
into a constant.
dbe595c
to
529905e
Compare
e2eb3a1
to
04839d3
Compare
77306e2
to
b4bee37
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. Can backlog my comments if you're under pressure.
import { Fill } from './types'; | ||
|
||
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs | ||
|
||
const RUN_LIMIT_DECAY_FACTOR = 0.8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder how detrimental 0.5
would be...
const rfqtPromise = getRfqtIndicativeQuotesAsync( | ||
nativeOrders[0].makerAssetData, | ||
nativeOrders[0].takerAssetData, | ||
MarketOperation.Sell, | ||
takerAmount, | ||
_opts, | ||
); | ||
|
||
const balancerPromise = this._sampler.executeAsync( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like balancer shouldn't belong to the sampler anymore?
Description
Adds the BalancerBridge contract and corresponding AssetSwapper support.
AssetSwapper uses the Balancer SOR package to get pool addresses and compute quotes off-chain.
Also includes some related AssetSwapper refactors, most notably merging all Curves into a single
ERC20BridgeSource
. To account for the fact that different Curves have significantly different gas costs, we change the fee schedule so that each source is given a fee estimate function instead of a constant.Testing instructions
Types of changes
Checklist:
[WIP]
if necessary.