-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
145 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |