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

Reduce rewards every X blocks #3

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
54 changes: 44 additions & 10 deletions contracts/Minter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ contract SPSMinter {
struct Pool {
address receiver;
uint256 amountPerBlock;
uint256 reductionBlocks;
uint256 reductionPercentage;
uint256 lastUpdate;
}
/// @notice Array to store all pools
Pool[] public pools;

/// @notice Emitted when mint() is called
event Mint(address indexed receiver, uint256 amount);
/// @notice Emitted when pool is added
event PoolAdded(address indexed newReceiver, uint256 newAmount);
event PoolAdded(address indexed newReceiver, uint256 newAmount, uint256 newReductionBlocks, uint256 newReductionPercentage, uint256 newLastUpdate);
apbendi marked this conversation as resolved.
Show resolved Hide resolved
/// @notice Emitted when pool is updated
event PoolUpdated(uint256 index, address indexed newReceiver, uint256 newAmount);
event PoolUpdated(uint256 index, address indexed newReceiver, uint256 newAmount, uint256 newReductionBlocks, uint256 newReductionPercentage, uint256 newLastUpdate);
/// @notice Emitted when pool is removed
event PoolRemoved(uint256 index, address indexed receiver, uint256 amount);
/// @notice Emitted when admin address is updated
Expand Down Expand Up @@ -76,7 +79,7 @@ contract SPSMinter {
function mint() public {
require(totalMinted < cap, "SPSMinter: Cap reached");
require(block.number > lastMintBlock, "SPSMinter: Mint block not yet reached");

updateEmissions(0, true);

uint256 mintDifference;
unchecked {
Expand All @@ -85,7 +88,8 @@ contract SPSMinter {

lastMintBlock = block.number;

for (uint256 i = 0; i < pools.length; i++){
uint256 poolsLength = pools.length;
for (uint256 i = 0; i < poolsLength;){
uint256 amount = pools[i].amountPerBlock * mintDifference;

if(totalMinted + amount >= cap){
Expand All @@ -100,32 +104,62 @@ contract SPSMinter {
token.mint(pools[i].receiver, amount);

emit Mint(pools[i].receiver, amount);

unchecked { ++i; }
}
}

/**
* @notice Add new pool, can be called by admin
* @param newReceiver Address of the receiver
* @param newAmount Amount of tokens per block
* @param newReductionBlocks Number of blocks between emission reduction
* @param newReductionPercentage Percentage to reduce emission
*/
function addPool(address newReceiver, uint256 newAmount) external onlyAdmin {
function addPool(address newReceiver, uint256 newAmount, uint256 newReductionBlocks, uint256 newReductionPercentage) external onlyAdmin {
require(pools.length < poolsCap, 'SPSMinter: Pools cap reached');
require(newAmount <= maxToPoolPerBlock, 'SPSMinter: Maximum amount per block reached');
pools.push(Pool(newReceiver, newAmount));
emit PoolAdded(newReceiver, newAmount);
pools.push(Pool(newReceiver, newAmount, newReductionBlocks, newReductionPercentage, block.number));
emit PoolAdded(newReceiver, newAmount, newReductionBlocks, newReductionPercentage, block.number);
}

/**
* @notice Update pool, can be called by admin
* @param index Index in the array of the pool
* @param newReceiver Address of the receiver
* @param newAmount Amount of tokens per block
* @param newReductionBlocks Number of blocks between emission reduction
* @param newReductionPercentage Percentage to reduce emission
apbendi marked this conversation as resolved.
Show resolved Hide resolved
*/
function updatePool(uint256 index, address newReceiver, uint256 newAmount) external onlyAdmin {
function updatePool(uint256 index, address newReceiver, uint256 newAmount, uint256 newReductionBlocks, uint256 newReductionPercentage) external onlyAdmin {
require(newAmount <= maxToPoolPerBlock, 'SPSMinter: Maximum amount per block reached');
mint();
pools[index] = Pool(newReceiver, newAmount);
emit PoolUpdated(index, newReceiver, newAmount);
pools[index] = Pool(newReceiver, newAmount, newReductionBlocks, newReductionPercentage, block.number);
apbendi marked this conversation as resolved.
Show resolved Hide resolved
emit PoolUpdated(index, newReceiver, newAmount, newReductionBlocks, newReductionPercentage, block.number);
}

/**
* @notice Update emissions for pools
* @param index Index in the array of the pool
* @param updateAll If true, all pools will be updated
*/
function updateEmissions(uint256 index, bool updateAll) public {
apbendi marked this conversation as resolved.
Show resolved Hide resolved
if (updateAll){
uint256 length = pools.length;
for (uint256 i = 0; i < length;){
if (block.number - pools[i].lastUpdate > pools[i].reductionBlocks){
pools[i].amountPerBlock = pools[i].amountPerBlock / 100 * (100 - pools[i].reductionPercentage);
apbendi marked this conversation as resolved.
Show resolved Hide resolved
pools[i].lastUpdate = block.number;
}

unchecked { ++i; }
}
} else {
if (block.number - pools[index].lastUpdate > pools[index].reductionBlocks){
pools[index].amountPerBlock = pools[index].amountPerBlock / 10000 * (10000 - (pools[index].reductionPercentage * 100));
apbendi marked this conversation as resolved.
Show resolved Hide resolved
pools[index].lastUpdate = block.number;
}
}
}

/**
Expand Down
152 changes: 141 additions & 11 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ describe("Minter", async function () {
it("should add pool", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1);
let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

let getPool = await minter.getPool(0);
let getPoolLength = await minter.getPoolLength();

expect(getPool.receiver).to.equal(accounts[0].address)
expect(getPool.amountPerBlock.toNumber()).to.equal(1)
expect(getPool.amountPerBlock.toNumber()).to.equal(1000000)
expect(getPoolLength.toNumber()).to.equal(1)
});

Expand All @@ -50,17 +50,34 @@ describe("Minter", async function () {
});

it("should update pool", async function () {
let update = await minter.updatePool(0, accounts[0].address, 10);
let update = await minter.updatePool(0, accounts[0].address, 10000000, 50, 1);
await update.wait();

let getPool = await minter.getPool(0);
let getPoolLength = await minter.getPoolLength();

expect(getPool.receiver).to.equal(accounts[0].address)
expect(getPool.amountPerBlock.toNumber()).to.equal(10)
expect(getPool.amountPerBlock.toNumber()).to.equal(10000000)
expect(getPoolLength.toNumber()).to.equal(1)
});

it("should mint tokens after reduction", async function () {
let startLastMintBlock = await minter.lastMintBlock()
await mineBlocks(60)

let balanceBefore = await testToken.balanceOf(accounts[0].address)

let mint = await minter.mint();
await mint.wait();

let endLastMintBlock = await minter.lastMintBlock()

let balance = await testToken.balanceOf(accounts[0].address)
let getPool = await minter.getPool(0);

expect(balance.toNumber() - balanceBefore.toNumber()).to.equal(getPool.amountPerBlock.toNumber() * (endLastMintBlock - startLastMintBlock))
});

it("should fail when removing invalid pool if there are pools", async function () {
try {
let remove = await minter.removePool(1);
Expand Down Expand Up @@ -139,7 +156,7 @@ describe("Minter", async function () {

let lastBlock = await minter.lastMintBlock()

await minter.addPool(accounts[0].address, '1000');
await minter.addPool(accounts[0].address, '1000000', 50, 1);
await minter.mint(); //mine for the first time
await minter.mint(); //should mine 0 tokens, since it's in the same block

Expand All @@ -149,16 +166,16 @@ describe("Minter", async function () {

await network.provider.send("evm_setAutomine", [true]);

expect(getSupply.toString()).to.equal("1000")
expect(getSupply.toString()).to.equal("1000000")
});

it("should add multiple pools", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1);
let add = await minter.addPool(accounts[0].address, 10000000, 50, 1);
await add.wait();

let add_2 = await minter.addPool(accounts[1].address, 5);
let add_2 = await minter.addPool(accounts[1].address, 50000000, 50, 1);
await add_2.wait();

let getPool_1 = await minter.getPool(0);
Expand All @@ -167,9 +184,9 @@ describe("Minter", async function () {
let getPoolLength = await minter.getPoolLength();

expect(getPool_1.receiver).to.equal(accounts[0].address)
expect(getPool_1.amountPerBlock.toNumber()).to.equal(1)
expect(getPool_1.amountPerBlock.toNumber()).to.equal(10000000)
expect(getPool_2.receiver).to.equal(accounts[1].address)
expect(getPool_2.amountPerBlock.toNumber()).to.equal(5)
expect(getPool_2.amountPerBlock.toNumber()).to.equal(50000000)
expect(getPoolLength.toNumber()).to.equal(2)
});

Expand All @@ -193,7 +210,7 @@ describe("Minter", async function () {
});

it("should add pool with 0 emission and mint 0 tokens to it after 10 blocks", async function () {
let add = await minter.addPool(accounts[2].address, 0);
let add = await minter.addPool(accounts[2].address, 0, 50000000, 1);
await add.wait();

let getPool = await minter.getPool(2);
Expand All @@ -213,6 +230,119 @@ describe("Minter", async function () {

expect(balance.toNumber()).to.equal(0)
});

it("should update emission of one pool by index", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, false)).wait()

let pool = await minter.getPool(0)

expect(pool.amountPerBlock.toNumber()).to.equal(1000000 * 0.99)
});

it("should not update emission of one pool by index if it's not the time yet", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, false)).wait()
await (await minter.updateEmissions(0, false)).wait()
await (await minter.updateEmissions(0, false)).wait()

let pool = await minter.getPool(0)

expect(pool.amountPerBlock.toNumber()).to.equal(1000000 * 0.99)
});

it("should update emission of one pool by index if enough time has passed", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, false)).wait()
await mineBlocks(60);
await (await minter.updateEmissions(0, false)).wait()

let pool = await minter.getPool(0)

expect(pool.amountPerBlock.toNumber()).to.equal(980100)
});

it("should update emission of all pools", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

let add2 = await minter.addPool(accounts[0].address, 2000000, 50, 1);
await add2.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, true)).wait()

let pool = await minter.getPool(0)
let pool2 = await minter.getPool(1)

expect(pool.amountPerBlock.toNumber()).to.equal(1000000 * 0.99)
expect(pool2.amountPerBlock.toNumber()).to.equal(2000000 * 0.99)
});

it("should not update emission of all pools if it's not the time yet", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

let add2 = await minter.addPool(accounts[0].address, 2000000, 50, 1);
await add2.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, true)).wait()
await (await minter.updateEmissions(0, true)).wait()
await (await minter.updateEmissions(0, true)).wait()

let pool = await minter.getPool(0)
let pool2 = await minter.getPool(1)

expect(pool.amountPerBlock.toNumber()).to.equal(1000000 * 0.99)
expect(pool2.amountPerBlock.toNumber()).to.equal(2000000 * 0.99)
});

it("should update emission of all pools if enough time has passed", async function () {
await init()

let add = await minter.addPool(accounts[0].address, 1000000, 50, 1);
await add.wait();

let add2 = await minter.addPool(accounts[0].address, 2000000, 50, 1);
await add2.wait();

await mineBlocks(60);

await (await minter.updateEmissions(0, true)).wait()
await mineBlocks(60);
await (await minter.updateEmissions(0, true)).wait()

let pool = await minter.getPool(0)
let pool2 = await minter.getPool(1)

expect(pool.amountPerBlock.toNumber()).to.equal((1000000 * 0.99) * 0.99)
expect(pool2.amountPerBlock.toNumber()).to.equal((2000000 * 0.99) * 0.99)
});
});


Expand Down