Skip to content

Commit

Permalink
User can deposit with a token transfer (#42)
Browse files Browse the repository at this point in the history
Implemented mint tickets via simple token transfer.

Gas cost: 75000 on top of mint tickets
  • Loading branch information
asselstine committed Jun 23, 2020
1 parent 0528152 commit e082ab3
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 3 deletions.
20 changes: 20 additions & 0 deletions contracts/counterfactual-action/CounterfactualAction.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
pragma solidity ^0.6.4;

import "../periodic-prize-pool/PeriodicPrizePoolInterface.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";

contract CounterfactualAction {
function mintTickets(address payable user, PeriodicPrizePoolInterface prizePool, bytes calldata data) external {
IERC20 token = prizePool.token();
uint256 amount = token.balanceOf(address(this));
token.approve(address(prizePool), amount);
prizePool.mintTickets(user, amount, data);
selfdestruct(user);
}

function cancel(address payable user, PeriodicPrizePoolInterface prizePool) external {
IERC20 token = prizePool.token();
token.transfer(user, token.balanceOf(address(this)));
selfdestruct(user);
}
}
43 changes: 43 additions & 0 deletions contracts/counterfactual-action/CounterfactualActionFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma solidity ^0.6.4;

import "@openzeppelin/contracts-ethereum-package/contracts/utils/Create2.sol";
import "./CounterfactualAction.sol";
import "../utils/MinimalProxyLibrary.sol";

contract CounterfactualActionFactory {

CounterfactualAction depositor;
PeriodicPrizePoolInterface prizePool;

function initialize(PeriodicPrizePoolInterface _prizePool) external {
require(address(_prizePool) != address(0), "CounterfactualActionFactory/prize-pool-not-zero");
depositor = new CounterfactualAction();
prizePool = _prizePool;
}

function calculateAddress(address payable user) external view returns (address) {
return Create2.computeAddress(salt(user), keccak256(MinimalProxyLibrary.minimalProxy(address(depositor))));
}

function mintTickets(address payable user, bytes calldata data) external {
CounterfactualAction d = newAction(user);
d.mintTickets(user, prizePool, data);
}

function cancel(address payable user) external {
CounterfactualAction d = newAction(user);
d.cancel(user, prizePool);
}

function newAction(address payable user) internal returns (CounterfactualAction) {
return CounterfactualAction(Create2.deploy(0, salt(user), MinimalProxyLibrary.minimalProxy(address(depositor))));
}

function salt(address payable user) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(user));
}

function code() external view returns (bytes memory) {
return MinimalProxyLibrary.minimalProxy(address(depositor));
}
}
1 change: 0 additions & 1 deletion contracts/periodic-prize-pool/InterestTracker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ abstract contract InterestTracker is AbstractYieldService {

emit InterestCaptured(msg.sender, interest);


return interest;
}

Expand Down
3 changes: 1 addition & 2 deletions contracts/periodic-prize-pool/PeriodicPrizePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,9 @@ abstract contract PeriodicPrizePool is Timelock, BaseRelayRecipient, ReentrancyG
bytes calldata data,
bytes calldata operatorData
)
external nonReentrant returns (uint256)
external nonReentrant onlyOperator(__ticket, from) returns (uint256)
{
address sender = _msgSender();


uint256 userInterestRatioMantissa = _ticketInterestRatioMantissa(from);
uint256 exitFee = calculateExitFee(tickets, userInterestRatioMantissa);
Expand Down
22 changes: 22 additions & 0 deletions contracts/utils/MinimalProxyLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pragma solidity ^0.6.4;

// solium-disable security/no-inline-assembly
library MinimalProxyLibrary {
function minimalProxy(address _logic) internal pure returns (bytes memory clone) {
// Adapted from https://github.com/optionality/clone-factory/blob/32782f82dfc5a00d103a7e61a17a5dedbd1e8e9d/contracts/CloneFactory.sol
bytes20 targetBytes = bytes20(_logic);
assembly {
let size := 0x37
// allocate output byte array - this could also be done without assembly
// by using clone = new bytes(size)
clone := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(clone, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(clone, size)
mstore(add(clone, 0x20), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x34), targetBytes)
mstore(add(clone, 0x48), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
}
}
}
64 changes: 64 additions & 0 deletions test/CounterfactualActionFactory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { expect } = require('chai')
const CounterfactualActionFactory = require('../build/CounterfactualActionFactory.json')
const PeriodicPrizePoolInterface = require('../build/PeriodicPrizePoolInterface.json')
const IERC20 = require('../build/IERC20.json')
const { ethers } = require('./helpers/ethers')
const buidler = require('./helpers/buidler')
const { deployContract, deployMockContract } = require('ethereum-waffle')

const toWei = ethers.utils.parseEther

describe('CounterfactualActionFactory', () => {

let wallet, wallet2

let token, prizePool

let provider

beforeEach(async () => {
[wallet, wallet2] = await buidler.ethers.getSigners()
provider = buidler.ethers.provider

token = await deployMockContract(wallet, IERC20.abi)
prizePool = await deployMockContract(wallet, PeriodicPrizePoolInterface.abi)
await prizePool.mock.token.returns(token.address)

factory = await deployContract(wallet, CounterfactualActionFactory, [])
await factory.initialize(prizePool.address)
})

describe('mintTickets', () => {
it('should allow mintTickets from anyone', async () => {
let address = await factory.calculateAddress(wallet._address)
let depositAmount = toWei('100')

await token.mock.balanceOf.withArgs(address).returns(depositAmount)
await token.mock.approve.withArgs(prizePool.address, depositAmount).returns(true)
await prizePool.mock.mintTickets.withArgs(wallet._address, depositAmount, []).returns()

await factory.mintTickets(wallet._address, [])
})
})

describe('cancel', () => {
it('should someone to withdraw their funds', async () => {
let address = await factory.calculateAddress(wallet._address)
let depositAmount = toWei('100')

await token.mock.balanceOf.withArgs(address).returns(depositAmount)
await token.mock.transfer.withArgs(wallet._address, depositAmount).returns(true)

await factory.cancel(wallet._address, [])
})
})

describe('code()', () => {
it("should show the same code", async () => {
let code = await factory.code()

expect(code.length).to.equal(112)
})
})

})

0 comments on commit e082ab3

Please sign in to comment.