Skip to content
This repository has been archived by the owner on May 22, 2023. It is now read-only.

Commit

Permalink
Merge pull request #142 from keep-network/bond-seize
Browse files Browse the repository at this point in the history
Seize bond

The holder of a bond can seize some or all of a locked bond by calling 
seizeBond(operator, reference, amount). The specified amount must be 
equal or less than the bond at lockedBonds[operator, holder, reference]. 
The amount is subtracted from the bond and transferred to the holder.
  • Loading branch information
pdyraga authored Jan 22, 2020
2 parents 9923cda + fc1c72c commit ccdb756
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 4 deletions.
21 changes: 20 additions & 1 deletion solidity/contracts/KeepBonding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ contract KeepBonding {
// Unassigned ether values deposited by operators.
mapping(address => uint256) internal unbondedValue;
// References to created bonds. Bond identifier is built from operator's
// address, holder's address and reference assigned on bond creation.
// address, holder's address and reference ID assigned on bond creation.
mapping(bytes32 => uint256) internal lockedBonds;

/// @notice Returns value of ether available for bonding for the operator.
Expand Down Expand Up @@ -76,6 +76,25 @@ contract KeepBonding {
lockedBonds[bondID] = 0;
}

/// @notice Seizes the bond by moving some or all of a locked bond to holder's
/// account.
/// @dev Function requires that a caller is the holder of the bond which is
/// being seized.
/// @param operator Address of the bonded operator.
/// @param referenceID Reference ID of the bond.
/// @param amount Amount to be seized.
function seizeBond(address operator, uint256 referenceID, uint256 amount) public {
require(amount > 0, "Requested amount should be greater than zero");

address payable holder = msg.sender;
bytes32 bondID = keccak256(abi.encodePacked(operator, holder, referenceID));

require(lockedBonds[bondID] >= amount, "Requested amount is greater than the bond");

lockedBonds[bondID] -= amount;
holder.transfer(amount);
}

/// @notice Checks if the caller is an authorized contract.
/// @dev Throws an error if called by any account other than one of the authorized
/// contracts.
Expand Down
69 changes: 66 additions & 3 deletions solidity/test/KeepBondingTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ contract('KeepBonding', (accounts) => {
expect(unbonded).to.eq.BN(expectedUnbonded, 'invalid unbonded value')

const lockedBonds = await keepBonding.getLockedBonds(holder, operator, reference)
expect(lockedBonds).to.eq.BN(value, 'invalid locked bonds')
expect(lockedBonds).to.eq.BN(value, 'unexpected bond value')
})

it('creates two bonds with the same reference for different operators', async () => {
Expand All @@ -140,10 +140,10 @@ contract('KeepBonding', (accounts) => {
expect(unbonded2).to.eq.BN(expectedUnbonded, 'invalid unbonded value 2')

const lockedBonds1 = await keepBonding.getLockedBonds(holder, operator, reference)
expect(lockedBonds1).to.eq.BN(bondValue, 'invalid locked bonds')
expect(lockedBonds1).to.eq.BN(bondValue, 'unexpected bond value 1')

const lockedBonds2 = await keepBonding.getLockedBonds(holder, operator2, reference)
expect(lockedBonds2).to.eq.BN(bondValue, 'invalid locked bonds')
expect(lockedBonds2).to.eq.BN(bondValue, 'unexpected bond value 2')
})

it('fails to create two bonds with the same reference for the same operator', async () => {
Expand Down Expand Up @@ -234,4 +234,67 @@ contract('KeepBonding', (accounts) => {
)
})
})

describe('seizeBond', async () => {
const operator = accounts[1]
const holder = accounts[2]
const bondValue = new BN(100)
const reference = 777

beforeEach(async () => {
await keepBonding.deposit(operator, { value: bondValue })
await keepBonding.createBond(operator, reference, bondValue, { from: holder })
})

it('transfers whole bond amount to holder\'s account', async () => {
const amount = bondValue
let expectedBalance = web3.utils.toBN(await web3.eth.getBalance(holder)).add(amount)

const tx = await keepBonding.seizeBond(operator, reference, amount, { from: holder })

const gasPrice = web3.utils.toBN(await web3.eth.getGasPrice())
const txCost = gasPrice.mul(web3.utils.toBN(tx.receipt.gasUsed))
expectedBalance = expectedBalance.sub(txCost)

const actualBalance = await web3.eth.getBalance(holder)
expect(actualBalance).to.eq.BN(expectedBalance, 'invalid holder\'s account balance')

const lockedBonds = await keepBonding.getLockedBonds(holder, operator, reference)
expect(lockedBonds).to.eq.BN(0, 'unexpected remaining bond value')
})

it('transfers less than bond amount to holder\'s account', async () => {
const remainingBond = new BN(1)
const amount = bondValue.sub(remainingBond)
let expectedBalance = web3.utils.toBN(await web3.eth.getBalance(holder)).add(amount)

const tx = await keepBonding.seizeBond(operator, reference, amount, { from: holder })

const gasPrice = web3.utils.toBN(await web3.eth.getGasPrice())
const txCost = gasPrice.mul(web3.utils.toBN(tx.receipt.gasUsed))
expectedBalance = expectedBalance.sub(txCost)

const actualBalance = await web3.eth.getBalance(holder)
expect(actualBalance).to.eq.BN(expectedBalance, 'invalid holder\'s account balance')

const lockedBonds = await keepBonding.getLockedBonds(holder, operator, reference)
expect(lockedBonds).to.eq.BN(remainingBond, 'unexpected remaining bond value')
})

it('fails if seized amount equals zero', async () => {
const amount = new BN(0)
await expectRevert(
keepBonding.seizeBond(operator, reference, amount, { from: holder }),
"Requested amount should be greater than zero"
)
})

it('fails if seized amount is greater than bond value', async () => {
const amount = bondValue.add(new BN(1))
await expectRevert(
keepBonding.seizeBond(operator, reference, amount, { from: holder }),
"Requested amount is greater than the bond"
)
})
})
})

0 comments on commit ccdb756

Please sign in to comment.