diff --git a/contracts/DCAManager.sol b/contracts/DCAManager.sol new file mode 100644 index 0000000..7d56508 --- /dev/null +++ b/contracts/DCAManager.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.8.0; + +import "./interfaces/IDCAManager.sol"; +import '@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol'; + +contract DCAManager is IDCAManager { + + IUniswapV2Router02 public uniswapRouter; + + constructor(address _uniswapV2Router) public { + uniswapRouter = IUniswapV2Router02(_uniswapV2Router); + } + + /* + * See IDCAManager.sol + */ + function swapOnUsersBehalf( + address baseToken, + uint amountBaseToken, + address destinationToken, + uint fromTime, + uint toTime, + uint8 v, + bytes32 r, + bytes32 s + ) public override returns (bool) { + require(block.timestamp >= fromTime && block.timestamp <= toTime, "failed to meet the time requirement"); + // TODO have to be careful with this, if signed by a user via metamask it will add the ethereum message prefix + bytes memory message = abi.encodePacked( + baseToken, + amountBaseToken, + destinationToken, + fromTime, + toTime + ); + bytes32 messageHash = keccak256(message); + address user = ecrecover(messageHash, v, r, s); + address[] memory path = new address[](2); + path[0] = baseToken; + path[1] = destinationToken; + + uniswapRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens( + amountBaseToken, + 0, // TODO set min amount of WETH using some kind of oracle + path, + user, + toTime + ); + return true; + } + +} \ No newline at end of file diff --git a/contracts/Greeter.sol b/contracts/Greeter.sol deleted file mode 100644 index 8f67bdf..0000000 --- a/contracts/Greeter.sol +++ /dev/null @@ -1,23 +0,0 @@ -//SPDX-License-Identifier: Unlicense -pragma solidity ^0.7.0; - -import "hardhat/console.sol"; - - -contract Greeter { - string greeting; - - constructor(string memory _greeting) { - console.log("Deploying a Greeter with greeting:", _greeting); - greeting = _greeting; - } - - function greet() public view returns (string memory) { - return greeting; - } - - function setGreeting(string memory _greeting) public { - console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); - greeting = _greeting; - } -} diff --git a/contracts/interfaces/IDCAManager.sol b/contracts/interfaces/IDCAManager.sol new file mode 100644 index 0000000..816ce8a --- /dev/null +++ b/contracts/interfaces/IDCAManager.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.8.0; + +interface IDCAManager { + + /* + * @dev allow a relayer to trigger a trade on behalf of a user with their signed permission, + * requires that the user approve this contract to move the base token + * @param baseToken - the token to trade from + * @param amountBaseToken - the amount of the base token to trade into the destination token + * @param destinationToken - the address of the token that the user wants to swap their base token into + * @param fromTime - the desired time for this swap to occur + * @param toTime - this swap should happen no later than this + * @param v - signature param + * @param r - signature param + * @param s - signature param + * @returns true if successful + */ + function swapOnUsersBehalf( + address baseToken, + uint amountBaseToken, + address destinationToken, + uint fromTime, + uint toTime, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (bool); + +} \ No newline at end of file diff --git a/hardhat.config.js b/hardhat.config.js index 07a449f..613ec94 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,15 +1,5 @@ require("@nomiclabs/hardhat-waffle"); -// This is a sample Hardhat task. To learn how to create your own go to -// https://hardhat.org/guides/create-task.html -task("accounts", "Prints the list of accounts", async () => { - const accounts = await ethers.getSigners(); - - for (const account of accounts) { - console.log(account.address); - } -}); - // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more @@ -17,7 +7,7 @@ task("accounts", "Prints the list of accounts", async () => { * @type import('hardhat/config').HardhatUserConfig */ module.exports = { - solidity: "0.7.3", + solidity: "0.8.0", defaultNetwork: "hardhat", networks: { hardhat: { diff --git a/package-lock.json b/package-lock.json index bca159c..6a0da5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -667,6 +667,11 @@ "@types/web3": "1.0.19" } }, + "@openzeppelin/contracts": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.1.0.tgz", + "integrity": "sha512-TihZitscnaHNcZgXGj9zDLDyCqjziytB4tMCwXq0XimfWkAjBYyk5/pOsDbbwcavhlc79HhpTEpQcrMnPVa1mw==" + }, "@resolver-engine/core": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@resolver-engine/core/-/core-0.3.3.tgz", @@ -969,6 +974,25 @@ "@types/underscore": "*" } }, + "@uniswap/lib": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@uniswap/lib/-/lib-1.1.1.tgz", + "integrity": "sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg==" + }, + "@uniswap/v2-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@uniswap/v2-core/-/v2-core-1.0.0.tgz", + "integrity": "sha512-BJiXrBGnN8mti7saW49MXwxDBRFiWemGetE58q8zgfnPPzQKq55ADltEILqOt6VFZ22kVeVKbF8gVd8aY3l7pA==" + }, + "@uniswap/v2-periphery": { + "version": "1.1.0-beta.0", + "resolved": "https://registry.npmjs.org/@uniswap/v2-periphery/-/v2-periphery-1.1.0-beta.0.tgz", + "integrity": "sha512-6dkwAMKza8nzqYiXEr2D86dgW3TTavUvCR0w2Tu33bAbM8Ah43LKAzH7oKKPRT5VJQaMi1jtkGs1E8JPor1n5g==", + "requires": { + "@uniswap/lib": "1.1.1", + "@uniswap/v2-core": "1.0.0" + } + }, "@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", diff --git a/package.json b/package.json index 53d0916..b405b77 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,9 @@ { "name": "drip-finance", + "scripts": { + "test": "npx hardhat test", + "compile": "npx hardhat compile" + }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-waffle": "^2.0.1", @@ -7,5 +11,9 @@ "ethereum-waffle": "^3.3.0", "ethers": "^5.1.4", "hardhat": "^2.2.1" + }, + "dependencies": { + "@openzeppelin/contracts": "^4.1.0", + "@uniswap/v2-periphery": "^1.1.0-beta.0" } } diff --git a/scripts/sample-script.js b/scripts/sample-script.js deleted file mode 100644 index 1aa1a24..0000000 --- a/scripts/sample-script.js +++ /dev/null @@ -1,32 +0,0 @@ -// We require the Hardhat Runtime Environment explicitly here. This is optional -// but useful for running the script in a standalone fashion through `node