Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Uniswap on-chain liquidation #265

Merged
merged 12 commits into from
Oct 18, 2019
Merged

Conversation

liamzebedee
Copy link
Contributor

@liamzebedee liamzebedee commented Aug 28, 2019

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.

  • adds Uniswap exchange stubs
  • implements onchain liquidation functionality in attemptToLiquidateOnchain
  • tests attemptToLiquidateOnchain
    • using a stub
    • using real Uniswap
  • introduces Ganache snapshots to cleanly revert state between unit tests, without additional resetState stub methods

Refs #105

This was referenced Aug 29, 2019
@liamzebedee liamzebedee marked this pull request as ready for review August 31, 2019 17:45
@liamzebedee
Copy link
Contributor Author

Busy fixing tests

return false;
}

uint tbtcAmount = TBTCConstants.getLotSize().add(DepositUtils.beneficiaryReward());
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

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 {
Copy link
Contributor

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());
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Member

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 😉

Copy link
Member

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

Copy link
Contributor Author

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?

Copy link
Contributor Author

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

Copy link
Contributor

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;
Copy link
Contributor

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)

Copy link
Contributor Author

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;
}
Copy link
Contributor

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?

Copy link
Member

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?

Copy link
Contributor Author

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 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump above @gakonst

Copy link
Contributor

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
Copy link
Contributor

@gakonst gakonst Sep 3, 2019

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
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

@Shadowfiend Shadowfiend Oct 9, 2019

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)
Copy link
Contributor

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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation

@nkuba
Copy link
Member

nkuba commented Sep 19, 2019

@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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's happening here?

Copy link
Contributor Author

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

@liamzebedee
Copy link
Contributor Author

@pdyraga @nkuba ready for another pass!

@pdyraga pdyraga assigned pdyraga and unassigned nkuba Sep 30, 2019
@pdyraga
Copy link
Member

pdyraga commented Sep 30, 2019

Is it the last piece we need or do we expect more Uniswap PRs?

@pdyraga pdyraga assigned Shadowfiend and unassigned pdyraga Oct 3, 2019
@@ -0,0 +1,82 @@
# Mount 3 volumes
Copy link
Contributor

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…

Copy link
Contributor Author

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@Shadowfiend Shadowfiend Oct 4, 2019

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.

Copy link
Contributor Author

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 💯!

Copy link
Contributor

@Shadowfiend Shadowfiend left a 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");
Copy link
Contributor

@Shadowfiend Shadowfiend Oct 9, 2019

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?

Copy link
Contributor Author

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 🤷‍♂

Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

@Shadowfiend Shadowfiend Oct 9, 2019

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()) {
Copy link
Contributor

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“.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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
Copy link
Contributor

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());
Copy link
Contributor

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).

Copy link
Contributor Author

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

Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.
Copy link
Contributor

@Shadowfiend Shadowfiend left a 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
)
Copy link
Contributor

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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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)
})
})
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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 😒

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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) => {
Copy link
Contributor

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
Copy link
Contributor

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`.
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous run of what?

Copy link
Contributor Author

@liamzebedee liamzebedee Oct 17, 2019

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)
Copy link
Contributor

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?

Copy link
Contributor Author

@liamzebedee liamzebedee Oct 17, 2019

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')
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discovered it myself through running up against the "revert" brick wall and reading the code.

Jackie Chan wondering wtf is going on

Copy link
Contributor

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.
Copy link
Contributor

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.

@Shadowfiend
Copy link
Contributor

Shadowfiend commented Oct 15, 2019

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:

@liamzebedee liamzebedee force-pushed the uniswap-onchain-liquidation branch 3 times, most recently from 04b7d2e to a22db06 Compare October 18, 2019 13:34
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.
@liamzebedee
Copy link
Contributor Author

@Shadowfiend cleaned up, tests passing, ready for another review.

Copy link
Contributor

@Shadowfiend Shadowfiend left a 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.

@Shadowfiend Shadowfiend merged commit 246c330 into master Oct 18, 2019
@Shadowfiend Shadowfiend deleted the uniswap-onchain-liquidation branch October 18, 2019 19:04
@Shadowfiend
Copy link
Contributor

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 😉

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants