From e28c253d7d9cb4e4c2933f437d55e6400e5d21c7 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Mon, 28 Jun 2021 09:22:20 -0700 Subject: [PATCH 01/21] WIP --- .../connectors/pooltogether/events.sol | 6 ++ .../connectors/pooltogether/interface.sol | 0 .../mainnet/connectors/pooltogether/main.sol | 63 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 contracts/mainnet/connectors/pooltogether/events.sol create mode 100644 contracts/mainnet/connectors/pooltogether/interface.sol create mode 100644 contracts/mainnet/connectors/pooltogether/main.sol diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol new file mode 100644 index 00000000..6009945f --- /dev/null +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositTo(address to, uint256 amount, address controlledToken, address referrer); + event LogWithdrawInstantlyFrom(address from, uint256 amount, address controlledToken, uint256 maximumExitFee); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol new file mode 100644 index 00000000..6dbe7b55 --- /dev/null +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.7.0; + +/** + * @title PoolTogether + * @dev Deposit & Withdraw from PoolTogether + */ + + import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { TokenInterface } from "../../common/interfaces.sol"; +import ( Events ) from "./events.sol"; +import { DSMath } from "../../common/math.sol"; + +abstract contract PoolTogetherResolver is Events, DSMath { + using SafeERC20 for IERC20; + + /** + * @dev Deposit into Prize Pool + * @param to Address to whom the controlled tokens should be minted + * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. + * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. + * @param referrer The address that should receive referral awards, if any. + */ + + function depositTo( + address: to, + uint256 amount, + address controlledToken, + address referrer + ) external payable returns ( string memory _eventName, bytes memory _eventParam) { + + + _eventName = "LogDepositTo(address, uint256, address, address)"; + _eventParam = abi.encode(address(to), amount, address(controlledToken), address(referrer)); + } + + /** + * #dev Withdraw from Prize Pool + * @param from The address to withdraw from. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. + * @param amount THe amount to withdraw + * @param controlledToken The controlled token to withdraw from. + * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. + */ + + function withdrawInstantlyFrom ( + address from, + uint256 amount, + address controlledToken, + uint256 maximumExitFee + ) external returns (string memory _eventName, bytes memory _eventParam) { + + + _eventName = "LogWithdrawInstantlyFrom(address, uint256, address, uint256)"; + _eventParams = abi.encode(address(from), amount, address(controlledToken), maximumExitFee); + } + + +} + +contract ConnectV2PoolTogether is PoolTogetherResolver { + string public constant name = "PoolTogether-v1"; +} \ No newline at end of file From 475612f506dc341d0ca2a342cfd66b6fc48e26d7 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Fri, 2 Jul 2021 19:49:43 -0700 Subject: [PATCH 02/21] WIP --- .../connectors/pooltogether/events.sol | 4 +- .../connectors/pooltogether/interface.sol | 6 +++ .../mainnet/connectors/pooltogether/main.sol | 40 ++++++++++++++----- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 6009945f..4bb8184e 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -1,6 +1,6 @@ pragma solidity ^0.7.0; contract Events { - event LogDepositTo(address to, uint256 amount, address controlledToken, address referrer); - event LogWithdrawInstantlyFrom(address from, uint256 amount, address controlledToken, uint256 maximumExitFee); + event LogDepositTo(address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); + event LogWithdrawInstantlyFrom(address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index e69de29b..1ff58783 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.7.0; + +interface PrizePoolInterface { + function depositTo( address to, uint256 amount, address controlledToken, address referrer) external; + function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 6dbe7b55..03a8ccc9 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -7,52 +7,74 @@ pragma solidity ^0.7.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + import { PrizePoolInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import ( Events ) from "./events.sol"; import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; -abstract contract PoolTogetherResolver is Events, DSMath { +abstract contract PoolTogetherResolver is Events, DSMath, Basic { using SafeERC20 for IERC20; /** * @dev Deposit into Prize Pool + * @notice Deposit a token into a prize pool + * @param prizePool PrizePool address to deposit to * @param to Address to whom the controlled tokens should be minted * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. * @param referrer The address that should receive referral awards, if any. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. */ function depositTo( + address prizePool, address: to, uint256 amount, address controlledToken, - address referrer - ) external payable returns ( string memory _eventName, bytes memory _eventParam) { + address referrer, + uint256 getId, + uint256 setId + ) external returns ( string memory _eventName, bytes memory _eventParam) { + uint _amount = getUint(getId, amount); + PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); - _eventName = "LogDepositTo(address, uint256, address, address)"; - _eventParam = abi.encode(address(to), amount, address(controlledToken), address(referrer)); + // Approve prizePool + + prizePoolContract.depositTo(to, amount, controlledToken, referrer); + + setUint(setId, _amount); + + _eventName = "LogDepositTo(address,uint256,address,address,uint256, uint256)"; + _eventParam = abi.encode(address(to), amount, address(controlledToken), address(referrer), getId, setId); } /** - * #dev Withdraw from Prize Pool + * @dev Withdraw from Prize Pool + * @notice Withdraw a token from a prize pool * @param from The address to withdraw from. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. * @param amount THe amount to withdraw * @param controlledToken The controlled token to withdraw from. * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. */ function withdrawInstantlyFrom ( address from, uint256 amount, address controlledToken, - uint256 maximumExitFee + uint256 maximumExitFee, + uint256 getId, + uint256 setId ) external returns (string memory _eventName, bytes memory _eventParam) { - _eventName = "LogWithdrawInstantlyFrom(address, uint256, address, uint256)"; - _eventParams = abi.encode(address(from), amount, address(controlledToken), maximumExitFee); + _eventName = "LogWithdrawInstantlyFrom(address,uint256,address,uint256,uint256,uint256)"; + _eventParams = abi.encode(address(from), amount, address(controlledToken), maximumExitFee, getId, setId); } From aa6e61a6d7a80ef004a7a2301414b5dcc0873916 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Sat, 3 Jul 2021 23:55:45 -0700 Subject: [PATCH 03/21] WIP: Deposit test seems to be working --- .../mainnet/connectors/pooltogether/main.sol | 10 +- test/pooltogether/pooltogether.test.js | 150 ++++++++++++++++++ 2 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 test/pooltogether/pooltogether.test.js diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 03a8ccc9..2648591d 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -10,7 +10,7 @@ pragma solidity ^0.7.0; import { PrizePoolInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; -import ( Events ) from "./events.sol"; +import { Events } from "./events.sol"; import { DSMath } from "../../common/math.sol"; import { Basic } from "../../common/basic.sol"; @@ -21,6 +21,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @dev Deposit into Prize Pool * @notice Deposit a token into a prize pool * @param prizePool PrizePool address to deposit to + * @param token Token to deposit * @param to Address to whom the controlled tokens should be minted * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. @@ -31,7 +32,8 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function depositTo( address prizePool, - address: to, + address token, + address to, uint256 amount, address controlledToken, address referrer, @@ -43,6 +45,8 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); // Approve prizePool + TokenInterface tokenContract = TokenInterface(token); + tokenContract.approve(prizePool, _amount); prizePoolContract.depositTo(to, amount, controlledToken, referrer); @@ -74,7 +78,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _eventName = "LogWithdrawInstantlyFrom(address,uint256,address,uint256,uint256,uint256)"; - _eventParams = abi.encode(address(from), amount, address(controlledToken), maximumExitFee, getId, setId); + _eventParam = abi.encode(address(from), amount, address(controlledToken), maximumExitFee, getId, setId); } diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js new file mode 100644 index 00000000..67dcea98 --- /dev/null +++ b/test/pooltogether/pooltogether.test.js @@ -0,0 +1,150 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle + +const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js") +const buildDSAv2 = require("../../scripts/buildDSAv2") +const encodeSpells = require("../../scripts/encodeSpells.js") +const getMasterSigner = require("../../scripts/getMasterSigner") + +const addresses = require("../../scripts/constant/addresses"); +const abis = require("../../scripts/constant/abis"); +const constants = require("../../scripts/constant/constant"); +const tokens = require("../../scripts/constant/tokens"); + +const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json") +const connectV2PoolTogetherArtifacts = require("../../artifacts/contracts/mainnet/connectors/pooltogether/main.sol/ConnectV2PoolTogether.json") + +describe("PoolTogether", function () { + const connectorName = "COMPOUND-TEST-A" + const ptConnectorName = "POOLTOGETHER-TEST-A" + + let dsaWallet0 + let masterSigner; + let instaConnectorsV2; + let connector; + let ptConnector; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + masterSigner = await getMasterSigner(wallet3) + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: connectV2CompoundArtifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + ptConnector = await deployAndEnableConnector({ + connectorName: ptConnectorName, + contractArtifact: connectV2PoolTogetherArtifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("PTConnector address", ptConnector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!ptConnector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("Should deposit ETH in Compound", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", amount, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should borrow DAI from Compound and deposit DAI into DAI Prize Pool", async function () { + const amount = 100 // 100 DAI + const setId = "83478237" + const token = tokens.dai.address // DAI Token + const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool + const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["DAI-A", amount, 0, setId] + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [prizePool, token, dsaWallet0.address, amount, controlledToken, constants.address_zero, setId, 0] + } + ] + + let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + const balance = await cToken.balanceOf(dsaWallet0.address) + const tokenName = await cToken.name() + console.log("Balance: ", balance.toString(), tokenName) + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + const balanceAfter = await cToken.balanceOf(dsaWallet0.address) + console.log("Balance: ", balanceAfter.toString(), tokenName) + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + // Should have 100 PT DAI Tickets + expect(balanceAfter.toNumber()).to.be.eq(100); + }); + + // it("Should deposit all ETH in Compound", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "deposit", + // args: ["ETH-A", constants.max_value, 0, 0] + // } + // ] + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + // const receipt = await tx.wait() + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("0")); + // }); + + // it("Should withdraw all ETH from Compound", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "withdraw", + // args: ["ETH-A", constants.max_value, 0, 0] + // } + // ] + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + // const receipt = await tx.wait() + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + // }); + }) +}) From 8702de7dd13083023fbfc487c9ea252f36450c45 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Mon, 5 Jul 2021 06:52:20 -0700 Subject: [PATCH 04/21] WIP: Withdrawal Working --- .../connectors/pooltogether/events.sol | 4 +- .../connectors/pooltogether/interface.sol | 1 + .../mainnet/connectors/pooltogether/main.sol | 25 +++--- test/pooltogether/pooltogether.test.js | 85 +++++++++++-------- 4 files changed, 68 insertions(+), 47 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 4bb8184e..aed564dd 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -1,6 +1,6 @@ pragma solidity ^0.7.0; contract Events { - event LogDepositTo(address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); - event LogWithdrawInstantlyFrom(address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); + event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); + event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index 1ff58783..ad72cf8b 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -1,6 +1,7 @@ pragma solidity ^0.7.0; interface PrizePoolInterface { + function token() external view returns (address); function depositTo( address to, uint256 amount, address controlledToken, address referrer) external; function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 2648591d..a9b0a95e 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -21,7 +21,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @dev Deposit into Prize Pool * @notice Deposit a token into a prize pool * @param prizePool PrizePool address to deposit to - * @param token Token to deposit * @param to Address to whom the controlled tokens should be minted * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. @@ -32,7 +31,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function depositTo( address prizePool, - address token, address to, uint256 amount, address controlledToken, @@ -43,22 +41,24 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint _amount = getUint(getId, amount); PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); + address prizePoolToken = prizePoolContract.token(); // Approve prizePool - TokenInterface tokenContract = TokenInterface(token); + TokenInterface tokenContract = TokenInterface(prizePoolToken); tokenContract.approve(prizePool, _amount); - prizePoolContract.depositTo(to, amount, controlledToken, referrer); + prizePoolContract.depositTo(to, _amount, controlledToken, referrer); setUint(setId, _amount); - _eventName = "LogDepositTo(address,uint256,address,address,uint256, uint256)"; - _eventParam = abi.encode(address(to), amount, address(controlledToken), address(referrer), getId, setId); + _eventName = "LogDepositTo(address,address,uint256,address,address,uint256, uint256)"; + _eventParam = abi.encode(address(prizePool), address(to), _amount, address(controlledToken), address(referrer), getId, setId); } /** * @dev Withdraw from Prize Pool * @notice Withdraw a token from a prize pool + * @param prizePool PrizePool address to deposit to * @param from The address to withdraw from. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. * @param amount THe amount to withdraw * @param controlledToken The controlled token to withdraw from. @@ -68,6 +68,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { */ function withdrawInstantlyFrom ( + address prizePool, address from, uint256 amount, address controlledToken, @@ -75,13 +76,17 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint256 getId, uint256 setId ) external returns (string memory _eventName, bytes memory _eventParam) { + uint _amount = getUint(getId, amount); + PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); - _eventName = "LogWithdrawInstantlyFrom(address,uint256,address,uint256,uint256,uint256)"; - _eventParam = abi.encode(address(from), amount, address(controlledToken), maximumExitFee, getId, setId); + prizePoolContract.withdrawInstantlyFrom(from, _amount, controlledToken, maximumExitFee); + + setUint(setId, _amount); + + _eventName = "LogWithdrawInstantlyFrom(address,address,uint256,address,uint256,uint256,uint256)"; + _eventParam = abi.encode(address(prizePool), address(from), _amount, address(controlledToken), maximumExitFee, getId, setId); } - - } contract ConnectV2PoolTogether is PoolTogetherResolver { diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 67dcea98..1f831888 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -16,6 +16,10 @@ const tokens = require("../../scripts/constant/tokens"); const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json") const connectV2PoolTogetherArtifacts = require("../../artifacts/contracts/mainnet/connectors/pooltogether/main.sol/ConnectV2PoolTogether.json") +const token = tokens.dai.address // DAI Token +const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool +const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket + describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" const ptConnectorName = "POOLTOGETHER-TEST-A" @@ -31,20 +35,20 @@ describe("PoolTogether", function () { before(async () => { masterSigner = await getMasterSigner(wallet3) instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ connectorName, contractArtifact: connectV2CompoundArtifacts, signer: masterSigner, connectors: instaConnectorsV2 }) - console.log("Connector address", connector.address) + ptConnector = await deployAndEnableConnector({ connectorName: ptConnectorName, contractArtifact: connectV2PoolTogetherArtifacts, signer: masterSigner, connectors: instaConnectorsV2 }) - console.log("PTConnector address", ptConnector.address) }) it("Should have contracts deployed.", async function () { @@ -87,11 +91,8 @@ describe("PoolTogether", function () { }); it("Should borrow DAI from Compound and deposit DAI into DAI Prize Pool", async function () { - const amount = 100 // 100 DAI + const amount = ethers.utils.parseEther("100") // 100 DAI const setId = "83478237" - const token = tokens.dai.address // DAI Token - const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool - const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket const spells = [ { connector: connectorName, @@ -101,50 +102,64 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [prizePool, token, dsaWallet0.address, amount, controlledToken, constants.address_zero, setId, 0] + args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, setId, 0] } ] + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("DAI balance before: ", daiBalance.toString()); let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) const balance = await cToken.balanceOf(dsaWallet0.address) const tokenName = await cToken.name() - console.log("Balance: ", balance.toString(), tokenName) + console.log("PTDAI balance before: ", balance.toString(), tokenName) const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() + + // Expect DAI balance to equal 0 + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("DAI balance after: ", daiBalance.toString()); + expect(daiBalance).to.be.eq(ethers.utils.parseEther("0")); + + // Expect PT DAI Ticket to equal 100 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("Balance: ", balanceAfter.toString(), tokenName) + console.log("PTDAI balance after: ", balanceAfter.toString(), tokenName) + expect(balanceAfter.toString()).to.be.eq(ethers.utils.parseEther("100")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); - // Should have 100 PT DAI Tickets - expect(balanceAfter.toNumber()).to.be.eq(100); }); - // it("Should deposit all ETH in Compound", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "deposit", - // args: ["ETH-A", constants.max_value, 0, 0] - // } - // ] + it("Should withdraw all PrizePool", async function () { + const amount = ethers.utils.parseEther("100") // 100 DAI + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + } + ] + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("DAI balance before: ", daiBalance.toString()); - // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) - // const receipt = await tx.wait() - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("0")); - // }); + let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + const balance = await cToken.balanceOf(dsaWallet0.address) + const tokenName = await cToken.name() + console.log("PTDAI balance before: ", balance.toString(), tokenName) - // it("Should withdraw all ETH from Compound", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "withdraw", - // args: ["ETH-A", constants.max_value, 0, 0] - // } - // ] + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() - // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) - // const receipt = await tx.wait() - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); - // }); + // Expect DAI balance to be greater than 90 + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("DAI balance after: ", daiBalance.toString()); + expect(daiBalance).to.be.gt(ethers.utils.parseEther("90")); + + // Expect PT Dai Ticket to equal 0 + const balanceAfter = await cToken.balanceOf(dsaWallet0.address) + console.log("PTDAI balance after: ", balanceAfter.toString(), tokenName) + expect(balanceAfter.toNumber()).to.be.eq(0); + }); }) }) From 1de8b7d5b3024c20a7c540e36452be6888dae082 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Mon, 5 Jul 2021 09:30:43 -0700 Subject: [PATCH 05/21] Add some comments --- test/pooltogether/pooltogether.test.js | 35 +++++++++++++++++++------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 1f831888..b1ce4f8b 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -36,6 +36,7 @@ describe("PoolTogether", function () { masterSigner = await getMasterSigner(wallet3) instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + // Deploy and enable Compound Connector connector = await deployAndEnableConnector({ connectorName, contractArtifact: connectV2CompoundArtifacts, @@ -43,6 +44,7 @@ describe("PoolTogether", function () { connectors: instaConnectorsV2 }) + // Deploy and enable Pool Together Connector ptConnector = await deployAndEnableConnector({ connectorName: ptConnectorName, contractArtifact: connectV2PoolTogetherArtifacts, @@ -105,26 +107,33 @@ describe("PoolTogether", function () { args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, setId, 0] } ] + // Before Spell + // DAI balance 0 let daiToken = await ethers.getContractAt(abis.basic.erc20, token) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("DAI balance before: ", daiBalance.toString()); + console.log("Before spell:"); + console.log("\tDAI balance before: ", daiBalance.toString()); + // PT DAI Ticket balance is 0 let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) const balance = await cToken.balanceOf(dsaWallet0.address) const tokenName = await cToken.name() - console.log("PTDAI balance before: ", balance.toString(), tokenName) + console.log("\tPTDAI balance before: ", balance.toString(), tokenName) + // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() + // After spell // Expect DAI balance to equal 0 daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("DAI balance after: ", daiBalance.toString()); + console.log("After spell:"); + console.log("\tDAI balance after: ", daiBalance.toString()); expect(daiBalance).to.be.eq(ethers.utils.parseEther("0")); // Expect PT DAI Ticket to equal 100 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("PTDAI balance after: ", balanceAfter.toString(), tokenName) + console.log("\tPTDAI balance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toString()).to.be.eq(ethers.utils.parseEther("100")); expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); @@ -139,26 +148,34 @@ describe("PoolTogether", function () { args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] } ] + + // Before spell + // DAI balance is 0 let daiToken = await ethers.getContractAt(abis.basic.erc20, token) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("DAI balance before: ", daiBalance.toString()); + console.log("Before Spell:") + console.log("\tDAI balance before: ", daiBalance.toString()); + // PT Dai Ticket is 100 let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) const balance = await cToken.balanceOf(dsaWallet0.address) const tokenName = await cToken.name() - console.log("PTDAI balance before: ", balance.toString(), tokenName) + console.log("\tPTDAI balance before: ", balance.toString(), tokenName) + // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() - // Expect DAI balance to be greater than 90 + // After spell + // Expect DAI balance to be greater than 90, because of early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("DAI balance after: ", daiBalance.toString()); + console.log("After spell: "); + console.log("\tDAI balance after: ", daiBalance.toString()); expect(daiBalance).to.be.gt(ethers.utils.parseEther("90")); // Expect PT Dai Ticket to equal 0 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("PTDAI balance after: ", balanceAfter.toString(), tokenName) + console.log("\tPTDAI balance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toNumber()).to.be.eq(0); }); }) From 8d9600b3b263876221858fc72e2fc5008c2a4dea Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 5 Aug 2021 10:56:02 -0700 Subject: [PATCH 06/21] Add TokenFaucet and claim function --- .../connectors/pooltogether/events.sol | 1 + .../connectors/pooltogether/interface.sol | 6 +- .../mainnet/connectors/pooltogether/main.sol | 21 ++++- test/pooltogether/pooltogether.test.js | 91 ++++++++++++++++--- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index aed564dd..840f5906 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -3,4 +3,5 @@ pragma solidity ^0.7.0; contract Events { event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); + event LogClaim(address tokenFaucet, address user); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index ad72cf8b..e7e4ac2f 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -4,4 +4,8 @@ interface PrizePoolInterface { function token() external view returns (address); function depositTo( address to, uint256 amount, address controlledToken, address referrer) external; function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); -} \ No newline at end of file +} + +interface TokenFaucetInterface { + function claim( address user) external returns (uint256); +} diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index a9b0a95e..3936f86c 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -7,7 +7,7 @@ pragma solidity ^0.7.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - import { PrizePoolInterface } from "./interface.sol"; + import { PrizePoolInterface, TokenFaucetInterface} from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import { Events } from "./events.sol"; @@ -87,6 +87,25 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _eventName = "LogWithdrawInstantlyFrom(address,address,uint256,address,uint256,uint256,uint256)"; _eventParam = abi.encode(address(prizePool), address(from), _amount, address(controlledToken), maximumExitFee, getId, setId); } + + /** + * @dev Claim token from a Token Faucet + * @notice Claim token from a Token Faucet + * @param tokenFaucet TokenFaucet address + * @param user The user to claim tokens for + */ + function claim ( + address tokenFaucet, + address user + ) external returns (string memory _eventName, bytes memory _eventParam) { + TokenFaucetInterface tokenFaucetContract = TokenFaucetInterface(tokenFaucet); + + tokenFaucetContract.claim(user); + + _eventName = "LogClaim(address,address)"; + _eventParam = abi.encode(address(tokenFaucet), address(user)); + } + } contract ConnectV2PoolTogether is PoolTogetherResolver { diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index b1ce4f8b..6d2c5256 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -19,6 +19,9 @@ const connectV2PoolTogetherArtifacts = require("../../artifacts/contracts/mainne const token = tokens.dai.address // DAI Token const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket +const daiPoolFaucet = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb" // DAI POOL Faucet +const poolTokenAddress = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e" +const tokenFaucetProxyFactory = "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117" // TokenFaucetProxyFactory for claimAll describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" @@ -75,7 +78,7 @@ describe("PoolTogether", function () { }); }); - describe("Main", function () { + describe("Main - DAI Prize Pool Test", function () { it("Should deposit ETH in Compound", async function () { const amount = ethers.utils.parseEther("1") // 1 ETH @@ -112,13 +115,13 @@ describe("PoolTogether", function () { let daiToken = await ethers.getContractAt(abis.basic.erc20, token) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); console.log("Before spell:"); - console.log("\tDAI balance before: ", daiBalance.toString()); + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); // PT DAI Ticket balance is 0 let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) const balance = await cToken.balanceOf(dsaWallet0.address) const tokenName = await cToken.name() - console.log("\tPTDAI balance before: ", balance.toString(), tokenName) + console.log("\tBalance before: ", balance.toString(), tokenName) // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) @@ -128,18 +131,18 @@ describe("PoolTogether", function () { // Expect DAI balance to equal 0 daiBalance = await daiToken.balanceOf(dsaWallet0.address); console.log("After spell:"); - console.log("\tDAI balance after: ", daiBalance.toString()); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); expect(daiBalance).to.be.eq(ethers.utils.parseEther("0")); // Expect PT DAI Ticket to equal 100 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tPTDAI balance after: ", balanceAfter.toString(), tokenName) + console.log("\tBalance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toString()).to.be.eq(ethers.utils.parseEther("100")); expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); }); - it("Should withdraw all PrizePool", async function () { + it("Should withdraw all PrizePool and get back 100 DAI", async function () { const amount = ethers.utils.parseEther("100") // 100 DAI const spells = [ { @@ -154,29 +157,93 @@ describe("PoolTogether", function () { let daiToken = await ethers.getContractAt(abis.basic.erc20, token) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); console.log("Before Spell:") - console.log("\tDAI balance before: ", daiBalance.toString()); + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); // PT Dai Ticket is 100 let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) const balance = await cToken.balanceOf(dsaWallet0.address) const tokenName = await cToken.name() - console.log("\tPTDAI balance before: ", balance.toString(), tokenName) + console.log("\tBalance before: ", balance.toString(), tokenName) + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11*24*60*60]); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // Expect DAI balance to be greater than 90, because of early withdrawal fee + // Expect DAI balance to be equal to 100, because of no early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); console.log("After spell: "); - console.log("\tDAI balance after: ", daiBalance.toString()); - expect(daiBalance).to.be.gt(ethers.utils.parseEther("90")); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.eq(ethers.utils.parseEther("100")); // Expect PT Dai Ticket to equal 0 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tPTDAI balance after: ", balanceAfter.toString(), tokenName) + console.log("\tBalance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toNumber()).to.be.eq(0); }); + + it("Should deposit and withdraw all PrizePool, get back less than 100 DAI, and claim POOL", async function() { + const amount = ethers.utils.parseEther("100") // 100 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, 0, 0] + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + }, + { + connector: ptConnectorName, + method: "claim", + args: [daiPoolFaucet, dsaWallet0.address] + } + ] + + // Before spell + // DAI balance is 0 + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("Before Spell:") + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + + // PT Dai Ticket is 100 + let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + const balance = await cToken.balanceOf(dsaWallet0.address) + const tokenName = await cToken.name() + console.log("\tBalance before: ", balance.toString(), tokenName) + + // PoolToken is 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + // Expect DAI balance to be less than 100, because of early withdrawal fee + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("After spell: "); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.lt(ethers.utils.parseEther("100")); + + // Expect PT Dai Ticket to equal 0 + const balanceAfter = await cToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", balanceAfter.toString(), tokenName) + expect(balanceAfter.toNumber()).to.be.eq(0); + + // Expect Pool Token Balance to be greate than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + }); }) }) From bf80497bb27eddf13a5c99677fb6841048f176b1 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Wed, 11 Aug 2021 15:36:57 -0700 Subject: [PATCH 07/21] Add claimAll function --- .../connectors/pooltogether/events.sol | 3 + .../connectors/pooltogether/interface.sol | 4 + .../mainnet/connectors/pooltogether/main.sol | 21 +++- test/pooltogether/pooltogether.test.js | 99 +++++++++++++++++-- 4 files changed, 118 insertions(+), 9 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 840f5906..459f7431 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -1,7 +1,10 @@ pragma solidity ^0.7.0; +import { TokenFaucetInterface } from "./interface.sol"; + contract Events { event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user); + event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index e7e4ac2f..c82d1b8b 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -9,3 +9,7 @@ interface PrizePoolInterface { interface TokenFaucetInterface { function claim( address user) external returns (uint256); } + +interface TokenFaucetProxyFactoryInterface { + function claimAll(address user, TokenFaucetInterface[] calldata tokenFaucets) external; +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 3936f86c..6684d2bf 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -7,7 +7,7 @@ pragma solidity ^0.7.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - import { PrizePoolInterface, TokenFaucetInterface} from "./interface.sol"; + import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import { Events } from "./events.sol"; @@ -106,6 +106,25 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _eventParam = abi.encode(address(tokenFaucet), address(user)); } + /** + * @dev Runs claim on all passed comptrollers for a user. + * @notice Runs claim on all passed comptrollers for a user. + * @param tokenFaucetProxyFactory The TokenFaucetProxyFactory address + * @param user The user to claim tokens for + * @param tokenFaucets The tokenFaucets to call claim on. + */ + function claimAll ( + address tokenFaucetProxyFactory, + address user, + TokenFaucetInterface[] calldata tokenFaucets + ) external returns (string memory _eventName, bytes memory _eventParam) { + TokenFaucetProxyFactoryInterface tokenFaucetProxyFactoryContract = TokenFaucetProxyFactoryInterface(tokenFaucetProxyFactory); + + tokenFaucetProxyFactoryContract.claimAll(user, tokenFaucets); + + _eventName = "LogClaimAll(address,address,TokenFaucetInterface[])"; + _eventParam = abi.encode(address(tokenFaucetProxyFactory), address(user), tokenFaucets); + } } contract ConnectV2PoolTogether is PoolTogetherResolver { diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 6d2c5256..22f120d8 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -142,13 +142,18 @@ describe("PoolTogether", function () { expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); }); - it("Should withdraw all PrizePool and get back 100 DAI", async function () { + it("Should wait 11 days, withdraw all PrizePool, get back 100 DAI, and claim POOL", async function () { const amount = ethers.utils.parseEther("100") // 100 DAI const spells = [ { connector: ptConnectorName, method: "withdrawInstantlyFrom", args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + }, + { + connector: ptConnectorName, + method: "claim", + args: [daiPoolFaucet, dsaWallet0.address] } ] @@ -165,6 +170,12 @@ describe("PoolTogether", function () { const tokenName = await cToken.name() console.log("\tBalance before: ", balance.toString(), tokenName) + // PoolToken is 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); @@ -183,9 +194,14 @@ describe("PoolTogether", function () { const balanceAfter = await cToken.balanceOf(dsaWallet0.address) console.log("\tBalance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toNumber()).to.be.eq(0); + + // Expect Pool Token Balance to be greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); }); - it("Should deposit and withdraw all PrizePool, get back less than 100 DAI, and claim POOL", async function() { + it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { const amount = ethers.utils.parseEther("100") // 100 DAI const spells = [ { @@ -197,11 +213,6 @@ describe("PoolTogether", function () { connector: ptConnectorName, method: "withdrawInstantlyFrom", args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] - }, - { - connector: ptConnectorName, - method: "claim", - args: [daiPoolFaucet, dsaWallet0.address] } ] @@ -240,7 +251,79 @@ describe("PoolTogether", function () { console.log("\tBalance after: ", balanceAfter.toString(), tokenName) expect(balanceAfter.toNumber()).to.be.eq(0); - // Expect Pool Token Balance to be greate than 0 + // Expect Pool Token Balance to greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + }); + + it("Should deposit, wait 11 days, and withdraw all PrizePool, get 99 DAI, and claim all POOL using claimAll", async function() { + const amount = ethers.utils.parseEther("99") // 99 DAI + const depositSpells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, 0, 0] + } + ] + + const withdrawSpells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + }, + { + connector: ptConnectorName, + method: "claimAll", + args: [tokenFaucetProxyFactory, dsaWallet0.address, [daiPoolFaucet]] + } + ] + + // Before spell + // DAI balance is 0 + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("Before Spell:") + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + + // PT Dai Ticket is 0 + let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + const balance = await cToken.balanceOf(dsaWallet0.address) + const tokenName = await cToken.name() + console.log("\tBalance before: ", balance.toString(), tokenName) + + // PoolToken is 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(depositSpells), wallet1.address) + const receipt = await tx.wait() + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + + // Run spell transaction + const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(withdrawSpells), wallet1.address) + const receipt2 = await tx2.wait() + + // After spell + // Expect DAI balance to be 99 + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("After spell: "); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + + // Expect PT Dai Ticket to equal 0 + const balanceAfter = await cToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", balanceAfter.toString(), tokenName) + expect(balanceAfter.toNumber()).to.be.eq(0); + + // Expect Pool Token Balance to be greateir than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); From 7ea00d7d03393019184abbb6eddc91a50801687c Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Wed, 18 Aug 2021 21:58:00 -0700 Subject: [PATCH 08/21] Add depositToPod and withdrawFromPod functions --- .../connectors/pooltogether/events.sol | 2 + .../connectors/pooltogether/interface.sol | 5 + .../mainnet/connectors/pooltogether/main.sol | 77 +++++++- test/pooltogether/pooltogether.test.js | 175 +++++++++++++++++- 4 files changed, 250 insertions(+), 9 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 459f7431..e1a844d8 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -7,4 +7,6 @@ contract Events { event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); + event LogDepositToPod(address prizePool, address pod, address to, uint256 amount, uint256 getId, uint256 setId); + event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 maxFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index c82d1b8b..6ac71665 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -12,4 +12,9 @@ interface TokenFaucetInterface { interface TokenFaucetProxyFactoryInterface { function claimAll(address user, TokenFaucetInterface[] calldata tokenFaucets) external; +} + +interface PodInterface { + function depositTo(address to, uint256 tokenAmount) external returns (uint256); + function withdraw(uint256 shareAmount, uint256 maxFee) external returns (uint256); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 6684d2bf..85c52185 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -7,7 +7,7 @@ pragma solidity ^0.7.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface } from "./interface.sol"; + import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface, PodInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import { Events } from "./events.sol"; @@ -37,7 +37,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { address referrer, uint256 getId, uint256 setId - ) external returns ( string memory _eventName, bytes memory _eventParam) { + ) external payable returns ( string memory _eventName, bytes memory _eventParam) { uint _amount = getUint(getId, amount); PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); @@ -75,7 +75,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint256 maximumExitFee, uint256 getId, uint256 setId - ) external returns (string memory _eventName, bytes memory _eventParam) { + ) external payable returns (string memory _eventName, bytes memory _eventParam) { uint _amount = getUint(getId, amount); PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); @@ -97,7 +97,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function claim ( address tokenFaucet, address user - ) external returns (string memory _eventName, bytes memory _eventParam) { + ) external payable returns (string memory _eventName, bytes memory _eventParam) { TokenFaucetInterface tokenFaucetContract = TokenFaucetInterface(tokenFaucet); tokenFaucetContract.claim(user); @@ -117,7 +117,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] calldata tokenFaucets - ) external returns (string memory _eventName, bytes memory _eventParam) { + ) external payable returns (string memory _eventName, bytes memory _eventParam) { TokenFaucetProxyFactoryInterface tokenFaucetProxyFactoryContract = TokenFaucetProxyFactoryInterface(tokenFaucetProxyFactory); tokenFaucetProxyFactoryContract.claimAll(user, tokenFaucets); @@ -125,6 +125,73 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _eventName = "LogClaimAll(address,address,TokenFaucetInterface[])"; _eventParam = abi.encode(address(tokenFaucetProxyFactory), address(user), tokenFaucets); } + + /** + * @dev Deposit into Pod + * @notice Deposit assets into the Pod in exchange for share tokens + * @param prizePool PrizePool address to deposit to + * @param pod Pod address to deposit to + * @param to The address that shall receive the Pod shares + * @param tokenAmount The amount of tokens to deposit. These are the same tokens used to deposit into the underlying prize pool. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function depositToPod( + address prizePool, + address pod, + address to, + uint256 tokenAmount, + uint256 getId, + uint256 setId + ) external payable returns ( string memory _eventName, bytes memory _eventParam) { + uint _tokenAmount= getUint(getId, tokenAmount); + + PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); + address prizePoolToken = prizePoolContract.token(); + + PodInterface podContract = PodInterface(pod); + + // Approve pod + TokenInterface tokenContract = TokenInterface(prizePoolToken); + tokenContract.approve(pod, _tokenAmount); + + podContract.depositTo(to, _tokenAmount); + + setUint(setId, _tokenAmount); + + _eventName = "LogDepositToPod(address,address,address,uint256,uint256, uint256)"; + _eventParam = abi.encode(address(prizePool), address(pod), address(to), _tokenAmount, getId, setId); + } + + /** + * @dev Withdraw from shares from Pod + * @dev The function should first withdraw from the 'float'; i.e. the funds that have not yet been deposited. + * @notice Withdraws a users share of the prize pool. + * @param pod Pod address + * @param shareAmount The number of Pod shares to burn. + * @param maxFee Max fee amount for withdrawl if amount isn't available in float. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + + function withdrawFromPod ( + address pod, + uint256 shareAmount, + uint256 maxFee, + uint256 getId, + uint256 setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _shareAmount = getUint(getId, shareAmount); + + PodInterface podContract = PodInterface(pod); + + podContract.withdraw(_shareAmount, maxFee); + + setUint(setId, _shareAmount); + + _eventName = "LogWithdrawFromPod(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(address(pod), _shareAmount, maxFee, getId, setId); + } } contract ConnectV2PoolTogether is PoolTogetherResolver { diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 22f120d8..feeafa92 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -22,6 +22,7 @@ const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI T const daiPoolFaucet = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb" // DAI POOL Faucet const poolTokenAddress = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e" const tokenFaucetProxyFactory = "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117" // TokenFaucetProxyFactory for claimAll +const daiPod = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829" // DAI Pod describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" @@ -156,7 +157,7 @@ describe("PoolTogether", function () { args: [daiPoolFaucet, dsaWallet0.address] } ] - + // Before spell // DAI balance is 0 let daiToken = await ethers.getContractAt(abis.basic.erc20, token) @@ -215,7 +216,7 @@ describe("PoolTogether", function () { args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] } ] - + // Before spell // DAI balance is 0 let daiToken = await ethers.getContractAt(abis.basic.erc20, token) @@ -229,7 +230,7 @@ describe("PoolTogether", function () { const tokenName = await cToken.name() console.log("\tBalance before: ", balance.toString(), tokenName) - // PoolToken is 0 + // PoolToken is 0 let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) const poolTokenName = await poolToken.name() @@ -329,4 +330,170 @@ describe("PoolTogether", function () { expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); }); }) -}) + + describe("Main - DAI Pod Test", function() { + it("Should deposit in Pod", async function() { + const amount = ethers.utils.parseEther("99") // 99 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [prizePool, daiPod, dsaWallet0.address, amount, 0, 0] + } + ] + + // Before spell + // DAI balance is 99 + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("Before Spell:") + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + + // PoolToken is 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // PodToken is 0 + let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + const podBalance = await podToken.balanceOf(dsaWallet0.address) + const podTokenName = await podToken.name() + console.log("\tBalance before: ", podBalance.toString(), podTokenName) + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + // Expect DAI balance to be less than 100, because of early withdrawal fee + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("After spell: "); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.lt(ethers.utils.parseEther("100")); + + // Expect Pool Token Balance to greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + // Expect Pod Token Balance to greater than 0 + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) + expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("99")); + }); + + it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function () { + const amount = ethers.utils.parseEther("99") // 99 DAI + const maxFee = 0; + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [daiPod, amount, maxFee, 0, 0] + } + ] + + // Before spell + // DAI balance is 0 + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("Before Spell:") + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + + // PoolToken is 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // PodToken is 99 + let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + const podBalance = await podToken.balanceOf(dsaWallet0.address) + const podTokenName = await podToken.name() + console.log("\tBalance before: ", podBalance.toString(), podTokenName) + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + // Expect DAI balance to be equal to 99, because of no early withdrawal fee + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("After spell: "); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + + // Expect Pool Token Balance to be greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + // Expect Pod Token Balance to equal 0 + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) + expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + }); + + it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { + const amount = ethers.utils.parseEther("99") + const maxFee = 0; + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [prizePool, daiPod, dsaWallet0.address, amount, 0, 0] + }, + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [daiPod, amount, maxFee, 0, 0] + } + ] + + // Before spell + // DAI balance is 0 + let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("Before Spell:") + console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + + // PoolToken is greater than 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // PodToken is 0 + let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + const podBalance = await podToken.balanceOf(dsaWallet0.address) + const podTokenName = await podToken.name() + console.log("\tBalance before: ", podBalance.toString(), podTokenName) + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + // Expect DAI balance to be equal to 99, because funds still in 'float' + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + console.log("After spell: "); + console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + + // Expect Pool Token Balance to greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + // Expect Pod Token Balance to equal 0 + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) + expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + }); + }) +}) \ No newline at end of file From 8748fc3734dcfe1ca3bb3ffacd4623683560e98f Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Sat, 28 Aug 2021 10:34:55 -0700 Subject: [PATCH 09/21] Add test for Uniswap POOL LP PrizePool and Faucet, update hardhat fork blockNumber --- hardhat.config.js | 2 +- test/pooltogether/pooltogether.test.js | 155 +++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/hardhat.config.js b/hardhat.config.js index 86ed948b..2ff779aa 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -57,7 +57,7 @@ module.exports = { hardhat: { forking: { url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, - blockNumber: 12696000, + blockNumber: 12970000, }, blockGasLimit: 12000000, }, diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index feeafa92..74e811cf 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -15,17 +15,25 @@ const tokens = require("../../scripts/constant/tokens"); const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json") const connectV2PoolTogetherArtifacts = require("../../artifacts/contracts/mainnet/connectors/pooltogether/main.sol/ConnectV2PoolTogether.json") +const connectV2UniswapArtifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/main.sol/ConnectV2UniswapV2.json") const token = tokens.dai.address // DAI Token + +// PoolTogether Address: https://docs.pooltogether.com/resources/networks/ethereum const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket const daiPoolFaucet = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb" // DAI POOL Faucet const poolTokenAddress = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e" const tokenFaucetProxyFactory = "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117" // TokenFaucetProxyFactory for claimAll const daiPod = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829" // DAI Pod +const uniswapPoolETHLPPrizePool = "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A" // Uniswap Pool/ETH LP PrizePool +const uniswapPoolETHLPFaucet = "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370" // Uniswap Pool/ETH LP Faucet +const uniswapPOOLETHLPToken = "0x85cb0bab616fe88a89a35080516a8928f38b518b" +const ptUniswapPOOLETHLPTicket = "0xeb8928ee92efb06c44d072a24c2bcb993b61e543" describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" + const uniswapConnectorName = "UNISWAP-TEST-A" const ptConnectorName = "POOLTOGETHER-TEST-A" let dsaWallet0 @@ -33,6 +41,7 @@ describe("PoolTogether", function () { let instaConnectorsV2; let connector; let ptConnector; + let uniswapConnector; const wallets = provider.getWallets() const [wallet0, wallet1, wallet2, wallet3] = wallets @@ -55,12 +64,21 @@ describe("PoolTogether", function () { signer: masterSigner, connectors: instaConnectorsV2 }) + + // Deploy and enable Uniswap Connector + uniswapConnector = await deployAndEnableConnector({ + connectorName: uniswapConnectorName, + contractArtifact: connectV2UniswapArtifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) }) it("Should have contracts deployed.", async function () { expect(!!instaConnectorsV2.address).to.be.true; expect(!!connector.address).to.be.true; expect(!!ptConnector.address).to.be.true; + expect(!!uniswapConnector.address).to.be.true; expect(!!masterSigner.address).to.be.true; }); @@ -496,4 +514,141 @@ describe("PoolTogether", function () { expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); }); }) + + describe("Main - UNISWAP POOL/ETH Prize Pool Test", function () { + it("Should use uniswap to swap ETH for POOL, deposit to POOL/ETH LP, deposit POOL/ETH LP to PrizePool", async function () { + const amount = ethers.utils.parseEther("100") // 100 POOL + const unitAmount = ethers.utils.parseEther((11.5/2000).toString()) // unitAmt, just used avg price at the blockTime. + const slippage = ethers.utils.parseEther("0.1"); + const setId = "83478237" + const spells = [ + { + connector: uniswapConnectorName, + method: "buy", + args: [poolTokenAddress, tokens.eth.address, amount, unitAmount, 0, setId] + }, + { + connector: uniswapConnectorName, + method: "deposit", + args: [poolTokenAddress, tokens.eth.address, amount, unitAmount, slippage, 0, setId] + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [uniswapPoolETHLPPrizePool, dsaWallet0.address, 0, ptUniswapPOOLETHLPTicket, constants.address_zero, setId, 0] + } + ] + + // Before Spell + // ETH balance + let ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + console.log("Before spell:"); + console.log("\tBalance before: ", ethBalance.toString(), "ETH"); + + // PoolToken > 0 + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // Uniswap POOL/ETH LP is 0 + let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, uniswapPOOLETHLPToken) + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf(dsaWallet0.address) + const uniswapPoolEthLPTokenName = await uniswapLPToken.name() + console.log("\tBalance before: ", uniswapPoolEthBalance.toString(), uniswapPoolEthLPTokenName) + + // Expect PT Uniswap POOL/ETH LP is 0 + let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, ptUniswapPOOLETHLPTicket) + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) + const ptUniswapPoolEthLPTokenName = await ptUniswapPoolEthToken.name() + console.log("\tBalance before: ", ptUniswapPoolEthBalance.toString(), ptUniswapPoolEthLPTokenName) + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + // ETH balance < 0 + ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + console.log("After spell:"); + console.log("\tBalance after: ", ethBalance.toString(), "ETH"); + + // Expect Pool Token Balance to greater than 0 + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.eq(poolBalance); + + // Expect Uniswap POOL/ETH LP to greater than 0 + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", uniswapPoolEthBalanceAfter.toString(), uniswapPoolEthLPTokenName) + expect(uniswapPoolEthBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + + // Expect PT Uniswap POOL/ETH LP to greater than 0 + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", ptUniswapPoolEthBalanceAfter.toString(), ptUniswapPoolEthLPTokenName) + expect(ptUniswapPoolEthBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should wait 11 days, withdraw all PrizePool, get back Uniswap LP, and claim POOL", async function () { + let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, ptUniswapPOOLETHLPTicket) + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [uniswapPoolETHLPPrizePool, dsaWallet0.address, ptUniswapPoolEthBalance, ptUniswapPOOLETHLPTicket, 0, 0, 0] + }, + { + connector: ptConnectorName, + method: "claim", + args: [uniswapPoolETHLPFaucet , dsaWallet0.address] + } + ] + + // Before spell + console.log("Before spell:"); + // PoolToken + let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + const poolTokenName = await poolToken.name() + console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + + // Uniswap POOL/ETH LP is 0 + let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, uniswapPOOLETHLPToken) + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf(dsaWallet0.address) + const uniswapPoolEthLPTokenName = await uniswapLPToken.name() + console.log("\tBalance before: ", uniswapPoolEthBalance.toString(), uniswapPoolEthLPTokenName) + + // Expect PT Uniswap POOL/ETH LP > 0 + const ptUniswapPoolEthLPTokenName = await ptUniswapPoolEthToken.name() + console.log("\tBalance before: ", ptUniswapPoolEthBalance.toString(), ptUniswapPoolEthLPTokenName) + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + console.log("After spell:"); + // Expect Pool Token Balance to be greater than balance before spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) + expect(poolBalanceAfter).to.be.gt(poolBalance); + + // Expect Uniswap POOL/ETH LP to greater than 0 + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", uniswapPoolEthBalanceAfter.toString(), uniswapPoolEthLPTokenName) + expect(uniswapPoolEthBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + + // Expect PT Uniswap POOL/ETH LP equal 0 + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", ptUniswapPoolEthBalanceAfter.toString(), ptUniswapPoolEthLPTokenName) + expect(ptUniswapPoolEthBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + }); + }) }) \ No newline at end of file From c797730f96bcbe895f130a6f455b925547ea621a Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Sat, 28 Aug 2021 16:01:15 -0700 Subject: [PATCH 10/21] Add claimed amount for setId in claim function and event --- .../connectors/pooltogether/events.sol | 2 +- .../mainnet/connectors/pooltogether/main.sol | 12 ++++++--- test/pooltogether/pooltogether.test.js | 27 ++++++++++++++++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index e1a844d8..b1b8f485 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -5,7 +5,7 @@ import { TokenFaucetInterface } from "./interface.sol"; contract Events { event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); - event LogClaim(address tokenFaucet, address user); + event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); event LogDepositToPod(address prizePool, address pod, address to, uint256 amount, uint256 getId, uint256 setId); event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 maxFee, uint256 getId, uint256 setId); diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 85c52185..6e892b90 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -93,17 +93,21 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @notice Claim token from a Token Faucet * @param tokenFaucet TokenFaucet address * @param user The user to claim tokens for + * @param setId Set claimed amount at this ID in `InstaMemory` Contract. */ function claim ( address tokenFaucet, - address user + address user, + uint256 setId ) external payable returns (string memory _eventName, bytes memory _eventParam) { TokenFaucetInterface tokenFaucetContract = TokenFaucetInterface(tokenFaucet); - tokenFaucetContract.claim(user); + uint256 claimed = tokenFaucetContract.claim(user); - _eventName = "LogClaim(address,address)"; - _eventParam = abi.encode(address(tokenFaucet), address(user)); + setUint(setId, claimed); + + _eventName = "LogClaim(address,address, uint256, uint256)"; + _eventParam = abi.encode(address(tokenFaucet), address(user), claimed, setId); } /** diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 74e811cf..4c900b63 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -30,6 +30,8 @@ const uniswapPoolETHLPPrizePool = "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A" const uniswapPoolETHLPFaucet = "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370" // Uniswap Pool/ETH LP Faucet const uniswapPOOLETHLPToken = "0x85cb0bab616fe88a89a35080516a8928f38b518b" const ptUniswapPOOLETHLPTicket = "0xeb8928ee92efb06c44d072a24c2bcb993b61e543" +const poolPoolPrizePool = "0x396b4489da692788e327e2e4b2b0459a5ef26791" +const ptPoolTicket = "0x27d22a7648e955e510a40bdb058333e9190d12d4" describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" @@ -172,7 +174,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "claim", - args: [daiPoolFaucet, dsaWallet0.address] + args: [daiPoolFaucet, dsaWallet0.address, 0] } ] @@ -591,9 +593,10 @@ describe("PoolTogether", function () { expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); }); - it("Should wait 11 days, withdraw all PrizePool, get back Uniswap LP, and claim POOL", async function () { + it("Should wait 11 days, withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function () { let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, ptUniswapPOOLETHLPTicket) const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) + const setId = "83478237" const spells = [ { @@ -604,7 +607,12 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "claim", - args: [uniswapPoolETHLPFaucet , dsaWallet0.address] + args: [uniswapPoolETHLPFaucet , dsaWallet0.address, setId] + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [poolPoolPrizePool, dsaWallet0.address, 0, ptPoolTicket, constants.address_zero, setId, 0] } ] @@ -626,6 +634,12 @@ describe("PoolTogether", function () { const ptUniswapPoolEthLPTokenName = await ptUniswapPoolEthToken.name() console.log("\tBalance before: ", ptUniswapPoolEthBalance.toString(), ptUniswapPoolEthLPTokenName) + // PoolTogether Pool Ticket + let poolPoolTicket = await ethers.getContractAt(abis.basic.erc20, ptPoolTicket) + const poolPoolTicketBalance = await poolPoolTicket.balanceOf(dsaWallet0.address) + const poolPoolTicketName = await poolPoolTicket.name() + console.log("\tBalance before: ", poolPoolTicketBalance.toString(), poolPoolTicketName) + // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); @@ -638,7 +652,7 @@ describe("PoolTogether", function () { // Expect Pool Token Balance to be greater than balance before spell const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(poolBalance); + expect(poolBalanceAfter).to.be.eq(poolBalance); // Expect Uniswap POOL/ETH LP to greater than 0 const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf(dsaWallet0.address) @@ -649,6 +663,11 @@ describe("PoolTogether", function () { const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) console.log("\tBalance after: ", ptUniswapPoolEthBalanceAfter.toString(), ptUniswapPoolEthLPTokenName) expect(ptUniswapPoolEthBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + + // Expoect PoolTogether Pool Ticket > 0 + const poolPoolTicketBalanceAfter = await poolPoolTicket.balanceOf(dsaWallet0.address) + console.log("\tBalance after: ", poolPoolTicketBalanceAfter.toString(), poolPoolTicketName) + expect(poolPoolTicketBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); }); }) }) \ No newline at end of file From 3207bdd3b01560d0dfa1c65558fe5e75a308cb47 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Tue, 31 Aug 2021 09:57:45 -0700 Subject: [PATCH 11/21] Fix uniswap LP test --- test/pooltogether/pooltogether.test.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 4c900b63..4487daad 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -32,6 +32,7 @@ const uniswapPOOLETHLPToken = "0x85cb0bab616fe88a89a35080516a8928f38b518b" const ptUniswapPOOLETHLPTicket = "0xeb8928ee92efb06c44d072a24c2bcb993b61e543" const poolPoolPrizePool = "0x396b4489da692788e327e2e4b2b0459a5ef26791" const ptPoolTicket = "0x27d22a7648e955e510a40bdb058333e9190d12d4" +const WETHAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" @@ -520,9 +521,17 @@ describe("PoolTogether", function () { describe("Main - UNISWAP POOL/ETH Prize Pool Test", function () { it("Should use uniswap to swap ETH for POOL, deposit to POOL/ETH LP, deposit POOL/ETH LP to PrizePool", async function () { const amount = ethers.utils.parseEther("100") // 100 POOL - const unitAmount = ethers.utils.parseEther((11.5/2000).toString()) // unitAmt, just used avg price at the blockTime. - const slippage = ethers.utils.parseEther("0.1"); + const slippage = ethers.utils.parseEther("0.03"); const setId = "83478237" + + const UniswapV2Router02ABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" + ]; + + const UniswapV2Router02 = await ethers.getContractAt(UniswapV2Router02ABI, "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"); + const amounts = await UniswapV2Router02.getAmountsOut(amount, [poolTokenAddress, WETHAddress]); + const unitAmount = ethers.utils.parseEther(((amounts[1]*1.03)/amounts[0]).toString()); + const spells = [ { connector: uniswapConnectorName, From a8ad8158147394d6d28fb0e1d5e24877ab1caa93 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Tue, 31 Aug 2021 17:19:15 -0700 Subject: [PATCH 12/21] Clean up code a bit --- .../mainnet/connectors/pooltogether/main.sol | 16 +- test/pooltogether/pooltogether.test.js | 371 +++++++----------- 2 files changed, 139 insertions(+), 248 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 6e892b90..4586a164 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -19,9 +19,9 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { /** * @dev Deposit into Prize Pool - * @notice Deposit a token into a prize pool + * @notice Deposit assets into the Prize Pool in exchange for tokens * @param prizePool PrizePool address to deposit to - * @param to Address to whom the controlled tokens should be minted + * @param to The address receiving the newly minted tokens * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. * @param referrer The address that should receive referral awards, if any. @@ -57,12 +57,12 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { /** * @dev Withdraw from Prize Pool - * @notice Withdraw a token from a prize pool + * @notice Withdraw assets from the Prize Pool instantly. A fairness fee may be charged for an early exit. * @param prizePool PrizePool address to deposit to - * @param from The address to withdraw from. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. - * @param amount THe amount to withdraw - * @param controlledToken The controlled token to withdraw from. - * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. + * @param from The address to redeem tokens from.. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. + * @param amount The amount of tokens to redeem for assets. + * @param controlledToken The address of the token to redeem (i.e. ticket or sponsorship) + * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. This should be pre-calculated by the calculateExitFee() fxn. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. */ @@ -169,8 +169,8 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { /** * @dev Withdraw from shares from Pod - * @dev The function should first withdraw from the 'float'; i.e. the funds that have not yet been deposited. * @notice Withdraws a users share of the prize pool. + * @dev The function should first withdraw from the 'float'; i.e. the funds that have not yet been deposited. * @param pod Pod address * @param shareAmount The number of Pod shares to burn. * @param maxFee Max fee amount for withdrawl if amount isn't available in float. diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 4487daad..faec1463 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -17,22 +17,22 @@ const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/co const connectV2PoolTogetherArtifacts = require("../../artifacts/contracts/mainnet/connectors/pooltogether/main.sol/ConnectV2PoolTogether.json") const connectV2UniswapArtifacts = require("../../artifacts/contracts/mainnet/connectors/uniswap/main.sol/ConnectV2UniswapV2.json") -const token = tokens.dai.address // DAI Token +const DAI_TOKEN_ADDR = tokens.dai.address // DAI Token // PoolTogether Address: https://docs.pooltogether.com/resources/networks/ethereum -const prizePool = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool -const controlledToken = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket -const daiPoolFaucet = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb" // DAI POOL Faucet -const poolTokenAddress = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e" -const tokenFaucetProxyFactory = "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117" // TokenFaucetProxyFactory for claimAll -const daiPod = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829" // DAI Pod -const uniswapPoolETHLPPrizePool = "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A" // Uniswap Pool/ETH LP PrizePool -const uniswapPoolETHLPFaucet = "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370" // Uniswap Pool/ETH LP Faucet -const uniswapPOOLETHLPToken = "0x85cb0bab616fe88a89a35080516a8928f38b518b" -const ptUniswapPOOLETHLPTicket = "0xeb8928ee92efb06c44d072a24c2bcb993b61e543" -const poolPoolPrizePool = "0x396b4489da692788e327e2e4b2b0459a5ef26791" -const ptPoolTicket = "0x27d22a7648e955e510a40bdb058333e9190d12d4" -const WETHAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" +const DAI_PRIZE_POOL_ADDR = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a" // DAI Prize Pool +const PT_DAI_TICKET_ADDR = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF" // PT DAI Ticket +const DAI_POOL_FAUCET_ADDR = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb" // DAI POOL Faucet +const POOL_TOKEN_ADDRESS = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e" // POOL Tocken +const TOKEN_FAUCET_PROXY_FACTORY_ADDR = "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117" // TokenFaucetProxyFactory for claimAll +const DAI_POD_ADDR = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829" // DAI Pod +const UNISWAP_POOLETHLP_PRIZE_POOL_ADDR = "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A" // Uniswap Pool/ETH LP PrizePool +const UNISWAP_POOLETHLP_FAUCET_ADDR = "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370" // Uniswap Pool/ETH LP Faucet +const UNISWAP_POOLETHLP_TOKEN_ADDR = "0x85cb0bab616fe88a89a35080516a8928f38b518b" // Uniswap Pool/ETH Token +const PT_UNISWAP_POOLETHLP_TICKET_ADDR = "0xeb8928ee92efb06c44d072a24c2bcb993b61e543" // Pool Together Uniswap Pool/ETH LP Ticket +const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791" // POOL Prize Pool +const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4" // Pool Together POOL Ticket +const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" // WETH describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" @@ -91,7 +91,7 @@ describe("PoolTogether", function () { expect(!!dsaWallet0.address).to.be.true; }); - it("Deposit ETH into DSA wallet", async function () { + it("Deposit 10 ETH into DSA wallet", async function () { await wallet0.sendTransaction({ to: dsaWallet0.address, value: ethers.utils.parseEther("10") @@ -102,7 +102,7 @@ describe("PoolTogether", function () { describe("Main - DAI Prize Pool Test", function () { - it("Should deposit ETH in Compound", async function () { + it("Should deposit 1 ETH in Compound", async function () { const amount = ethers.utils.parseEther("1") // 1 ETH const spells = [ { @@ -117,7 +117,7 @@ describe("PoolTogether", function () { expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); }); - it("Should borrow DAI from Compound and deposit DAI into DAI Prize Pool", async function () { + it("Should borrow 100 DAI from Compound and deposit DAI into DAI Prize Pool", async function () { const amount = ethers.utils.parseEther("100") // 100 DAI const setId = "83478237" const spells = [ @@ -129,38 +129,30 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, setId, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, setId, 0] } ] // Before Spell - // DAI balance 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before spell:"); - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI balance is 0`).to.be.eq(ethers.utils.parseEther("0")); - // PT DAI Ticket balance is 0 - let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + let cToken = await ethers.getContractAt(abis.basic.erc20, PT_DAI_TICKET_ADDR) const balance = await cToken.balanceOf(dsaWallet0.address) - const tokenName = await cToken.name() - console.log("\tBalance before: ", balance.toString(), tokenName) + expect(balance,`PoolTogether DAI Ticket balance is 0`).to.be.eq(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // Expect DAI balance to equal 0 daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell:"); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.eq(ethers.utils.parseEther("0")); + expect(daiBalance, `Expect DAI balance to still equal 0 since it was deposited into Prize Pool`).to.be.eq(0); - // Expect PT DAI Ticket to equal 100 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", balanceAfter.toString(), tokenName) - expect(balanceAfter.toString()).to.be.eq(ethers.utils.parseEther("100")); + expect(balanceAfter, `PoolTogether DAI Ticket balance equals 100`).to.be.eq(ethers.utils.parseEther("100")); + // ETH used for transaction expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); }); @@ -170,33 +162,27 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] }, { connector: ptConnectorName, method: "claim", - args: [daiPoolFaucet, dsaWallet0.address, 0] + args: [DAI_POOL_FAUCET_ADDR, dsaWallet0.address, 0] } ] // Before spell - // DAI balance is 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI balance equals 0`).to.be.eq(ethers.utils.parseEther("0")); - // PT Dai Ticket is 100 - let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + let cToken = await ethers.getContractAt(abis.basic.erc20, PT_DAI_TICKET_ADDR) const balance = await cToken.balanceOf(dsaWallet0.address) - const tokenName = await cToken.name() - console.log("\tBalance before: ", balance.toString(), tokenName) + expect(balance, `PoolTogether Dai Ticket is 100`).to.be.eq(ethers.utils.parseEther("100")); - // PoolToken is 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token equals 0`).to.be.eq(ethers.utils.parseEther("0")); // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); @@ -206,21 +192,16 @@ describe("PoolTogether", function () { const receipt = await tx.wait() // After spell - // Expect DAI balance to be equal to 100, because of no early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.eq(ethers.utils.parseEther("100")); + expect(daiBalance, + `DAI balance to be equal to 100, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("100")); - // Expect PT Dai Ticket to equal 0 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", balanceAfter.toString(), tokenName) - expect(balanceAfter.toNumber()).to.be.eq(0); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); - // Expect Pool Token Balance to be greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `POOL Token Balance to be greater than 0`).to.be.gt(ethers.utils.parseEther("0")); }); it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { @@ -229,54 +210,43 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] }, { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] } ] // Before spell - // DAI balance is 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq(ethers.utils.parseEther("100")); - // PT Dai Ticket is 100 - let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + let cToken = await ethers.getContractAt(abis.basic.erc20, PT_DAI_TICKET_ADDR) const balance = await cToken.balanceOf(dsaWallet0.address) - const tokenName = await cToken.name() - console.log("\tBalance before: ", balance.toString(), tokenName) + expect(balance, `PoolTogether DAI Ticket equals 0`).to.be.eq(0); - // PoolToken is 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `PoolTogether Token greater than 0`).to.be.gt(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // Expect DAI balance to be less than 100, because of early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.lt(ethers.utils.parseEther("100")); + expect(daiBalance, + `DAI balance to be less than 100, because of early withdrawal fee` + ).to.be.lt(ethers.utils.parseEther("100")); - // Expect PT Dai Ticket to equal 0 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", balanceAfter.toString(), tokenName) - expect(balanceAfter.toNumber()).to.be.eq(0); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); - // Expect Pool Token Balance to greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `POOL Token Balance to greater than 0`).to.be.gt(ethers.utils.parseEther("0")); }); @@ -286,7 +256,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [prizePool, dsaWallet0.address, amount, controlledToken, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] } ] @@ -294,33 +264,27 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [prizePool, dsaWallet0.address, amount, controlledToken, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] }, { connector: ptConnectorName, method: "claimAll", - args: [tokenFaucetProxyFactory, dsaWallet0.address, [daiPoolFaucet]] + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, dsaWallet0.address, [DAI_POOL_FAUCET_ADDR]] } ] // Before spell - // DAI balance is 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI balance less than 100`).to.be.lt(ethers.utils.parseEther("100")); - // PT Dai Ticket is 0 - let cToken = await ethers.getContractAt(abis.basic.erc20, controlledToken) + let cToken = await ethers.getContractAt(abis.basic.erc20, PT_DAI_TICKET_ADDR) const balance = await cToken.balanceOf(dsaWallet0.address) - const tokenName = await cToken.name() - console.log("\tBalance before: ", balance.toString(), tokenName) + expect(balance, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); - // PoolToken is 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token is greater than 0`).to.be.gt(ethers.utils.parseEther("0")); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(depositSpells), wallet1.address) @@ -334,74 +298,55 @@ describe("PoolTogether", function () { const receipt2 = await tx2.wait() // After spell - // Expect DAI balance to be 99 daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + expect(daiBalance, `DAI balance equals 99`).to.be.eq(ethers.utils.parseEther("99")); - // Expect PT Dai Ticket to equal 0 const balanceAfter = await cToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", balanceAfter.toString(), tokenName) - expect(balanceAfter.toNumber()).to.be.eq(0); + expect(balanceAfter, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); - // Expect Pool Token Balance to be greateir than 0 + // Expect const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `Pool Token to be greateir than 0`).to.be.gt(ethers.utils.parseEther("0")); }); }) describe("Main - DAI Pod Test", function() { - it("Should deposit in Pod", async function() { + it("Should deposit 99 DAI in DAI Pod", async function() { const amount = ethers.utils.parseEther("99") // 99 DAI const spells = [ { connector: ptConnectorName, method: "depositToPod", - args: [prizePool, daiPod, dsaWallet0.address, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, dsaWallet0.address, amount, 0, 0] } ] // Before spell - // DAI balance is 99 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI balance equals 99`).to.be.eq(ethers.utils.parseEther("99")); - // PoolToken is 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); - // PodToken is 0 - let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR) const podBalance = await podToken.balanceOf(dsaWallet0.address) - const podTokenName = await podToken.name() - console.log("\tBalance before: ", podBalance.toString(), podTokenName) + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // Expect DAI balance to be less than 100, because of early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.lt(ethers.utils.parseEther("100")); + expect(daiBalance, `DAI equals 0`).to.be.eq(0); - // Expect Pool Token Balance to greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `POOL Token greater than 0`).to.be.gt(0); - // Expect Pod Token Balance to greater than 0 const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) - expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("99")); + expect(podBalanceAfter, `Pod DAI token greater than 0`).to.be.eq(ethers.utils.parseEther("99")); }); it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function () { @@ -411,28 +356,22 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawFromPod", - args: [daiPod, amount, maxFee, 0, 0] + args: [DAI_POD_ADDR, amount, maxFee, 0, 0] } ] // Before spell - // DAI balance is 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq(0); - // PoolToken is 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token balance greater than 0`).to.be.gt(0); - // PodToken is 99 - let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR) const podBalance = await podToken.balanceOf(dsaWallet0.address) - const podTokenName = await podToken.name() - console.log("\tBalance before: ", podBalance.toString(), podTokenName) + expect(podBalance, `Pod DAI Token equals 99`).to.be.eq(ethers.utils.parseEther("99")); // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); @@ -442,21 +381,16 @@ describe("PoolTogether", function () { const receipt = await tx.wait() // After spell - // Expect DAI balance to be equal to 99, because of no early withdrawal fee daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + expect(daiBalance, + `DAI balance equals 99, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("99")); - // Expect Pool Token Balance to be greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `POOL Token to be greater than 0`).to.be.gt(0); - // Expect Pod Token Balance to equal 0 const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) - expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(0); }); it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { @@ -467,54 +401,45 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositToPod", - args: [prizePool, daiPod, dsaWallet0.address, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, dsaWallet0.address, amount, 0, 0] }, { connector: ptConnectorName, method: "withdrawFromPod", - args: [daiPod, amount, maxFee, 0, 0] + args: [DAI_POD_ADDR, amount, maxFee, 0, 0] } ] // Before spell - // DAI balance is 0 - let daiToken = await ethers.getContractAt(abis.basic.erc20, token) + let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("Before Spell:") - console.log("\tBalance before: ", daiBalance.toString(), tokens.dai.symbol); + expect(daiBalance, `DAI equals 99`).to.be.eq(ethers.utils.parseEther("99")); - // PoolToken is greater than 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); // PodToken is 0 - let podToken = await ethers.getContractAt(abis.basic.erc20, daiPod) + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR) const podBalance = await podToken.balanceOf(dsaWallet0.address) - const podTokenName = await podToken.name() - console.log("\tBalance before: ", podBalance.toString(), podTokenName) + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // Expect DAI balance to be equal to 99, because funds still in 'float' daiBalance = await daiToken.balanceOf(dsaWallet0.address); - console.log("After spell: "); - console.log("\tBalance after: ", daiBalance.toString(), tokens.dai.symbol); - expect(daiBalance).to.be.eq(ethers.utils.parseEther("99")); + expect(daiBalance, + `DAI balance to be equal to 99, because funds still in 'float` + ).to.be.eq(ethers.utils.parseEther("99")); - // Expect Pool Token Balance to greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq(poolBalance); // Expect Pod Token Balance to equal 0 const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", podBalanceAfter.toString(), podTokenName) - expect(podBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(ethers.utils.parseEther("0")); }); }) @@ -528,82 +453,65 @@ describe("PoolTogether", function () { "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" ]; + // Get amount of ETH for 100 POOL from Uniswap const UniswapV2Router02 = await ethers.getContractAt(UniswapV2Router02ABI, "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"); - const amounts = await UniswapV2Router02.getAmountsOut(amount, [poolTokenAddress, WETHAddress]); + const amounts = await UniswapV2Router02.getAmountsOut(amount, [POOL_TOKEN_ADDRESS, WETH_ADDR]); const unitAmount = ethers.utils.parseEther(((amounts[1]*1.03)/amounts[0]).toString()); const spells = [ { connector: uniswapConnectorName, method: "buy", - args: [poolTokenAddress, tokens.eth.address, amount, unitAmount, 0, setId] + args: [POOL_TOKEN_ADDRESS, tokens.eth.address, amount, unitAmount, 0, setId] }, { connector: uniswapConnectorName, method: "deposit", - args: [poolTokenAddress, tokens.eth.address, amount, unitAmount, slippage, 0, setId] + args: [POOL_TOKEN_ADDRESS, tokens.eth.address, amount, unitAmount, slippage, 0, setId] }, { connector: ptConnectorName, method: "depositTo", - args: [uniswapPoolETHLPPrizePool, dsaWallet0.address, 0, ptUniswapPOOLETHLPTicket, constants.address_zero, setId, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, dsaWallet0.address, 0, PT_UNISWAP_POOLETHLP_TICKET_ADDR, constants.address_zero, setId, 0] } ] // Before Spell - // ETH balance let ethBalance = await ethers.provider.getBalance(dsaWallet0.address); - console.log("Before spell:"); - console.log("\tBalance before: ", ethBalance.toString(), "ETH"); + expect(ethBalance, `ETH Balance equals 9`).to.be.eq(ethers.utils.parseEther("9")); - // PoolToken > 0 - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); - // Uniswap POOL/ETH LP is 0 - let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, uniswapPOOLETHLPToken) + let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, UNISWAP_POOLETHLP_TOKEN_ADDR) const uniswapPoolEthBalance = await uniswapLPToken.balanceOf(dsaWallet0.address) - const uniswapPoolEthLPTokenName = await uniswapLPToken.name() - console.log("\tBalance before: ", uniswapPoolEthBalance.toString(), uniswapPoolEthLPTokenName) + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); - // Expect PT Uniswap POOL/ETH LP is 0 - let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, ptUniswapPOOLETHLPTicket) + let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, PT_UNISWAP_POOLETHLP_TICKET_ADDR) const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) - const ptUniswapPoolEthLPTokenName = await ptUniswapPoolEthToken.name() - console.log("\tBalance before: ", ptUniswapPoolEthBalance.toString(), ptUniswapPoolEthLPTokenName) + expect(ptUniswapPoolEthBalance, `PoolTogether Uniswap POOL?ETH LP equals 0`).to.be.eq(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() // After spell - // ETH balance < 0 ethBalance = await ethers.provider.getBalance(dsaWallet0.address); - console.log("After spell:"); - console.log("\tBalance after: ", ethBalance.toString(), "ETH"); + expect(ethBalance, `ETH Balance less than 9`).to.be.lt(ethers.utils.parseEther("9")); - // Expect Pool Token Balance to greater than 0 const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.eq(poolBalance); + expect(poolBalanceAfter, `POOL Token to be same after spell`).to.be.eq(poolBalance); - // Expect Uniswap POOL/ETH LP to greater than 0 const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", uniswapPoolEthBalanceAfter.toString(), uniswapPoolEthLPTokenName) - expect(uniswapPoolEthBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + expect(uniswapPoolEthBalanceAfter, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); - // Expect PT Uniswap POOL/ETH LP to greater than 0 const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", ptUniswapPoolEthBalanceAfter.toString(), ptUniswapPoolEthLPTokenName) - expect(ptUniswapPoolEthBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); - - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + expect(ptUniswapPoolEthBalanceAfter, `PT Uniswap POOL/ETH LP to greater than 0`).to.be.gt(0); }); it("Should wait 11 days, withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function () { - let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, ptUniswapPOOLETHLPTicket) + let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, PT_UNISWAP_POOLETHLP_TICKET_ADDR) const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) const setId = "83478237" @@ -611,43 +519,35 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [uniswapPoolETHLPPrizePool, dsaWallet0.address, ptUniswapPoolEthBalance, ptUniswapPOOLETHLPTicket, 0, 0, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, dsaWallet0.address, ptUniswapPoolEthBalance, PT_UNISWAP_POOLETHLP_TICKET_ADDR, 0, 0, 0] }, { connector: ptConnectorName, method: "claim", - args: [uniswapPoolETHLPFaucet , dsaWallet0.address, setId] + args: [UNISWAP_POOLETHLP_FAUCET_ADDR , dsaWallet0.address, setId] }, { connector: ptConnectorName, method: "depositTo", - args: [poolPoolPrizePool, dsaWallet0.address, 0, ptPoolTicket, constants.address_zero, setId, 0] + args: [POOL_PRIZE_POOL_ADDR, dsaWallet0.address, 0, PT_POOL_TICKET_ADDR, constants.address_zero, setId, 0] } ] // Before spell - console.log("Before spell:"); - // PoolToken - let poolToken = await ethers.getContractAt(abis.basic.erc20, poolTokenAddress) + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) const poolBalance = await poolToken.balanceOf(dsaWallet0.address) - const poolTokenName = await poolToken.name() - console.log("\tBalance before: ", poolBalance.toString(), poolTokenName) + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); // Uniswap POOL/ETH LP is 0 - let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, uniswapPOOLETHLPToken) + let uniswapLPToken = await ethers.getContractAt(abis.basic.erc20, UNISWAP_POOLETHLP_TOKEN_ADDR) const uniswapPoolEthBalance = await uniswapLPToken.balanceOf(dsaWallet0.address) - const uniswapPoolEthLPTokenName = await uniswapLPToken.name() - console.log("\tBalance before: ", uniswapPoolEthBalance.toString(), uniswapPoolEthLPTokenName) + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); - // Expect PT Uniswap POOL/ETH LP > 0 - const ptUniswapPoolEthLPTokenName = await ptUniswapPoolEthToken.name() - console.log("\tBalance before: ", ptUniswapPoolEthBalance.toString(), ptUniswapPoolEthLPTokenName) + expect(ptUniswapPoolEthBalance, `PT Uniswap POOL/ETH LP greater than 0`).to.be.gt(0); - // PoolTogether Pool Ticket - let poolPoolTicket = await ethers.getContractAt(abis.basic.erc20, ptPoolTicket) + let poolPoolTicket = await ethers.getContractAt(abis.basic.erc20, PT_POOL_TICKET_ADDR) const poolPoolTicketBalance = await poolPoolTicket.balanceOf(dsaWallet0.address) - const poolPoolTicketName = await poolPoolTicket.name() - console.log("\tBalance before: ", poolPoolTicketBalance.toString(), poolPoolTicketName) + expect(poolPoolTicketBalance, `PoolTogether POOL Ticket equals 0`).to.be.eq(0); // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); @@ -657,26 +557,17 @@ describe("PoolTogether", function () { const receipt = await tx.wait() // After spell - console.log("After spell:"); - // Expect Pool Token Balance to be greater than balance before spell const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolBalanceAfter.toString(), poolTokenName) - expect(poolBalanceAfter).to.be.eq(poolBalance); + expect(poolBalanceAfter, `Pool Token Balance equal to balance before spell`).to.be.eq(poolBalance); - // Expect Uniswap POOL/ETH LP to greater than 0 const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", uniswapPoolEthBalanceAfter.toString(), uniswapPoolEthLPTokenName) - expect(uniswapPoolEthBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(uniswapPoolEthBalanceAfter, `Uniswap POOL/ETH LP to greater than 0`).to.be.gt(0); - // Expect PT Uniswap POOL/ETH LP equal 0 const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", ptUniswapPoolEthBalanceAfter.toString(), ptUniswapPoolEthLPTokenName) - expect(ptUniswapPoolEthBalanceAfter).to.be.eq(ethers.utils.parseEther("0")); + expect(ptUniswapPoolEthBalanceAfter, `PT Uniswap POOL/ETH LP equal 0`).to.be.eq(0); - // Expoect PoolTogether Pool Ticket > 0 const poolPoolTicketBalanceAfter = await poolPoolTicket.balanceOf(dsaWallet0.address) - console.log("\tBalance after: ", poolPoolTicketBalanceAfter.toString(), poolPoolTicketName) - expect(poolPoolTicketBalanceAfter).to.be.gt(ethers.utils.parseEther("0")); + expect(poolPoolTicketBalanceAfter, `PoolTogether POOL Ticket greater than 0`).to.be.gt(0); }); }) }) \ No newline at end of file From f9762d8dfc61690b69a104effa8014c8d238cac7 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 10:35:02 -0700 Subject: [PATCH 13/21] Fix: replace where relevant with address(this) --- .../mainnet/connectors/pooltogether/main.sol | 30 +++++++------------ test/pooltogether/pooltogether.test.js | 28 ++++++++--------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 4586a164..aa5b1106 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -21,7 +21,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @dev Deposit into Prize Pool * @notice Deposit assets into the Prize Pool in exchange for tokens * @param prizePool PrizePool address to deposit to - * @param to The address receiving the newly minted tokens * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. * @param referrer The address that should receive referral awards, if any. @@ -31,7 +30,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function depositTo( address prizePool, - address to, uint256 amount, address controlledToken, address referrer, @@ -47,19 +45,18 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { TokenInterface tokenContract = TokenInterface(prizePoolToken); tokenContract.approve(prizePool, _amount); - prizePoolContract.depositTo(to, _amount, controlledToken, referrer); + prizePoolContract.depositTo(address(this), _amount, controlledToken, referrer); setUint(setId, _amount); _eventName = "LogDepositTo(address,address,uint256,address,address,uint256, uint256)"; - _eventParam = abi.encode(address(prizePool), address(to), _amount, address(controlledToken), address(referrer), getId, setId); + _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), address(referrer), getId, setId); } /** * @dev Withdraw from Prize Pool * @notice Withdraw assets from the Prize Pool instantly. A fairness fee may be charged for an early exit. * @param prizePool PrizePool address to deposit to - * @param from The address to redeem tokens from.. This means you can withdraw on another user's behalf if you have an allowance for the controlled token. * @param amount The amount of tokens to redeem for assets. * @param controlledToken The address of the token to redeem (i.e. ticket or sponsorship) * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. This should be pre-calculated by the calculateExitFee() fxn. @@ -69,7 +66,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function withdrawInstantlyFrom ( address prizePool, - address from, uint256 amount, address controlledToken, uint256 maximumExitFee, @@ -80,54 +76,50 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); - prizePoolContract.withdrawInstantlyFrom(from, _amount, controlledToken, maximumExitFee); + prizePoolContract.withdrawInstantlyFrom(address(this), _amount, controlledToken, maximumExitFee); setUint(setId, _amount); _eventName = "LogWithdrawInstantlyFrom(address,address,uint256,address,uint256,uint256,uint256)"; - _eventParam = abi.encode(address(prizePool), address(from), _amount, address(controlledToken), maximumExitFee, getId, setId); + _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), maximumExitFee, getId, setId); } /** * @dev Claim token from a Token Faucet * @notice Claim token from a Token Faucet * @param tokenFaucet TokenFaucet address - * @param user The user to claim tokens for * @param setId Set claimed amount at this ID in `InstaMemory` Contract. */ function claim ( address tokenFaucet, - address user, uint256 setId ) external payable returns (string memory _eventName, bytes memory _eventParam) { TokenFaucetInterface tokenFaucetContract = TokenFaucetInterface(tokenFaucet); - uint256 claimed = tokenFaucetContract.claim(user); + uint256 claimed = tokenFaucetContract.claim(address(this)); setUint(setId, claimed); _eventName = "LogClaim(address,address, uint256, uint256)"; - _eventParam = abi.encode(address(tokenFaucet), address(user), claimed, setId); + _eventParam = abi.encode(address(tokenFaucet), address(this), claimed, setId); } /** * @dev Runs claim on all passed comptrollers for a user. * @notice Runs claim on all passed comptrollers for a user. * @param tokenFaucetProxyFactory The TokenFaucetProxyFactory address - * @param user The user to claim tokens for * @param tokenFaucets The tokenFaucets to call claim on. */ function claimAll ( address tokenFaucetProxyFactory, - address user, TokenFaucetInterface[] calldata tokenFaucets ) external payable returns (string memory _eventName, bytes memory _eventParam) { TokenFaucetProxyFactoryInterface tokenFaucetProxyFactoryContract = TokenFaucetProxyFactoryInterface(tokenFaucetProxyFactory); - tokenFaucetProxyFactoryContract.claimAll(user, tokenFaucets); + tokenFaucetProxyFactoryContract.claimAll(address(this), tokenFaucets); _eventName = "LogClaimAll(address,address,TokenFaucetInterface[])"; - _eventParam = abi.encode(address(tokenFaucetProxyFactory), address(user), tokenFaucets); + _eventParam = abi.encode(address(tokenFaucetProxyFactory), address(this), tokenFaucets); } /** @@ -135,7 +127,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @notice Deposit assets into the Pod in exchange for share tokens * @param prizePool PrizePool address to deposit to * @param pod Pod address to deposit to - * @param to The address that shall receive the Pod shares * @param tokenAmount The amount of tokens to deposit. These are the same tokens used to deposit into the underlying prize pool. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. @@ -143,7 +134,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { function depositToPod( address prizePool, address pod, - address to, uint256 tokenAmount, uint256 getId, uint256 setId @@ -159,12 +149,12 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { TokenInterface tokenContract = TokenInterface(prizePoolToken); tokenContract.approve(pod, _tokenAmount); - podContract.depositTo(to, _tokenAmount); + podContract.depositTo(address(this), _tokenAmount); setUint(setId, _tokenAmount); _eventName = "LogDepositToPod(address,address,address,uint256,uint256, uint256)"; - _eventParam = abi.encode(address(prizePool), address(pod), address(to), _tokenAmount, getId, setId); + _eventParam = abi.encode(address(prizePool), address(pod), address(this), _tokenAmount, getId, setId); } /** diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index faec1463..8081b8f2 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -129,7 +129,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, setId, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, setId, 0] } ] // Before Spell @@ -162,12 +162,12 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] }, { connector: ptConnectorName, method: "claim", - args: [DAI_POOL_FAUCET_ADDR, dsaWallet0.address, 0] + args: [DAI_POOL_FAUCET_ADDR, 0] } ] @@ -210,12 +210,12 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] }, { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] } ] @@ -256,7 +256,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] } ] @@ -264,12 +264,12 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, dsaWallet0.address, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] }, { connector: ptConnectorName, method: "claimAll", - args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, dsaWallet0.address, [DAI_POOL_FAUCET_ADDR]] + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]] } ] @@ -317,7 +317,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositToPod", - args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, dsaWallet0.address, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, amount, 0, 0] } ] @@ -401,7 +401,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositToPod", - args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, dsaWallet0.address, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, amount, 0, 0] }, { connector: ptConnectorName, @@ -472,7 +472,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, dsaWallet0.address, 0, PT_UNISWAP_POOLETHLP_TICKET_ADDR, constants.address_zero, setId, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, 0, PT_UNISWAP_POOLETHLP_TICKET_ADDR, constants.address_zero, setId, 0] } ] @@ -519,17 +519,17 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, dsaWallet0.address, ptUniswapPoolEthBalance, PT_UNISWAP_POOLETHLP_TICKET_ADDR, 0, 0, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, ptUniswapPoolEthBalance, PT_UNISWAP_POOLETHLP_TICKET_ADDR, 0, 0, 0] }, { connector: ptConnectorName, method: "claim", - args: [UNISWAP_POOLETHLP_FAUCET_ADDR , dsaWallet0.address, setId] + args: [UNISWAP_POOLETHLP_FAUCET_ADDR , setId] }, { connector: ptConnectorName, method: "depositTo", - args: [POOL_PRIZE_POOL_ADDR, dsaWallet0.address, 0, PT_POOL_TICKET_ADDR, constants.address_zero, setId, 0] + args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, constants.address_zero, setId, 0] } ] From 09aa2913e7bc6ce8b2b9591bf547159507268fe1 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 13:29:55 -0700 Subject: [PATCH 14/21] Fix: Add condition for uint256(-1) case. --- contracts/mainnet/connectors/pooltogether/interface.sol | 1 + contracts/mainnet/connectors/pooltogether/main.sol | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index 6ac71665..1780cc57 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -17,4 +17,5 @@ interface TokenFaucetProxyFactoryInterface { interface PodInterface { function depositTo(address to, uint256 tokenAmount) external returns (uint256); function withdraw(uint256 shareAmount, uint256 maxFee) external returns (uint256); + function balanceOf(address account) external view returns (uint256); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index aa5b1106..88e27fa4 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -43,6 +43,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { // Approve prizePool TokenInterface tokenContract = TokenInterface(prizePoolToken); + _amount = _amount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _amount; tokenContract.approve(prizePool, _amount); prizePoolContract.depositTo(address(this), _amount, controlledToken, referrer); @@ -76,6 +77,9 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); + TokenInterface ticketToken = TokenInterface(controlledToken); + _amount = _amount == uint256(-1) ? ticketToken.balanceOf(address(this)) : _amount; + prizePoolContract.withdrawInstantlyFrom(address(this), _amount, controlledToken, maximumExitFee); setUint(setId, _amount); @@ -147,6 +151,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { // Approve pod TokenInterface tokenContract = TokenInterface(prizePoolToken); + _tokenAmount = _tokenAmount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _tokenAmount; tokenContract.approve(pod, _tokenAmount); podContract.depositTo(address(this), _tokenAmount); @@ -178,6 +183,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint _shareAmount = getUint(getId, shareAmount); PodInterface podContract = PodInterface(pod); + _shareAmount = _shareAmount == uint256(-1) ? podContract.balanceOf(address(this)) : _shareAmount; podContract.withdraw(_shareAmount, maxFee); From 3a052b9800e5fcc42967beb2874eaf471b2d404c Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 13:43:23 -0700 Subject: [PATCH 15/21] Fix: Remove referrer as it's not used --- contracts/mainnet/connectors/pooltogether/events.sol | 2 +- contracts/mainnet/connectors/pooltogether/main.sol | 8 +++----- test/pooltogether/pooltogether.test.js | 10 +++++----- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index b1b8f485..a73d9658 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -3,7 +3,7 @@ pragma solidity ^0.7.0; import { TokenFaucetInterface } from "./interface.sol"; contract Events { - event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, address referrer, uint256 getId, uint256 setId); + event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, uint256 getId, uint256 setId); event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 88e27fa4..f2528a15 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -23,7 +23,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { * @param prizePool PrizePool address to deposit to * @param amount The amount of the underlying asset the user wishes to deposit. The Prize Pool contract should have been pre-approved by the caller to transfer the underlying ERC20 tokens. * @param controlledToken The address of the token that they wish to mint. For our default Prize Strategy this will either be the Ticket address or the Sponsorship address. Those addresses can be looked up on the Prize Strategy. - * @param referrer The address that should receive referral awards, if any. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. */ @@ -32,7 +31,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { address prizePool, uint256 amount, address controlledToken, - address referrer, uint256 getId, uint256 setId ) external payable returns ( string memory _eventName, bytes memory _eventParam) { @@ -46,12 +44,12 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _amount = _amount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _amount; tokenContract.approve(prizePool, _amount); - prizePoolContract.depositTo(address(this), _amount, controlledToken, referrer); + prizePoolContract.depositTo(address(this), _amount, controlledToken, address(0)); setUint(setId, _amount); - _eventName = "LogDepositTo(address,address,uint256,address,address,uint256, uint256)"; - _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), address(referrer), getId, setId); + _eventName = "LogDepositTo(address,address,uint256,address,uint256,uint256)"; + _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), getId, setId); } /** diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 8081b8f2..433d3abc 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -129,7 +129,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, setId, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, setId, 0] } ] // Before Spell @@ -210,7 +210,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0] }, { connector: ptConnectorName, @@ -256,7 +256,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, constants.address_zero, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0] } ] @@ -472,7 +472,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, 0, PT_UNISWAP_POOLETHLP_TICKET_ADDR, constants.address_zero, setId, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, 0, PT_UNISWAP_POOLETHLP_TICKET_ADDR, setId, 0] } ] @@ -529,7 +529,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositTo", - args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, constants.address_zero, setId, 0] + args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, setId, 0] } ] From ac504364d92caf9f2edd5ec03a052facb8159bd2 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 22:35:39 -0700 Subject: [PATCH 16/21] Fix: Add maximumExitFee examples to test --- .../connectors/pooltogether/interface.sol | 1 + .../mainnet/connectors/pooltogether/main.sol | 2 +- test/pooltogether/pooltogether.test.js | 77 +++++++++++++------ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index 1780cc57..dc32d955 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -4,6 +4,7 @@ interface PrizePoolInterface { function token() external view returns (address); function depositTo( address to, uint256 amount, address controlledToken, address referrer) external; function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); + function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit); } interface TokenFaucetInterface { diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index f2528a15..8bd73f0b 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -55,7 +55,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { /** * @dev Withdraw from Prize Pool * @notice Withdraw assets from the Prize Pool instantly. A fairness fee may be charged for an early exit. - * @param prizePool PrizePool address to deposit to + * @param prizePool PrizePool address to withdraw from * @param amount The amount of tokens to redeem for assets. * @param controlledToken The address of the token to redeem (i.e. ticket or sponsorship) * @param maximumExitFee The maximum early exit fee the caller is willing to pay. This prevents the Prize Strategy from changing the fee on the fly. This should be pre-calculated by the calculateExitFee() fxn. diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 433d3abc..3656a630 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -34,6 +34,14 @@ const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791" // P const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4" // Pool Together POOL Ticket const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" // WETH +const prizePoolABI = [ + "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)" +] + +const podABI = [ + "function getEarlyExitFee(uint256 amount) external returns (uint256)" +] + describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" const uniswapConnectorName = "UNISWAP-TEST-A" @@ -158,11 +166,16 @@ describe("PoolTogether", function () { it("Should wait 11 days, withdraw all PrizePool, get back 100 DAI, and claim POOL", async function () { const amount = ethers.utils.parseEther("100") // 100 DAI + + let prizePoolContract = new ethers.Contract(DAI_PRIZE_POOL_ADDR, prizePoolABI, ethers.provider); + let earlyExitFee = await prizePoolContract.callStatic["calculateEarlyExitFee"](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect(earlyExitFee.exitFee, "Exit Fee equal to 1 DAI because starts at 10%").to.be.eq(ethers.utils.parseEther("1")); + const spells = [ { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, earlyExitFee.exitFee, 0, 0] }, { connector: ptConnectorName, @@ -186,6 +199,10 @@ describe("PoolTogether", function () { // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + await ethers.provider.send("evm_mine"); + + earlyExitFee = await prizePoolContract.callStatic["calculateEarlyExitFee"](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect(earlyExitFee.exitFee, "Exit Fee equal to 0 DAI because past 10 days").to.be.eq(0); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) @@ -206,6 +223,7 @@ describe("PoolTogether", function () { it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { const amount = ethers.utils.parseEther("100") // 100 DAI + const exitFee = ethers.utils.parseEther("1") // 1 DAI is 10% of 100 DAI const spells = [ { connector: ptConnectorName, @@ -215,7 +233,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, exitFee, 0, 0] } ] @@ -260,19 +278,6 @@ describe("PoolTogether", function () { } ] - const withdrawSpells = [ - { - connector: ptConnectorName, - method: "withdrawInstantlyFrom", - args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, amount, 0, 0] - }, - { - connector: ptConnectorName, - method: "claimAll", - args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]] - } - ] - // Before spell let daiToken = await ethers.getContractAt(abis.basic.erc20, DAI_TOKEN_ADDR) let daiBalance = await daiToken.balanceOf(dsaWallet0.address); @@ -290,8 +295,30 @@ describe("PoolTogether", function () { const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(depositSpells), wallet1.address) const receipt = await tx.wait() + const prizePoolContract = new ethers.Contract(DAI_PRIZE_POOL_ADDR, prizePoolABI, ethers.provider); + let earlyExitFee = await prizePoolContract.callStatic["calculateEarlyExitFee"](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect(earlyExitFee.exitFee, "Exit Fee equal to .99 DAI because starts at 10%").to.be.eq(ethers.utils.parseEther(".99")); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + await ethers.provider.send("evm_mine"); + + earlyExitFee = await prizePoolContract.callStatic["calculateEarlyExitFee"](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect(earlyExitFee.exitFee, "Exit Fee equal to 0 DAI because past 10 days").to.be.eq(0); + + const withdrawSpells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, earlyExitFee.exitFee, 0, 0] + }, + { + connector: ptConnectorName, + method: "claimAll", + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]] + } + ] // Run spell transaction const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(withdrawSpells), wallet1.address) @@ -351,7 +378,12 @@ describe("PoolTogether", function () { it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function () { const amount = ethers.utils.parseEther("99") // 99 DAI - const maxFee = 0; + + const podContract = new ethers.Contract(DAI_POD_ADDR, podABI, ethers.provider); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + expect(maxFee, "Exit Fee equal to 0 DAI because token still in float").to.be.eq(0); + // maxFee depends on if token has been deposited to PrizePool yet + const spells = [ { connector: ptConnectorName, @@ -395,7 +427,7 @@ describe("PoolTogether", function () { it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { const amount = ethers.utils.parseEther("99") - const maxFee = 0; + const maxFee = 0; // maxFee 0 since it doesn't give chance for Pod to actually deposit into PrizePool const spells = [ { @@ -510,16 +542,20 @@ describe("PoolTogether", function () { expect(ptUniswapPoolEthBalanceAfter, `PT Uniswap POOL/ETH LP to greater than 0`).to.be.gt(0); }); - it("Should wait 11 days, withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function () { + it("Should withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function () { let ptUniswapPoolEthToken = await ethers.getContractAt(abis.basic.erc20, PT_UNISWAP_POOLETHLP_TICKET_ADDR) const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf(dsaWallet0.address) const setId = "83478237" + let uniswapPrizePoolContract = new ethers.Contract(UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, prizePoolABI, ethers.provider); + let earlyExitFee = await uniswapPrizePoolContract.callStatic["calculateEarlyExitFee"](dsaWallet0.address, PT_UNISWAP_POOLETHLP_TICKET_ADDR, ptUniswapPoolEthBalance); + expect(earlyExitFee.exitFee, "Exit Fee equals 0 because no early exit fee for this prize pool").to.be.eq(0); + const spells = [ { connector: ptConnectorName, method: "withdrawInstantlyFrom", - args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, ptUniswapPoolEthBalance, PT_UNISWAP_POOLETHLP_TICKET_ADDR, 0, 0, 0] + args: [UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, ptUniswapPoolEthBalance, PT_UNISWAP_POOLETHLP_TICKET_ADDR, earlyExitFee.exitFee, 0, 0] }, { connector: ptConnectorName, @@ -549,9 +585,6 @@ describe("PoolTogether", function () { const poolPoolTicketBalance = await poolPoolTicket.balanceOf(dsaWallet0.address) expect(poolPoolTicketBalance, `PoolTogether POOL Ticket equals 0`).to.be.eq(0); - // Increase time by 11 days so we get back all DAI without early withdrawal fee - await ethers.provider.send("evm_increaseTime", [11*24*60*60]); - // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) const receipt = await tx.wait() From 0518a123fe8249183824d52f5f9e4038d247c48c Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 22:57:08 -0700 Subject: [PATCH 17/21] Fix: use token address instead of prizePool address in depositToPod() --- contracts/mainnet/connectors/pooltogether/events.sol | 2 +- contracts/mainnet/connectors/pooltogether/main.sol | 9 +++------ test/pooltogether/pooltogether.test.js | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index a73d9658..4c6c905e 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -7,6 +7,6 @@ contract Events { event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); - event LogDepositToPod(address prizePool, address pod, address to, uint256 amount, uint256 getId, uint256 setId); + event LogDepositToPod(address prizePoolToken, address pod, address to, uint256 amount, uint256 getId, uint256 setId); event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 maxFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 8bd73f0b..1e910bb5 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -127,14 +127,14 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { /** * @dev Deposit into Pod * @notice Deposit assets into the Pod in exchange for share tokens - * @param prizePool PrizePool address to deposit to + * @param prizePoolToken Prize Pool Token Address * @param pod Pod address to deposit to * @param tokenAmount The amount of tokens to deposit. These are the same tokens used to deposit into the underlying prize pool. * @param getId Get token amount at this ID from `InstaMemory` Contract. * @param setId Set token amount at this ID in `InstaMemory` Contract. */ function depositToPod( - address prizePool, + address prizePoolToken, address pod, uint256 tokenAmount, uint256 getId, @@ -142,9 +142,6 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { ) external payable returns ( string memory _eventName, bytes memory _eventParam) { uint _tokenAmount= getUint(getId, tokenAmount); - PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); - address prizePoolToken = prizePoolContract.token(); - PodInterface podContract = PodInterface(pod); // Approve pod @@ -157,7 +154,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { setUint(setId, _tokenAmount); _eventName = "LogDepositToPod(address,address,address,uint256,uint256, uint256)"; - _eventParam = abi.encode(address(prizePool), address(pod), address(this), _tokenAmount, getId, setId); + _eventParam = abi.encode(address(prizePoolToken), address(pod), address(this), _tokenAmount, getId, setId); } /** diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 3656a630..d50ace47 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -344,7 +344,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositToPod", - args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, amount, 0, 0] + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0] } ] @@ -433,7 +433,7 @@ describe("PoolTogether", function () { { connector: ptConnectorName, method: "depositToPod", - args: [DAI_PRIZE_POOL_ADDR, DAI_POD_ADDR, amount, 0, 0] + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0] }, { connector: ptConnectorName, From a8a7dd48d0d69e371a5c5689b4ceec0a009d0fb6 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 2 Sep 2021 23:05:34 -0700 Subject: [PATCH 18/21] Fix: Use return values _podShare and _tokenAmount from depositTo and withdraw in setUint() --- .../mainnet/connectors/pooltogether/events.sol | 4 ++-- .../mainnet/connectors/pooltogether/main.sol | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 4c6c905e..12e4af63 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -7,6 +7,6 @@ contract Events { event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); - event LogDepositToPod(address prizePoolToken, address pod, address to, uint256 amount, uint256 getId, uint256 setId); - event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 maxFee, uint256 getId, uint256 setId); + event LogDepositToPod(address prizePoolToken, address pod, address to, uint256 amount, uint256 podShare, uint256 getId, uint256 setId); + event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 tokenAmount, uint256 maxFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 1e910bb5..5218cfa9 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -149,12 +149,12 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _tokenAmount = _tokenAmount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _tokenAmount; tokenContract.approve(pod, _tokenAmount); - podContract.depositTo(address(this), _tokenAmount); + uint256 _podShare = podContract.depositTo(address(this), _tokenAmount); - setUint(setId, _tokenAmount); + setUint(setId, _podShare); - _eventName = "LogDepositToPod(address,address,address,uint256,uint256, uint256)"; - _eventParam = abi.encode(address(prizePoolToken), address(pod), address(this), _tokenAmount, getId, setId); + _eventName = "LogDepositToPod(address,address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(address(prizePoolToken), address(pod), address(this), _tokenAmount, _podShare, getId, setId); } /** @@ -180,12 +180,12 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PodInterface podContract = PodInterface(pod); _shareAmount = _shareAmount == uint256(-1) ? podContract.balanceOf(address(this)) : _shareAmount; - podContract.withdraw(_shareAmount, maxFee); + uint256 _tokenAmount = podContract.withdraw(_shareAmount, maxFee); - setUint(setId, _shareAmount); + setUint(setId, _tokenAmount); - _eventName = "LogWithdrawFromPod(address,uint256,uint256,uint256,uint256)"; - _eventParam = abi.encode(address(pod), _shareAmount, maxFee, getId, setId); + _eventName = "LogWithdrawFromPod(address,uint256,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(address(pod), _shareAmount, _tokenAmount, maxFee, getId, setId); } } From 2705ab4ad9f858f1cad1c3aa677705822f2f0efb Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Thu, 9 Sep 2021 16:32:24 -0700 Subject: [PATCH 19/21] Fix: Add convertEthToWeth logic for WETH PrizePools --- .../connectors/pooltogether/events.sol | 2 +- .../connectors/pooltogether/interface.sol | 2 +- .../mainnet/connectors/pooltogether/main.sol | 40 ++++- test/pooltogether/pooltogether.test.js | 159 +++++++++++++++++- 4 files changed, 192 insertions(+), 11 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 12e4af63..46415d0d 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -4,7 +4,7 @@ import { TokenFaucetInterface } from "./interface.sol"; contract Events { event LogDepositTo(address prizePool, address to, uint256 amount, address controlledToken, uint256 getId, uint256 setId); - event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 getId, uint256 setId); + event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 exitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); event LogDepositToPod(address prizePoolToken, address pod, address to, uint256 amount, uint256 podShare, uint256 getId, uint256 setId); diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index dc32d955..c63c08cc 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -4,7 +4,6 @@ interface PrizePoolInterface { function token() external view returns (address); function depositTo( address to, uint256 amount, address controlledToken, address referrer) external; function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); - function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit); } interface TokenFaucetInterface { @@ -16,6 +15,7 @@ interface TokenFaucetProxyFactoryInterface { } interface PodInterface { + function token() external view returns (address); function depositTo(address to, uint256 tokenAmount) external returns (uint256); function withdraw(uint256 shareAmount, uint256 maxFee) external returns (uint256); function balanceOf(address account) external view returns (uint256); diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 5218cfa9..09d23506 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -10,6 +10,7 @@ pragma solidity ^0.7.0; import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface, PodInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; +import { Stores } from "../../common/stores.sol"; import { Events } from "./events.sol"; import { DSMath } from "../../common/math.sol"; import { Basic } from "../../common/basic.sol"; @@ -39,10 +40,18 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); address prizePoolToken = prizePoolContract.token(); - // Approve prizePool + bool isEth = prizePoolToken == wethAddr; TokenInterface tokenContract = TokenInterface(prizePoolToken); - _amount = _amount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _amount; - tokenContract.approve(prizePool, _amount); + + if (isEth) { + _amount = _amount == uint256(-1) ? address(this).balance : _amount; + convertEthToWeth(isEth, tokenContract, _amount); + } else { + _amount = _amount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _amount; + } + + // Approve prizePool + approve(tokenContract, prizePool, _amount); prizePoolContract.depositTo(address(this), _amount, controlledToken, address(0)); @@ -74,16 +83,22 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint _amount = getUint(getId, amount); PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool); + address prizePoolToken = prizePoolContract.token(); + TokenInterface tokenContract = TokenInterface(prizePoolToken); TokenInterface ticketToken = TokenInterface(controlledToken); _amount = _amount == uint256(-1) ? ticketToken.balanceOf(address(this)) : _amount; - prizePoolContract.withdrawInstantlyFrom(address(this), _amount, controlledToken, maximumExitFee); + uint exitFee = prizePoolContract.withdrawInstantlyFrom(address(this), _amount, controlledToken, maximumExitFee); + + _amount = _amount - exitFee; + + convertWethToEth(prizePoolToken == wethAddr, tokenContract, _amount); setUint(setId, _amount); - _eventName = "LogWithdrawInstantlyFrom(address,address,uint256,address,uint256,uint256,uint256)"; - _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), maximumExitFee, getId, setId); + _eventName = "LogWithdrawInstantlyFrom(address,address,uint256,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(address(prizePool), address(this), _amount, address(controlledToken), maximumExitFee, exitFee, getId, setId); } /** @@ -144,10 +159,17 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { PodInterface podContract = PodInterface(pod); + bool isEth = prizePoolToken == wethAddr; + // Approve pod TokenInterface tokenContract = TokenInterface(prizePoolToken); - _tokenAmount = _tokenAmount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _tokenAmount; - tokenContract.approve(pod, _tokenAmount); + if (isEth) { + _tokenAmount = _tokenAmount == uint256(-1) ? address(this).balance : _tokenAmount; + convertEthToWeth(isEth, tokenContract, _tokenAmount); + } else { + _tokenAmount = _tokenAmount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _tokenAmount; + } + approve(tokenContract, pod, _tokenAmount); uint256 _podShare = podContract.depositTo(address(this), _tokenAmount); @@ -182,6 +204,8 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { uint256 _tokenAmount = podContract.withdraw(_shareAmount, maxFee); + convertWethToEth(address(podContract.token()) == wethAddr, TokenInterface(podContract.token()), _tokenAmount); + setUint(setId, _tokenAmount); _eventName = "LogWithdrawFromPod(address,uint256,uint256,uint256,uint256,uint256)"; diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index d50ace47..52084e7f 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -34,12 +34,22 @@ const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791" // P const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4" // Pool Together POOL Ticket const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" // WETH +// Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats +const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3" // Community WETH Prize Pool (Rari) +const WETH_POOL_TICKET_ADDR = "0x9b5c30aeb9ce2a6a121cea9a85bc0d662f6d9b40" // Community WETH Prize Pool Ticket (Rari) + const prizePoolABI = [ "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)" ] const podABI = [ - "function getEarlyExitFee(uint256 amount) external returns (uint256)" + "function getEarlyExitFee(uint256 amount) external returns (uint256)", + "function balanceOfUnderlying(address user) external view returns (uint256 amount)" +] + +const POD_FACTORY_ADDRESS = "0x4e3a9f9fbafb2ec49727cffa2a411f7a0c1c4ce1" +const podFactoryABI = [ + "function create( address _prizePool, address _ticket, address _faucet, address _manager, uint8 _decimals) external returns (address pod)" ] describe("PoolTogether", function () { @@ -603,4 +613,151 @@ describe("PoolTogether", function () { expect(poolPoolTicketBalanceAfter, `PoolTogether POOL Ticket greater than 0`).to.be.gt(0); }); }) + + describe("Main - WETH Prize Pool Test", function () { + it("Deposit 1 ETH into WETH Prize Pool and withdraw immediately", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const setId = "83478237" + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, setId] + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, amount, setId, 0] + }, + ] + // Before Spell + const ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + const ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + // ETH used for transaction + expect(ethBalanceAfter, `ETH Balance less than before spell because of early withdrawal fee`).to.be.lte(ethBalanceBefore); + }); + + it("Deposit 1 ETH into WETH Prize Pool, wait 14 days, then withdraw", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const depositSpell = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, 0] + } + ] + + const withdrawSpell = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, amount, 0, 0] + } + ] + + // Before Deposit Spell + let ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // Run deposit spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(depositSpell), wallet1.address) + const receipt = await tx.wait() + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + expect(ethBalanceAfter, `ETH Balance less than before spell`).to.be.lte(ethBalanceBefore); + + // Increase time by 11 days so we get back all ETH without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [14*24*60*60]); + await ethers.provider.send("evm_mine"); + + // Run withdraw spell transaction + const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(withdrawSpell), wallet1.address) + const receipt2 = await tx.wait() + + // After Deposit spell + ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + expect(ethBalanceAfter, `ETH Balance equal to before spell because no early exit fee`).to.be.eq(ethBalanceBefore); + }); + }); + + describe("Main - WETH Pod Test", function() { + let podAddress + it("Should deposit 1 ETH in WETH Pod and get Pod Ticket", async function() { + const amount = ethers.utils.parseEther("1") + + // Create Pod for WETH Prize Pool (Rari) + const podFactoryContract = new ethers.Contract(POD_FACTORY_ADDRESS, podFactoryABI, masterSigner) + podAddress = await podFactoryContract.callStatic.create(WETH_PRIZE_POOL_ADDR, WETH_POOL_TICKET_ADDR, constants.address_zero, wallet0.address, 18) + await podFactoryContract.create(WETH_PRIZE_POOL_ADDR, WETH_POOL_TICKET_ADDR, constants.address_zero, wallet0.address, 18) + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [WETH_ADDR, podAddress, amount, 0, 0] + } + ] + + // Before Deposit Spell + const podContract = new ethers.Contract(podAddress, podABI, ethers.provider); + let podBalanceBefore = await podContract.balanceOfUnderlying(dsaWallet0.address) + expect(podBalanceBefore, `Pod balance equal to 0`).to.be.eq(0); + + let ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalanceAfter, `ETH balance less than before`).to.be.lt(ethBalanceBefore); + + podBalanceAfter = await podContract.balanceOfUnderlying(dsaWallet0.address) + expect(podBalanceAfter, `Pod balance equal to 1`).to.be.eq(ethers.utils.parseEther("1")); + }); + + it("Should withdraw 1 Ticket from WETH Pod and get back ETH", async function() { + const amount = ethers.utils.parseEther("1") + + const podContract = new ethers.Contract(podAddress, podABI, ethers.provider); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + expect(maxFee, "Exit Fee equal to 0 DAI because token still in float").to.be.eq(0); + // maxFee depends on if token has been deposited to PrizePool yet + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [podAddress, amount, maxFee, 0, 0] + } + ] + + // Before Deposit Spell + let podBalanceBefore = await podContract.balanceOfUnderlying(dsaWallet0.address) + expect(podBalanceBefore, `Pod balance equal to 1`).to.be.eq(ethers.utils.parseEther("1")); + + let ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalanceAfter, `ETH balance greater than before`).to.be.gt(ethBalanceBefore); + + podBalanceAfter = await podContract.balanceOfUnderlying(dsaWallet0.address) + expect(podBalanceAfter, `Pod balance equal to 0`).to.be.eq(ethers.utils.parseEther("0")); + }); + }); }) \ No newline at end of file From e28def660cdbfc1d82f4a152ed05ad7d00fe3e85 Mon Sep 17 00:00:00 2001 From: eccheung4 Date: Sun, 19 Sep 2021 10:24:24 -0700 Subject: [PATCH 20/21] feat: add claim() for Pod TokenDrop --- .../connectors/pooltogether/events.sol | 1 + .../connectors/pooltogether/interface.sol | 4 ++ .../mainnet/connectors/pooltogether/main.sol | 22 +++++++- test/pooltogether/pooltogether.test.js | 52 ++++++++++++++++++- 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/contracts/mainnet/connectors/pooltogether/events.sol b/contracts/mainnet/connectors/pooltogether/events.sol index 46415d0d..564b21bc 100644 --- a/contracts/mainnet/connectors/pooltogether/events.sol +++ b/contracts/mainnet/connectors/pooltogether/events.sol @@ -7,6 +7,7 @@ contract Events { event LogWithdrawInstantlyFrom(address prizePool, address from, uint256 amount, address controlledToken, uint256 maximumExitFee, uint256 exitFee, uint256 getId, uint256 setId); event LogClaim(address tokenFaucet, address user, uint256 claimed, uint256 setId); event LogClaimAll(address tokenFaucetProxyFactory, address user, TokenFaucetInterface[] tokenFaucets); + event LogClaimPodTokenDrop(address podTokenDrop, address user, uint256 claimed, uint256 setId); event LogDepositToPod(address prizePoolToken, address pod, address to, uint256 amount, uint256 podShare, uint256 getId, uint256 setId); event LogWithdrawFromPod(address pod, uint256 shareAmount, uint256 tokenAmount, uint256 maxFee, uint256 getId, uint256 setId); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/pooltogether/interface.sol b/contracts/mainnet/connectors/pooltogether/interface.sol index c63c08cc..7dd1ae94 100644 --- a/contracts/mainnet/connectors/pooltogether/interface.sol +++ b/contracts/mainnet/connectors/pooltogether/interface.sol @@ -6,6 +6,10 @@ interface PrizePoolInterface { function withdrawInstantlyFrom( address from, uint256 amount, address controlledToken, uint256 maximumExitFee) external returns (uint256); } +interface PodTokenDropInterface { + function claim(address user) external returns (uint256); +} + interface TokenFaucetInterface { function claim( address user) external returns (uint256); } diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index 09d23506..bb15feac 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -7,7 +7,7 @@ pragma solidity ^0.7.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface, PodInterface } from "./interface.sol"; + import { PrizePoolInterface, TokenFaucetInterface, TokenFaucetProxyFactoryInterface, PodInterface, PodTokenDropInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import { Stores } from "../../common/stores.sol"; @@ -139,6 +139,26 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { _eventParam = abi.encode(address(tokenFaucetProxyFactory), address(this), tokenFaucets); } + /** + * @dev Claim asset rewards from a Pod TokenDrop + * @notice Claim asset rewards from a TokenDrop + * @param podTokenDrop Pod TokenDrop address + * @param setId Set claimed amount at this ID in `InstaMemory` Contract. + */ + function claimPodTokenDrop ( + address podTokenDrop, + uint256 setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + PodTokenDropInterface podTokenDropContract = PodTokenDropInterface(podTokenDrop); + + uint256 claimed = podTokenDropContract.claim(address(this)); + + setUint(setId, claimed); + + _eventName = "LogClaimPodTokenDrop(address,address,uint256,uint256)"; + _eventParam = abi.encode(address(podTokenDrop), address(this), claimed, setId); + } + /** * @dev Deposit into Pod * @notice Deposit assets into the Pod in exchange for share tokens diff --git a/test/pooltogether/pooltogether.test.js b/test/pooltogether/pooltogether.test.js index 52084e7f..6dc2697b 100644 --- a/test/pooltogether/pooltogether.test.js +++ b/test/pooltogether/pooltogether.test.js @@ -33,6 +33,7 @@ const PT_UNISWAP_POOLETHLP_TICKET_ADDR = "0xeb8928ee92efb06c44d072a24c2bcb993b61 const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791" // POOL Prize Pool const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4" // Pool Together POOL Ticket const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" // WETH +const DAI_POD_TOKEN_DROP = "0xc5209623E3dFdf9C0cCbe497c8012883C4147731" // Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3" // Community WETH Prize Pool (Rari) @@ -44,7 +45,9 @@ const prizePoolABI = [ const podABI = [ "function getEarlyExitFee(uint256 amount) external returns (uint256)", - "function balanceOfUnderlying(address user) external view returns (uint256 amount)" + "function balanceOfUnderlying(address user) external view returns (uint256 amount)", + "function drop() public returns (uint256)", + "function balanceOf(address account) external view returns (uint256)" ] const POD_FACTORY_ADDRESS = "0x4e3a9f9fbafb2ec49727cffa2a411f7a0c1c4ce1" @@ -52,6 +55,10 @@ const podFactoryABI = [ "function create( address _prizePool, address _ticket, address _faucet, address _manager, uint8 _decimals) external returns (address pod)" ] +const tokenDropABI = [ + "function claim(address user) external returns (uint256)", +] + describe("PoolTogether", function () { const connectorName = "COMPOUND-TEST-A" const uniswapConnectorName = "UNISWAP-TEST-A" @@ -386,13 +393,50 @@ describe("PoolTogether", function () { expect(podBalanceAfter, `Pod DAI token greater than 0`).to.be.eq(ethers.utils.parseEther("99")); }); + it("Should claim rewards from pod token drop", async function() { + const spells = [ + { + connector: ptConnectorName, + method: "claimPodTokenDrop", + args: [DAI_POD_TOKEN_DROP, 0] + } + ] + + const tokenDropContract = new ethers.Contract(DAI_POD_TOKEN_DROP, tokenDropABI, ethers.provider); + const podContract = new ethers.Contract(DAI_POD_ADDR, podABI, masterSigner); + + // drop(): Claim TokenDrop asset for PrizePool Pod and transfers token(s) to external Pod TokenDrop + // dropt() also calls batch which, Deposit Pod float into PrizePool. Deposits the current float + // amount into the PrizePool and claims current POOL rewards. + const dropTx = await podContract.drop(); + await dropTx.wait(); + + // POOL Rewards able to claim from Pod Token Drop + let claimAmount = await tokenDropContract.callStatic["claim"](dsaWallet0.address); + + // Before spell + let poolToken = await ethers.getContractAt(abis.basic.erc20, POOL_TOKEN_ADDRESS) + const poolBalance = await poolToken.balanceOf(dsaWallet0.address) + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + // After spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address) + const total = claimAmount.add(poolBalance); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq(total); + }); + it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function () { const amount = ethers.utils.parseEther("99") // 99 DAI const podContract = new ethers.Contract(DAI_POD_ADDR, podABI, ethers.provider); let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); - expect(maxFee, "Exit Fee equal to 0 DAI because token still in float").to.be.eq(0); // maxFee depends on if token has been deposited to PrizePool yet + // since we called drop in previous test case, the tokens were deposited to PrizePool + expect(maxFee, "Exit Fee equal to .99 DAI because token still in float").to.be.eq(ethers.utils.parseEther(".99")); const spells = [ { @@ -417,6 +461,7 @@ describe("PoolTogether", function () { // Increase time by 11 days so we get back all DAI without early withdrawal fee await ethers.provider.send("evm_increaseTime", [11*24*60*60]); + await ethers.provider.send("evm_mine"); // Run spell transaction const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) @@ -435,6 +480,9 @@ describe("PoolTogether", function () { expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(0); }); + + + it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { const amount = ethers.utils.parseEther("99") const maxFee = 0; // maxFee 0 since it doesn't give chance for Pod to actually deposit into PrizePool From 3229bddfcb6d629c1434e9c1d1d48c0a0df4b082 Mon Sep 17 00:00:00 2001 From: Thrilok kumar Date: Tue, 12 Oct 2021 14:42:44 +0530 Subject: [PATCH 21/21] Fixed claim event --- contracts/mainnet/connectors/pooltogether/main.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mainnet/connectors/pooltogether/main.sol b/contracts/mainnet/connectors/pooltogether/main.sol index bb15feac..cf738ea9 100644 --- a/contracts/mainnet/connectors/pooltogether/main.sol +++ b/contracts/mainnet/connectors/pooltogether/main.sol @@ -117,7 +117,7 @@ abstract contract PoolTogetherResolver is Events, DSMath, Basic { setUint(setId, claimed); - _eventName = "LogClaim(address,address, uint256, uint256)"; + _eventName = "LogClaim(address,address,uint256,uint256)"; _eventParam = abi.encode(address(tokenFaucet), address(this), claimed, setId); }