how-layerzero-works

how layerzero works

flowchart

main compoent

LayerZero Endpoint

The LayerZero Endpoint is an immutable smart contract that implements a standardized interface for Omnichain Applications (OApps) to manage security configurations and seamlessly send and receive messages.

Message Library

Each MessageLib is an immutable verification library that OApp owners can configure their application to use. The protocol enforces the contract owner’s unique OApp Configuration before sending and receiving messages.

Message Packet

The Message Packet standardizes the format and size of messages that are sent between different blockchains:

1
2
3
4
5
6
7
8
9
struct Packet {
uint64 nonce; // the nonce of the message in the pathway
uint32 srcEid; // the source endpoint ID
address sender; // the sender address
uint32 dstEid; // the destination endpoint ID
bytes32 receiver; // the receiving address
bytes32 guid; // a global unique identifier
bytes message; // the message payload
}

Security Stack (DVNs)

Security Stack (DVNs) is used to check the payloadHash emitted for message integrity.
There are two number here:

  • required Decentralized Verifier Networks(DVNs)
  • optional Decentralized Verifier Networks(DVNs)

Executors

Executors is used to invoke target chain lzReceive function in the Endpoint contract once the message payload is verified.

Methods

_lzSend

the function your application must implement to send an omnichain message.

_lzReceive

the function to receive an omnichain message

_lzCompose

Since each composable call is created as a separate message packet via lzCompose, this pattern can be extended for as many steps as your application needs (B1 -> B2 -> B3, etc).

Message Execution Options

LayerZero provides robust Message Execution Options, which allow you to specify arbitrary logic as part of the message transaction, such as the gas amount and msg.value the Executor pays for message delivery, the order of message execution, or dropping an amount of gas to a destination address.

quote

estimate of how much gas a message will cost to be sent and received

Integration Checklist

https://docs.layerzero.network/v2/developers/evm/troubleshooting/integration-checklist

resources

https://docs.layerzero.network/v2/developers/evm/overview
https://github.com/LayerZero-Labs/LayerZero-v2/tree/main/packages/layerzero-v2/evm/oapp/contracts

pyth-docs

How Pyth Works

flowchart

  • price data pushlisher submit data to pyth’s oracel program
  • pyth’s oracel program combines all price data and compute the final price based on it’s own algorithm
  • pyth transfer price data from pythnet to mutiple target chains eg,:evm,bnb,arbiturm
  • hermes(web service) listens to price update.
  • price consumer get the latest price from hermes(web service),verify the lastest price data on chain via oracel program on each chain,the program then return the price once price data is valid

Pyth Methods On EVM Table

Method Need check publishTime Most recent price
getPrice
getPriceUnsafe
getPriceNoOlderThan
getEmaPrice
getEmaPriceUnsafe
getEmaPriceNoOlderThan
getUpdateFee
getValidTimePeriod
parsePriceFeedUpdate
parsePriceFeeUpdateUnique
updatePriceFeeds
updatePriceFeedsIfNecessary

Pyth Methods On EVM

getPrice

arguments:

  • price feed id

getPrice() revert due to the price has not been update within getValidTimePeriod() seconds which is typically 1 minute.

1
2
3
EVM call reverted with exception:

StalePrice()

let’s take eth/usd as example, the return price is 291769250000, and the price is 2917.69250000 usd.
The only argument is price feed id which is a 32-byte id written as a hexadecimal string.For eth/usd it’s 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace. You can get the full list from https://pyth.network/developers/price-feed-ids page.

1
2
3
4
5
6
7
8
9
10
EVM call succeeded with result:

{
price: {
price: 291769250000,
conf: 166231345,
expo: -8,
publishTime: 1722775361,
},
}

getPriceUnsafe

arguments:

  • price feed id

The input arguments and return data format for getPriceUnsafe() are identical to those of the getPrice() function. However, the key distinction lies in their behavior: getPriceUnsafe() may return a price from an arbitrary point in the past, potentially outdated. Consequently, the responsibility falls on the caller or price consumer to ensure the returned data meets their recency requirements. This involves comparing the current block.timestamp with the returned publishTime to verify that the price update is sufficiently recent for their specific use case.

getPriceNoOlderThan

arguments:

  • price feed id
  • age

This function reverts with a PriceFeedNotFound error if the requested feed id has never received a price update. Calling updatePriceFeed to fix this issue.

getEmaPrice

arguments:

  • price feed id

Get the latest exponentially-weighted moving average (EMA) price and confidence interval for the requested price feed id.

1
2
3
4
5
6
7
8
9
10
EVM call succeeded with result:

{
price: {
price: 6088503200000,
conf: 2786383200,
expo: -8,
publishTime: 1722780106,
},
}

getEmaPriceUnsafe

arguments:

  • price feed id
    This function may return a price from arbitrarily far in the past
    Get the latest exponentially-weighted moving average (EMA) price and confidence interval for the requested price feed id.

getEmaPriceNoOlderTham

arguments:

  • price feed id
  • age
    Get the latest exponentially-weighted moving average (EMA) price and confidence interval for the requested price feed id
    The caller provides an age argument that specifies how old the price can be.The call can be revert due to StalePriceError.

getUpdateFee

arguments:

  • updateData

Note that updataData is retrieved from Hermes REST API.

1
2
3
4
5
EVM call succeeded with result:

{
feeAmount: 1,
}

getValidTimePeriod

Get the default valid time period in seconds.
Above some getting function revert if current on-chain price is older than this period.

1
2
3
4
5
EVM call succeeded with result:

{
validTimePeriod: 60,
}

parsePriceFeedUpdates

arguments:

  • updateData* hex[]
  • priceIds* bytes32[]
  • minPublishTime* uint64
  • maxPublishTime* uint64
  • fee* wei

Parse updateData and return the price feeds for the given priceIds within, if they are all published between minPublishTime and maxPublishTime (minPublishTime <= publishTime <= maxPublishTime).
Please note that this function not returned the most recent price,instead it returned a price for fixed time(minPublishTime <= publishTime <= maxPublishTime).
If caller need a most recent price they should call updatePriceFeeds followed by getPrice or one of its variants.

Unlike updatePriceFeeds, calling this function will not update the on-chain price

parsePriceFeedUpdatesUnique.

arguments:

  • updateData* hex[]
  • priceIds* bytes32[]
  • minPublishTime* uint64
  • maxPublishTime* uint64
  • fee* wei

the price update is the earliest update after the minPublishTime.

condition:prevPublishTime < minPublishTime <= publishTime <= maxPublishTime

updatePriceFeed

arguments:

  • updateData* hex[]
  • fee* wei

Update the on-chain price feeds using the provided updateData
Caller first retrieve a latest price from Hermes REST API,and then invoke this function to update the on-chain price if this price is more recent than on-chain price,otherwise the provided update will be ignored.

updatePriceFeedsIfNecessary

arguments:

  • updateData* hex[]
  • priceIds* bytes32[]
  • publishTimes* uint64[]
  • fee* wei

Update the on-chain price feeds using the provided updateData if the on-chain data is not sufficiently fresh. The caller provides two matched arrays, priceIds and publishTimes. This function applies the update if there exists an index i such that priceIds[i]’s last publishTime is before than publishTimes[i]. Callers should typically pass publishTimes[i] to be equal to the publishTime of the corresponding price id in updateData. If this condition is not satisfied, the call will revert with a NoFreshUpdate error.

Resources

PythOracle:if price.expo is less than 0, wrong prices will be recorded https://github.com/sherlock-audit/2023-07-perennial-judging/issues/56

Using stale price in Pyth Network https://solodit.xyz/issues/m-07-using-stale-price-in-pyth-network-pashov-none-astrolab-markdown

Audit review for 2024-04-interest-rate-model from sherlock

1.The calculation of released in config function of RewardsController is wrong

summary

1
2
3
4
rewardData.lastConfig = uint32(block.timestamp);
...
released =
rewardData.lastConfigReleased + rewardData.releaseRate * (block.timestamp - rewardData.lastConfig);

As we can see, the protocol uses the time period from lastConfig to the present to calculate the released amount. However, consider a scenario where the start time is set in the future.

root cause
protocol assume start time is equal to the configuration time.

2.Expired maturities longer than FixedLib.INTERVAL with unaccrued earnings may be arbitraged and/or might lead to significant bad debt creation

summary

1
2
3
4
5
6
7
8
9
10
11
12
13
>     uint256 latestMaturity = block.timestamp - (block.timestamp % FixedLib.INTERVAL);
> uint256 maxMaturity = latestMaturity + maxFuturePools * FixedLib.INTERVAL;

for (uint256 maturity = latestMaturity; maturity <= maxMaturity; maturity += FixedLib.INTERVAL) {
FixedLib.Pool storage pool = fixedPools[maturity];
uint256 lastAccrual = pool.lastAccrual;

if (maturity > lastAccrual) {
backupEarnings += block.timestamp < maturity
? pool.unassignedEarnings.mulDivDown(block.timestamp - lastAccrual, maturity - lastAccrual)
: pool.unassignedEarnings;
}
}

when calculate latestMaturity use block.timestamp - (block.timestamp % FixedLib.INTERVAL) which can lead to a replay more than 1 INTERVAL not accounted.
root cause
block.timestamp - (block.timestamp % FixedLib.INTERVAL) has a time range.

Audit review for 2024-04-panoptic from code4rena

https://github.com/code-423n4/2024-04-panoptic-findings

High Risk

1.SettleLongPremium is incorrectly implemented: premium should be deducted instead of added

summary

1
2
3
4
5
            // current available assets belonging to PLPs (updated after settlement) excluding any premium paid
int256 updatedAssets = int256(uint256(s_poolAssets)) - swappedAmount;

// add premium to be paid/collected on position close
> int256 tokenToPay = -realizedPremium;

In the code, the int type is used for numbers, and the sign of the subtrahend is used to control whether the final result is an increase or a decrease.

root cause

As a result, an error in the sign of the input parameters leads to the final operation being contrary to the expectation.

learned

When we use the int type, we need to pay special attention to the sign of the values

2.

summary

1
2
3
4
5
6
7
unchecked {
assets = Math.mulDivRoundingUp(
shares * DECIMALS,
totalAssets(),
totalSupply * (DECIMALS - COMMISSION_FEE)
);
}

unchecked block can lead to silent overflow

root cause

Due to user can mint huge amounts of shares for free the shares can be much greater than expected.

learned

Double check the unchecked block make sure it can’t overflow.

Medium Risk

1.PanopticFactory uses spot price when deploying new pools, resulting in liquidity manipulation when minting

summary

1
@>	(uint160 currentSqrtPriceX96, , , , , , ) = v3Pool.slot0();

When deployNewPool is called it uses the spot price of the pool, which can be manipulated through a flashloan and thus could return a highly inaccurate result.

root cause

uses spot price

learned

spot price can be manipulated through a flashloan, try to use TWAP price instead.

2._validatePositionList() does not check for duplicate tokenIds, allowing attackers to bypass solvency checks

summary

The user can pass in an array of tokenIds, but there is no check for duplicates.

root cause

no check for duplicates

learned

Think about if pass in array need to be checked for duplicates.

3.Removed liquidity can overflow when calling SemiFungiblePositionManager.mintTokenizedPosition function

summary

1
2
3
4
5
6
if (!isBurn) {
// we can't remove more liquidity than we add in the first place, so this can't overflow
unchecked {
removedLiquidity += chunkLiquidity;
}
}

options can be repeatly which can lead to removedLiquidity sum up mutiple times.

root cause

unchecked block overflow

learned

need to double check the unchecked block make sure it can not be overflow

4.Wrong leg chunkKey calculation in haircutPremia function

summary

when loop tokenId , uses always the index 0 when calculating the leg chunkKey instead of using the actual leg index

root cause

logic error

learned

pay more attention to the logic.

5.Panoptic pool can be non-profitable by specific Uniswap governance

summary

The developers only considered the current range of Uniswap fees, but these fees may change through governance, affecting the existing logic.

root cause

fee may changes in the future.

learned

It is necessary to consider the impact of governance on the current fees.

Audit review for 2024-03-zivoe from sherlcok

https://github.com/sherlock-audit/2024-03-zivoe-judging/issues

1.DAO unable to withdraw their funds due to Convex admin action

summary

The administrator can cause a DoS (Denial of Service) in the protocol by passing malicious parameters

root cause

according to docs : admin’s action is RESTRICTED

learned

When a role’s behavior is RESTRICTED, it is necessary to examine the consequences of any suspicious actions

2.Inadequate Allowance Handling in convertAndForward Function of OCT_DAO & OCT_YDL

summary

stricted allowance assertion check lead to transaction failed

root cause

protocol suffers from inadequate handling of token allowances for the 1inch router,however they are not reset afterward.

learned

take care of the allowance assertion check

3.cannot forward extra rewards from both OCY_Convex to OCT_YDL

summary

1
2
-               if (rewardAmount > 0) { IERC20(rewardContract).safeTransfer(OCT_YDL, rewardAmount); }
+ if (rewardAmount > 0) { IBaseRewardPool_OCY_Convex_C(rewardContract).rewardToken().safeTransfer(OCT_YDL, rewardAmount); }

root cause

use safeTransfer in a none erc20 contract

learned

4.ZivoeYDL::earningsTrancheuse() always assumes that daysBetweenDistributions have passed, which might not be the case

summary

The protocol relies on keepers to call distributeYield. However, there is no guarantee that the keeper will make the call immediately.

root cause

The calculation of the APY depends on block.timestamp.

learned

When the calculation of APY depends on the timestamp, ensure it is called immediately

5.ZivoeYDL::distributeYield yield distribution is flash-loan manipulatable

summary

distributeYield is depends on totalSupply, however totalSupply can be manipulable through a flashloan.A 1-transaction inflated staked amount allows to inflate stakers distribution at the loss of vesters distribution

root cause

distributeYield amount is calculated with totalSupply

6.distributeYield() calls earningsTrancheuse() with outdated emaSTT & emaJTT while calculating senior & junior tranche yield distributions

summary

The earningsTrancheuse function uses emaSTT and emaJTT to calculate earnings, and then updates the latest emaSTT and emaJTT. It is recommended to update the latest values first and then use them to calculate earnings.

root cause

the value is not lastest need to be updated.

7.User cannot withdraw stakingToken due to incorrect calculation of _totalSupply

summary

When the user revokes a stake, the total amount is subtracted instead of the currently withdrawable amount, leading to an overflow DoS.

root cause

logic error

How does blast l2 receive and claim ETH yield

How does blast l2 receive and claim ETH yield

1.overview

One of the key differences between Blast L2 and other Layer 2 solutions is that in Blast L2, ETH functions as a native rebasing token.

1.1 what is rebasing token?

A rebasing token in the Ethereum Virtual Machine (EVM) context is a type of cryptocurrency whose supply automatically adjusts, or rebases periodically to maintain a target price or peg. This mechanism is designed to achieve certain economic goals, such as price stability or controlled inflation/deflation.

1.2 why blast l2 ?

  • Blast users and smart contracts can earn ~4% from beacon chain staking yield
  • Blast redirects sequencer fees to the dapps that induced them

2.three options for yield modes

Smart contract must interact with 0x4300000000000000000000000000000000000002 to change their gas mode.

2.1 Void (DEFAULT)

Void is default yield modes for smart contract which deploy on blast l2. The balance of smart contract never changes, no yield is earned . Any normal dapp can be deploy on blast without any changes.
Thus if you already have smart contract you can just move it to blast.

2.2 Automatic

Since eth is native rebasing on blast l2 , if deposit 1 eth into smart contract, the contract’s balance will grow to 1.04 ETH in a year.

2.3 Claimable

For claimable mode, if deposit 1 eth into smart contract , the contract’s balance will emain at 1 eth even after a year. We can implement two functions to claim yield:

  • claimYield
  • claimAllYield
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface IBlast{
// See IBlast interface source code below
}

contract MyContract {
constructor() {
IBlast(0x43...02).configureClaimableYield()
}

function claimYield(address recipient, uint256 amount) external {
//This function is public meaning anyone can claim the yield
IBlast(0x43...02).claimYield(address(this), recipient, amount);
}

function claimAllYield(address recipient) external {
//This function is public meaning anyone can claim the yield
IBlast(0x43...02).claimAllYield(address(this), recipient);
}
}

How to use google spread sheet to generate merkle tree for ERC20 token airdrop

1.what’s merkle tree

Refer to the definetion of merkle tree on wikipedia:https://en.wikipedia.org/wiki/Merkle_tree
In cryptography and computer science, a hash tree or Merkle tree is a tree in which every “leaf” node is labelled with the cryptographic hash of a data block, and every node that is not a leaf (called a branch, inner node, or inode) is labelled with the cryptographic hash of the labels of its child nodes. A hash tree allows efficient and secure verification of the contents of a large data structure. A hash tree is a generalization of a hash list and a hash chain.
As we know, an airdrop may contain numerous eligible addresses. Storing all these addresses would incur significant gas fees. Instead, we can simply store the Merkle root, which is a 32-byte value.
If you’re not a Solidity developer, grasping the basics of Merkle trees can be challenging. That’s why we’ve developed a tool called oneclicktoken. It simplifies the process of generating Merkle tree roots and, furthermore, enables the creation of ERC20 tokens with just a click. You won’t need any prior knowledge of web3, Solidity, or the ERC20 standard
At this early stage of our project, we’d like to highlight some of the features and supported chains

1.1 supported chains:

  • base
  • sepolia(test)
  • More Coming…

1.2 features:

  • generate merkle root from google spread sheet
  • generate ERC20 token
    • Premint
    • Mintable
    • Pauseable
    • VerifyContract(if you need)

2.How to generate merkle tree root from an online google spread

eg:https://docs.google.com/spreadsheets/d/1jVX3j-TlLtI3VJ-27DuOuZuCM3x6_zf9W5tI6k67TvQ/edit#gid=0
Currently, we only support the following format:

1
2
--------A----------|-----------B-----------|
claimer(address) amount(uint256)

2.1 get params

  • id
  • sheet name

To extract the unique ID from a Google Sheets URL, you can locate the string of characters following the /d/ and preceding the /edit in the URL. For example, in the given URL, the unique ID is 1jVX3j-TlLtI3VJ-27DuOuZuCM3x6_zf9W5tI6k67TvQ.

Next, to identify the specific sheet within the spreadsheet, navigate to the bottom left corner of the Google Sheets interface, where a list of sheets is displayed. From there, you can determine the name of the sheet you need to access. In the provided URL, the sheet name is sheet1.

Once you have obtained the unique ID and sheet name, you can proceed to our tool website, oneclicktoken . There, input the required parameters and click the Calculate button. This will generate the Merkle tree root and provide a download URL for the Merkle tree JSON file.

3.How to generate ERC20 with airdrop using a single click?

  • token name
  • token symbol
  • premint amount(1 == 1 ether token)
  • contract owner(deployer is the default owner)
  • merkle root(if you need an airdrop)
    Once you have obtained above params, navigate to our webiste oneclicktoken input the required parameters and click the deploy button , then you can deploy your custom ERC20 token to target chains. If you need to test your token you can deploy it to sepolia testnet first it’s free.

Our project is in the very early stages, so you might encounter some issues while using it. If you need assistance, please don’t hesitate to reach out to me through the following channels. Additionally, if you require a custom airdrop website or need assistance building a custom Solidity contract, feel free to contact me.

4.origin

https://blog.oneclicktoken.xyz/24-05-14.html

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd

Audit H/M report Summary from sherlock olympus

1.getBunniTokenPrice wrongly returns the total price of all tokens

The function getBunniTokenPrice() is supposed to return the price of 1 Bunni token (1 share) like all other feeds, but it doesn't. It returns the total price of all minted tokens/shares for a specific pool (total value of position's reserves) which is wrong.

2.Possible incorrect price for tokens in Balancer stable pool due to amplification parameter update

3.Incorrect StablePool BPT price calculation

`getProtocolOwnedLiquidityOhm` is in scope , but any user can invoke deposit to add assets, deposit is not in scope.need to check those out of scope functions.

4.getReservesByCategory() when useSubmodules =true and submoduleReservesSelector=bytes4(0) will revert

when add not check parameters lead to some category can't get Reserves

5.Price calculation can be manipulated by intentionally reverting some of price feeds

when use uniswap and balancer to calculate price, both price feed can revert due to re-enter lead to user can manipulate price.

Audit H/M report Summary from c4 rl

0.learned from this contest

  • use selfdestruct can send eth to any contract,even contract not implement receive{}

1.Bidder can use donations to get VerbsToken from auction that already ended.

protocol use address(this).balance to check if bid amount is bigger than current eth value in contract , attacker can send eth to protocol to bypass above check.
need to take care of address(this).balance check.

2.Incorrect amounts of ETH are transferred to the DAO treasury in ERC20TokenEmitter::buyToken(), causing a value leak in every transaction

need to track eth

3.VerbsToken.tokenURI() is vulnerable to JSON injection attacks

4.encodedData argument of hashStruct is not calculated perfectly for EIP712 singed messages in CultureIndex.sol

hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s))

5.ERC20TokenEmitter::buyToken function mints more tokens to users than it should do

6.Anyone can pause AuctionHouse in _createAuction #195

when mint revert the protocol gonna be paused,attacker can use specific amount of gas to send transaction which can lead to transaction OOG.

Audit H/M report Summary from NextGen C4

1.Attacker can reenter to mint all the collection supply

when mint ERC721 token,protocol invoke minter’s onERC721Received function can lead to re-entrancy issue.

2.Attacker can drain all ETH from AuctionDemo when block.timestamp == auctionEndTime

3.Adversary can block claimAuction() due to push-strategy to transfer assets to multiple bidders

claimAuction() implements a push-strategy instead of a pull-strategy for returning the bidders funds.
protocol invoke user’s address to send eth,user can implement revert on receive{} function to brick the process.

4.Multiple mints can brick any form of salesOption 3 mintings

1
2
lastMintDate[col] = collectionPhases[col].allowlistStartTime
+ (collectionPhases[col].timePeriod * (gencore.viewCirSupply(col) - 1));

due to mint at different period , first mint period extend time result in lastMintDate bigger than second period’s start time.

5.MinterContract::payArtist can result in double the intended payout

inconsistent between set percent and calculate acturally pay out.

Audit H/M report Summary from Notional sherlock

1.Weighted pool spot price calculation is incorrect

Notional calculate the spot price of Weighted pool using the balances of token. Which need to be upscaled according to the balancer but Notional doesn’t.

Not farmilar with balancer wighted pool.

2.Single-sided instead of proportional exit is performed during emergency exit

Single-sided instead of proportional exit is performed during emergency exit, which could lead to a loss of assets during emergency exit and vault restoration.

Comment should use proportionally withdraw but used single token out , pay more attention on comments.

3.Native ETH not received when removing liquidity from Curve V2 pools

Not farmilar with Curve V2 pool removing LP.

4.Different spot prices used during the comparison

5.Incorrect invariant used for Balancer’s composable pools

Not farmilar with balancer need to check balancer contract next time.

6.Fewer than expected LP tokens if the pool is imbalanced during vault restoration

The vault restoration function intends to perform a proportional deposit

If the pool is imibalanced not 1:1?

OlympusRBS2

What is Olympus?

  • Defi
  • Leverages
  • Swap(RBS)
  • Loans

OHM && gOHM

OHM aims to fill the gap between fiat stablecoins and volatile crypto assets, providing relative stability and scalability in a fully autonomous way with no off-chain dependencies
OHM:0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5

gOHM:0x0ab87046fBb341D058F17CBC4c1133F25a20a52f

Treasury

he Treasury represents all assets owned and controlled by the protocol

Protocol Owned Liquidity

Dex

Uniswap v3 pool OHM/wETH

RBS

Provide OHM/DAI LP

Range Bound Stability

Cooler Loans

Cooler Loans is a decentralized lending facility that allows OHM (Olympus) token holders to borrow DAI by using their gOHM (governance OHM) tokens as collateral.

Cross-Chain Bridge

The OHM token is available cross-chain! Olympus utilizes LayerZero
Ethereum: 0x45e563c39cddba8699a90078f42353a57509543a
Arbitrum: 0x20B3834091f038Ce04D8686FAC99CA44A0FB285c

Audit H/M report Summary from Ethena code4rena

1.FULL_RESTRICTED Stakers can bypass restriction through approvals

The openzeppelin ERC4626 contract allows approved address to withdraw and redeem on behalf of another address so far there is an approval。
Encountering an unfamiliar EIP requires a thorough understanding of its code before proceeding with the subsequent audit.

2.Soft Restricted Staker Role can withdraw stUSDe for USDe

The code does not satisfy that condition, when a holder has the SOFT_RESTRICTED_STAKER_ROLE, they can exchange their stUSDe for USDe using StakedUSDeV2。
When faced with strictly limiting conditions, it is essential to consider whether there are any possible ways to bypass them through various means.

3.users still forced to follow previously set cooldownDuration even when cooldown is off (set to zero) before unstaking

The main issue with this question is that when setting global parameters, the impact on previous users was not taken into consideration

4.Malicious users can front-run to cause a denial of service (DoS) for StakedUSDe due to MinShares checks

This issue arises from the fact that the first user of the vault can have a certain impact on subsequent users. Therefore, it is important to be mindful of the effects of the initial configuration when handling shares.

Audit H/M report Summary from WildCat code4rena

1.check contract exist using bytes32(0)

<address>.codehash != bytes32(0) check is insufficient to determine if an address has existing code
An account is considered empty when it has no code and zero nonce and zero balance.
if anyone transfers 1 wei to an address, .codehash will return keccak256("") instead of bytes32(0)

Fix for this is easier than suggested - just change from x.codehash != bytes32(0) to x.code.length != 0

2.Borrower has no way to update maxTotalSupply of market or close market

Developer define a function but no way to interact with it

3.When withdrawalBatchDuration is set to zero lenders can withdraw more then allocated to a batch

wirte a full test 

4.Any address can withdraw from markets contrary

function access control

5.Borrower can drain all funds of a sanctioned lender

missmatched parmeters in different functions.

Audit H/M report Summary from Allo sherlock

1.protocol use CREATE3 that’s not avaliable in the Zksync Era.

2.two modifer not cover block.timestamp == allocationEndTime.

1
2
3
4
5
6
7
8
9
10
11
12
13
function _checkOnlyActiveAllocation() internal view {
if (allocationStartTime > block.timestamp || block.timestamp > allocationEndTime) {
revert ALLOCATION_NOT_ACTIVE();
}
}

/// @notice Checks if the allocation has ended and reverts if not.
/// @dev This will revert if the allocation has not ended.
function _checkOnlyAfterAllocation() internal view {
if (block.timestamp < allocationEndTime) {
revert ALLOCATION_NOT_ENDED();
}
}

When block.timestamp == allocationEndTime none of above modifers revert.

3.Manager vote every recipient to update his state once reacted the threshold

Since we have serval states protocol not check if user have already reacted the threshold.

4._distribute amount not correctly calculated.

5.missing access modifer

6.Can not create a pool by cloning strategies on zkSync network

Can not create pool by cloning strategies on zkSync network because of different behaviors from EVM instructions between zkSync and Ethereum

7.RFPSimpleStrategy milestones can be set multiple times

1
if (upcomingMilestone != 0) revert MILESTONES_ALREADY_SET();

the value of upcomingMilestone not be updated after set milestones lead to manager can set milestones mutiple times.

8.fundPool does not work with fee-on-transfer token

protocol increase amount directly

origin

https://oneclicktoken.xyz/23-10-17.html

contact me for SC private review

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd

Audit H/M report Summary from Ondo code4rena

1.Chain support chain cannot be removed or cleared in bridge contracts.

Contracts provide a way to add support for a chain; however, they do not provide a way to delete it.
When a contract offers an addition method, it’s important to consider whether it should also provide a deletion method

2.Contract use msg.sender as remote chain receiver when bridge token.

AA wallet like safe has different wallet address on different chains.
This vulnerability requires us to be familiar with some commonly used third-party tools in order to discover it

3.Two different transactions can result in the same txnHash value, thus breaking the approval process of transaction minting

1
txnHashToTransaction[txnHash] = Transaction(srcSender, amt); 

Consider whether the keys in similar mappings are always unique?

4.Admin can’t burn tokens from blocklisted addresses because of a check in _beforeTokenTransfer

The burn operation prematurely calls the beforeTransfer function, but beforeTransfer needs to check if the address is blocked, which hinders the successful execution of the burn

origin

https://oneclicktoken.xyz/23-10-13.html

contact me for SC private review

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd

Audit H/M report Summary from xETH code4rena

1.Invalid Validation

Percentage of xETH in the Curve pool is between REBALANCE_UP_THRESHOLD and REBALANCE_DOWN_THRESHOLD. Rebalance operations are allowed When percentage of xETH is out of range.But the amount of xETH not checked which could lead to Percentage out of range after rebalance.

2.Invalid Validation

The value of the check does not match the expectation

3.ERC20

zero amount token transfer can cause DOS.

4.ERC20

check allowance == 0 should only use in initial or resetting to zero.

5.ERC20

when totalSupply() == 0

6.ERC20

bc of xETH.balanceOf(address(this)),staker can inflate the exchange rate by transferring tokens directly to the contract

7.MEV bot

uncorrect calculate slippage value.

8.Context

an array has add but no remove function.

9.ERC20

transfer token to target contract but can’t withdraw

origin

https://oneclicktoken.xyz/23-08-21.html

contact me

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd

Audit H/M report Summary from Lybra code4rena

1.Access Control

The recipient of the flash loan will burn a certain amount of tokens as a fee. The recipient can be set to an arbitrary user who impelement receiver.onFlashLoan function.

In certain token-consuming operations, it’s necessary to consider whether only oneself is able to perform this action

2.Context

3.Common

Incorrectly implemented modifiers.

Always check modifiers revert?

4.Upgradable

Logic contact constructor should not update storage variable,cuz it will not be reflected in the proxy’s state.Instead we need use initializer function.

5.Precision Lost

user share can be burned cuz _EUSDAmount.mul(_totalShares).div(totalMintedEUSD) to zero.

6.Context

When using conditional statements, verify whether the values on the left and right sides of the equation meet the expected criteria.

7.Context

threshold value if different from the explanation in the document.

8.Dos

use IVault(pool).vaultType() vaultType can’t be a internel variable.

9.Math(M4)

when calculate token per second use wrong total amount.

10.Math(M6)

use a fixed 1e18 when calculating reward.

11.Context(M7)

not checking locking state when withdraw fund.

12.Context(M8)

calculate total debt not include fee.

How to use chainlink on your own evm contract

what is blockchain oracle?

As we know , blockchains are self-contained worlds.Smart contract can’t get information by invoking api like we do in web2.At the same time,centralized data providers are also not very reliable.For example,we need to know the price ETH/USDC when we swaping on defi like uniswap.Can we trust one single centralized data provider?The answer is obvious.So we need some decentralized data providers.Oracel act as an message bridge between blockchain and off-chain data provider.

why do SMART CONTRACTS need oracle?

To achieve deterministic execution, blockchains limit nodes to reaching consensus on simple binary (true/false) questions using only data stored on the blockchain itself.If blockchain get information from external data provider it would be impossible to achieve determinism.Different blockchain node maybe return different executes result.To solve those problems we create oracles.Oracle smart contract taking information from off-chain sources and then store the information on the blockchain.Since information stored on-chain is unalterable and publicly available, Ethereum nodes can safely use the oracle imported off-chain data to compute state changes without breaking consensus

Let’s dive into an example,assume that we need to get the BTC/USD price on our smart contract.
Chainlink provide data feed server on several networks.

  • Ethereum Mainnet
  • Sepolia Testnet
  • Gogerli Testnet

We gonna test the data feed server on goerli testnet by foundry tool.

First we need to import chainlink contract interface @chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol.

Secondly,let’s init the smart contract using a specify target smart contract address which we copy from chainlink official document website.

Lastly,we just need invoke latestRoundData function which return the current price about BTC/USD.

Here is the whole code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

contract DataConsumerV3 {
AggregatorV3Interface internal dataFeed;

constructor() {
dataFeed = AggregatorV3Interface(
0xA39434A63A52E749F02807ae27335515BA4b07F7
);
}

function getLatestData() public view returns (int) {
// prettier-ignore
(
/* uint80 roundID */,
int answer,
/*uint startedAt*/,
/*uint timeStamp*/,
/*uint80 answeredInRound*/
) = dataFeed.latestRoundData();
return answer;
}
}

we can use forge create command to deploy the contract.And then we invoke the getLatestData function via cast call command.Trust me you’ll get some return like this:

1
2
eth_goerli
0x000000000000000000000000000000000000000000000000000002a6386b1c96

If you are using Chainlink Data Feeds on L2 networks like Arbitrum, Optimism, and Metis, you must also check the latest answer from the L2 Sequencer Uptime Feed to ensure that the data is accurate in the event of an L2 sequencer outage

origin

https://oneclicktoken.xyz/23-07-28.html

contact me

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd

All you need to know about openzeppelin access control

what is access control

Alt access control

Basiclly,access control means “who is allowed to do this thing”.It’s quite important for smart contract to specify a particular address who can totally control the whole system.Therefor it’s critical to fully understand how to use access control before using it in your project or just copy some example code from somewhere.
In openzeppelin there are mainly two ways to implementing access control.

  • Single onlyOwner role
  • Role-Based Access Control (RBAC)

Let’s dive into how to master this 2 ways.

master how to use it by practice

OnlyOwner

1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyContract is Ownable {
function normalThing() public {
// anyone can call this normalThing()
}

function specialThing() public onlyOwner {
// only the owner can call specialThing()!
}
}

let’s create two user for our test.

  • owner address
  • arbitrary address
1
2
address owner = address(0x100);
address arbitrary = address(0x101);

and the we use owner address to create the smart contract,it’s quite simple you don’t even need to pass any parameters.openzeppelin Ownable would set msg.sender as it’s owner as default.we use foundry for testing,if you’re not familiar with foundry,go and learn it first before proceeding further.

Firstly,we use owner address create our smart contract above in foundry test contract setUp function.

1
2
3
4
5
//let's set address owner to msg.sender.
vm.prank(owner);

//create the contract.
myContract = new MyContract();

And then we use the arbitrary address to invoke the specialThing function which is protected by onlyOwner modifier.As expect the invoke revert because of access control.Here goes the code:

1
2
3
4
//use arbitrary address invoke specialThing() function.
vm.prank(arbitrary);
vm.expectRevert("Ownable: caller is not the owner");
myContract.specialThing();

Finally,we use our owner who master the contract invoke the specialThing function.As you see the owne succeed.

1
2
3
//switch to owner.
vm.prank(owner);
myContract.specialThing();

If you want the whole example codes go for:https://gist.github.com/coffiasd/84552b5a33845fa567bfc3aa5204d460
If you have any questions, you can also reach me through the contact information below.

Role-Based Access Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken2 is ERC20, AccessControl {
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor(address minter) ERC20("MyToken", "TKN") {
_grantRole(DEFAULT_ADMIN_ROLE, minter);
// Grant the minter role to a specified account
_grantRole(MINTER_ROLE, minter);
}

function mint(address to, uint256 amount) public {
// Check that the calling account has the minter role
require(hasRole(MINTER_ROLE, msg.sender), "Caller is not a minter");
_mint(to, amount);
}
}

We can create whatever role we want in Role-Based Access Control (RBAC) contract.Let’s say we want create a minter role who can mint some ERC20 token.We can use a special constant bytes32 as the name of the role like this:

1
2
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

Firstly,we use owner address as our minter to create our access control contract.

1
myToken = new MyToken2(owner);

And then we use the arbitrary user to mint some ERC20 token:

1
2
3
4
//use arbitrary address invoke mint() function.
vm.prank(arbitrary);
vm.expectRevert("Caller is not a minter");
myToken.mint(address(this), 1);

the above invoke would be revert because of arbitrary don’t hold the minter role.

1
2
3
//switch to owner.
vm.prank(owner);
myToken.mint(address(this), 1);

If we switch msg.sender to owner the call would successed.

What’s more? we can use owner address to grant a minter role whoever we want.Let’s say we grant the minter role to the arbitrary address:

1
2
3
4
5
vm.prank(owner);
myToken.grantRole(keccak256("MINTER_ROLE"), arbitrary);

vm.prank(arbitrary);
myToken.mint(address(this), 1);

After invoke the grantRole function the arbitrary has the right to mint token.

At the same time,arbitrary address don’t has the right to grant role to another address:

1
2
3
vm.prank(arbitrary);
vm.expectRevert();
myToken.grantRole(keccak256("MINTER_ROLE"), address(0x1001));

As the owner we also have the right to revoke role we grant before:

1
2
3
4
5
6
vm.prank(owner);
myToken.revokeRole(keccak256("MINTER_ROLE"), arbitrary);

vm.prank(arbitrary);
vm.expectRevert("Caller is not a minter");
myToken.mint(address(this), 1);

After we invoke role of arbitrary,he can’t mint ERC20 token anymore.

If you want the whole example codes go for:https://gist.github.com/coffiasd/340241d63980dc9d423e4ece2f9b20db
If you have any questions, you can also reach me through the contact information below.

let’s connect

Twitter:https://twitter.com/coffiasse
Github:https://github.com/coffiasd
TG:@coffiasd