Skip to content

Commit

Permalink
feat(trader): add Ranger Trader, Exchange Adapter and initial trader …
Browse files Browse the repository at this point in the history
…implementation (UMAprotocol#2579)

* more nits

Signed-off-by: Christopher Maree <[email protected]>

* Restore CHANGELOG.md

* Restore CHANGELOG.md

* fixed another typo

Signed-off-by: Christopher Maree <[email protected]>

* small typo

Signed-off-by: Christopher Maree <[email protected]>

* typo

Signed-off-by: Christopher Maree <[email protected]>

* further refinement

Signed-off-by: Christopher Maree <[email protected]>

* Restore CHANGELOG.md

* Restore CHANGELOG.md

* Updated comments

Signed-off-by: Christopher Maree <[email protected]>

* refined typos

Signed-off-by: Christopher Maree <[email protected]>

* Updated comments

Signed-off-by: Christopher Maree <[email protected]>

* added more comment changes

Signed-off-by: Christopher Maree <[email protected]>

* cleaned more vars

Signed-off-by: Christopher Maree <[email protected]>

* Added trader implementation

Signed-off-by: Christopher Maree <[email protected]>

* Updated package imports

Signed-off-by: Christopher Maree <[email protected]>

* Restore liquidationStrategy.js

* Restore liquidationStrategy.js

* Restore liquidator.js

* Restore Liquidator.js

* Restore README.md

* Clean

Signed-off-by: Christopher Maree <[email protected]>

* removed DSProxy logic

Signed-off-by: Christopher Maree <[email protected]>

* Updated natspec

Signed-off-by: Christopher Maree <[email protected]>

* Updated comments

Signed-off-by: Christopher Maree <[email protected]>

* Updated comments

Signed-off-by: Christopher Maree <[email protected]>

* reverted logger changes

Signed-off-by: Christopher Maree <[email protected]>

* fixed typo

Signed-off-by: Christopher Maree <[email protected]>

* Added additional comments

Signed-off-by: Christopher Maree <[email protected]>

* fixed a typo

Signed-off-by: Christopher Maree <[email protected]>

* Added another lint fix

Signed-off-by: Christopher Maree <[email protected]>

* Updated interface type

Signed-off-by: Christopher Maree <[email protected]>

* Anoter nit

Signed-off-by: Christopher Maree <[email protected]>

* nit and lint types

Signed-off-by: Christopher Maree <[email protected]>

* Updated more types

Signed-off-by: Christopher Maree <[email protected]>

* Updated packages

Signed-off-by: Christopher Maree <[email protected]>

* Added another nit

Signed-off-by: Christopher Maree <[email protected]>

* updated yarn lock

Signed-off-by: Christopher Maree <[email protected]>
  • Loading branch information
chrismaree authored Mar 5, 2021
1 parent b23d7cf commit 8bc3dad
Show file tree
Hide file tree
Showing 18 changed files with 1,064 additions and 85 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ module.exports = {
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
]
],
rules: {
"@typescript-eslint/no-var-requires": 0
}
}
],
settings: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
}
},
"lint-staged": {
"*.js": "eslint --cache --fix"
"*.{js,ts,tsx}": "eslint --cache --fix"
},
"workspaces": [
"packages/*"
Expand Down
43 changes: 21 additions & 22 deletions packages/core/contracts/trader/uniswap-broker/UniswapBroker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,56 @@ contract UniswapBroker {
* possible to the truePrice.
* @dev True price is expressed in the ratio of token A to token B.
* @dev The caller must approve this contract to spend whichever token is intended to be swapped.
* @param tradingAsEOA bool to indicate if the UniswapBroker is being called by a DSProxy or an EOA.
* @param uniswapRouter address of the uniswap router used to facilate trades.
* @param uniswapFactory address of the uniswap factory used to fetch current pair reserves.
* @param tokenA address of the first token in the uniswap pair.
* @param tokenA address of the second token in the uniswap pair.
* @param truePriceTokenA the nominator of the true price.
* @param truePriceTokenB the denominatornominator of the true price.
* @param maxSpendTokenA maximum to spend in tokenA. Note can be set to zero, thereby limiting the direction of trade.
* @param maxSpendTokenA maximum to spend in tokenB. Note can be set to zero, thereby limiting the direction of trade.
* @param swappedTokens array of addresses which are to be swapped. The order does not matter as the function will figure
* out which tokens need to be exchanged to move the market to the desired "true" price.
* @param truePriceTokens array of unit used to represent the true price. 0th value is the numerator of the true price
* and the 1st value is the the denominator of the true price.
* @param maxSpendTokens array of unit to represent the max to spend in the two tokens.
* @param to recipient of the trade proceeds.
* @param to deadline to limit when the trade can execute. If the tx is mined after this timestamp then revert.
* @param deadline to limit when the trade can execute. If the tx is mined after this timestamp then revert.
*/

function swapToPrice(
bool tradingAsEOA,
address uniswapRouter,
address uniswapFactory,
address tokenA,
address tokenB,
uint256 truePriceTokenA,
uint256 truePriceTokenB,
uint256 maxSpendTokenA,
uint256 maxSpendTokenB,
address[2] memory swappedTokens,
uint256[2] memory truePriceTokens,
uint256[2] memory maxSpendTokens,
address to,
uint256 deadline
) public {
IUniswapV2Router01 router = IUniswapV2Router01(uniswapRouter);

// true price is expressed as a ratio, so both values must be non-zero
require(truePriceTokenA != 0 && truePriceTokenB != 0, "SwapToPrice: ZERO_PRICE");
require(truePriceTokens[0] != 0 && truePriceTokens[1] != 0, "SwapToPrice: ZERO_PRICE");
// caller can specify 0 for either if they wish to swap in only one direction, but not both
require(maxSpendTokenA != 0 || maxSpendTokenB != 0, "SwapToPrice: ZERO_SPEND");
require(maxSpendTokens[0] != 0 || maxSpendTokens[1] != 0, "SwapToPrice: ZERO_SPEND");

bool aToB;
uint256 amountIn;
{
(uint256 reserveA, uint256 reserveB) = getReserves(uniswapFactory, tokenA, tokenB);
(aToB, amountIn) = computeTradeToMoveMarket(truePriceTokenA, truePriceTokenB, reserveA, reserveB);
(uint256 reserveA, uint256 reserveB) = getReserves(uniswapFactory, swappedTokens[0], swappedTokens[1]);
(aToB, amountIn) = computeTradeToMoveMarket(truePriceTokens[0], truePriceTokens[1], reserveA, reserveB);
}

require(amountIn > 0, "SwapToPrice: ZERO_AMOUNT_IN");

// spend up to the allowance of the token in
uint256 maxSpend = aToB ? maxSpendTokenA : maxSpendTokenB;
uint256 maxSpend = aToB ? maxSpendTokens[0] : maxSpendTokens[1];
if (amountIn > maxSpend) {
amountIn = maxSpend;
}

address tokenIn = aToB ? tokenA : tokenB;
address tokenOut = aToB ? tokenB : tokenA;
TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);
address tokenIn = aToB ? swappedTokens[0] : swappedTokens[1];
address tokenOut = aToB ? swappedTokens[1] : swappedTokens[0];

TransferHelper.safeApprove(tokenIn, address(router), amountIn);

if (tradingAsEOA) TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn);

address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;
Expand Down
32 changes: 14 additions & 18 deletions packages/core/test/trader/uniswap-broker/UniswapBroker.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const { MAX_UINT_VAL } = require("@uma/common");
const { toWei, toBN, fromWei } = web3.utils;
const { getTruffleContract } = require("@uma/core");
const truffleContract = require("@truffle/contract");

// Tested Contract
const UniswapBroker = artifacts.require("UniswapBroker");
const UniswapBroker = getTruffleContract("UniswapBroker", web3);
const Token = getTruffleContract("ExpandedERC20", web3);
const WETH9 = getTruffleContract("WETH9", web3);

Expand All @@ -22,8 +23,7 @@ let pairAddress;

// Takes in a json object from a compiled contract and returns a truffle contract instance that can be deployed.
const createContractObjectFromJson = contractJsonObject => {
const contract = require("@truffle/contract");
let truffleContractCreator = contract(contractJsonObject);
let truffleContractCreator = truffleContract(contractJsonObject);
truffleContractCreator.setProvider(web3.currentProvider);
return truffleContractCreator;
};
Expand All @@ -49,7 +49,7 @@ const getAmountOut = async (amountIn, aToB) => {

contract("UniswapBroker", function(accounts) {
const deployer = accounts[0];
const trader = accounts[0];
const trader = accounts[1];
before(async () => {
const WETH = await WETH9.new();
// deploy Uniswap V2 Factory & router.
Expand All @@ -72,8 +72,8 @@ contract("UniswapBroker", function(accounts) {
await tokenA.mint(trader, toWei("100000000000000"));
await tokenB.mint(trader, toWei("100000000000000"));

await tokenA.approve(router.address, toWei("100000000000000"));
await tokenB.approve(router.address, toWei("100000000000000"));
await tokenA.approve(router.address, toWei("100000000000000"), { from: trader });
await tokenB.approve(router.address, toWei("100000000000000"), { from: trader });
await tokenA.approve(uniswapBroker.address, MAX_UINT_VAL, { from: trader });
await tokenB.approve(uniswapBroker.address, MAX_UINT_VAL, { from: trader });

Expand Down Expand Up @@ -147,14 +147,12 @@ contract("UniswapBroker", function(accounts) {

// Now we can actually execute the swapToPrice method to ensure that the contract correctly modifies the spot price.
await uniswapBroker.swapToPrice(
true, // The swap is being executed as an EOA. This ensures that the correct token transfers are done.
router.address,
factory.address,
tokenA.address,
tokenB.address,
"1000", // The "true" price of the pair is expressed as the ratio of token A to token B. A price of 1000 is simply 1000/1.
"1",
MAX_UINT_VAL, // Set to the max posable value as we want to let the broker trade as much as needed in this example.
MAX_UINT_VAL,
[tokenA.address, tokenB.address],
["1000", "1"], // The "true" price of the pair is expressed as the ratio of token A to token B. A price of 1000 is simply 1000/1.
[MAX_UINT_VAL, MAX_UINT_VAL], // Set to the max posable value as we want to let the broker trade as much as needed in this example.
trader,
(await web3.eth.getBlock("latest")).timestamp + 10,
{ from: trader }
Expand Down Expand Up @@ -221,14 +219,12 @@ contract("UniswapBroker", function(accounts) {

// Now we can actually execute the swapToPrice method to ensure that the contract correctly modifies the spot price.
await uniswapBroker.swapToPrice(
true, // The swap is being executed as an EOA. This ensures that the correct token transfers are done.
router.address,
factory.address,
tokenA.address,
tokenB.address,
"1000", // The "true" price of the pair is expressed as the ratio of token A to token B. A price of 1000 is simply 1000/1.
"1",
MAX_UINT_VAL, // Set to the max posable value as we want to let the broker trade as much as needed in this example.
MAX_UINT_VAL,
[tokenA.address, tokenB.address],
["1000", "1"], // The "true" price of the pair is expressed as the ratio of token A to token B. A price of 1000 is simply 1000/1.
[MAX_UINT_VAL, MAX_UINT_VAL], // Set to the max posable value as we want to let the broker trade as much as needed in this example.
trader,
(await web3.eth.getBlock("latest")).timestamp + 10,
{ from: trader }
Expand Down
4 changes: 3 additions & 1 deletion packages/financial-templates-lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ module.exports = {
...require("./src/helpers/GasEstimator"),
...require("./src/logger/Logger"),
...require("./src/logger/SpyTransport"),
...require("./src/price-feed/UniswapPriceFeed"),
...require("./src/price-feed/CreatePriceFeed"),
...require("./src/price-feed/Networker"),
...require("./src/price-feed/PriceFeedMock"),
...require("./src/price-feed/PriceFeedMockScaled"),
...require("./src/price-feed/InvalidPriceFeedMock")
...require("./src/price-feed/InvalidPriceFeedMock"),
...require("./src/proxy-transaction-handler/DSProxyManager")
};
23 changes: 14 additions & 9 deletions packages/financial-templates-lib/src/price-feed/CreatePriceFeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ async function createUniswapPriceFeedForFinancialContract(

const userConfig = config || {};

// Check if there is an override for the getTime method in the price feed config. Specifically, we can replace the
// get time method with the current block time.
if (userConfig.getTimeOverride?.useBlockTime) {
getTime = async () => (await web3.eth.getBlock("latest")).timestamp;
}

logger.debug({
at: "createUniswapPriceFeedForFinancialContract",
message: "Inferred default config from identifier or Financial Contract address",
Expand Down Expand Up @@ -695,21 +701,20 @@ async function createReferencePriceFeedForFinancialContract(
}
// Check if there is an override for the getTime method in the price feed config. Specifically, we can replace the
// get time method with the current block time.
if (combinedConfig.getTimeOverride) {
if (combinedConfig.getTimeOverride.useBlockTime) {
getTime = async () =>
web3.eth.getBlock("latest").then(block => {
return block.timestamp;
});
}
if (combinedConfig.getTimeOverride?.useBlockTime) {
getTime = async () => (await web3.eth.getBlock("latest")).timestamp;
}
}
return await createPriceFeed(logger, web3, networker, getTime, combinedConfig);
}

function getFinancialContractIdentifierAtAddress(web3, financialContractAddress) {
const ExpiringMultiParty = getTruffleContract("ExpiringMultiParty", web3, "1.2.0");
return new web3.eth.Contract(ExpiringMultiParty.abi, financialContractAddress);
try {
const ExpiringMultiParty = getTruffleContract("ExpiringMultiParty", web3, "1.2.0");
return new web3.eth.Contract(ExpiringMultiParty.abi, financialContractAddress);
} catch (error) {
throw new Error({ message: "Something went wrong in fetching the financial contract identifier", error });
}
}

module.exports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@ class DSProxyManager {
this.logger.debug({
at: "DSProxyManager",
message: "Executing function on library that will be deployed in the same transaction",
callCode,
callData
callData,
callCode
});

const executeTransaction = await this.dsProxy.methods["execute(bytes,bytes)"](callCode, callData).send({
Expand All @@ -139,7 +139,7 @@ class DSProxyManager {

this.logger.info({
at: "DSProxyManager",
message: "Executed function on a freshly minted library, created in the same tx 🤗",
message: "Executed function on a freshly deployed library, created in the same tx 🤗",
callCodeHash: web3.utils.soliditySha3(callCode),
callData,
tx: executeTransaction.transactionHash
Expand Down
5 changes: 4 additions & 1 deletion packages/trader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"dependencies": {
"@uma/common": "^2.0.1",
"async-retry": "^1.3.1",
"dotenv": "^8.2.0"
"dotenv": "^8.2.0",
"@uma/core": "2.0.1",
"@uma/financial-templates-lib": "^1.2.0",
"@umaprotocol/ynatm": "^0.0.1"
},
"devDependencies": {
"@nomiclabs/hardhat-truffle5": "^2.0.0",
Expand Down
Loading

0 comments on commit 8bc3dad

Please sign in to comment.