Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

towns token #1990

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@prb/math": "^4.1.0",
"@river-build/diamond": "github:river-build/diamond#5b9f60d7ff0d4ff99c560d95c15a5e7e8caf922a",
"account-abstraction": "github:eth-infinitism/account-abstraction",
"solady": "^0.0.267"
"solady": "^0.0.289"
},
"devDependencies": {
"@prb/test": "^0.6.4",
Expand Down
61 changes: 61 additions & 0 deletions contracts/scripts/deployments/utils/DeployTownsBase.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

//interfaces

//libraries

//contracts
import {Deployer} from "contracts/scripts/common/Deployer.s.sol";
import {Towns} from "contracts/src/tokens/towns/base/Towns.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract DeployTownsBase is Deployer {
address public bridgeBase; // L2StandardBridge
address public l1Token;

function versionName() public pure override returns (string memory) {
return "towns";
}

function __deploy(address deployer) public override returns (address) {
l1Token = _getToken();
bridgeBase = _getBridge(deployer);

vm.startBroadcast(deployer);
address implementation = address(
new Towns({_bridge: bridgeBase, _remoteToken: l1Token})
);

address proxy = address(
new ERC1967Proxy(
implementation,
abi.encodeCall(Towns.initialize, deployer)
)
);

vm.stopBroadcast();

return proxy;
}

function _getBridge(address deployer) internal view returns (address) {
if (block.chainid == 31337 || block.chainid == 31338) {
return deployer;
} else {
return 0x4200000000000000000000000000000000000010;
}
}

function _getToken() internal view returns (address) {
if (block.chainid == 8453) {
// if deploying to base use mainnet token
return address(0);
} else if (block.chainid == 84532) {
// if deploying to base-sepolia use sepolia token
return address(0);
} else {
return address(0);
}
}
}
42 changes: 42 additions & 0 deletions contracts/scripts/deployments/utils/DeployTownsMainnet.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

//interfaces
import {ITownsBase} from "contracts/src/tokens/towns/mainnet/ITowns.sol";

//libraries

//contracts
import {Deployer} from "contracts/scripts/common/Deployer.s.sol";
import {Towns} from "contracts/src/tokens/towns/mainnet/Towns.sol";

contract DeployTownsMainnet is Deployer, ITownsBase {
address public constant vault =
address(0x23b168657744124360d3553F3bc257f3E28cBaf9);
address public constant manager =
address(0x18038ee5692FCE1cc0B0b3F2D63e09639A597F94);

function inflationConfig() public pure returns (InflationConfig memory) {
return
InflationConfig({
initialMintTime: 1_709_667_671, // Tuesday, March 5, 2024 7:41:11 PM
initialInflationRate: 800,
finalInflationRate: 200,
inflationDecayRate: 600,
finalInflationYears: 20,
inflationReceiver: vault
});
}

function versionName() public pure override returns (string memory) {
return "townsMainnet";
}

function __deploy(address deployer) public override returns (address) {
vm.broadcast(deployer);
return
address(
new Towns({vault: vault, manager: manager, config: inflationConfig()})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,34 @@ pragma solidity ^0.8.23;
// contracts

interface IVotesEnumerable {
/// @notice Get all delegators who have delegated their voting power
/// @return Array of delegator addresses
function getDelegators() external view returns (address[] memory);

/// @notice Get the total number of delegators
/// @return Total number of delegators
function getDelegatorsCount() external view returns (uint256);

/// @notice Get a paginated list of delegators
/// @param cursor The starting index for pagination
/// @param size The number of delegators to return
/// @return delegators Array of delegator addresses for the requested page
/// @return next The cursor for the next page, returns 0 if no more pages
function getPaginatedDelegators(
uint256 cursor,
uint256 size
) external view returns (address[] memory delegators, uint256 next);

/// @notice Get all delegators who have delegated their voting power to a specific account
/// @param account The delegatee address to get delegators for
/// @return Array of delegator addresses who delegated to the specified account
function getDelegatorsByDelegatee(
address account
) external view returns (address[] memory);

/// @notice Get the timestamp when a delegator last delegated their voting power
/// @param account The delegator address to get delegation time for
/// @return Timestamp of the last delegation, returns 0 if never delegated
function getDelegationTimeForDelegator(
address account
) external view returns (uint256);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ abstract contract VotesEnumerable is IVotesEnumerable {
return VotesEnumerableStorage.layout().delegationTimeForDelegator[account];
}

function getDelegatorsCount() external view returns (uint256) {
return VotesEnumerableStorage.layout().delegators.length();
giuseppecrj marked this conversation as resolved.
Show resolved Hide resolved
}

function getPaginatedDelegators(
uint256 cursor,
uint256 size
) external view returns (address[] memory, uint256 next) {}

function _setDelegators(
address account,
address newDelegatee,
Expand Down
194 changes: 194 additions & 0 deletions contracts/src/tokens/towns/base/Towns.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

// interfaces
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IOptimismMintableERC20, ILegacyMintableERC20} from "contracts/src/tokens/river/base/IOptimismMintableERC20.sol";
import {ISemver} from "contracts/src/tokens/river/base/ISemver.sol";

// libraries

// contracts
import {Initializable} from "solady/utils/Initializable.sol";
import {UUPSUpgradeable} from "solady/utils/UUPSUpgradeable.sol";
import {IntrospectionBase} from "@river-build/diamond/src/facets/introspection/IntrospectionBase.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {ERC20Votes} from "solady/tokens/ERC20Votes.sol";

contract Towns is
IOptimismMintableERC20,
ILegacyMintableERC20,
ISemver,
IntrospectionBase,
Initializable,
ERC20Votes,
UUPSUpgradeable,
Ownable
shuhuiluo marked this conversation as resolved.
Show resolved Hide resolved
shuhuiluo marked this conversation as resolved.
Show resolved Hide resolved
{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Constants & Immutables */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Semantic version.
string public constant version = "1.3.0";
shuhuiluo marked this conversation as resolved.
Show resolved Hide resolved

/// @notice The name of the token
string public constant NAME = "Towns";

/// @notice The symbol of the token
string public constant SYMBOL = "TOWNS";

/// @notice The name hash of the token
bytes32 public constant NAME_HASH = keccak256(bytes(NAME));
giuseppecrj marked this conversation as resolved.
Show resolved Hide resolved

///@notice Address of the corresponding version of this token on the remote chain
address public immutable REMOTE_TOKEN;

/// @notice Address of the StandardBridge on this network.
address public immutable BRIDGE;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Modifiers */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice A modifier that only allows the bridge to call
modifier onlyBridge() {
require(msg.sender == BRIDGE, "Towns: only bridge can mint and burn");
_;
}

constructor(address _bridge, address _remoteToken) {
// set the bridge
BRIDGE = _bridge;

// set the remote token
REMOTE_TOKEN = _remoteToken;

_disableInitializers();
}

function initialize(address _owner) external initializer {
// set the owner
_initializeOwner(_owner);

// add interface
_addInterface(type(IERC20).interfaceId);
_addInterface(type(IERC20Metadata).interfaceId);
_addInterface(type(IERC20Permit).interfaceId);
shuhuiluo marked this conversation as resolved.
Show resolved Hide resolved
_addInterface(type(IERC6372).interfaceId);
_addInterface(type(IERC165).interfaceId);
_addInterface(type(IVotes).interfaceId);
_addInterface(type(IOptimismMintableERC20).interfaceId);
_addInterface(type(ILegacyMintableERC20).interfaceId);
_addInterface(type(ISemver).interfaceId);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Bridge */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @custom:legacy
/// @notice Legacy getter for the remote token. Use REMOTE_TOKEN going forward.
function l1Token() external view returns (address) {
return REMOTE_TOKEN;
}

/// @custom:legacy
/// @notice Legacy getter for the bridge. Use BRIDGE going forward.
function l2Bridge() external view returns (address) {
return BRIDGE;
}

/// @custom:legacy
/// @notice Legacy getter for REMOTE_TOKEN
function remoteToken() external view returns (address) {
return REMOTE_TOKEN;
}

/// @custom:legacy
/// @notice Legacy getter for BRIDGE.
function bridge() external view returns (address) {
return BRIDGE;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Introspection */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @inheritdoc IERC165
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return _supportsInterface(interfaceId);
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Overrides */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function name() public pure override returns (string memory) {
return NAME;
}

function symbol() public pure override returns (string memory) {
return SYMBOL;
}

/// @notice Allows the StandardBridge on this network to mint tokens.
/// @param to Address to mint tokens to.
/// @param amount Amount of tokens to mint.
function mint(
address to,
uint256 amount
) external override(IOptimismMintableERC20, ILegacyMintableERC20) onlyBridge {
_mint(to, amount);
}

/// @notice Allows the StandardBridge on this network to burn tokens.
/// @param from Address to burn tokens from.
/// @param amount Amount of tokens to burn.
function burn(
address from,
uint256 amount
) external override(IOptimismMintableERC20, ILegacyMintableERC20) onlyBridge {
_burn(from, amount);
}

/// @notice Clock used for flagging checkpoints, overridden to implement timestamp based
/// checkpoints (and voting).
function clock() public view override returns (uint48) {
return uint48(block.timestamp);
}

/// @notice Machine-readable description of the clock as specified in EIP-6372.
function CLOCK_MODE() public pure override returns (string memory) {
return "mode=timestamp";
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* Internal Overrides */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}

/// @dev Override the name hash to be a constant value for performance in EIP-712
function _constantNameHash() internal pure override returns (bytes32) {
return NAME_HASH;
}

/// @dev This allows Permit2 to be used without prior approval.
function _givePermit2InfiniteAllowance()
internal
pure
override
returns (bool)
{
return true;
}
}
Loading
Loading