feat: initial copy of mainnet connector for polygon

This commit is contained in:
eccheung4 2021-10-26 13:11:45 -07:00
parent 8519f08d81
commit f04469dc23
4 changed files with 688 additions and 0 deletions

View File

@ -0,0 +1,11 @@
pragma solidity ^0.7.0;
// import { TokenFaucetInterface } from "./interface.sol";
contract Events {
event LogDepositTo(address prizePool, address to, uint256 amount, uint256 getId, uint256 setId);
event LogDepositToDelegate(address prizePool, address to, uint256 amount, address delegate, uint256 getId, uint256 setId);
event LogWithdrawFrom(address prizePool, address from, uint256 amount, uint256 getId, uint256 setId);
event LogDelegated(address prizePool, address user, address to);
event LogClaim(address prizeDistributor, address user, uint32[] drawIds, bytes data, uint256 payout, uint256 setId);
}

View File

@ -0,0 +1,16 @@
pragma solidity ^0.7.0;
interface PrizePoolInterface {
function getToken() external view returns (address);
function depositTo(address to, uint256 amount) external;
function depositToAndDelegate(address to, uint256 amount, address delegate) external;
function withdrawFrom(address from, uint256 amount) external returns (uint256);
}
interface TicketInterface {
function delegate(address _to) external;
}
interface PrizeDistributorInterface {
function claim(address user, uint32[] calldata drawIds, bytes calldata data) external returns (uint256);
}

View File

@ -0,0 +1,190 @@
pragma solidity ^0.7.0;
/**
* @title PoolTogether V4
* @dev Deposit & Withdraw from PoolTogether V4
*/
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { PrizePoolInterface, TicketInterface, PrizeDistributorInterface } 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";
abstract contract PoolTogetherV4Resolver is Events, DSMath, Basic {
using SafeERC20 for IERC20;
/**
* @dev Deposit into Prize Pool
* @notice Deposit assets into the Prize Pool in exchange for tokens
* @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 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,
uint256 amount,
uint256 getId,
uint256 setId
) external payable returns ( string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool);
address prizePoolToken = prizePoolContract.getToken();
bool isEth = prizePoolToken == wethAddr;
TokenInterface tokenContract = TokenInterface(prizePoolToken);
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);
setUint(setId, _amount);
_eventName = "LogDepositTo(address, address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(prizePool), address(this), _amount, getId, setId);
}
/**
* @dev Deposit into Prize Pool
* @notice Deposit assets into the Prize Pool in exchange for tokens
* @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 delegateTo The address to delegate to for the caller
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function depositToAndDelegate(
address prizePool,
uint256 amount,
address delegateTo,
uint256 getId,
uint256 setId
) external payable returns ( string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool);
address prizePoolToken = prizePoolContract.getToken();
bool isEth = prizePoolToken == wethAddr;
TokenInterface tokenContract = TokenInterface(prizePoolToken);
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.depositToAndDelegate(address(this), _amount,delegateTo);
setUint(setId, _amount);
_eventName = "LogDepositToDelegate(address, address,uint256,address,uint256,uint256)";
_eventParam = abi.encode(address(prizePool), address(this), _amount, address(delegateTo), getId, setId);
}
/**
* @dev Withdraw from Prize Pool
* @notice Withdraw assets from the Prize Pool instantly.
* @param prizePool PrizePool address to withdraw from
* @param amount The amount of tokens to redeem for assets.
* @param getId Get token amount at this ID from `InstaMemory` Contract.
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function withdrawFrom (
address prizePool,
uint256 amount,
uint256 getId,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
uint _amount = getUint(getId, amount);
PrizePoolInterface prizePoolContract = PrizePoolInterface(prizePool);
address prizePoolToken = prizePoolContract.getToken();
TokenInterface tokenContract = TokenInterface(prizePoolToken);
// TokenInterface ticketToken = TokenInterface(controlledToken);
_amount = _amount == uint256(-1) ? tokenContract.balanceOf(address(this)) : _amount;
_amount = prizePoolContract.withdrawFrom(address(this), _amount);
convertWethToEth(prizePoolToken == wethAddr, tokenContract, _amount);
setUint(setId, _amount);
_eventName = "LogWithdrawFrom(address,address,uint256,uint256,uint256)";
_eventParam = abi.encode(address(prizePool), address(this), _amount, getId, setId);
}
/**
* @dev Delegates a users chance to another
* @notice Delegates a users chance to another
* @param ticket Prizepool ticket address
* @param to The delegate
*/
function delegate (
address ticket,
address to
) external payable returns (string memory _eventName, bytes memory _eventParam) {
TicketInterface ticketContract = TicketInterface(ticket);
ticketContract.delegate(to);
_eventName = "LogDelegated(address,address,address)";
_eventParam = abi.encode(address(ticket), address(this), address(to));
}
/**
* @notice Claim prize payout(s) by submitting valud drawId(s) and winning pick indice(s). The user address
is used as the "seed" phrase to generate random numbers.
* @dev The claim function is public and any wallet may execute claim on behalf of another user.
Prizes are always paid out to the designated user account and not the caller (msg.sender).
Claiming prizes is not limited to a single transaction. Reclaiming can be executed
subsequentially if an "optimal" prize was not included in previous claim pick indices. The
payout difference for the new claim is calculated during the award process and transfered to user.
* @param prizeDistributor PrizeDistributor address
* @param drawIds Draw IDs from global DrawBuffer reference
* @param data The data to pass to the draw calculator
* @param setId Set token amount at this ID in `InstaMemory` Contract.
*/
function claim(
address prizeDistributor,
uint32[] calldata drawIds,
bytes calldata data,
uint256 setId
) external payable returns (string memory _eventName, bytes memory _eventParam) {
PrizeDistributorInterface prizeDistributorContract = PrizeDistributorInterface(prizeDistributor);
uint256 payout = prizeDistributorContract.claim(address(this), drawIds, data);
setUint(setId, payout);
_eventName = "LogClaim(address,address,uint32[],bytes,uint256,uint256)";
_eventParam = abi.encode(address(prizeDistributor), address(this), drawIds, data, payout, setId);
}
}
contract ConnectV2PoolTogetherV4 is PoolTogetherV4Resolver {
string public constant name = "PoolTogetherV4-v1";
}

View File

@ -0,0 +1,471 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers, artifacts} = hre;
const { provider, deployMockContract } = waffle
const ALCHEMY_ID = process.env.ALCHEMY_ID;
const impersonate = require("../../scripts/impersonate.js")
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 connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json")
const connectV2PoolTogetherV4Artifacts = require("../../artifacts/contracts/mainnet/connectors/pooltogether_v4/main.sol/ConnectV2PoolTogetherV4.json")
const PrizeDistributionBuffer = require('./artifacts/PrizeDistributionBuffer.json')
const DrawBeacon = require('./artifacts/DrawBeacon.json')
const DrawBuffer = require('./artifacts/DrawBuffer.json')
// https://www.npmjs.com/package/@pooltogether/draw-calculator-js
const {drawCalculator, Draw, PrizeDistribution, generatePicks, prepareClaims, computePicks, batchCalculateDrawResults, calculateNumberOfPicksForUser } = require("@pooltogether/draw-calculator-js")
// Mainnet Test Addresses https://v4.docs.pooltogether.com/protocol/reference/deployments/mainnet
const PRIZE_POOL_ADDR = "0xd89a09084555a7D0ABe7B111b1f78DFEdDd638Be" // Prize Pool
const PRIZE_POOL_TOKEN_ADDR = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // ERC20 USDC
const PRIZE_POOL_TICKET_ADDR = "0xdd4d117723C257CEe402285D3aCF218E9A8236E1" // ERC20 TICKET
const PRIZE_DISTRIBUTOR_ADDR = "0xb9a179DcA5a7bf5f8B9E088437B3A85ebB495eFe"
const PRIZE_DISTRIBUTION_BUFFER_ADDR = "0xf025a8d9E6080F885e841C8cC0E324368D7C6577"
const DRAW_BEACON_ADDR = "0x0D33612870cd9A475bBBbB7CC38fC66680dEcAC5"
const DRAW_BUFFER_ADDR = "0x78Ea5a9595279DC2F9608283875571b1151F19D4"
const DRAW_CALCULATOR_ADDR = "0x14d0675580C7255043a3AeD3726F5D7f33292730"
const TICKET_ABI = [
"function delegateOf(address _user) external view returns (address)",
"function getBalanceAt(address user, uint64 timestamp) external view returns (uint256)",
"function balanceOf(address user) external view returns (uint256)"
]
const DRAW_CALCULATOR_ABI = [
"function getPrizeDistributionBuffer() external view returns (IPrizeDistributionBuffer)",
"function getNormalizedBalancesForDrawIds(address _user, uint32[] calldata _drawIds) external view returns (uint256[] memory)"
]
const RNGBLOCKHASH_ABI = [
"function getRequestFee() external view returns (address feeToken, uint256 requestFee)",
"function isRequestComplete(uint32 requestId) external view returns (bool isCompleted)",
"function randomNumber(uint32 requestId) external returns (uint256 randomNum)",
"function getLastRequestId() external view returns (uint32 requestId)",
"function requestRandomNumber() external returns (uint32 requestId, uint32 lockBlock)"
]
describe("PoolTogether", function () {
const connectorName = "COMPOUND-TEST-A"
const ptConnectorName = "POOLTOGETHERV4-TEST-A"
let dsaWallet0
let masterSigner;
let instaConnectorsV2;
let connector;
let ptConnector;
let rng;
const wallets = provider.getWallets()
const [wallet0, wallet1, wallet2, wallet3] = wallets
before(async () => {
await hre.network.provider.request({
method: "hardhat_reset",
params: [
{
forking: {
jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`,
blockNumber: 13475671,
},
},
],
});
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,
signer: masterSigner,
connectors: instaConnectorsV2
})
// Deploy and enable Pool Together Connector
ptConnector = await deployAndEnableConnector({
connectorName: ptConnectorName,
contractArtifact: connectV2PoolTogetherV4Artifacts,
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(!!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("Should compute winning pick from hashed dsaWallet0.address, setup Mock RNG Contract", async function() {
// First pick generated from hashed dsaWallet0.address
const hashedAddress = ethers.utils.solidityKeccak256(['address'], [dsaWallet0.address]);
const pick0 = computePicks(hashedAddress,[ethers.BigNumber.from(0)])[0];
// Deploy Mock RNG Contract with hard coded winning random number of dsaWallet
rng = await deployMockContract(wallet0, RNGBLOCKHASH_ABI);
await rng.mock.getRequestFee.returns(ethers.constants.AddressZero, 0);
await rng.mock.randomNumber.returns(pick0.hash);
await rng.mock.isRequestComplete.returns(true);
await rng.mock.getLastRequestId.returns(0);
await rng.mock.requestRandomNumber.returns(1, await ethers.provider.getBlockNumber());
expect(!!rng.address).to.be.true;
})
it("Deposit 10 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 - Prize Pool Test", function () {
it("Should deposit 5 ETH in Compound", async function () {
const amount = ethers.utils.parseEther("5") // 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("5"));
});
it("Should borrow 10000 USDC from Compound and deposit 1000 USDC into USDC Prize Pool without delegating (depositTo)", async function () {
const borrowAmount = ethers.utils.parseUnits("10000", 6);
const amount = ethers.utils.parseUnits("1000", 6) // 1000 USDC
const spells = [
{
connector: connectorName,
method: "borrow",
args: ["USDC-A", borrowAmount, 0, 0]
},
{
connector: ptConnectorName,
method: "depositTo",
args: [PRIZE_POOL_ADDR, amount, 0, 0]
}
]
const usdcToken = await ethers.getContractAt(abis.basic.erc20, PRIZE_POOL_TOKEN_ADDR);
const ticketToken = await ethers.getContractAt(TICKET_ABI, PRIZE_POOL_TICKET_ADDR);
// Before Spell
const balance = await usdcToken.balanceOf(dsaWallet0.address)
console.log("TokenBalanceBefore: ", balance.toString());
expect(balance, `USDC balance is 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
// ticket.balanceOf is users total tickets deposited
// ticket.getBalanceAt is users balance delegated to them. In this case using "depositTo", the deposit is not delegated to anyone so user is not eligibile to win.
const ticketBalanceOf = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAt = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfBefore: ", ticketBalanceOf.toString());
console.log("TicketBalanceAtBefore: ", ticketBalanceAt.toString());
expect(ticketBalanceOf, `PoolTogether Ticket balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
expect(ticketBalanceAt, `PoolTogether Ticket eligible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
// Run spell transaction
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
// After spell
// usdcBalance = await usdcToken.balanceOf(dsaWallet0.address);
const balanceAfter = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceAfter: ", balanceAfter.toString());
expect(balanceAfter, `Token balance equals 9000`).to.be.eq(ethers.utils.parseUnits("9000", 6));
const ticketBalanceOfAfter = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAtAfter = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfAfter: ", ticketBalanceOfAfter.toString());
console.log("TicketBalanceAtAfter: ", ticketBalanceAtAfter.toString());
expect(ticketBalanceOfAfter, `PoolTogether Ticket balance equals 1000`).to.be.eq(ethers.utils.parseUnits("1000", 6));
expect(ticketBalanceAtAfter, `PoolTogether Ticket elgible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
});
it("Should deposit USDC into USDC Prize Pool and delegate to dsaWallet0", async function () {
const amount = ethers.utils.parseUnits("1000", 6) // 1000 USDC
const spells = [
{
connector: ptConnectorName,
method: "depositToAndDelegate",
args: [PRIZE_POOL_ADDR, amount, dsaWallet0.address, 0, 0]
}
]
// Before Spell
const usdcToken = await ethers.getContractAt(abis.basic.erc20, PRIZE_POOL_TOKEN_ADDR);
const balance = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceBefore: ", balance.toString());
expect(balance, `USDC balance is 9000`).to.be.eq(ethers.utils.parseUnits("9000", 6));
const ticketToken = await ethers.getContractAt(TICKET_ABI, PRIZE_POOL_TICKET_ADDR);
const ticketBalanceOf = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAt = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfBefore: ", ticketBalanceOf.toString());
console.log("TicketBalanceAtBefore: ", ticketBalanceAt.toString());
expect(ticketBalanceOf, `PoolTogether Ticket balance equals 1000`).to.be.eq(ethers.utils.parseUnits("1000", 6));
expect(ticketBalanceAt, `PoolTogether Ticket elgible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
// Run spell transaction
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
// After spell
const balanceAfter = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceAfter: ", balanceAfter.toString());
expect(balanceAfter, `Token balance equals 8000`).to.be.eq(ethers.utils.parseUnits("8000", 6));
const ticketBalanceOfAfter = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAtAfter = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfAfter: ", ticketBalanceOfAfter.toString());
console.log("TicketBalanceAtAfter: ", ticketBalanceAtAfter.toString());
expect(ticketBalanceOfAfter, `PoolTogether Ticket balance equals 200`).to.be.eq(ethers.utils.parseUnits("2000", 6));
expect(ticketBalanceAtAfter, `PoolTogether Ticket elgible to win balance equals 200`).to.be.eq(ethers.utils.parseUnits("2000", 6));
});
it("Should withdraw 2000 USDC from USDC Prize Pool", async function () {
const amount = ethers.utils.parseUnits("2000", 6) // USDC
const setId = "83478237"
const spells = [
{
connector: ptConnectorName,
method: "withdrawFrom",
args: [PRIZE_POOL_ADDR, amount, 0, 0]
}
]
// Before Spell
let usdcToken = await ethers.getContractAt(abis.basic.erc20, PRIZE_POOL_TOKEN_ADDR)
const balance = await usdcToken.balanceOf(dsaWallet0.address)
console.log("TokenBalanceBefore: ", balance.toString());
expect(balance, `USDC balance is 8000`).to.be.eq(ethers.utils.parseUnits("8000", 6));
const ticketToken = await ethers.getContractAt(TICKET_ABI, PRIZE_POOL_TICKET_ADDR);
const ticketBalanceOf = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAt = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfBefore: ", ticketBalanceOf.toString());
console.log("TicketBalanceAtBefore: ", ticketBalanceAt.toString());
expect(ticketBalanceOf, `PoolTogether Ticket balance equals 200`).to.be.eq(ethers.utils.parseUnits("2000", 6));
expect(ticketBalanceAt, `PoolTogether Ticket elgible to win balance equals 200`).to.be.eq(ethers.utils.parseUnits("2000", 6));
// Run spell transaction
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
// After spell
const balanceAfter = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceAfter: ", balanceAfter.toString());
expect(balanceAfter, `Token balance equals 10000`).to.be.eq(ethers.utils.parseUnits("10000", 6));
const ticketBalanceOfAfter = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAtAfter = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfAfter: ", ticketBalanceOfAfter.toString());
console.log("TicketBalanceAtAfter: ", ticketBalanceAtAfter.toString());
expect(ticketBalanceAtAfter, `PoolTogether Ticket elgible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
expect(ticketBalanceOfAfter, `PoolTogether Ticket balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
});
it("Should deposit USDC into USDC Prize Pool and delegate to dsaWallet0", async function () {
const amount = ethers.utils.parseUnits("10000", 6) // 1000 USDC
const setId = "83478237"
const spells = [
{
connector: ptConnectorName,
method: "depositTo",
args: [PRIZE_POOL_ADDR, amount, 0, 0]
},
{
connector: ptConnectorName,
method: "delegate",
args: [PRIZE_POOL_TICKET_ADDR, dsaWallet0.address]
}
]
const usdcToken = await ethers.getContractAt(abis.basic.erc20, PRIZE_POOL_TOKEN_ADDR);
// Before Spell
const balance = await usdcToken.balanceOf(dsaWallet0.address)
console.log("TokenBalanceBefore: ", balance.toString());
expect(balance, `USDC balance is 10000`).to.be.eq(ethers.utils.parseUnits("10000", 6));
const ticketToken = await ethers.getContractAt(TICKET_ABI, PRIZE_POOL_TICKET_ADDR);
const ticketBalanceOf = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAt = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfBefore: ", ticketBalanceOf.toString());
console.log("TicketBalanceAtBefore: ", ticketBalanceAt.toString());
expect(ticketBalanceAt, `PoolTogether Ticket elgible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
expect(ticketBalanceOf, `PoolTogether Ticket balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
// Run spell transaction
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
// After spell
const balanceAfter = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceAfter: ", balanceAfter.toString());
expect(balanceAfter, `Token balance equals 0`).to.be.eq(ethers.utils.parseUnits("0", 6));
const ticketBalanceOfAfter = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAtAfter = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfAfter: ", ticketBalanceOfAfter.toString());
console.log("TicketBalanceAtAfter: ", ticketBalanceAtAfter.toString());
expect(ticketBalanceOfAfter, `PoolTogether Ticket balance equals 100`).to.be.eq(ethers.utils.parseUnits("10000", 6));
expect(ticketBalanceAtAfter, `PoolTogether Ticket elgible to win balance equals 1000`).to.be.eq(ethers.utils.parseUnits("10000", 6));
});
it("Trigger draw, claim winnings and withdraw winnings to dsaWallet0", async function () {
const amount = ethers.utils.parseUnits("1000", 6) // 1000 USDC
const setId = "83478237"
// Impersonate owner to set mock RNGService so we can set random number to always win
const OWNER_ADDR = "0x029Aa20Dcc15c022b1b61D420aaCf7f179A9C73f"
const owner = await impersonate([OWNER_ADDR]);
await wallet0.sendTransaction({
to: OWNER_ADDR,
value: ethers.utils.parseEther("1")
});
const drawBeaconContract = await ethers.getContractAt(DrawBeacon.abi, DRAW_BEACON_ADDR);
await drawBeaconContract.connect(owner[0]).setRngService(rng.address);
// Get next drawId
const drawCalculatorContract = await ethers.getContractAt(DRAW_CALCULATOR_ABI, DRAW_CALCULATOR_ADDR);
const prizeDistributionBufferContract = await ethers.getContractAt(PrizeDistributionBuffer.abi, PRIZE_DISTRIBUTION_BUFFER_ADDR);
const drawId = await drawBeaconContract.getNextDrawId();
// Get previous prizeDistribution
// https://v4.docs.pooltogether.com/protocol/concepts/prize-distribution
const prizeDistribution = await prizeDistributionBufferContract.getPrizeDistribution(drawId-2);
console.log(prizeDistribution);
console.log("NumberofPicks:", prizeDistribution.numberOfPicks.toString());
console.log("prize:", prizeDistribution.prize.toString());
console.log("\nCan Start Draw: ", await drawBeaconContract.canStartDraw());
console.log("Can Complete Draw: ", await drawBeaconContract.canCompleteDraw());
console.log("Beacon Period Remaining Seconds: ", (await drawBeaconContract.beaconPeriodRemainingSeconds()).toString());
console.log("Next Draw Id: ", (await drawBeaconContract.getNextDrawId()).toString());
// Make sure beacon period ended by increasing time so we can startDraw
await ethers.provider.send("evm_increaseTime", [1*24*60*60]);
await ethers.provider.send("evm_mine");
console.log("\nIncrease Time");
console.log("Can Start Draw: ", await drawBeaconContract.canStartDraw());
console.log("Can Complete Draw: ", await drawBeaconContract.canCompleteDraw());
console.log("Beacon Period Remaining Seconds: ", (await drawBeaconContract.beaconPeriodRemainingSeconds()).toString());
// Start Draw
await drawBeaconContract.startDraw();
console.log("\nStart Draw");
console.log("Can Start Draw: ", await drawBeaconContract.canStartDraw());
console.log("Can Complete Draw: ", await drawBeaconContract.canCompleteDraw());
console.log("Beacon Period Remaining Seconds: ", (await drawBeaconContract.beaconPeriodRemainingSeconds()).toString());
console.log("Next Draw Id: ", drawId);
// Complete Draw
await drawBeaconContract.completeDraw();
console.log("\nComplete Draw");
console.log("Can Start Draw: ", await drawBeaconContract.canStartDraw());
console.log("Can Complete Draw: ", await drawBeaconContract.canCompleteDraw());
console.log("Beacon Period Remaining Seconds: ", (await drawBeaconContract.beaconPeriodRemainingSeconds()).toString());
console.log("Next Draw Id: ", (await drawBeaconContract.getNextDrawId()).toString());
// Push previous prizeDistribution, then latest one for draw
await prizeDistributionBufferContract.connect(owner[0]).pushPrizeDistribution(drawId-1, prizeDistribution);
await prizeDistributionBufferContract.connect(owner[0]).pushPrizeDistribution(drawId, prizeDistribution);
// User normalized Balances, used to determine number of picks
const normalizedBalances = await drawCalculatorContract.getNormalizedBalancesForDrawIds(dsaWallet0.address,[drawId]);
console.log("\nNormalized Balances For DrawIds: ", normalizedBalances.toString());
console.log("User number of Picks", calculateNumberOfPicksForUser(prizeDistribution, ethers.BigNumber.from(normalizedBalances.toString())));
const drawBufferContract = await ethers.getContractAt(DrawBuffer.abi, DRAW_BUFFER_ADDR);
const draw = await drawBufferContract.getDraw(drawId);
console.log("\nDRAW DATA");
console.log("drawId: ", draw.drawId);
console.log("timestamp:: ", draw.timestamp.toString());
console.log("winningRandomNumber: ", draw.winningRandomNumber);
const user = {
address: dsaWallet0.address,
normalizedBalances
}
// Use draw data to determine what prizes user won
const results = batchCalculateDrawResults([prizeDistribution], [draw], user)
for (let i = 0; i < results.length; i++) {
console.log("\nDraw id: ", results[i].drawId);
for (let j = 0; j < results[i].prizes.length; j++) {
console.log("\tPrize Pick", results[i].prizes[j].pick.toString());
console.log("\t\tamount", results[i].prizes[j].amount.toString());
console.log("\t\tindex (Tier)", results[i].prizes[j].distributionIndex);
}
console.log("TotalValue: ", results[i].totalValue.toString());
}
// Prepare data for claim transaction
const claim = prepareClaims(user, results);
const spells = [
{
connector: ptConnectorName,
method: "claim",
args: [PRIZE_DISTRIBUTOR_ADDR, claim.drawIds, claim.encodedWinningPickIndices, setId]
},
{
connector: ptConnectorName,
method: "withdrawFrom",
args: [PRIZE_POOL_ADDR, amount, setId, 0]
}
]
// Before Spell
let usdcToken = await ethers.getContractAt(abis.basic.erc20, PRIZE_POOL_TOKEN_ADDR)
let balance = await usdcToken.balanceOf(dsaWallet0.address);
console.log("\nTokenBalanceBefore: ", balance.toString());
expect(balance, `USDC balance is 0`).to.be.eq(ethers.utils.parseEther("0"));
const ticketToken = await ethers.getContractAt(TICKET_ABI, PRIZE_POOL_TICKET_ADDR);
const ticketBalanceOf = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAt = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfBefore: ", ticketBalanceOf.toString());
console.log("TicketBalanceAtBefore: ", ticketBalanceAt.toString());
expect(ticketBalanceOf, `PoolTogether Ticket balance equals 0`).to.be.eq(ethers.utils.parseUnits("10000", 6));
expect(ticketBalanceAt, `PoolTogether Ticket elgible to win balance equals 0`).to.be.eq(ethers.utils.parseUnits("10000", 6));
// // Run spell transaction
const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)
const receipt = await tx.wait()
// After spell
const balanceAfter = await usdcToken.balanceOf(dsaWallet0.address);
console.log("TokenBalanceAfter: ", balanceAfter.toString());
expect(balanceAfter, `Expect USDC balance be greater than 0 since withdraw some winnings`).to.be.gt(0);
const ticketBalanceOfAfter = await ticketToken.balanceOf(dsaWallet0.address);
const ticketBalanceAtAfter = await ticketToken.getBalanceAt(dsaWallet0.address, (await ethers.provider.getBlock('latest')).timestamp);
console.log("TicketBalanceOfAfter: ", ticketBalanceOfAfter.toString());
console.log("TicketBalanceAtAfter: ", ticketBalanceAtAfter.toString());
expect(ticketBalanceOfAfter, `PoolTogether Ticket balance equals 100`).to.be.eq(ethers.utils.parseUnits("10000", 6));
expect(ticketBalanceAtAfter, `PoolTogether Ticket elgible to win balance equals 100`).to.be.eq(ethers.utils.parseUnits("10000", 6));
});
})
})