Skip to content

Add burn and burnFrom #2

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

Merged
merged 2 commits into from
Mar 30, 2021
Merged
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
43 changes: 43 additions & 0 deletions contracts/Forth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,49 @@ contract Forth {
_moveDelegates(address(0), delegates[dst], amount);
}

/**
* @notice Destroys `amount` tokens from the caller
* @param rawAmount The number of tokens to burn
*/
function burn(uint256 rawAmount) external {
uint96 amount = safe96(rawAmount, "Forth::burn: rawAmount exceeds 96 bits");
_burn(msg.sender, amount);
}

/**
* @notice Destroys `amount` tokens from `account`, deducting from the caller's allowance
* @param account The address of the account to burn from
* @param rawAmount The number of tokens to burn
*/
function burnFrom(address account, uint256 rawAmount) external {
uint96 amount = safe96(rawAmount, "Forth::burnFrom: rawAmount exceeds 96 bits");

uint96 decreasedAllowance =
sub96(allowances[account][msg.sender], amount, "Forth::burnFrom: amount exceeds allowance");
allowances[account][msg.sender] = decreasedAllowance;
emit Approval(account, msg.sender, decreasedAllowance);

_burn(account, amount);
}

/**
* @notice Destroys `amount` tokens from `account`, reducing the total supply
* @param account The address of the account to burn from
* @param amount The number of tokens to burn
*/
function _burn(address account, uint96 amount) internal {
require(account != address(0), "Forth::_burn: burn from the zero address");

uint96 supply = safe96(totalSupply, "Forth::_burn: old supply exceeds 96 bits");
totalSupply = sub96(supply, amount, "Forth::_burn: amount exceeds totalSupply");

balances[account] = sub96(balances[account], amount, "Forth::_burn: amount exceeds balance");
emit Transfer(account, address(0), amount);

// move delegates
_moveDelegates(delegates[account], address(0), amount);
}

/**
* @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
* @param account The address of the account holding the funds
Expand Down
72 changes: 72 additions & 0 deletions test/Forth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,76 @@ describe('Forth', () => {
'Forth::mint: exceeded mint cap'
)
})

it('burn', async () => {
const { timestamp: now } = await provider.getBlock('latest')
const forth = await deployContract(wallet, Forth, [wallet.address, wallet.address, now + 60 * 60])
const supply = await forth.totalSupply()

// burn 0
let balanceBefore = await forth.balanceOf(wallet.address)
await forth.connect(wallet).burn(0)
expect(await forth.balanceOf(wallet.address)).to.be.eq(balanceBefore)
expect(await forth.totalSupply()).to.be.eq(supply)

// burn non-zero
await forth.connect(wallet).burn(1)
expect(await forth.balanceOf(wallet.address)).to.be.eq(balanceBefore.sub(1))
expect(await forth.totalSupply()).to.be.eq(supply.sub(1))

// burn > totalSupply
await expect(forth.connect(wallet).burn(supply + 2)).to.be.revertedWith('Forth::_burn: amount exceeds totalSupply')

// burn > balance
await forth.connect(wallet).transfer(other0.address, 100)
balanceBefore = await forth.balanceOf(wallet.address)
await expect(forth.connect(wallet).burn(balanceBefore.add(1))).to.be.revertedWith(
'Forth::_burn: amount exceeds balance'
)
})

it('burnFrom', async () => {
const { timestamp: now } = await provider.getBlock('latest')
const forth = await deployContract(wallet, Forth, [wallet.address, wallet.address, now + 60 * 60])
const supply = await forth.totalSupply()

// burn 0
let balanceBefore = await forth.balanceOf(wallet.address)
await forth.connect(other0).burnFrom(wallet.address, 0)
expect(await forth.balanceOf(wallet.address)).to.be.eq(balanceBefore)
expect(await forth.totalSupply()).to.be.eq(supply)

// burn non-zero
await forth.connect(wallet).approve(other0.address, 100)
await forth.connect(other0).burnFrom(wallet.address, 1)
expect(await forth.balanceOf(wallet.address)).to.be.eq(balanceBefore.sub(1))
expect(await forth.totalSupply()).to.be.eq(supply.sub(1))

// burn > approval
balanceBefore = await forth.balanceOf(wallet.address)
await forth.connect(wallet).approve(other0.address, 100)
await expect(forth.connect(other0).burnFrom(wallet.address, 101)).to.be.revertedWith(
'Forth::burnFrom: amount exceeds allowance'
)

// burn > totalSupply
balanceBefore = await forth.balanceOf(wallet.address)
await forth.connect(wallet).approve(other0.address, balanceBefore.add(1))
await expect(forth.connect(other0).burnFrom(wallet.address, balanceBefore.add(1))).to.be.revertedWith(
'Forth::_burn: amount exceeds totalSupply'
)

// burn > balance
await forth.connect(wallet).transfer(other0.address, 100)
balanceBefore = await forth.balanceOf(wallet.address)
await forth.connect(wallet).approve(other0.address, balanceBefore.add(1))
await expect(forth.connect(other0).burnFrom(wallet.address, balanceBefore.add(1))).to.be.revertedWith(
'Forth::_burn: amount exceeds balance'
)

// Zero Address
await expect(forth.connect(wallet).burnFrom('0x0000000000000000000000000000000000000000', 0)).to.be.revertedWith(
'Forth::_burn: burn from the zero address'
)
})
})