Skip to content

Commit

Permalink
feat: SWIP 21 (#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xCardinalError authored Dec 3, 2024
1 parent a39a031 commit 9173bc3
Show file tree
Hide file tree
Showing 23 changed files with 1,577 additions and 437 deletions.
4 changes: 4 additions & 0 deletions deploy/test/005_deploy_roles_postage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const func: DeployFunction = async function ({ deployments, getNamedAccounts })
).address
);

// Set default price for postageStamp
const currentPrice = await read('PriceOracle', 'currentPrice');
await execute('PriceOracle', { from: deployer }, 'setPrice', currentPrice);

log('----------------------------------------------------');
};

Expand Down
87 changes: 62 additions & 25 deletions deployments/testnet/PriceOracle.json

Large diffs are not rendered by default.

183 changes: 117 additions & 66 deletions deployments/testnet/Redistribution.json

Large diffs are not rendered by default.

135 changes: 95 additions & 40 deletions deployments/testnet/StakeRegistry.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/294e4684ec82046939090fa9e468f317.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/99c9ef5f7ef53ea99ef6c816b26b1e5c.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions deployments/testnet/solcInputs/be98d61f2910c0140b59702042017efa.json

Large diffs are not rendered by default.

44 changes: 22 additions & 22 deletions deployments/testnetlight/PostageStamp.json

Large diffs are not rendered by default.

89 changes: 63 additions & 26 deletions deployments/testnetlight/PriceOracle.json

Large diffs are not rendered by default.

190 changes: 110 additions & 80 deletions deployments/testnetlight/Redistribution.json

Large diffs are not rendered by default.

158 changes: 86 additions & 72 deletions deployments/testnetlight/StakeRegistry.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,15 @@ const config: HardhatUserConfig = {
chainId: 11155111,
urls: {
apiURL: 'https://api-sepolia.etherscan.io/api',
browserURL: 'https://sepolia.etherscan.io/address/',
browserURL: 'https://sepolia.etherscan.io/',
},
},
{
network: 'testnetlight',
chainId: 11155111,
urls: {
apiURL: 'https://api-sepolia.etherscan.io/api',
browserURL: 'https://sepolia.etherscan.io/address/',
browserURL: 'https://sepolia.etherscan.io/',
},
},
{
Expand Down
30 changes: 24 additions & 6 deletions src/PriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ contract PriceOracle is AccessControl {
*@dev Emitted on every price update.
*/
event PriceUpdate(uint256 price);
event StampPriceUpdateFailed(uint256 attemptedPrice);

// ----------------------------- Custom Errors ------------------------------
error CallerNotAdmin(); // Caller is not the admin
Expand All @@ -72,7 +73,7 @@ contract PriceOracle is AccessControl {
* @notice Manually set the price.
* @dev Can only be called by the admin role.
* @param _price The new price.
*/ function setPrice(uint32 _price) external {
*/ function setPrice(uint32 _price) external returns (bool) {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) {
revert CallerNotAdmin();
}
Expand All @@ -86,12 +87,19 @@ contract PriceOracle is AccessControl {
}
currentPriceUpScaled = _currentPriceUpScaled;

// Price in postagestamp is set at 256 so we need to upcast it
postageStamp.setPrice(uint256(currentPrice()));
// Check if the setting of price in postagestamp succeded
(bool success, ) = address(postageStamp).call(
abi.encodeWithSignature("setPrice(uint256)", uint256(currentPrice()))
);
if (!success) {
emit StampPriceUpdateFailed(currentPrice());
return false;
}
emit PriceUpdate(currentPrice());
return true;
}

function adjustPrice(uint16 redundancy) external {
function adjustPrice(uint16 redundancy) external returns (bool) {
if (isPaused == false) {
if (!hasRole(PRICE_UPDATER_ROLE, msg.sender)) {
revert CallerNotPriceUpdater();
Expand Down Expand Up @@ -119,7 +127,7 @@ contract PriceOracle is AccessControl {
uint64 _minimumPriceUpscaled = minimumPriceUpscaled;
uint32 _priceBase = priceBase;

// Set the number of rounds that were skipped
// Set the number of rounds that were skipped, we substract 1 as lastAdjustedRound is set below and default result is 1
uint64 skippedRounds = currentRoundNumber - lastAdjustedRound - 1;

// We first apply the increase/decrease rate for the current round
Expand All @@ -141,9 +149,19 @@ contract PriceOracle is AccessControl {

currentPriceUpScaled = _currentPriceUpScaled;
lastAdjustedRound = currentRoundNumber;
postageStamp.setPrice(uint256(currentPrice()));

// Check if the price set in postagestamp succeded
(bool success, ) = address(postageStamp).call(
abi.encodeWithSignature("setPrice(uint256)", uint256(currentPrice()))
);
if (!success) {
emit StampPriceUpdateFailed(currentPrice());
return false;
}
emit PriceUpdate(currentPrice());
return true;
}
return false;
}

function pause() external {
Expand Down
45 changes: 36 additions & 9 deletions src/Redistribution.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "./Util/Signatures.sol";
import "./interface/IPostageStamp.sol";

interface IPriceOracle {
function adjustPrice(uint16 redundancy) external;
function adjustPrice(uint16 redundancy) external returns (bool);
}

interface IStakeRegistry {
Expand All @@ -24,6 +24,8 @@ interface IStakeRegistry {

function overlayOfAddress(address _owner) external view returns (bytes32);

function heightOfAddress(address _owner) external view returns (uint8);

function nodeEffectiveStake(address _owner) external view returns (uint256);
}

Expand Down Expand Up @@ -64,6 +66,7 @@ contract Redistribution is AccessControl, Pausable {
bytes32 overlay;
address owner;
bool revealed;
uint8 height;
uint256 stake;
bytes32 obfuscatedHash;
uint256 revealIndex;
Expand Down Expand Up @@ -181,7 +184,7 @@ contract Redistribution is AccessControl, Pausable {
/**
* @dev Logs that an overlay has committed
*/
event Committed(uint256 roundNumber, bytes32 overlay);
event Committed(uint256 roundNumber, bytes32 overlay, uint8 height);
/**
* @dev Emit from Postagestamp contract valid chunk count at the end of claim
*/
Expand All @@ -192,6 +195,16 @@ contract Redistribution is AccessControl, Pausable {
*/
event CurrentRevealAnchor(uint256 roundNumber, bytes32 anchor);

/**
* @dev Output external call status
*/
event PriceAdjustmentSkipped(uint16 redundancyCount);

/**
* @dev Withdraw not successful in claim
*/
event WithdrawFailed(address owner);

/**
* @dev Logs that an overlay has revealed
*/
Expand Down Expand Up @@ -281,6 +294,7 @@ contract Redistribution is AccessControl, Pausable {
bytes32 _overlay = Stakes.overlayOfAddress(msg.sender);
uint256 _stake = Stakes.nodeEffectiveStake(msg.sender);
uint256 _lastUpdate = Stakes.lastUpdatedBlockNumberOfAddress(msg.sender);
uint8 _height = Stakes.heightOfAddress(msg.sender);

if (!currentPhaseCommit()) {
revert NotCommitPhase();
Expand Down Expand Up @@ -329,13 +343,14 @@ contract Redistribution is AccessControl, Pausable {
overlay: _overlay,
owner: msg.sender,
revealed: false,
height: _height,
stake: _stake,
obfuscatedHash: _obfuscatedHash,
revealIndex: 0
})
);

emit Committed(_roundNumber, _overlay);
emit Committed(_roundNumber, _overlay, _height);
}

/**
Expand Down Expand Up @@ -372,9 +387,10 @@ contract Redistribution is AccessControl, Pausable {
bytes32 obfuscatedHash = wrapCommit(_overlay, _depth, _hash, _revealNonce);
uint256 id = findCommit(_overlay, obfuscatedHash);
Commit memory revealedCommit = currentCommits[id];
uint8 depthResponsibility = _depth - revealedCommit.height;

// Check that commit is in proximity of the current anchor
if (!inProximity(revealedCommit.overlay, currentRevealRoundAnchor, _depth)) {
if (!inProximity(revealedCommit.overlay, currentRevealRoundAnchor, depthResponsibility)) {
revert OutOfDepthReveal(currentRevealRoundAnchor);
}
// Check that the commit has not already been revealed
Expand All @@ -391,7 +407,7 @@ contract Redistribution is AccessControl, Pausable {
owner: revealedCommit.owner,
depth: _depth,
stake: revealedCommit.stake,
stakeDensity: revealedCommit.stake * uint256(2 ** _depth),
stakeDensity: revealedCommit.stake * uint256(2 ** depthResponsibility),
hash: _hash
})
);
Expand All @@ -400,7 +416,7 @@ contract Redistribution is AccessControl, Pausable {
cr,
revealedCommit.overlay,
revealedCommit.stake,
revealedCommit.stake * uint256(2 ** _depth),
revealedCommit.stake * uint256(2 ** depthResponsibility),
_hash,
_depth
);
Expand Down Expand Up @@ -465,7 +481,14 @@ contract Redistribution is AccessControl, Pausable {

estimateSize(entryProofLast.proofSegments[0]);

PostageContract.withdraw(winnerSelected.owner);
// Do the check if the withdraw was success
(bool success, ) = address(PostageContract).call(
abi.encodeWithSignature("withdraw(address)", winnerSelected.owner)
);
if (!success) {
emit WithdrawFailed(winnerSelected.owner);
}

emit WinnerSelected(winnerSelected);
emit ChunkCount(PostageContract.validChunkCount());
}
Expand Down Expand Up @@ -549,7 +572,10 @@ contract Redistribution is AccessControl, Pausable {
}
}

OracleContract.adjustPrice(uint16(redundancyCount));
bool success = OracleContract.adjustPrice(uint16(redundancyCount));
if (!success) {
emit PriceAdjustmentSkipped(uint16(redundancyCount));
}
currentClaimRound = cr;
}

Expand Down Expand Up @@ -802,6 +828,7 @@ contract Redistribution is AccessControl, Pausable {
*/
function isParticipatingInUpcomingRound(address _owner, uint8 _depth) public view returns (bool) {
uint256 _lastUpdate = Stakes.lastUpdatedBlockNumberOfAddress(_owner);
uint8 _depthResponsibility = _depth - Stakes.heightOfAddress(_owner);

if (currentPhaseReveal()) {
revert WrongPhase();
Expand All @@ -815,7 +842,7 @@ contract Redistribution is AccessControl, Pausable {
revert MustStake2Rounds();
}

return inProximity(Stakes.overlayOfAddress(_owner), currentRoundAnchor(), _depth);
return inProximity(Stakes.overlayOfAddress(_owner), currentRoundAnchor(), _depthResponsibility);
}

// ----------------------------- Reveal ------------------------------
Expand Down
63 changes: 48 additions & 15 deletions src/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ contract StakeRegistry is AccessControl, Pausable {
uint256 potentialStake;
// Block height the stake was updated, also used as flag to check if the stake is set
uint256 lastUpdatedBlockNumber;
// Node indicating its increased reserve
uint8 height;
}

// Associate every stake id with node address data.
Expand All @@ -51,14 +53,15 @@ contract StakeRegistry is AccessControl, Pausable {
// ----------------------------- Events ------------------------------

/**
* @dev Emitted when a stake is created or updated by `owner` of the `overlay` by `committedStake`, and `potentialStake` during `lastUpdatedBlock`.
* @dev Emitted when a stake is created or updated by `owner` of the `overlay`.
*/
event StakeUpdated(
address indexed owner,
uint256 committedStake,
uint256 potentialStake,
bytes32 overlay,
uint256 lastUpdatedBlock
uint256 lastUpdatedBlock,
uint8 height
);

/**
Expand Down Expand Up @@ -112,33 +115,48 @@ contract StakeRegistry is AccessControl, Pausable {
* @dev At least `_initialBalancePerChunk*2^depth` number of tokens need to be preapproved for this contract.
* @param _setNonce Nonce that was used for overlay calculation.
* @param _addAmount Deposited amount of ERC20 tokens, equals to added Potential stake value
* @param _height increased reserve by registering the number of doublings
*/
function manageStake(bytes32 _setNonce, uint256 _addAmount) external whenNotPaused {
function manageStake(bytes32 _setNonce, uint256 _addAmount, uint8 _height) external whenNotPaused {
bytes32 _previousOverlay = stakes[msg.sender].overlay;
uint256 _stakingSet = stakes[msg.sender].lastUpdatedBlockNumber;
bytes32 _newOverlay = keccak256(abi.encodePacked(msg.sender, reverse(NetworkId), _setNonce));
uint256 _addCommittedStake = _addAmount / OracleContract.currentPrice(); // losing some decimals from start 10n16 becomes 99999999999984000

// First time adding stake, check the minimum is added
if (_addAmount < MIN_STAKE && _stakingSet == 0) {
// First time adding stake, check the minimum is added, take into account height
if (_addAmount < MIN_STAKE * 2 ** _height && _stakingSet == 0) {
revert BelowMinimumStake();
}

if (_stakingSet != 0 && !addressNotFrozen(msg.sender)) revert Frozen();
uint256 updatedCommittedStake = stakes[msg.sender].committedStake + _addCommittedStake;
uint256 updatedPotentialStake = stakes[msg.sender].potentialStake + _addAmount;

uint256 updatedPotentialStake = stakes[msg.sender].potentialStake;
uint256 updatedCommittedStake = stakes[msg.sender].committedStake;

// Only update stake values if _addAmount is greater than 0
if (_addAmount > 0) {
updatedPotentialStake = stakes[msg.sender].potentialStake + _addAmount;
updatedCommittedStake = updatedPotentialStake / (OracleContract.currentPrice() * 2 ** _height);
}

stakes[msg.sender] = Stake({
overlay: _newOverlay,
committedStake: updatedCommittedStake,
potentialStake: updatedPotentialStake,
lastUpdatedBlockNumber: block.number
lastUpdatedBlockNumber: block.number,
height: _height
});

// Transfer tokens and emit event that stake has been updated
if (_addAmount > 0) {
if (!ERC20(bzzToken).transferFrom(msg.sender, address(this), _addAmount)) revert TransferFailed();
emit StakeUpdated(msg.sender, updatedCommittedStake, updatedPotentialStake, _newOverlay, block.number);
emit StakeUpdated(
msg.sender,
updatedCommittedStake,
updatedPotentialStake,
_newOverlay,
block.number,
_height
);
}

// Emit overlay change event
Expand All @@ -153,7 +171,7 @@ contract StakeRegistry is AccessControl, Pausable {
function withdrawFromStake() external {
uint256 _potentialStake = stakes[msg.sender].potentialStake;
uint256 _surplusStake = _potentialStake -
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake);
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake, stakes[msg.sender].height);

if (_surplusStake > 0) {
stakes[msg.sender].potentialStake -= _surplusStake;
Expand Down Expand Up @@ -250,7 +268,11 @@ contract StakeRegistry is AccessControl, Pausable {
function nodeEffectiveStake(address _owner) public view returns (uint256) {
return
addressNotFrozen(_owner)
? calculateEffectiveStake(stakes[_owner].committedStake, stakes[_owner].potentialStake)
? calculateEffectiveStake(
stakes[_owner].committedStake,
stakes[_owner].potentialStake,
stakes[_owner].height
)
: 0;
}

Expand All @@ -259,7 +281,9 @@ contract StakeRegistry is AccessControl, Pausable {
*/
function withdrawableStake() public view returns (uint256) {
uint256 _potentialStake = stakes[msg.sender].potentialStake;
return _potentialStake - calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake);
return
_potentialStake -
calculateEffectiveStake(stakes[msg.sender].committedStake, _potentialStake, stakes[msg.sender].height);
}

/**
Expand All @@ -277,12 +301,21 @@ contract StakeRegistry is AccessControl, Pausable {
return stakes[_owner].overlay;
}

/**
* @dev Returns the currently height of the address.
* @param _owner address of node
*/
function heightOfAddress(address _owner) public view returns (uint8) {
return stakes[_owner].height;
}

function calculateEffectiveStake(
uint256 committedStake,
uint256 potentialStakeBalance
uint256 potentialStakeBalance,
uint8 height
) internal view returns (uint256) {
// Calculate the product of committedStake and unitPrice to get price in BZZ
uint256 committedStakeBzz = committedStake * OracleContract.currentPrice();
uint256 committedStakeBzz = (2 ** height) * committedStake * OracleContract.currentPrice();

// Return the minimum value between committedStakeBzz and potentialStakeBalance
if (committedStakeBzz < potentialStakeBalance) {
Expand Down
Loading

0 comments on commit 9173bc3

Please sign in to comment.