Summary
If a user locks a certain amount of sdl tokens for an arbitrary amount of time, he can get benefited from bridging his token between chains and have to wait less time than expected.
Vulnerability Details
block.timestamp
can differ between chains due to the differences of average block time. This difference of times can be benefited from users by bridging his locks between chains because when a lock is bridged, the timestamps when the lock was created are sent along.
https://github.com/Cyfrin/2023-12-stake-link/blob/main/contracts/core/ccip/RESDLTokenBridge.sol#L226-L228
RESDLTokenBridge
function _buildCCIPMessage(
address _receiver,
uint256 _tokenId,
ISDLPool.RESDLToken memory _reSDLToken,
address _destination,
address _feeTokenAddress,
bytes memory _extraArgs
) internal view returns (Client.EVM2AnyMessage memory) {
Client.EVMTokenAmount[] memory tokenAmounts = new Client.EVMTokenAmount[](1);
Client.EVMTokenAmount memory tokenAmount = Client.EVMTokenAmount({
token: address(sdlToken),
amount: _reSDLToken.amount
});
tokenAmounts[0] = tokenAmount;
Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
receiver: abi.encode(_destination),
data: abi.encode(
_receiver,
_tokenId,
_reSDLToken.amount,
_reSDLToken.boostAmount,
_reSDLToken.startTime,
_reSDLToken.duration,
_reSDLToken.expiry
),
tokenAmounts: tokenAmounts,
extraArgs: _extraArgs,
feeToken: _feeTokenAddress
});
return evm2AnyMessage;
}
If we go to Arbitrum docs we can find:
Block timestamps: Arbitrum vs. Ethereum
Block timestamps on Arbitrum are not linked to the timestamp of the L1 block. They are updated every L2 block based on the sequencer's clock. These timestamps must follow these two rules:
Must be always equal or greater than the previous L2 block timestamp
Must fall within the established boundaries (24 hours earlier than the current time or 1 hour in the future).
The same way happens for Optimism.
If we run the following python script we can see the block.timestamp differences:
from web3 import Web3
arbitrum_https = "your arbitrum https"
ethereum_https = "your ethereum https"
optimism_https = "your optimism https"
def get_arbitrum_block_timestamp(httpsAddress):
try:
web3 = Web3(Web3.HTTPProvider(httpsAddress))
if web3.is_connected():
block_number = web3.eth.block_number
block = web3.eth.get_block(block_number)
current_timestamp = block['timestamp']
return current_timestamp
else:
return "Failed to connect to the node"
except Exception as e:
return f"An error occurred: {e}"
ethereumTimestamp = get_arbitrum_block_timestamp(ethereum_https)
arbitrumTimestamp = get_arbitrum_block_timestamp(arbitrum_https)
optimismTimestamp = get_arbitrum_block_timestamp(optimism_https)
print("Ethereum: ", ethereumTimestamp)
print("Arbitrum: ", arbitrumTimestamp)
print("Optimism: ", optimismTimestamp)
Output:
Ethereum: 1704492011
Arbitrum: 1704492038
Optimism: 1704492023
Impact
Medium
Tools Used
Manual review
Recommendations
Do not allow to bridge a lock that has initiated the unlock of funds