From caf578bccf5a325403f96eb102cfa0377ef82cf6 Mon Sep 17 00:00:00 2001 From: aazhou1 Date: Tue, 29 Oct 2024 11:30:48 -0700 Subject: [PATCH] term finance vault wrapped votes token --- .../deploy-sepolia-vault-wrapped-votes.yaml | 40 +++++++++++++ script/TermFinanceVaultWrappedVotesToken.sol | 31 ++++++++++ .../TermFinanceVaultWrappedVotesToken.sol | 57 +++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 .github/workflows/deploy-sepolia-vault-wrapped-votes.yaml create mode 100644 script/TermFinanceVaultWrappedVotesToken.sol create mode 100644 src/util/TermFinanceVaultWrappedVotesToken.sol diff --git a/.github/workflows/deploy-sepolia-vault-wrapped-votes.yaml b/.github/workflows/deploy-sepolia-vault-wrapped-votes.yaml new file mode 100644 index 00000000..59e4fd77 --- /dev/null +++ b/.github/workflows/deploy-sepolia-vault-wrapped-votes.yaml @@ -0,0 +1,40 @@ +name: "[sepolia-deploy] deploy vault wrapped votes" +on: + workflow_dispatch: + inputs: + vaultToken: + description: 'Vault token address' + required: true + default: '0x' + wrappedTokenName: + description: 'Wrapped Votes Token name' + required: true + default: '0x' + wrappedTokenSymbol: + description: 'Wrapped Votes Token Symbol' + required: true + default: '0x' + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: sepolia + url: https://term-finance.github.io/yearn-v3-term-vault/ + steps: + - uses: actions/checkout@master + with: + fetch-depth: 0 + submodules: recursive + - uses: foundry-rs/foundry-toolchain@v1 + - run: forge install + - run: forge build + - run: forge tree + - run: forge script script/TermFinanceVaultWrappedVotesToken.s.sol:DeployTermFinanceVaultWrappedVotesToken --rpc-url $RPC_URL --broadcast --verify --verbosity 4 + env: + RPC_URL: ${{ secrets.RPC_URL }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + VAULT_TOKEN: ${{ github.event.inputs.vaultToken }} + WRAPPED_TOKEN_NAME: ${{ github.event.inputs.wrappedTokenName }} + WRAPPED_TOKEN_SYMBOL: ${{ github.event.inputs.wrappedTokenSymbol }} \ No newline at end of file diff --git a/script/TermFinanceVaultWrappedVotesToken.sol b/script/TermFinanceVaultWrappedVotesToken.sol new file mode 100644 index 00000000..7efa8788 --- /dev/null +++ b/script/TermFinanceVaultWrappedVotesToken.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import "forge-std/Script.sol"; +import "../src/util/TermFinanceVaultWrappedVotesToken.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract DeployTermFinanceVaultWrappedVotesToken is Script { + function run() external { + uint256 deployerPK = vm.envUint("PRIVATE_KEY"); + + // Set up the RPC URL (optional if you're using the default foundry config) + string memory rpcUrl = vm.envString("RPC_URL"); + + vm.startBroadcast(deployerPK); + + // Retrieve environment variables + address vaultToken = vm.envAddress("VAULT_TOKEN"); + string memory name = vm.envString("WRAPPED_TOKEN_NAME"); + string memory symbol = vm.envString("WRAPPED_TOKEN_SYMBOL"); + + TermFinanceVaultWrappedVotesToken wrappedToken = new TermFinanceVaultWrappedVotesToken( + IERC20(vaultToken), + name, + symbol + ); + console.log("deployed wrapped token contract to"); + console.log(address(wrappedToken)); + vm.stopBroadcast(); + } +} diff --git a/src/util/TermFinanceVaultWrappedVotesToken.sol b/src/util/TermFinanceVaultWrappedVotesToken.sol new file mode 100644 index 00000000..8ec6c93c --- /dev/null +++ b/src/util/TermFinanceVaultWrappedVotesToken.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract TermFinanceVaultWrappedVotesToken is ERC20Votes, Ownable { + IERC20 public immutable underlyingToken; + + // Mapping to track the amount of underlying tokens deposited by each account + mapping(address => uint256) public deposits; + + event Wrapped(address indexed user, uint256 amount); + event Unwrapped(address indexed user, uint256 amount); + + constructor( + IERC20 _underlyingToken, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20Permit(name) { + underlyingToken = _underlyingToken; + } + + // Function to wrap the underlying tokens and mint ERC20Votes tokens + function wrap(uint256 amount) external { + require(amount > 0, "Amount must be greater than zero"); + + // Transfer the underlying tokens from the user to the contract + require(underlyingToken.transferFrom(msg.sender, address(this), amount), "Transfer failed"); + + // Track the deposit + deposits[msg.sender] += amount; + + // Mint ERC20Votes tokens to the user + _mint(msg.sender, amount); + + emit Wrapped(msg.sender, amount); + } + + // Function to unwrap the ERC20Votes tokens and retrieve the underlying tokens + function unwrap(uint256 amount) external { + require(amount > 0, "Amount must be greater than zero"); + require(balanceOf(msg.sender) >= amount, "Insufficient balance"); + + // Burn the ERC20Votes tokens + _burn(msg.sender, amount); + + // Reduce the deposit record + deposits[msg.sender] -= amount; + + // Transfer the underlying tokens back to the user + require(underlyingToken.transfer(msg.sender, amount), "Transfer failed"); + + emit Unwrapped(msg.sender, amount); + } +}