Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RageQuitWithToken #736

Open
wants to merge 1 commit into
base: arc-factory
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions contracts/schemes/RageQuitWithToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
pragma solidity ^0.5.17;

import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "../controller/Controller.sol";


/**
* @title A scheme to rage quit from a dao with token.
* by sending the dao native token to the RageQuit function the sender will get is proportional share of the dao
* rageQuitToken (DAI in most case)
*/
contract RageQuitWithToken is Initializable {
using SafeMath for uint;

event RageQuit(
address indexed _avatar,
address indexed _rageQuitter,
uint256 indexed _refund
);

Avatar public avatar;
IERC20 public rageQuitToken; //the token which is given back for rageQuit - DAI in most cases

/**
* @dev initialize
* @param _avatar the avatar this scheme referring to.
* @param _rageQuitToken the token which is given back for rageQuit - DAI in most
*/
function initialize(
Avatar _avatar,
IERC20 _rageQuitToken
)
external
initializer
{
require(_avatar != Avatar(0), "avatar cannot be zero");
avatar = _avatar;
rageQuitToken = _rageQuitToken;
}

/**
* @dev rageQuit quit from the dao.
* @param _amountToRageQuitWith amount of native token to rageQuit with.
* @return refund the refund amount
*/
function rageQuit(uint256 _amountToRageQuitWith) public returns(uint256 refund) {

uint256 totalTokenSupply = avatar.nativeToken().totalSupply();
uint256 rageQuitTokenTotalSupply = rageQuitToken.balanceOf(address(avatar));
refund = _amountToRageQuitWith.mul(rageQuitTokenTotalSupply).div(totalTokenSupply);
//this will revert if the msg.sender token balance is less than _amountToRageQuitWith.
avatar.nativeToken().burnFrom(msg.sender, _amountToRageQuitWith);
require(
Controller(
avatar.owner()).externalTokenTransfer(rageQuitToken, msg.sender, refund), "send token failed");
emit RageQuit(address(avatar), msg.sender, refund);
}

}
18 changes: 12 additions & 6 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const VoteInOrganization = artifacts.require("./VoteInOrganizationScheme.sol");
const ARCVotingMachineCallbacksMock = artifacts.require("./ARCVotingMachineCallbacksMock.sol");
const JoinAndQuit = artifacts.require("./JoinAndQuit.sol");
const FundingRequest = artifacts.require("./FundingRequest.sol");
const RageQuitWithToken = artifacts.require("./RageQuitWithToken.sol");



const MAX_UINT_256 = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
Expand Down Expand Up @@ -153,6 +155,8 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000';
registration.arcVotingMachineCallbacksMock = await ARCVotingMachineCallbacksMock.new();
registration.joinAndQuit = await JoinAndQuit.new();
registration.fundingRequest = await FundingRequest.new();
registration.rageQuitWithToken = await RageQuitWithToken.new();




Expand Down Expand Up @@ -183,6 +187,8 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000';
await implementationDirectory.setImplementation("ARCVotingMachineCallbacksMock",registration.arcVotingMachineCallbacksMock.address);
await implementationDirectory.setImplementation("JoinAndQuit",registration.joinAndQuit.address);
await implementationDirectory.setImplementation("FundingRequest",registration.fundingRequest.address);
await implementationDirectory.setImplementation("RageQuitWithToken",registration.rageQuitWithToken.address);


registration.implementationDirectory = implementationDirectory;

Expand Down Expand Up @@ -261,9 +267,9 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000';
const setupOrganizationWithArraysDAOFactory = async function (proxyAdmin,
accounts,
registration,
daoFactoryOwner,
founderToken,
founderReputation,
founders,
foundersToken,
foundersReputation,
cap=0) {
var org = new Organization();
var nativeTokenData = await new web3.eth.Contract(registration.daoToken.abi)
Expand All @@ -272,9 +278,9 @@ const SOME_ADDRESS = '0x1000000000000000000000000000000000000000';
.encodeABI();
var tx = await registration.daoFactory.forgeOrg("testOrg",
nativeTokenData,
daoFactoryOwner,
founderToken,
founderReputation,
founders,
foundersToken,
foundersReputation,
[0,0,0],
{from:proxyAdmin,gas:constants.ARC_GAS_LIMIT});
assert.equal(tx.logs.length, 5);
Expand Down
91 changes: 91 additions & 0 deletions test/ragequitwithtoken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const helpers = require("./helpers");
const RageQuitWithToken = artifacts.require("./RageQuitWithToken.sol");
const ERC20Mock = artifacts.require('./test/ERC20Mock.sol');

class RageQuitWithTokenParams {
constructor() {
}
}

const setupRageQuitWithToken = async function(
accounts,
tokenAddress,
avatarAddress,
) {
var rageQuitWithTokenParams = new RageQuitWithTokenParams();
rageQuitWithTokenParams.initdata = await new web3.eth.Contract(registration.rageQuitWithToken.abi)
.methods
.initialize(avatarAddress,tokenAddress)
.encodeABI();
return rageQuitWithTokenParams;
};
var registration;
const setup = async function (accounts) {
var testSetup = new helpers.TestSetup();
testSetup.rageQuitToken = await ERC20Mock.new(accounts[0],100000);
registration = await helpers.registerImplementation();

testSetup.proxyAdmin = accounts[0];
testSetup.reputationArray = [0,0,0];
testSetup.tokenArray = [100,200,300];
testSetup.founderAccounts = [accounts[1],accounts[2],accounts[3]];

testSetup.org = await helpers.setupOrganizationWithArraysDAOFactory(testSetup.proxyAdmin,
accounts,
registration,
testSetup.founderAccounts,
testSetup.tokenArray,
testSetup.reputationArray);


testSetup.rageQuitWithTokenParams= await setupRageQuitWithToken(
accounts,
testSetup.rageQuitToken.address,
testSetup.org.avatar.address);

var permissions = "0x00000000";
var tx = await registration.daoFactory.setSchemes(
testSetup.org.avatar.address,
[web3.utils.fromAscii("RageQuitWithToken")],
testSetup.rageQuitWithTokenParams.initdata,
[helpers.getBytesLength(testSetup.rageQuitWithTokenParams.initdata)],
[permissions],
"metaData",{from:testSetup.proxyAdmin});
testSetup.rageQuitWithToken = await RageQuitWithToken.at(tx.logs[1].args._scheme);

return testSetup;
};
contract('RageQuitWithToken', accounts => {

it("initialize", async function() {
var testSetup = await setup(accounts);
assert.equal(await testSetup.rageQuitWithToken.rageQuitToken(),testSetup.rageQuitToken.address);
assert.equal(await testSetup.rageQuitWithToken.avatar(),testSetup.org.avatar.address);
});

it("rageQuit", async function() {
var testSetup = await setup(accounts);
//send the dao some rageQuitTokens
await testSetup.rageQuitToken.transfer(testSetup.org.avatar.address,1000);
assert.equal(await testSetup.rageQuitToken.balanceOf(testSetup.org.avatar.address),1000);
//accounts 1 ragequit with all is tokens (100)
assert.equal((await testSetup.org.token.balanceOf(accounts[1])).toNumber(),100);
//give approval for burn
await testSetup.org.token.approve(testSetup.rageQuitWithToken.address,100,{from:accounts[1]});
var tx = await testSetup.rageQuitWithToken.rageQuit(100,{from:accounts[1]});
assert.equal(tx.logs.length, 1);
var expectedRefund = Math.floor((100/(100+200+300)) * 1000);
assert.equal(tx.logs[0].event, "RageQuit");
assert.equal(tx.logs[0].args._rageQuitter, accounts[1]);
assert.equal(tx.logs[0].args._refund.toNumber(),expectedRefund);
assert.equal(await testSetup.rageQuitToken.balanceOf(testSetup.org.avatar.address),1000-expectedRefund);
assert.equal(await testSetup.rageQuitToken.balanceOf(accounts[1]),expectedRefund);
try {
await testSetup.org.token.approve(testSetup.rageQuitWithToken.address,100,{from:accounts[1]});
await testSetup.rageQuitWithToken.rageQuit(100,{from:accounts[1]});
assert(false, "cannot rageQuit twice");
} catch(error) {
helpers.assertVMException(error);
}
});
});