Last active
October 19, 2022 18:48
-
-
Save manolingam/90a66b02fae1219b963493c3c39587ef to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| // SPDX-License-Identifier: GPL-3.0 | |
| pragma solidity ^0.8.0; | |
| import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
| import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
| contract BufficornTraitSwapperEscrow { | |
| using SafeERC20 for IERC20; | |
| address public SPORK_TOKEN_ADDRESS; | |
| address public SPORK_DAO_ADDRESS; | |
| uint public SPORK_SWAP_FEE; | |
| uint public SWAP_TIME_WINDOW; | |
| enum StakeStaus {staked, unstaked} | |
| struct SwapInfo { | |
| uint swapId; | |
| address swapInitiator; | |
| uint initiatorBufficornId; | |
| address swapExecutor; | |
| uint executorBufficornId; | |
| uint swapInitiatedTimestamp; | |
| uint swapExecutedTimestamp; | |
| StakeStaus stakeStaus; | |
| } | |
| mapping (uint => SwapInfo) public swapInfo; | |
| mapping(uint => bool) internal swapIds; | |
| mapping(uint => bool) public isBufficornLocked; | |
| event newSwap(address initiator, | |
| uint initiatorBufficornId, | |
| uint executorBufficornId, | |
| uint swapInitiatedTimestamp, | |
| StakeStaus); | |
| event finishSwap(address executor, | |
| uint initiatorBufficornId, | |
| uint executorBufficornId, | |
| uint swapExecutedTimestamp, | |
| StakeStaus); | |
| event cancelSwap(address initiator, | |
| uint initiatorBufficornId, | |
| uint executorBufficornId, | |
| StakeStaus); | |
| constructor(address _sporkTokenAddress, | |
| address _sporkDaoAddress, | |
| uint _sporkSwapFee, | |
| uint _swapTimeWindow) { | |
| SPORK_TOKEN_ADDRESS = _sporkTokenAddress; | |
| SPORK_DAO_ADDRESS = _sporkDaoAddress; | |
| SPORK_SWAP_FEE = _sporkSwapFee; | |
| SWAP_TIME_WINDOW = _swapTimeWindow; | |
| } | |
| function initiateTraitSwap(uint _swapId, | |
| uint _initiatorBufficornId, | |
| uint _executorBufficornId) public { | |
| require(swapIds[_swapId] == false, 'duplicate swap id'); | |
| require(!isBufficornLocked[_initiatorBufficornId], 'initiator bufficorn locked'); | |
| require(!isBufficornLocked[_executorBufficornId], 'executor bufficorn locked'); | |
| require(IERC20(SPORK_TOKEN_ADDRESS).balanceOf(msg.sender) >= SPORK_SWAP_FEE, 'not enough spork'); | |
| uint _swapInitiatedTimestamp = block.timestamp; | |
| IERC20(SPORK_TOKEN_ADDRESS).safeTransfer(address(this), SPORK_SWAP_FEE); | |
| swapInfo[_swapId] = SwapInfo( | |
| _swapId, | |
| msg.sender, | |
| _initiatorBufficornId, | |
| address(0), | |
| _executorBufficornId, | |
| _swapInitiatedTimestamp, | |
| 0, | |
| StakeStaus.staked); | |
| isBufficornLocked[_initiatorBufficornId] = true; | |
| swapIds[_swapId] = true; | |
| emit newSwap(msg.sender, | |
| _initiatorBufficornId, | |
| _executorBufficornId, | |
| _swapInitiatedTimestamp, | |
| StakeStaus.staked); | |
| } | |
| function executeTraitSwap(uint _swapId, uint _executorBufficornId) public { | |
| require(swapIds[_swapId] == true, 'duplicate swap id'); | |
| require(swapInfo[_swapId].stakeStaus == StakeStaus.staked, 'not staked'); | |
| require(swapInfo[_swapId].swapInitiatedTimestamp + SWAP_TIME_WINDOW > block.timestamp, 'time expired'); | |
| require(!isBufficornLocked[_executorBufficornId], 'bufficorn locked'); | |
| require(IERC20(SPORK_TOKEN_ADDRESS).balanceOf(msg.sender) >= SPORK_SWAP_FEE, 'not enough spork'); | |
| uint _swapExecutedTimestamp = block.timestamp; | |
| IERC20(SPORK_TOKEN_ADDRESS).safeTransfer(address(this), SPORK_SWAP_FEE); | |
| // the split between spork dao & swappers need to be decided yet | |
| IERC20(SPORK_TOKEN_ADDRESS).safeTransfer(SPORK_DAO_ADDRESS, SPORK_SWAP_FEE * 2); | |
| swapInfo[_swapId].swapExecutor = msg.sender; | |
| swapInfo[_swapId].executorBufficornId = _executorBufficornId; | |
| swapInfo[_swapId].swapExecutedTimestamp = _swapExecutedTimestamp; | |
| swapInfo[_swapId].stakeStaus = StakeStaus.unstaked; | |
| isBufficornLocked[swapInfo[_swapId].initiatorBufficornId] = false; | |
| emit finishSwap(msg.sender, | |
| swapInfo[_swapId].initiatorBufficornId, | |
| _executorBufficornId, | |
| _swapExecutedTimestamp, | |
| StakeStaus.unstaked); | |
| } | |
| function withdrawSporkStake(uint _swapId) public { | |
| require(swapIds[_swapId], 'invalid swap id'); | |
| require(swapInfo[_swapId].stakeStaus != StakeStaus.unstaked, 'cannot unstake'); | |
| require(swapInfo[_swapId].swapInitiatedTimestamp + SWAP_TIME_WINDOW < block.timestamp, 'time not expired'); | |
| require(swapInfo[_swapId].swapInitiator == msg.sender, 'not a swapper'); | |
| require(IERC20(SPORK_TOKEN_ADDRESS).balanceOf(address(this)) > SPORK_SWAP_FEE, 'not enough balance'); | |
| IERC20(SPORK_TOKEN_ADDRESS).safeTransfer(msg.sender, SPORK_SWAP_FEE); | |
| swapInfo[_swapId].stakeStaus = StakeStaus.unstaked; | |
| isBufficornLocked[swapInfo[_swapId].initiatorBufficornId] = false; | |
| emit cancelSwap(swapInfo[_swapId].swapInitiator, | |
| swapInfo[_swapId].initiatorBufficornId, | |
| swapInfo[_swapId].executorBufficornId, | |
| StakeStaus.unstaked); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So the contract code looks good to me on a first pass in terms of core functionality. Before deployment, I'd definitely add some admin functions so that the SPORK DAO address could pause the contract and update the SPORK DAO address and fee later in time.
Personally, I'm not sure if we really need a contract to collect fees for the swaps at this point, since we could just have the front-end trigger a transaction that sends X SPORK directly to a SPORK DAO GnosisSafe and tell people that the "fee" is non-refundable even if they cancel the swap.
The way I think this contract could be made more valuable is if in addition to the initiator and executor bufficorn IDs we added a basic description of the trait being swapped (since we're on Polygon a string is probably fine) and that way we've started to collect an on-chain record of the actual swaps.