Skip to content

Commit

Permalink
feat: add staked avail impl
Browse files Browse the repository at this point in the history
  • Loading branch information
QEDK committed Nov 19, 2024
1 parent d0e90b3 commit 5368f98
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ runs = 10000
[rpc_endpoints]
sepolia = "https://ethereum-sepolia.publicnode.com"
mainnet = "https://ethereum-rpc.publicnode.com"
base = "https://base-rpc.publicnode.com"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
18 changes: 18 additions & 0 deletions script/DeployStakedAvailWormhole.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.25;

import {TransparentUpgradeableProxy} from
"lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {StakedAvailWormhole} from "src/StakedAvailWormhole.sol";
import {Script} from "forge-std/Script.sol";

contract Deploy is Script {
function run() external {
vm.startBroadcast();
address admin = vm.envAddress("ADMIN");
address impl = address(new StakedAvailWormhole());
StakedAvailWormhole stavail = StakedAvailWormhole(address(new TransparentUpgradeableProxy(impl, admin, "")));
stavail.initialize(admin);
vm.stopBroadcast();
}
}
36 changes: 36 additions & 0 deletions src/StakedAvailWormhole.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.25;

import {
ERC20Upgradeable,
ERC20PermitUpgradeable
} from "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {AccessControlDefaultAdminRulesUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol";
import {INttToken} from "src/interfaces/INttToken.sol";

/// @title StakedAvail
/// @author Deq Protocol
/// @title Staked Avail ERC20 token with support for Wormhole
/// @notice A Staked Avail token implementation for Wormhole-based bridges
contract StakedAvailWormhole is AccessControlDefaultAdminRulesUpgradeable, ERC20PermitUpgradeable, INttToken {
bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor() {
_disableInitializers();
}

function initialize(address governance) external initializer {
__ERC20Permit_init("Staked Avail (Wormhole)");
__ERC20_init("Staked Avail (Wormhole)", "stAVAIL.W");
__AccessControlDefaultAdminRules_init(0, governance);
}

function mint(address account, uint256 amount) external onlyRole(MINTER_ROLE) {
_mint(account, amount);
}

function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
}
12 changes: 12 additions & 0 deletions src/interfaces/INttToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.25;

interface INttToken {
// NOTE: the `mint` method is not present in the standard ERC20 interface.
function mint(address account, uint256 amount) external;

// NOTE: NttTokens in `burn` mode require the `burn` method to be present.
// This method is not present in the standard ERC20 interface, but is
// found in the `ERC20Burnable` interface.
function burn(uint256 amount) external;
}
78 changes: 78 additions & 0 deletions test/StakedAvailWormhole.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.25;

import {StakedAvailWormhole} from "src/StakedAvailWormhole.sol";
import {TransparentUpgradeableProxy} from
"lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IAccessControl} from "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol";
import {Vm, Test} from "forge-std/Test.sol";

contract StakedAvailWormholeTest is Test {
StakedAvailWormhole stavail;
address owner;
address governance;
address minter;
bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE");

function setUp() external {
governance = makeAddr("governance");
minter = makeAddr("minter");
address impl = address(new StakedAvailWormhole());
stavail = StakedAvailWormhole(address(new TransparentUpgradeableProxy(impl, msg.sender, "")));
stavail.initialize(governance);
vm.prank(governance);
stavail.grantRole(MINTER_ROLE, minter);
}

function testRevert_initialize(address rand) external {
vm.expectRevert();
stavail.initialize(rand);
}

function test_initialize() external view {
assertEq(stavail.totalSupply(), 0);
assertNotEq(stavail.owner(), address(0));
assertEq(stavail.owner(), governance);
assertNotEq(stavail.name(), "");
assertEq(stavail.name(), "Staked Avail (Wormhole)");
assertNotEq(stavail.symbol(), "");
assertEq(stavail.symbol(), "stAVAIL.W");
}

function testRevertOnlyMinter_mint(address to, uint256 amount) external {
address rand = makeAddr("rand");
vm.assume(rand != minter);
vm.expectRevert(
abi.encodeWithSelector((IAccessControl.AccessControlUnauthorizedAccount.selector), rand, MINTER_ROLE)
);
vm.prank(rand);
stavail.mint(to, amount);
}

function test_mint(address to, uint256 amount) external {
vm.assume(to != address(0));
vm.prank(minter);
stavail.mint(to, amount);
assertEq(stavail.balanceOf(to), amount);
}

function test_burn(address from, uint256 amount) external {
vm.assume(from != address(0));
vm.prank(minter);
stavail.mint(from, amount);
assertEq(stavail.balanceOf(from), amount);
vm.prank(from);
stavail.burn(amount);
assertEq(stavail.balanceOf(from), 0);
}

function test_burn2(address from, uint256 amount, uint256 amount2) external {
vm.assume(from != address(0) && amount2 < amount);
vm.prank(minter);
stavail.mint(from, amount);
assertEq(stavail.balanceOf(from), amount);
vm.prank(from);
stavail.burn(amount2);
assertEq(stavail.balanceOf(from), amount - amount2);
}
}

0 comments on commit 5368f98

Please sign in to comment.