-
Notifications
You must be signed in to change notification settings - Fork 42
Conversation
Busy fixing tests |
return false; | ||
} | ||
|
||
uint tbtcAmount = TBTCConstants.getLotSize().add(DepositUtils.beneficiaryReward()); |
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.
Wouldn't it be better to call ERC20(_d.TBTCToken).balance(address(this))
instead of getLotSize
? It can be the case that the Deposit
receives TBTC during its lifetime.
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.
Fuaaa - @mhluongo what's our approach for dirty flows like this?
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.
@mhluongo bump on this protocol design question?
Do we design for unexpected behaviour (burn entire balance) or expected behaviour (ignore random tBTC and deal with protocol constants)?
/// @notice Starts signer liquidation due to fraud | ||
/// @dev We first attempt to liquidate on chain, then by auction | ||
/// @param _d deposit storage pointer | ||
function startSignerFraudLiquidation(DepositUtils.Deposit storage _d) public { |
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.
Shouldn't this be internal
?
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.
Good catch!
/// @notice Starts signer liquidation due to abort or undercollateralization | ||
/// @dev We first attempt to liquidate on chain, then by auction | ||
/// @param _d deposit storage pointer | ||
function startSignerAbortLiquidation(DepositUtils.Deposit storage _d) public { |
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.
Shouldn't this be internal
?
TBTCToken _tbtc = TBTCToken(_d.TBTCToken); | ||
|
||
if (_d.requesterAddress != address(0)) { // redemption | ||
_tbtc.transferFrom(address(this), _d.requesterAddress, TBTCConstants.getLotSize()); |
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 as above, this should probably be the deposit's ERC20 balance instead of getLotSize, in case there's any leftovers (or maybe the lotSize changed somehow)
|
||
/// @notice Tries to liquidate the position on-chain using the signer bond | ||
/// @dev Calls out to other contracts, watch for re-entrance | ||
/// @return True if Liquidated, False otherwise |
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.
Instead of returning bool, it could return how much ETH it sent to Uniswap and/or how much TBTC it got in return
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 is @prestwich's code not mine - but I believe the intention is to decide whether it was successful within this block. The naming (attempt) is supposed to signal that we're attempting to instantly liquidate the entire position (as opposed to the ongoing auction flow), and such the binary RV of successful attempt or failure. What do you think, should it be improved?
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 is our code - we propose it in the PR, we review it, and we accept or reject it. We all need to understand what is it doing 😉
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.
Strongly agree with @pdyraga, though I imagine the man himself will take a peek when it's a little later PT
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 need to learn not to be more attentive with words haha - I like how this is implemented for now. @gakonst could you elaborate on why returning the amount might be useful?
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.
@gakonst blocking, bump on this
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.
Let’s not block on this, but capture it in a speculative issue and discuss/implement/close there.
return false; | ||
} | ||
|
||
uint deadline = block.timestamp + 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.
Given that all these transactions get executed together, the +1 shouldn't be required. Also might be good to add a comment so that a reader can understand the purpose of deadline in the context of uniswap (ie to ensure that the order does not get withheld by miners and submitted later)
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 good explanation (frontrunning?).
|
||
if(address(this).balance < ethAmount) { | ||
return 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.
Do we want to use Uniswap only when we have enough funds to buy 1.0 TBTC? Can it be desirable that we buy some from Uniswap before slippage starts to become significant, and buy the rest of the TBTC via auction?
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.
It's unclear what is a "good" price on Uniswap from our perspective, so this is a little tricky- can you propose pseudocode to make that decision?
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.
Bump @gakonst, would like to hear your answer to this 😄
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.
Bump above @gakonst
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.
Let’s punt here as well. Either we can capture it as a checkbox on #105, or as a separate issue, but I don’t think it needs to block this PR.
|
||
TBTCToken _tbtc = TBTCToken(_d.TBTCToken); | ||
|
||
if (_d.requesterAddress != address(0)) { // redemption |
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 find overloading requesterAddress == 0
to act as a flag to signal the burning of the TBTC to be slightly unclear (& potentially dangerous). Might be better to add a shouldLiquidate
flag on the Deposit
and prohibit requesterAddress
from taking 'invalid' values.
_d.distributeBeneficiaryReward(); | ||
|
||
} else if (!_liquidated) { | ||
_d.liquidationInitiated = block.timestamp; // Store the timestamp for auction |
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.
Will any auction related logic be here, or will it be implemented as separate functionality that checks the liduidationInitiated
value?
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 believe it's used in auctionValue
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 be emitting* some sort of event here to indicate that we are initiating an auction? Doesn’t feel necessary for this PR, but asking while I’m looking at it.
const uniswapFactory = deployed.UniswapFactoryStub | ||
const uniswapExchange = await UniswapExchangeStub.new(tbtcToken.address) | ||
await uniswapFactory.setExchange(uniswapExchange.address) | ||
await deployed.TBTCSystemStub.setExternalAddresses(uniswapFactory.address) |
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.
Are these used anywhere in the test?
returns (uint256 eth_sold) | ||
{ | ||
deadline; | ||
require(msg.value == ethPrice, "incorrect eth sent"); |
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.
require(msg.value == ethPrice, "incorrect eth sent"); | |
require(msg.value == ethPrice, "incorrect eth sent"); |
const ev = evs[1] | ||
expect(ev.event).to.equal('Transfer') | ||
expect(ev.returnValues.from).to.equal(deposit.address) | ||
expect(ev.returnValues.to).to.equal('0x0000000000000000000000000000000000000000') |
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.
indentation
@liamzebedee can we update this branch with the latest changes from master? |
@@ -141,4 +141,128 @@ integration('Uniswap', (accounts) => { | |||
expect(balance).to.eq.BN(TBTC_BUY_AMOUNT) | |||
}) | |||
}) | |||
|
|||
// async function addLiquidity(ethAmount, tbtcAmount) { |
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.
What's happening here?
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.
It's an additional test from the previous iteration of this PR. Turns out I had blended in unit with integration tests, so moving it out into this file where it belongs
Is it the last piece we need or do we expect more Uniswap PRs? |
implementation/demo/Dockerfile
Outdated
@@ -0,0 +1,82 @@ | |||
# Mount 3 volumes |
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.
What is this file? Looks like it stumbled in during a merge…
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! Been fiddling with more Git branches than usual today.
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.
Hey @Shadowfiend - was that the entire review?
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.
No, but don't let that stop you from fixing this issue while I get the time to do the deeper review 😉
Since I'm not fully up on the system design context involving Uniswap here this'll probably take me a touch longer than a normal review. Easiest way to make sure that goes smoothly would be to make sure your PR or referenced issue description clearly lays out the goal, design, etc. Sloan's PRs (e. and g.*) are a solid standard in that department. When I looked through the issue it seemed like a lot of discussion happened but the action plan wasn't clear.
Either way, I'll be circling back Monday with more.
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.
Okay, done my best to do a writeup in the way of Sloan, explaining the context, important touch points and some design tradeoffs. Also touched up the PR description with some more details. Let's get this delivered as smoothly as possible 💯!
6e433f6
to
735892c
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.
Did a first pass that included everything except the testS. Shooting this out now so you can start having a look at it, but I’ll be continuing to dig in on the testing site as well. Generally looks good, most of the comments are either cosmetic, grammatical, or designated for future work. I also went ahead and called out a couple of things that were presented as blocking that I think we can punt to future work.
@@ -220,21 +161,21 @@ library DepositLiquidation { | |||
|
|||
// Burn the outstanding TBTC | |||
TBTCToken _tbtcToken = TBTCToken(_d.TBTCToken); | |||
require(_tbtcToken.balanceOf(msg.sender) >= TBTCConstants.getLotSize(), "Not enough TBTC to cover outstanding debt"); | |||
require(_tbtcToken.balanceOf(msg.sender) >= _d.redemptionTBTCAmount(), "Not enough TBTC to cover outstanding debt"); |
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.
Not sure I understand this commit: 0bceca0
We don’t seem to change redemptionTBTCAmount
despite saying we do, and it’s not clear why we switched from lot sizes to the redemption amount* (though it seems like the* obviously right choice). Why was it different before?
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.
We do change it on this line, removing signerFee
?
0bceca0#diff-eb5ee13eadec1a8b974e3eba934eb2d7L293
And I don't know why it was different before 🤷♂
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.
Hah, whoops. Thought it was a property rather than a method and thus missed the actual change 🤦♂️
Usually a good idea to double-check the whys on stuff like this (and then capture it in the commit)---it's a good opportunity to let the original author also be aware of the update, and make sure there isn't some missed understanding that led to the original implementation. Again, not trying to block this PR/not extra-worried here, but flagging it for the future.
|
||
/// @notice Tries to liquidate the position on-chain using the signer bond | ||
/// @dev Calls out to other contracts, watch for re-entrance | ||
/// @return True if Liquidated, False otherwise |
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.
Let’s not block on this, but capture it in a speculative issue and discuss/implement/close there.
/// @return True if Liquidated, False otherwise | ||
// TODO(liamz): check for re-entry | ||
function attemptToLiquidateOnchain( | ||
DepositUtils.Deposit storage _d |
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.
Not sure if we’ve made a habit of this, but if so it’s time to break it: let’s use full words, not single letters, for our variable names.
function attemptToLiquidateOnchain( | ||
DepositUtils.Deposit storage _d | ||
) internal returns (bool) { | ||
// Return early if there is no Uniswap TBTC Exchange |
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.
Periods are good on sentences 😉
} | ||
|
||
// Only liquidate if we can buy up enough TBTC to burn, | ||
// otherwise to go 100% for the falling-price auction |
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.
Otherwise go*
_d.distributeBeneficiaryReward(); | ||
|
||
} else if (!_liquidated) { | ||
_d.liquidationInitiated = block.timestamp; // Store the timestamp for auction |
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 be emitting* some sort of event here to indicate that we are initiating an auction? Doesn’t feel necessary for this PR, but asking while I’m looking at it.
/// @param _d deposit storage pointer | ||
function startSignerFraudLiquidation(DepositUtils.Deposit storage _d) internal { | ||
startLiquidation(_d); | ||
if(_d.inLiquidated()) { |
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.
Not for this PR, but it would make a lot of sense and read more naturally for these functions to start with “is“ rather than “in“.
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 is referring to being "in" a state of the state machine as described in the whitepaper.
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 get that for sure. in<State>State
/inState<State>
is one common way to express this, is<State>
is another (with appropriate naming of the states). in<State>
is a not-particularly-clear mishmash of the two, since “in” can just as easily mean “inside of” and there's no additional disambiguation.
It's one of those things that's very easy for someone who's in the code to adapt to, but makes it harder for someone who isn't in the code frequently to grok. The less hard it can be, the better overall, especially if it doesn't make it particularly harder for the day-to-day developers.
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 that's a good explanation of it. Notably, I just found we do adopt this pattern in the Deposit codebase, eg. inRedeemableState
.
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.
😬
Obviously I lean hard towards is<State>
as a pattern, but I ain't lookin' to die on that hill. Mostly, consistency is key.
And either way, we can handle it in a follow-up.
} | ||
|
||
/// @notice Starts signer liquidation due to abort or undercollateralization | ||
/// @dev We first attempt to liquidate on chain, then by auction |
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.
It would be useful for these docs to describe the differences between signer abort and fraud liquidation, in particular with regards to who gets any remaining balance and why.
@@ -59,7 +59,7 @@ library DepositRedemption { | |||
uint256 _red = _d.redemptionTBTCAmount(); | |||
require(_bal >= _red, "Not enough TBTC to cover outstanding debt"); | |||
_tbtc.burnFrom(msg.sender, TBTCConstants.getLotSize()); | |||
_tbtc.transferFrom(msg.sender, address(this), DepositUtils.signerFee().add(DepositUtils.beneficiaryReward())); | |||
_tbtc.transferFrom(msg.sender, address(this), DepositUtils.beneficiaryReward()); |
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.
Commits really need to start including reasoning (not links to reasoning, mind you, but distilled summaries of reasoning for the change).
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.
Good point - am getting better at this
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.
No worries---we're inconsistent across the team on this front, and I usually call it out when I go to a commit to figure out why a change happened and… Can't 😁
/// @notice Determines the amount of TBTC accepted in the auction | ||
/// @dev If requesterAddress is non-0, that means we came from redemption, and no auction should happen | ||
/// @return The amount of TBTC that must be paid at auction for the signer's bond | ||
// TODO(liamz): consider if redundant |
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.
Redundant with? In general it’s best to capture todos like this one in your personal task list or in an issue, depending on whether you are intending on tackling it yourself in the very near future.
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, will remove.
This commit breaks apart work from the tbtc#265 PR, hence the lack of semantic Git commit history. Not complete. Tests for DepositLIquidation and Uniswap integration are passing only.
…temptToLiquidateOnchain
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.
Ok, went through tests and left a bunch of additional thoughts.
One big theme: there are comments that aren't adding much over what the code already says. Let's avoid those, as they tend to become outdated and then give mixed signals.
Also on the comment front, if you've got comments on their own line, aim to make them complete, grammatically correct sentences. Only if they're at the end of a line for some very short clarification should they be fragments. This tends to track with longer comments wanting more space and needing more to be more clear in the future.
await tbtcSystemStub.initialize( | ||
keepRegistry.address, | ||
uniswapExchange.address | ||
) |
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.
Did this become necessary due to code from elsewhere?
const bnChai = require('bn-chai') | ||
chai.use(bnChai(BN)) | ||
|
||
export class AssertBalanceHelpers { |
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.
Given that this file strictly exports a single class, it feels right for the filename to match the class name.
On the other hand, I wonder if we need to encapsulate this in a class, vs passing the instance to the helpers directly. Slightly more typing, but it could also be clearer what you're doing at the call site, and make scenarios where you want to test against different TBTC instances (for example, to group tests for certain behaviors across different TBTC states together) more straightforward.
Don't think we need to move on the class encapsulation in this PR, but I do think we should make sure the filename is aligned properly with the current usage.
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.
make scenarios where you want to test against different TBTC instances (for example, to group tests for certain behaviors across different TBTC states together) more straightforward
Could you elaborate a little bit more on this? I'm trying to imagine a scenario when we would have multiple instances of TBTC that are are testing in unison.
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.
Reconsidering this, I think my real issue here is that it feels like we're using a class to approximate partial application, which we can do instead with Function.bind
. There is nothing relating these helpers other than that they happen to both depend on a tbtcInstance
.
To address your question directly though, I think any situation where we might run multiple tests in parallel against the tBTC system that expect different system states could use multiple instances.
if (err) rej(err) | ||
else res(result.result) | ||
}) | ||
}) |
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.
Given this is an async
function, this feels like it could just be:
return await web3.currentProvider.send(...)
No? Or if we're want result.result
, we can just do:
export ... {
const result = await web3.currentProvider.send...
return result.result
}
The await
will throw
if there's an error in the send
call, but that will be wrapped in a failed Promise
by the function's async
designation.
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.
Ah if it is, then much obliged. 😄 Truffle Web3 has changed over the course of my using it - I'll go ahead and try testing with the newer syntax.
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.
Just for completeness, async
/await
isn't Web3 but rather ECMAScript/JavaScript syntax. This particular aspect of async
/await
isn't immediately obvious, and is complicated further by the fact that a Promise
return of an async
function behaves the same as a non-Promise
return for the caller.
It's great 😒
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.
Wait till you see PromiEventEmitter's - event emitters that are also promises.
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 tried it but it appears .send
doesn't return a promise. Given that we've pinned Truffle in our config, how about we leave it for 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.
Got it! Yep, I'm down. I won't block this PR, but it would be nice to drop a quick note in the file to indicate that our current version of Truffle doesn't return proper Promise
objects and that's why we're jumping through hoops.
* @param {string} snapshotId the snapshot ID (hex) | ||
*/ | ||
export async function restoreSnapshot(snapshotId) { | ||
return await new Promise((res, rej) => { |
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 note as above about the Promise
indirection.
@@ -463,15 +471,13 @@ contract('DepositFraud', (accounts) => { | |||
utils.bytes32zero | |||
) | |||
|
|||
// Provide proof of a transaction where output is sent to a requestor, with | |||
// Provide proof of a transaction where output is sent to a redeemer, with |
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.
Most of this comment feels like a restatement of the test description, which I don't love. Not really for this PR, and didn't get introduced here, but wanted to call it out.
/** | ||
* Gets the cost of a transaction in ETH. | ||
* | ||
* This is often useful in the cases when you're trying to assert some eth was refunded to `msg.sender`. |
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.
“some ETH”. Also don't forget your 80-character wrap!
await UniswapHelpers.addLiquidity(accounts[0], uniswapExchange, tbtcToken, ethAmount, tbtcAmount) | ||
|
||
const minTbtcAmount = '100100000' | ||
// hard-coded from previous run |
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.
Previous run of what?
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.
From a previous run of Uniswap, supplied with the same liquidity.
Would that extra note be good?
await assertBalance.eth(deposit.address, expectedPrice.toString()) | ||
|
||
const price = await uniswapExchange.getEthToTokenOutputPrice.call(minTbtcAmount) | ||
expect(price).to.eq.BN(expectedPrice) |
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 feels like we're testing Uniswap's behavior rather than our own, no?
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.
True, we can remove this portion.
await tbtcToken.approve(uniswapExchange.address, tbtcSupply, { from: account }) | ||
|
||
// Uniswap requires a minimum of 1000000000 wei for the initial addLiquidity call | ||
const UNISWAP_MINIMUM_INITIAL_LIQUIDITY_WEI = new BN('1000000000') |
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 a constant that exists inside Uniswap? Is it exported in some way we can see, or alternatively is there something we can reference that indicates this minimum?
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.
We can reference the exact line of code. The constant wasn't documented anywhere, I discovered it myself through running up against the "revert" brick wall and reading the code.
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.
Here it is - https://github.com/Uniswap/contracts-vyper/blob/master/contracts/uniswap_exchange.vy#L65
Good idea, I'll add it in.
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.
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 there any chance we could work with them to expose this as a constant? Obviously we'd pursue that independent of this PR.
|
||
/* | ||
* Add TBTC and ETH liquidity to the Uniswap exchange. | ||
* This helper abstracts away detail in test code. |
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.
Don't really need this line.
Oh, also… Most of my remarks don't need to be handled in this PR, but can go into follow-up work. The key ones I think we should deal with (either by discussing or by making the changes) right now, in addition to remaining bits from the previous review, are: |
Matches the filename of where the class is implemented.
We don't need to test price here, as its behaviour of Uniswap, tested separately in UniswapTest.
04b7d2e
to
a22db06
Compare
These changes were introduced as part of properly testing the beneficiary reward, in liquidation flow tests. The changes should've been removed in e56f394 but were missed. They will be reintroduced in a later PR.
…chain-liquidation
@Shadowfiend cleaned up, tests passing, ready for another review. |
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.
All righty, let's rock and roll.
One thing remains here: let's do a final pass through comments and make sure everything that needs follow-up is captured in an issue or a personal todo for the next PR. In particular, we need issues for any remaining protocol questions. @liamzebedee can you take a pass on Monday? Almost made it to two months, but we finally wrangled it down 😉 |
This PR uses our deployed Uniswap to implement on-chain liquidation in
attemptToLiquidateOnchain
. Another breakaway from a monolithic PR, #160, this changeset is simpler and doesn't change any protocol/liquidation logic.attemptToLiquidateOnchain
attemptToLiquidateOnchain
resetState
stub methodsRefs #105