Skip to content

Commit

Permalink
chore: merge base branch & fix conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
0xteddybear committed Aug 23, 2024
2 parents 99b26fa + 407cc76 commit 542b539
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 95 deletions.
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ test: build-go-ffi
test-kontrol:
./test/kontrol/scripts/run-kontrol.sh script

test-medusa:
FOUNDRY_PROFILE=medusa medusa fuzz
test-medusa timeout='100':
FOUNDRY_PROFILE=medusa medusa fuzz --timeout {{timeout}}


test-halmos-all VERBOSE="-v":
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/test/properties/PROPERTIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ legend:
## Variable transition

| id | milestone | description | halmos | medusa |
| --- | ------------------- | ------------------------------------------------------------------------------------------------- | ------ | ------ |
| --- | --- | --- | --- | --- |
| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] |
| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] |
| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { ProtocolHandler } from "./handlers/Protocol.handler.t.sol";
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol";
import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";

contract ProtocolProperties is ProtocolHandler {
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;

// TODO: will need rework after
// - non-atomic bridge
// - `convert`
/// @custom:property-id 24
/// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)-
/// convert(super, legacy)
function property_totalSupplyAcrossChainsEqualsMints() external view {
// iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other
for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) {
uint256 totalSupply = 0;
(bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex);
// and then over all the (mocked) chain ids where that supertoken could be deployed
for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) {
address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt);
if (supertoken != address(0)) {
totalSupply += OptimismSuperchainERC20(supertoken).totalSupply();
}
}
assert(trackedSupply == totalSupply);
}
}

/// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId
/// @custom:property-id 14
/// @custom:property supertoken total supply starts at zero
function property_DeployNewSupertoken(
TokenDeployParams memory params,
uint256 chainId
)
external
validateTokenDeployParams(params)
{
chainId = bound(chainId, 0, MAX_CHAINS - 1);
OptimismSuperchainERC20 supertoken = _deploySupertoken(
remoteTokens[params.remoteTokenIndex],
WORDS[params.nameIndex],
WORDS[params.symbolIndex],
DECIMALS[params.decimalsIndex],
chainId
);
// 14
assert(supertoken.totalSupply() == 0);
}

/// @custom:property-id 22
/// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in
/// destination chain exactly by the input amount
/// @custom:property-id 23
/// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly
/// by the input amount
function property_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external {
destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1);
fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1);
OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]);
OptimismSuperchainERC20 destinationToken =
MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId);
// TODO: when implementing non-atomic bridging, allow for the token to
// not yet be deployed and funds be recovered afterwards.
require(address(destinationToken) != address(0));
uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender);
uint256 sourceSupplyBefore = sourceToken.totalSupply();
uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender);
uint256 destinationSupplyBefore = destinationToken.totalSupply();

vm.prank(msg.sender);
try sourceToken.sendERC20(msg.sender, amount, destinationChainId) {
uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender);
uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender);
// no free mint
assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter);
// 22
assert(sourceBalanceBefore - amount == sourceBalanceAfter);
assert(destinationBalanceBefore + amount == destinationBalanceAfter);
uint256 sourceSupplyAfter = sourceToken.totalSupply();
uint256 destinationSupplyAfter = destinationToken.totalSupply();
// 23
assert(sourceSupplyBefore - amount == sourceSupplyAfter);
assert(destinationSupplyBefore + amount == destinationSupplyAfter);
} catch {
assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { Test } from "forge-std/Test.sol";
import { TestBase } from "forge-std/Base.sol";
import { StdUtils } from "forge-std/StdUtils.sol";

import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol";
import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { MockL2ToL2CrossDomainMessenger } from "../helpers/MockL2ToL2CrossDomainMessenger.t.sol";
import { MockL2ToL2CrossDomainMessenger } from "../../helpers/MockL2ToL2CrossDomainMessenger.t.sol";

contract ProtocolAtomicFuzz is Test {
contract ProtocolHandler is TestBase, StdUtils {
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;

uint8 internal constant MAX_CHAINS = 4;
Expand Down Expand Up @@ -61,108 +62,24 @@ contract ProtocolAtomicFuzz is Test {
_;
}

/// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId
/// @custom:property-id 14
/// @custom:property supertoken total supply starts at zero
function fuzz_DeployNewSupertoken(
TokenDeployParams memory params,
uint256 chainId
)
external
validateTokenDeployParams(params)
{
chainId = bound(chainId, 0, MAX_CHAINS - 1);
OptimismSuperchainERC20 supertoken = _deploySupertoken(
remoteTokens[params.remoteTokenIndex],
WORDS[params.nameIndex],
WORDS[params.symbolIndex],
DECIMALS[params.decimalsIndex],
chainId
);
// 14
assert(supertoken.totalSupply() == 0);
}

/// @custom:property-id 22
/// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in
/// destination chain exactly by the input amount
/// @custom:property-id 23
/// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly
/// by the input amount
function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external {
destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1);
fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1);
OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]);
OptimismSuperchainERC20 destinationToken =
MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId);
// TODO: when implementing non-atomic bridging, allow for the token to
// not yet be deployed and funds be recovered afterwards.
require(address(destinationToken) != address(0));
uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender);
uint256 sourceSupplyBefore = sourceToken.totalSupply();
uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender);
uint256 destinationSupplyBefore = destinationToken.totalSupply();

vm.prank(msg.sender);
try sourceToken.sendERC20(msg.sender, amount, destinationChainId) {
uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender);
uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender);
// no free mint
assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter);
// 22
assert(sourceBalanceBefore - amount == sourceBalanceAfter);
assert(destinationBalanceBefore + amount == destinationBalanceAfter);
uint256 sourceSupplyAfter = sourceToken.totalSupply();
uint256 destinationSupplyAfter = destinationToken.totalSupply();
// 23
assert(sourceSupplyBefore - amount == sourceSupplyAfter);
assert(destinationSupplyBefore + amount == destinationSupplyAfter);
} catch {
assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount);
}
function handler_MockNewRemoteToken() external {
_deployRemoteToken();
}

/// @notice pick one already-deployed supertoken and mint an arbitrary amount of it
/// necessary so there is something to be bridged :D
/// TODO: will be replaced when testing the factories and `convert()`
function fuzz_MintSupertoken(uint256 index, uint96 amount) external {
function handler_MintSupertoken(uint256 index, uint96 amount) external {
index = bound(index, 0, allSuperTokens.length - 1);
address addr = allSuperTokens[index];
vm.prank(BRIDGE);
// medusa calls with different senders by default
OptimismSuperchainERC20(addr).mint(msg.sender, amount);
// currentValue will be zero if key is not present
(,uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr));
(, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr));
ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount);
}

// TODO: will need rework after
// - non-atomic bridge
// - `convert`
/// @custom:property-id 24
/// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)-
/// convert(super, legacy)
/// @notice deliberately not a view method so medusa runs it but not the view methods defined by Test
function property_totalSupplyAcrossChainsEqualsMints() external {
// iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other
for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) {
uint256 totalSupply = 0;
(bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex);
// and then over all the (mocked) chain ids where that supertoken could be deployed
for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) {
address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt);
if (supertoken != address(0)) {
totalSupply += OptimismSuperchainERC20(supertoken).totalSupply();
}
}
assert(trackedSupply == totalSupply);
}
}

function fuzz_MockNewRemoteToken() external {
_deployRemoteToken();
}

/// @notice deploy a remote token, that supertokens will be a representation of. They are never called, so there
/// is no need to actually deploy a contract for them
function _deployRemoteToken() internal {
Expand All @@ -183,7 +100,7 @@ contract ProtocolAtomicFuzz is Test {
uint256 chainId
)
internal
returns(OptimismSuperchainERC20 supertoken)
returns (OptimismSuperchainERC20 supertoken)
{
// this salt would be used in production. Tokens sharing it will be bridgable with each other
bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals));
Expand Down

0 comments on commit 542b539

Please sign in to comment.