-
Notifications
You must be signed in to change notification settings - Fork 42
Uniswap integration #160
Uniswap integration #160
Changes from 65 commits
8e774e7
b1489aa
6e15c3b
46a8c96
8b2ee5f
f6fa7b3
9f89aa5
756b93f
88eb55e
1fa9333
8af7289
e47cb93
63eb812
393cd00
f80d08e
8f49752
48a16c6
603c3c9
01fc708
3f2a224
a2ca3f7
064fa4c
5d3dec3
d29d02b
56445a8
9edf4d6
0f5ea65
8335067
baf4a9e
cc28f50
af77695
26bc55e
f0d91ad
91465d3
4398801
9cd1acc
c1dcd20
0e12f5b
811fe11
6a8a7e2
70e3246
223a077
10e2260
e39c087
13f8b3a
a1d5e32
31207de
9ece9f5
655a963
205f8a8
9efd8b5
9090ea7
471ed0d
7430c26
b028a55
9b00756
61f5fda
0ab419a
3d95b77
3571377
2a8b622
45db78b
38f4319
78acc02
189b7c6
4dfcdac
dd72854
c9fb027
5938dfd
82aab63
6abbfaf
887c100
2ee362f
5459bf0
a3cb6a8
6819e9a
364cf02
255b2e9
384b877
d2a01f2
7688c51
0cd33ad
3378a52
0a2f0de
922bd68
091bbc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,7 @@ | |
path = implementation/contracts/bitcoin-spv | ||
url = [email protected]:summa-tx/bitcoin-spv.git | ||
branch = 1.0.0-embedabble | ||
[submodule "implementation/uniswap/contracts-vyper"] | ||
path = implementation/uniswap/contracts-vyper | ||
url = https://github.com/Uniswap/contracts-vyper | ||
branch = c10c08d81d6114f694baa8bd32f555a40f6264da |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,15 @@ | ||
{ | ||
"presets": ["es2015", "stage-2", "stage-3"] | ||
"presets": [ | ||
[ | ||
"@babel/preset-env", | ||
{ | ||
"useBuiltIns": "entry", | ||
"corejs": 3, | ||
"targets": { | ||
"node": true | ||
}, | ||
} | ||
] | ||
], | ||
"sourceMaps": "inline" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ import {TBTCConstants} from "./TBTCConstants.sol"; | |
import {IKeep} from "../interfaces/IKeep.sol"; | ||
import {OutsourceDepositLogging} from "./OutsourceDepositLogging.sol"; | ||
import {IBurnableERC20} from "../interfaces/IBurnableERC20.sol"; | ||
import {IUniswapExchange} from "../uniswap/IUniswapExchange.sol"; | ||
import {ITBTCSystem} from "../interfaces/ITBTCSystem.sol"; | ||
|
||
library DepositLiquidation { | ||
|
||
|
@@ -21,8 +23,28 @@ library DepositLiquidation { | |
/// @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 | ||
function attemptToLiquidateOnchain() public pure returns (bool) { | ||
return false; | ||
// TODO(liamz): make attemptToLiquidateOnchain internal, check for re-entry | ||
function attemptToLiquidateOnchain( | ||
DepositUtils.Deposit storage _d | ||
) public returns (bool) { | ||
require(address(this).balance > 0, "no ether to liquidate"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would this error case be reached? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this error case IS reached, it bricks signer liquidation. If you think the check is necessary, do a |
||
|
||
IUniswapExchange exchange = IUniswapExchange(ITBTCSystem(_d.TBTCSystem).getTBTCUniswapExchange()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should prefer https://github.com/Uniswap/contracts-vyper/blob/master/contracts/uniswap_factory.vy#L33-L36 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (plus a check that a contract was returned) |
||
|
||
// with collateralization = 125%, we can assume an order of | ||
// 1.0005 TBTC (equivalent to will be filled | ||
uint MIN_TBTC = TBTCConstants.getLotSize().add(DepositUtils.beneficiaryReward()); | ||
|
||
uint ethSold = address(this).balance; | ||
uint tbtcBought = exchange.getEthToTokenInputPrice(ethSold); | ||
if(tbtcBought < MIN_TBTC) { | ||
return false; | ||
} | ||
|
||
uint deadline = block.timestamp + 1; | ||
exchange.ethToTokenSwapOutput.value(ethSold)(tbtcBought, deadline); | ||
|
||
return true; | ||
} | ||
|
||
/// @notice Notifies the keep contract of fraud | ||
|
@@ -77,28 +99,30 @@ library DepositLiquidation { | |
/// @param _d deposit storage pointer | ||
function startSignerFraudLiquidation(DepositUtils.Deposit storage _d) public { | ||
_d.logStartedLiquidation(true); | ||
|
||
// Reclaim used state for gas savings | ||
_d.redemptionTeardown(); | ||
uint256 _seized = _d.seizeSignerBonds(); | ||
|
||
if (_d.auctionTBTCAmount() == 0) { | ||
// we came from the redemption flow | ||
_d.setLiquidated(); | ||
_d.requesterAddress.transfer(_seized); | ||
_d.logLiquidated(); | ||
return; | ||
} | ||
|
||
bool _liquidated = attemptToLiquidateOnchain(); | ||
bool _liquidated = attemptToLiquidateOnchain(_d); | ||
|
||
if (_liquidated) { | ||
_d.distributeBeneficiaryReward(); | ||
_d.setLiquidated(); | ||
_d.logLiquidated(); | ||
address(0).transfer(address(this).balance); // burn it down | ||
} | ||
if (!_liquidated) { | ||
|
||
_d.distributeBeneficiaryReward(); | ||
|
||
if (_d.auctionTBTCAmount() == 0) { // redemption | ||
IBurnableERC20 _tbtc = IBurnableERC20(_d.TBTCToken); | ||
_tbtc.transferFrom(address(this), _d.requesterAddress, TBTCConstants.getLotSize()); | ||
address(_d.requesterAddress).transfer(address(this).balance); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mhluongo should we give any additonal TBTC or ETH to the requester here? they had some inconvenience There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same question for non-fraud version |
||
} else { | ||
// maintain supply peg | ||
_tbtc.burnFrom(address(this), TBTCConstants.getLotSize()); | ||
address(0).transfer(address(this).balance); // burn down eth | ||
} | ||
|
||
|
||
} else if (!_liquidated) { | ||
_d.setFraudLiquidationInProgress(); | ||
_d.liquidationInitiated = block.timestamp; // Store the timestamp for auction | ||
} | ||
|
@@ -111,9 +135,17 @@ library DepositLiquidation { | |
_d.logStartedLiquidation(false); | ||
// Reclaim used state for gas savings | ||
_d.redemptionTeardown(); | ||
_d.seizeSignerBonds(); | ||
uint256 _seized = _d.seizeSignerBonds(); | ||
|
||
if (_d.auctionTBTCAmount() == 0) { | ||
// we came from the redemption flow | ||
_d.setLiquidated(); | ||
_d.requesterAddress.transfer(_seized); | ||
_d.logLiquidated(); | ||
return; | ||
} | ||
|
||
bool _liquidated = attemptToLiquidateOnchain(); | ||
bool _liquidated = attemptToLiquidateOnchain(_d); | ||
|
||
if (_liquidated) { | ||
_d.distributeBeneficiaryReward(); | ||
|
@@ -122,8 +154,8 @@ library DepositLiquidation { | |
_d.logLiquidated(); | ||
} | ||
if (!_liquidated) { | ||
_d.liquidationInitiated = block.timestamp; // Store the timestamp for auction | ||
_d.setFraudLiquidationInProgress(); | ||
_d.liquidationInitiated = block.timestamp; // Store the timestamp for auction | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
pragma solidity 0.4.25; | ||
|
||
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; | ||
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract TBTC is ERC20Detailed, ERC20 { | ||
/// @dev Constructor, calls ERC20Detailed constructor to set Token info | ||
/// ERC20Detailed(TokenName, TokenSymbol, NumberOfDecimals) | ||
constructor() ERC20Detailed("Trustless bitcoin", "TBTC", 18) public { | ||
// solium-disable-previous-line no-empty-blocks | ||
} | ||
|
||
/// @dev Mints an amount of the token and assigns it to an account. | ||
/// Uses the internal _mint function | ||
/// @param _account The account that will receive the created tokens. | ||
/// @param _amount The amount of tokens that will be created. | ||
function mint(address _account, uint256 _amount) public returns (bool){ | ||
// NOTE: this is a public function with unchecked minting. | ||
// TODO: enforce calling authority. | ||
_mint(_account, _amount); | ||
return true; | ||
} | ||
|
||
/// @dev Burns an amount of the token of a given account | ||
/// deducting from the sender's allowance for said account. | ||
/// Uses the internal _burn function. | ||
/// @param _account The account whose tokens will be burnt. | ||
/// @param _amount The amount of tokens that will be burnt. | ||
function burnFrom(address _account, uint256 _amount) public { | ||
// NOTE: this uses internal function _burn instead of _burnFrom. | ||
// This will bypass allowance check for now. | ||
// TODO: enforce calling authority. | ||
_burn(_account, _amount); | ||
} | ||
|
||
/// @dev Transfer tokens from one address to another | ||
/// Uses the internal _transfer function. | ||
/// @param _from The address to send tokens from | ||
/// @param _to The address to transfer tokens to | ||
/// @param _value The amount of tokens to be transferred | ||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { | ||
// NOTE: this overrides transferFrom in openZeppelin ERC20.sol | ||
// in order to bypass allowance check for now. | ||
// TODO: enforce calling authority. | ||
_transfer(_from, _to, _value); | ||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
pragma solidity 0.4.25; | ||
|
||
/* solium-disable */ | ||
|
||
contract IUniswapExchange { | ||
event TokenPurchase(address indexed buyer, uint256 indexed eth_sold, uint256 indexed tokens_bought); | ||
event EthPurchase(address indexed buyer, uint256 indexed tokens_sold, uint256 indexed eth_bought); | ||
event AddLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount); | ||
event RemoveLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount); | ||
event Transfer(address indexed _from, uint256 indexed _value); | ||
event Approval(address indexed _owner, uint256 indexed _value); | ||
|
||
// Address of ERC20 token sold on this exchange | ||
function tokenAddress() external view returns (address token); | ||
// Address of Uniswap Factory | ||
function factoryAddress() external view returns (address factory); | ||
// Provide Liquidity | ||
function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256); | ||
function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256); | ||
// Get Prices | ||
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought); | ||
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold); | ||
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought); | ||
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold); | ||
// Trade ETH to ERC20 | ||
function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought); | ||
function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256 tokens_bought); | ||
function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256 eth_sold); | ||
function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256 eth_sold); | ||
// Trade ERC20 to ETH | ||
function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256 eth_bought); | ||
function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256 eth_bought); | ||
function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256 tokens_sold); | ||
function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256 tokens_sold); | ||
// Trade ERC20 to ERC20 | ||
function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256 tokens_bought); | ||
function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_bought); | ||
function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256 tokens_sold); | ||
function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_sold); | ||
// Trade ERC20 to Custom Pool | ||
function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256 tokens_bought); | ||
function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256 tokens_bought); | ||
function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256 tokens_sold); | ||
function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256 tokens_sold); | ||
// ERC20 comaptibility for liquidity tokens | ||
bytes32 public name; | ||
bytes32 public symbol; | ||
uint256 public decimals; | ||
function transfer(address _to, uint256 _value) external returns (bool); | ||
function transferFrom(address _from, address _to, uint256 value) external returns (bool); | ||
function approve(address _spender, uint256 _value) external returns (bool); | ||
function allowance(address _owner, address _spender) external view returns (uint256); | ||
function balanceOf(address _owner) external view returns (uint256); | ||
function totalSupply() external view returns (uint256); | ||
// Never use | ||
function setup(address token_addr) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
pragma solidity 0.4.25; | ||
|
||
/* solium-disable */ | ||
|
||
contract IUniswapFactory { | ||
// Public Variables | ||
address public exchangeTemplate; | ||
uint256 public tokenCount; | ||
// Create Exchange | ||
function createExchange(address token) external returns (address exchange); | ||
// Get Exchange and Token Info | ||
function getExchange(address token) external view returns (address exchange); | ||
function getToken(address exchange) external view returns (address token); | ||
function getTokenWithId(uint256 tokenId) external view returns (address token); | ||
// Never use | ||
function initializeFactory(address template) external; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
pragma solidity 0.4.25; | ||
|
||
/// @notice A contract for tracking the location of Uniswap testnet deployments | ||
/// @dev useful for usage in tests/migrations, but SHOULD NOT be used by other contracts | ||
contract UniswapDeployment { | ||
address public factory; | ||
address public exchange; | ||
|
||
constructor( | ||
address _factory, | ||
address _exchange | ||
) public { | ||
factory = _factory; | ||
exchange = _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.
Oversight from me: should be
internal