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

Deploy swap router degen #51

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ _**Please read the full README before using this template.**_
forge script Deploy --sig "run(address,address,address,address)" <ShowtimeVerifier Contract Address> <universalRouter> <WETH> <USDC> --fork-url $RPC_URL_OF_CHOICE --private-key <private key> --broadcast
```

You can also deploy a factory contract without a ShowtimeVerifier contract by running the following command:

```bash
forge script Deploy --sig "run(address,address,address)" <universalRouter> <WETH> <USDC> --fork-url $RPC_URL_OF_CHOICE --private-key <private key> --broadcast
```

To use this template, use one of the below approaches:

1. Run `forge init --template ScopeLift/foundry-template` in an empty directory.
Expand Down
48 changes: 48 additions & 0 deletions broadcast/DeployOpenFactory.s.sol/84532/run-1704936036.json

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions broadcast/DeployOpenFactory.s.sol/84532/run-latest.json

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions script/DeployOpenFactory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
// slither-disable-start reentrancy-benign

pragma solidity 0.8.20;

import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {OpenCreatorTokenFactory} from "src/OpenCreatorTokenFactory.sol";

contract DeployOpenFactory is Script {
function run() public {
vm.broadcast();
OpenCreatorTokenFactory openCreatorTokenFactory = new OpenCreatorTokenFactory();

console2.log("Deployed factory contract address %s", address(openCreatorTokenFactory));
}
}
25 changes: 25 additions & 0 deletions script/DeploySwapRouter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
// slither-disable-start reentrancy-benign

pragma solidity 0.8.20;

import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {CreatorTokenSwapRouterDegen} from "src/CreatorTokenSwapRouterDegen.sol";

contract DeploySwapRouter is Script {
/// @notice Deploy the contract
function run() public {
// Base mainnet addresses
address univRouterAddress = 0x198EF79F1F515F02dFE9e3115eD9fC07183f02fC;
address wethAddress = 0x4200000000000000000000000000000000000006;
address degenAddress = 0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed;

// Deploy the swap router contract
vm.broadcast();
CreatorTokenSwapRouter creatorTokenSwapRouter =
new CreatorTokenSwapRouter(_universalRouterAddress, _wethAddress, _usdcAddress);

console2.log("Deployed router contract address %s", address(creatorTokenSwapRouter));
}
}
64 changes: 64 additions & 0 deletions script/TryBondingCurve.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
// slither-disable-start reentrancy-benign

pragma solidity 0.8.20;

import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {CreatorToken} from "src/CreatorToken.sol";
import {SigmoidBondingCurve} from "src/SigmoidBondingCurve.sol";

contract TryBondingCurve is Script {
function run() public {
uint128 _basePrice = 100_000_000_000_000;
uint128 _linearPriceSlope = 1_000_000_000_000_000_000_000;
uint128 _inflectionPrice = 100_000_000_000_000_000_000_000;
uint32 _inflectionPoint = 400;

SigmoidBondingCurve _bondingCurve =
new SigmoidBondingCurve(_basePrice, _linearPriceSlope, _inflectionPrice, _inflectionPoint);

console2.log("Deployed bonding curve contract address %s", address(_bondingCurve));
uint256 _tokenNumber = _bondingCurve.priceForTokenNumber(1);
console2.log("print(f'1: {%s/10**18:_}')", _tokenNumber);

uint256 _tokenNumber2 = _bondingCurve.priceForTokenNumber(2);
console2.log("print(f'2: {%s/10**18:_}')", _tokenNumber2);

uint256 _tokenNumber3 = _bondingCurve.priceForTokenNumber(3);
console2.log("print(f'3: {%s/10**18:_}')", _tokenNumber3);

uint256 _tokenNumber10 = _bondingCurve.priceForTokenNumber(10);
console2.log("print(f'10: {%s/10**18:_}')", _tokenNumber10);

uint256 _tokenNumber100 = _bondingCurve.priceForTokenNumber(100);
console2.log("print(f'100: {%s/10**18:_}')", _tokenNumber100);

uint256 _tokenNumber200 = _bondingCurve.priceForTokenNumber(200);
console2.log("print(f'200: {%s/10**18:_}')", _tokenNumber200);

uint256 _tokenNumber300 = _bondingCurve.priceForTokenNumber(300);
console2.log("print(f'300: {%s/10**18:_}')", _tokenNumber300);

uint256 _tokenNumber400 = _bondingCurve.priceForTokenNumber(400);
console2.log("print(f'400: {%s/10**18:_}')", _tokenNumber400);

uint256 _tokenNumber500 = _bondingCurve.priceForTokenNumber(500);
console2.log("print(f'500: {%s/10**18:_}')", _tokenNumber500);

uint256 _tokenNumber600 = _bondingCurve.priceForTokenNumber(600);
console2.log("print(f'600: {%s/10**18:_}')", _tokenNumber600);

uint256 _tokenNumber700 = _bondingCurve.priceForTokenNumber(700);
console2.log("print(f'700: {%s/10**18:_}')", _tokenNumber700);

uint256 _tokenNumber800 = _bondingCurve.priceForTokenNumber(800);
console2.log("print(f'800: {%s/10**18:_}')", _tokenNumber800);

uint256 _tokenNumber900 = _bondingCurve.priceForTokenNumber(900);
console2.log("print(f'900: {%s/10**18:_}')", _tokenNumber900);

uint256 _tokenNumber1000 = _bondingCurve.priceForTokenNumber(1000);
console2.log("print(f'1000: {%s/10**18:_}')", _tokenNumber1000);
}
}
162 changes: 162 additions & 0 deletions src/CreatorTokenSwapRouterDegen.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {IUniversalRouter} from "src/lib/IUniversalRouter.sol";
import {ICreatorToken} from "src/interfaces/ICreatorToken.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";

/// @title CreatorTokenSwapRouter
/// @notice A contract for swapping ETH to DEGEN and then buying Creator Tokens.
/// @dev Make sure to get a quote for ETH to Creator Token conversion before interacting.
contract CreatorTokenSwapRouter {
/// @notice Uniswap UniversalRouter interface which is used to execute trades.
IUniversalRouter private immutable UNIVERSAL_ROUTER;

Check warning

Code scanning / Slither

Variable names too similar Warning

/// @notice Address of the Wrapped Ether (WETH) token.
address public immutable WETH_ADDRESS;

Check warning

Code scanning / Slither

Variable names too similar Warning

/// @notice Address of the DEGEN token.
address public immutable DEGEN_ADDRESS;

Check warning

Code scanning / Slither

Variable names too similar Warning


/// @notice Command bytes used for specific operations such as WRAP_ETH(0x0b),
/// V3_SWAP_EXACT_OUT(0x01), and UNWRAP_WETH(0x0c).
bytes private constant COMMANDS =
abi.encodePacked(bytes1(uint8(0x0b)), bytes1(uint8(0x01)), bytes1(uint8(0x0c)));
/// @notice Low fee tier for Uniswap V3 WETH-DEGEN swaps.
bytes3 private constant LOW_FEE_TIER = bytes3(uint24(500));

/// @notice Path for swapping WETH to DEGEN.
bytes private path;

/// @notice Contract constructor sets up the Universal Router and token addresses.
/// @param _universalRouter Address of the Universal Router contract.
/// @param _wethAddress Address of the WETH token contract.
/// @param _degenAddress Address of the DEGEN token contract.
constructor(address _universalRouter, address _wethAddress, address _degenAddress) {

Check notice

Code scanning / Slither

Missing zero address validation Low

Check notice

Code scanning / Slither

Missing zero address validation Low

UNIVERSAL_ROUTER = IUniversalRouter(_universalRouter);
WETH_ADDRESS = _wethAddress;
DEGEN_ADDRESS = _degenAddress;
// WETH -> DEGEN, order is switched because of V3_SWAP_EXACT_OUT
path =
bytes.concat(bytes20(address(DEGEN_ADDRESS)), LOW_FEE_TIER, bytes20(address(WETH_ADDRESS)));
}

/// @notice Buys a Creator Token with ETH from the caller.
/// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH
/// is then swapped for the equivalent amount of DEGEN needed to purchase the Creator Token.
/// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than
/// the amount of DEGEN required for the purchase.
/// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of DEGEN
/// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs:
/// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function
/// in `test/SwapRouter.fork.8453.t.sol.sol`
/// @param _creatorToken Address of the Creator Token contract.
/// @param _maxPayment Maximum amount of DEGEN willing to be paid for the token.
/// @return _amountOut The amount of DEGEN paid.
function buyWithEth(address _creatorToken, uint256 _maxPayment)
external
payable
returns (uint256 _amountOut)
{
_amountOut = buyWithEth(_creatorToken, msg.sender, _maxPayment);
}

/// @notice Buys creator tokens with ETH and sends them to a specified address.
/// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH
/// is then swapped for the equivalent amount of DEGEN needed to purchase the Creator Token.
/// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than
/// the amount of DEGEN required for the purchase.
/// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of DEGEN
/// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs:
/// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function
/// in `test/SwapRouter.fork.8453.t.sol.sol`
/// @param _creatorToken The address of the creator token to buy.
/// @param _to The address to send the purchased tokens.
/// @param _maxPayment The maximum amount of DEGEN to be paid.
/// @return _amountOut The amount of DEGEN paid.
function buyWithEth(address _creatorToken, address _to, uint256 _maxPayment)
public
payable
returns (uint256 _amountOut)
{
_swapEthForDEGEN(_creatorToken, 1, msg.value);
_approveCreatorToken(_creatorToken, _maxPayment);
_amountOut = ICreatorToken(_creatorToken).buy(_to, _maxPayment);
}

/// @notice Buys a specified number of creator tokens with ETH and sends them to the caller.
/// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH
/// is then swapped for the equivalent amount of DEGEN needed to purchase the Creator Tokens.
/// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than
/// the amount of DEGEN required for the purchase.
/// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of DEGEN
/// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs:
/// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function
/// in `test/SwapRouter.fork.8453.t.sol.sol`
/// @param _creatorToken The address of the creator token to buy.
/// @param _numOfTokens The number of tokens to buy.
/// @param _maxPayment The maximum amount of DEGEN to be paid.
/// @return _amountOut The amount of DEGEN paid.
function bulkBuyWithEth(address _creatorToken, uint256 _numOfTokens, uint256 _maxPayment)
external
payable
returns (uint256 _amountOut)
{
_amountOut = bulkBuyWithEth(_creatorToken, msg.sender, _numOfTokens, _maxPayment);
}

/// @notice Buys a specified number of creator tokens with ETH and sends them to a specified
/// address.
/// @dev Transactions calling this function must include an ETH payment as its `value`. This ETH
/// is then swapped for the equivalent amount of DEGEN needed to purchase the Creator Tokens.
/// @dev Ensure that the ETH `value` sent with the transaction is equal to or greater than
/// the amount of DEGEN required for the purchase.
/// @dev You can get a quote for the amount of ETH you have to pay to get a certain amount of DEGEN
/// by calling Uniswap's `QuoterV2` contract off-chain. Check Uniswap docs:
/// https://docs.uniswap.org/contracts/v3/reference/periphery/lens/QuoterV2 and `quote` function
/// in `test/SwapRouter.fork.8453.t.sol.sol`
/// @param _creatorToken The address of the creator token to buy.
/// @param _to The address to send the purchased creator tokens.
/// @param _numOfTokens The number of tokens to buy.
/// @param _maxPayment The maximum amount of DEGEN to be paid.
/// @return _amountOut The amount of DEGEN paid.
function bulkBuyWithEth(
address _creatorToken,
address _to,
uint256 _numOfTokens,
uint256 _maxPayment
) public payable returns (uint256 _amountOut) {
_swapEthForDEGEN(_creatorToken, _numOfTokens, msg.value);
_approveCreatorToken(_creatorToken, _maxPayment);
_amountOut = ICreatorToken(_creatorToken).bulkBuy(_to, _numOfTokens, _maxPayment);
}

/// @notice Swaps ETH for DEGEN.
/// @param _creatorToken The address of the creator token.
/// @param _numOfTokens The number of tokens to buy.
/// @param _amountIn The amount of ETH to swap.
function _swapEthForDEGEN(address _creatorToken, uint256 _numOfTokens, uint256 _amountIn) private {
// // Encoding the inputs for V3_SWAP_EXACT_IN
bytes[] memory inputs = new bytes[](3);
(uint256 _tokenPrice, uint256 _creatorFee, uint256 _adminFee) =
ICreatorToken(_creatorToken).priceToBuyNext(_numOfTokens);
// Amount of DEGEN required to purchase `_numOfTokens`
uint256 _amountOut = _tokenPrice + _creatorFee + _adminFee;
// WRAP_ETH
inputs[0] = abi.encode(address(UNIVERSAL_ROUTER), _amountIn);
// V3_SWAP_EXACT_OUT
inputs[1] = abi.encode(address(this), _amountOut, _amountIn, path, false);
// UNWRAP_WETH returns leftover ETH amount to the caller
inputs[2] = abi.encode(msg.sender, 0);

// // Execute on the UniversalRouter
UNIVERSAL_ROUTER.execute{value: _amountIn}(COMMANDS, inputs, block.timestamp + 60);
}

/// @dev Approves the creator token contract to transfer DEGEN from this contract.
/// @param _creatorToken The address of the creator token.
/// @param _maxPayment The amount of DEGEN to be spent.
function _approveCreatorToken(address _creatorToken, uint256 _maxPayment) private {
if (IERC20(DEGEN_ADDRESS).allowance(address(this), address(_creatorToken)) < _maxPayment) {
IERC20(DEGEN_ADDRESS).approve(address(_creatorToken), type(uint256).max);
}
}
Comment on lines +161 to +165
}
78 changes: 78 additions & 0 deletions src/OpenCreatorTokenFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {CreatorToken} from "src/CreatorToken.sol";
import {SigmoidBondingCurve} from "src/SigmoidBondingCurve.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {IShowtimeVerifier, Attestation} from "src/lib/IShowtimeVerifier.sol";

/// @notice Identical as CreatorTokenFactory, without verifier logic.
contract OpenCreatorTokenFactory {
/// @notice All the configuration parameters required to deploy a CreatorToken and its
/// SigmoidBondingCurve.
/// @param name The name of the ERC721 token.
/// @param symbol The symbol of the ERC721 token.
/// @param tokenURI The URI for the creator token.
/// @param creator Address of the creator.
/// @param creatorFee Creator fee in BIPs.
/// @param creatorRoyalty Creator royalty fee in BIPs.
/// @param admin Address of the admin.
/// @param adminFee Admin fee in BIPs.
/// @param referrer Address of the referrer.
/// @param payToken ERC20 token used for payments.
/// @param basePrice The base price at the start of the curve.
/// @param linearPriceSlope The linear coefficient used to fine tune the curve.
/// @param inflectionPrice The price at the point where the curve switches from quadratic to
/// square root.
/// @param inflectionPoint Where the curve switches from quadratic to square root.
struct DeploymentConfig {
string name;
string symbol;
string tokenURI;
address creator;
uint256 creatorFee;
uint96 creatorRoyalty;
address admin;
uint256 adminFee;
address referrer;
IERC20 payToken;
uint128 basePrice;
uint128 linearPriceSlope;
uint128 inflectionPrice;
uint32 inflectionPoint;
}

/// @notice Emitted when a new CreatorToken and SigmoidBondingCurve pair is successfully
/// deployed.
/// @param creatorToken The address of the newly deployed CreatorToken contract.
/// @param bondingCurve The address of the newly deployed SigmoidBondingCurve contract.
/// @param config The config object used to execute this deployment.
event CreatorTokenDeployed(
CreatorToken indexed creatorToken,
SigmoidBondingCurve indexed bondingCurve,
DeploymentConfig config
);

/// @notice Deploys a CreatorToken and SigmoidBondingCurve pair for the given configuration.
/// @param _config The configuration data for the would-be token and bonding curve contracts.
/// @return _creatorToken The address of the newly deployed CreatorToken contract.
function deploy(DeploymentConfig memory _config) external returns (CreatorToken _creatorToken) {
SigmoidBondingCurve _bondingCurve =
new SigmoidBondingCurve(_config.basePrice, _config.linearPriceSlope, _config.inflectionPrice, _config.inflectionPoint);

_creatorToken = new CreatorToken(
_config.name,
_config.symbol,
_config.tokenURI,
_config.creator,
_config.creatorFee,
_config.creatorRoyalty,
_config.admin,
_config.adminFee,
_config.referrer,
_config.payToken,
_bondingCurve
);
emit CreatorTokenDeployed(_creatorToken, _bondingCurve, _config);
}
}
Loading