Skip to content

Commit

Permalink
add enableAndSetClaims function to Pool (#1187)
Browse files Browse the repository at this point in the history
* add enableAndSetClaims function to Pool

* emit UseClaim for Pool
  • Loading branch information
dmosites authored Sep 17, 2023
1 parent 453ce7a commit 6f9a0ee
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 49 deletions.
35 changes: 30 additions & 5 deletions source/pool/contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,32 @@ contract Pool is IPool, Ownable2Step {
emit Enable(_tree, _root);
}

/**
* @notice Set previous claims for migrations
* @param _tree bytes32
* @param _root bytes32
* @param _accounts address[]
* @dev Only owner
*/
function enableAndSetClaimed(
bytes32 _tree,
bytes32 _root,
address[] memory _accounts
) external override multiAdmin {
// Enable the tree if not yet enabled
if (rootsByTree[_tree] == 0) {
rootsByTree[_tree] = _root;
emit Enable(_tree, _root);
}
// Iterate and set as claimed if not yet claimed
for (uint256 i = 0; i < _accounts.length; i++) {
if (claimed[_tree][_accounts[i]] == false) {
claimed[_tree][_accounts[i]] = true;
emit UseClaim(_accounts[i], _tree);
}
}
}

/**
* @notice Withdraw tokens using claims
* @param _claims Claim[] a set of claims
Expand All @@ -141,22 +167,21 @@ contract Pool is IPool, Ownable2Step {

Claim memory _claim;
bytes32 _root;
bytes32[] memory _treeList = new bytes32[](_claims.length);
uint256 _totalValue = 0;

// Iterate through claims to determine total value
for (uint256 i = 0; i < _claims.length; i++) {
_claim = _claims[i];
_root = rootsByTree[_claim.tree];

if (_root == 0) revert TreeDisabled(_claim.tree);
if (claimed[_claim.tree][msg.sender]) revert AlreadyClaimed();
if (_root == 0) revert TreeNotEnabled(_claim.tree);
if (claimed[_claim.tree][msg.sender]) revert ClaimAlreadyUsed();
if (!verify(msg.sender, _root, _claim.value, _claim.proof))
revert ProofInvalid(_claim.tree, _root);

_totalValue = _totalValue + _claim.value;
claimed[_claim.tree][msg.sender] = true;
_treeList[i] = _claim.tree;
emit UseClaim(msg.sender, _claim.tree);
}

// Determine withdrawable amount given total value
Expand All @@ -165,7 +190,7 @@ contract Pool is IPool, Ownable2Step {

// Transfer withdrawable amount to recipient
IERC20(_token).safeTransfer(_recipient, _amount);
emit Withdraw(_treeList, msg.sender, _token, _amount);
emit Withdraw(msg.sender, _recipient, _token, _amount);
}

/**
Expand Down
28 changes: 18 additions & 10 deletions source/pool/contracts/interfaces/IPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,28 @@ interface IPool {
event SetMax(uint256 max);
event SetScale(uint256 scale);
event UnsetAdmin(address admin);

event UseClaim(address account, bytes32 tree);
event Withdraw(
bytes32[] trees,
address account,
address recipient,
address token,
uint256 amount
);

error AddressInvalid(address);
error AdminNotSet(address);
error AlreadyClaimed();
error AmountInsufficient(uint256);
error ClaimAlreadyUsed();
error ClaimsNotProvided();
error MaxTooHigh(uint256);
error ProofInvalid(bytes32, bytes32);
error TreeDisabled(bytes32);
error TreeNotEnabled(bytes32);
error ScaleTooHigh(uint256);
error Unauthorized();

function drainTo(address[] calldata tokens, address dest) external;

function setScale(uint256 _scale) external;

function setMax(uint256 _max) external;
Expand All @@ -41,15 +45,14 @@ interface IPool {

function unsetAdmin(address _admin) external;

function drainTo(address[] calldata tokens, address dest) external;

function getStatus(
address _account,
bytes32[] calldata _trees
) external returns (bool[] memory statuses);

function enable(bytes32 _tree, bytes32 _root) external;

function enableAndSetClaimed(
bytes32 _tree,
bytes32 _root,
address[] memory _accounts
) external;

function withdraw(
Claim[] memory claims,
address token,
Expand All @@ -68,4 +71,9 @@ interface IPool {
uint256 score,
bytes32[] memory proof
) external pure returns (bool valid);

function getStatus(
address _account,
bytes32[] calldata _trees
) external returns (bool[] memory statuses);
}
101 changes: 67 additions & 34 deletions source/pool/test/Pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ describe('Pool Unit', () => {
).deploy(CLAIM_SCALE, CLAIM_MAX)
await pool.deployed()

await pool.setAdmin(deployer.address)

tree = generateTreeFromData({
[alice.address]: ALICE_SCORE,
[bob.address]: BOB_SCORE,
Expand All @@ -86,7 +84,7 @@ describe('Pool Unit', () => {
})
})

describe('Test constructor', async () => {
describe('constructor', async () => {
it('constructor sets values', async () => {
const storedScale = await pool.scale()
const storedMax = await pool.max()
Expand Down Expand Up @@ -118,10 +116,11 @@ describe('Pool Unit', () => {
})
})

describe('Test admin functions', async () => {
describe('admin functions', async () => {
it('enable a claim for a merkle root suceeds', async () => {
const root = getRoot(tree)
expect(await pool.connect(deployer).enable(TREE, root)).to.emit(
await pool.setAdmin(alice.address)
expect(await pool.connect(alice).enable(TREE, root)).to.emit(
pool,
'Enable'
)
Expand All @@ -136,16 +135,17 @@ describe('Pool Unit', () => {

it('enable a with the same tree overrwrites the previous root', async () => {
const root = getRoot(tree)
await pool.connect(deployer).enable(TREE, root)
await pool.setAdmin(alice.address)
await pool.connect(alice).enable(TREE, root)
const newRoot = getRoot(newTree)
await expect(pool.connect(deployer).enable(TREE, newRoot)).to.be.emit(
await expect(pool.connect(alice).enable(TREE, newRoot)).to.be.emit(
pool,
`Enable`
)
})
})

describe('Test withdraw', async () => {
describe('withdraw', async () => {
it('withdraw success', async () => {
await feeToken.mock.balanceOf.returns('100000')
await feeToken.mock.transfer.returns(true)
Expand Down Expand Up @@ -213,7 +213,7 @@ describe('Pool Unit', () => {
bob.address
)
)
.to.be.revertedWith(`TreeDisabled`)
.to.be.revertedWith(`TreeNotEnabled`)
.withArgs(TREE)

const isClaimed = await pool.claimed(TREE, bob.address)
Expand Down Expand Up @@ -257,7 +257,7 @@ describe('Pool Unit', () => {
WITHDRAW_MINIMUM,
bob.address
)
).to.be.revertedWith(`AlreadyClaimed`)
).to.be.revertedWith(`ClaimAlreadyUsed`)

const isClaimed = await pool.claimed(TREE, bob.address)
expect(isClaimed).to.equal(true)
Expand Down Expand Up @@ -356,17 +356,17 @@ describe('Pool Unit', () => {
})
})

describe('Test Calculate', async () => {
it('Test calculation input and output', async () => {
describe('Calculate', async () => {
it('calculation input and output', async () => {
await feeToken.mock.balanceOf.returns('100000')

const amount = await pool.calculate(ALICE_SCORE, feeToken.address)
expect(amount).to.equal('495')
})
})

describe('Test Verify', async () => {
it('Test verification is valid', async () => {
describe('Verify', async () => {
it('verification is valid', async () => {
await pool.setAdmin(alice.address)
const root = getRoot(tree)
await pool.connect(alice).enable(TREE, root)
Expand All @@ -376,7 +376,7 @@ describe('Pool Unit', () => {
expect(isValid).to.be.equal(true)
})

it('Test verification is invalid with wrong participant', async () => {
it('verification is invalid with wrong participant', async () => {
await pool.setAdmin(alice.address)
const root = getRoot(tree)
await pool.connect(alice).enable(TREE, root)
Expand All @@ -386,7 +386,7 @@ describe('Pool Unit', () => {
expect(isValid).to.be.equal(false)
})

it('Test verification is invalid with wrong scroe', async () => {
it('verification is invalid with wrong scroe', async () => {
await pool.setAdmin(alice.address)
const root = getRoot(tree)
await pool.connect(alice).enable(TREE, root)
Expand All @@ -397,90 +397,123 @@ describe('Pool Unit', () => {
})
})

describe('Test setting Scale', async () => {
it('Test setScale is successful', async () => {
describe('setting Scale', async () => {
it('setScale is successful', async () => {
const scale = 77
await expect(pool.setScale(scale)).to.emit(pool, 'SetScale')
expect(await pool.scale()).to.be.equal(`${scale}`)
})

it('Test setScale reverts when not owner', async () => {
it('setScale reverts when not owner', async () => {
const scale = 77
await expect(pool.connect(alice).setScale(scale)).to.be.revertedWith(
'Ownable: caller is not the owner'
)
})

it('Test setScale reverts', async () => {
it('setScale reverts', async () => {
const scale = 1000
await expect(pool.setScale(scale))
.to.be.revertedWith(`ScaleTooHigh`)
.withArgs(scale)
})
})

describe('Test setting Max', async () => {
it('Test setMax is successful', async () => {
describe('setting Max', async () => {
it('setMax is successful', async () => {
const max = 10
await expect(pool.setMax(max)).to.emit(pool, 'SetMax')
expect(await pool.scale()).to.be.equal(`${max}`)
})

it('Test setMax reverts when not owner', async () => {
it('setMax reverts when not owner', async () => {
const max = 10
await expect(pool.connect(alice).setMax(max)).to.be.revertedWith(
'Ownable: caller is not the owner'
)
})

it('Test setMax reverts', async () => {
it('setMax reverts', async () => {
const max = 101
await expect(pool.setMax(max))
.to.be.revertedWith(`MaxTooHigh`)
.withArgs(max)
})
})

describe('Test setting admin', async () => {
it('Test setAdmin is successful', async () => {
describe('setting admin', async () => {
it('setAdmin is successful', async () => {
await expect(pool.setAdmin(alice.address)).to.emit(pool, 'SetAdmin')
expect(await pool.admins(alice.address)).to.be.equal(true)
})

it('Test setAdmin reverts', async () => {
it('setAdmin reverts', async () => {
await expect(
pool.connect(alice).setAdmin(alice.address)
).to.be.revertedWith('Ownable: caller is not the owner')
})

it('Test setAdmin reverts with zero address', async () => {
it('setAdmin reverts with zero address', async () => {
await expect(pool.connect(deployer).setAdmin(ADDRESS_ZERO))
.to.be.revertedWith(`AddressInvalid`)
.withArgs(ADDRESS_ZERO)
})

it('Test unsetAdmin is successful', async () => {
it('unsetAdmin is successful', async () => {
await expect(pool.setAdmin(alice.address)).to.emit(pool, 'SetAdmin')
await expect(pool.unsetAdmin(alice.address)).to.emit(pool, 'UnsetAdmin')
expect(await pool.admins(alice.address)).to.be.equal(false)
})

it('Test unsetAdmin reverts', async () => {
it('unsetAdmin reverts', async () => {
await expect(pool.setAdmin(alice.address)).to.emit(pool, 'SetAdmin')
await expect(
pool.connect(alice).unsetAdmin(alice.address)
).to.be.revertedWith('Ownable: caller is not the owner')
})

it('Test unsetAdmin executed by non-admin reverts', async () => {
it('unsetAdmin executed by non-admin reverts', async () => {
await expect(pool.connect(deployer).unsetAdmin(alice.address))
.to.be.revertedWith(`AdminNotSet`)
.withArgs(alice.address)
})
})

describe('Test drain to', async () => {
it('Test drain to is successful', async () => {
describe('migration functions', async () => {
it('set claimed as owner is successful', async () => {
await feeToken.mock.balanceOf.returns('100000')
await await pool.connect(deployer).setAdmin(alice.address)

const root = getRoot(tree)
await pool.connect(alice).enableAndSetClaimed(TREE, root, [bob.address])

const proof = getProof(tree, soliditySha3(bob.address, BOB_SCORE))
await expect(
pool.connect(bob).withdraw(
[
{
tree: TREE,
value: BOB_SCORE,
proof,
},
],
feeToken.address,
WITHDRAW_MINIMUM,
bob.address
)
).to.be.revertedWith(`ClaimAlreadyUsed`)
})

it('set claimed with non-owner reverts', async () => {
await feeToken.mock.balanceOf.returns('100000')

const root = getRoot(tree)
await expect(
pool.connect(bob).enableAndSetClaimed(TREE, root, [bob.address])
).to.be.revertedWith('Unauthorized')
})

it('drain to is successful', async () => {
await feeToken.mock.balanceOf.returns('10')
await feeToken.mock.transfer.returns(true)
await feeToken2.mock.balanceOf.returns('10')
Expand All @@ -493,7 +526,7 @@ describe('Pool Unit', () => {
).to.emit(pool, 'DrainTo')
})

it('Test drain to is only callable by owner', async () => {
it('drain to is only callable by owner', async () => {
await expect(
pool
.connect(alice)
Expand Down

0 comments on commit 6f9a0ee

Please sign in to comment.