├── .gitignore ├── .misc ├── banner1.png └── banner2.webp ├── LICENSE ├── README.md ├── clubcoin ├── ClubCoinDropTokenV1.sol ├── ClubCoinDropV1.sol ├── factory │ ├── ClubCoinDropDeployer.sol │ └── ClubCoinDropFactoryV1.sol ├── interfaces │ ├── IClubCoinDropToken.sol │ ├── INonfungiblePositionManager.sol │ ├── IUniswapV3Factory.sol │ ├── IUniswapV3Pool.sol │ ├── IUniswapV3Router.sol │ └── IWETH.sol └── lib │ └── MerkleWhitelist.sol ├── domains ├── README.md ├── beb-controller-abi.json ├── beb-registrar-abi.json └── contracts │ ├── BEBRegistry.sol │ ├── BaseBebRegistryBulkRegister.sol │ ├── BaseBebRegistryOneStepController.sol │ ├── BaseRegistrar.sol │ ├── Beb.sol │ ├── BebRegistryBetaController.sol │ ├── BebRegistryBulkRegister.sol │ ├── BebRegistryOneStepController.sol │ ├── FlatBeb.sol │ ├── IBEBRegistry.sol │ ├── IBaseRegistrar.sol │ ├── IBebRegistryBetaController.sol │ ├── IBebRegistryOneStepController.sol │ ├── IPriceOracle.sol │ ├── IRoyaltyController.sol │ ├── OPBebRegistryOneStepController.sol │ ├── OPCastRegistryOneStepController.sol │ ├── OpBebRegistryBulkRegister.sol │ ├── PriceOracle.sol │ ├── RoyaltyController.sol │ ├── StringUtils.sol │ ├── VibeBaseRegistrar.sol │ └── VibePriceOracle.sol ├── fartoken ├── BondingCurveV2.sol ├── FarAgentTips.sol ├── FarAgentTipsFactoryImpl.sol ├── FarTokenV2.sol ├── FarTokenV2TokenFactoryImpl.sol ├── interfaces │ ├── IFarAgentTips.sol │ ├── IFarAgentTipsFactory.sol │ ├── IFarTokenV2.sol │ ├── IFarTokenV2Factory.sol │ ├── IGuardian.sol │ ├── IIdRegistry.sol │ ├── IKeyGateway.sol │ ├── IKeyRegistry.sol │ ├── IMigrations.sol │ ├── INonfungiblePositionManager.sol │ ├── IProtocolRewards.sol │ ├── ISignature.sol │ ├── ISwapRouter.sol │ ├── IUniswapV3Pool.sol │ ├── IUniswapV3SwapCallback.sol │ └── IWETH.sol ├── lib │ ├── EIP712.sol │ ├── Signatures.sol │ ├── TransferHelper.sol │ └── TrustedCaller.sol └── v1 │ ├── BondingCurve.sol │ ├── FarToken.sol │ ├── FarTokenFactoryImpl.sol │ ├── ProtocolRewards.sol │ └── interfaces │ ├── IFarToken.sol │ ├── IFarTokenFactory.sol │ ├── INonfungiblePositionManager.sol │ ├── IProtocolRewards.sol │ ├── ISwapRouter.sol │ ├── IUniswapV3Pool.sol │ ├── IUniswapV3SwapCallback.sol │ └── IWETH.sol ├── funding.json └── marketplace ├── FidMarketplace.sol └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env 15 | node_modules 16 | .DS_Store 17 | -------------------------------------------------------------------------------- /.misc/banner1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieldlabs/contracts/7573c09554e43714bedbb51e6c3647c72bc266d8/.misc/banner1.png -------------------------------------------------------------------------------- /.misc/banner2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieldlabs/contracts/7573c09554e43714bedbb51e6c3647c72bc266d8/.misc/banner2.webp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wield Labs Smart Contracts 2 | 3 | 4 | 5 | - See the [`domains/`](https://github.com/wieldlabs/contracts/tree/main/domains) folder for more details on .cast handles! 6 | - See the [`marketplace/`](https://github.com/wieldlabs/contracts/tree/main/marketplace) folder for [far.quest/market](https://far.quest/market)! 7 | -------------------------------------------------------------------------------- /clubcoin/ClubCoinDropTokenV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; 7 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 8 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 9 | import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; 10 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 11 | 12 | import {IUniswapV3Factory} from "./interfaces/IUniswapV3Factory.sol"; 13 | import {IUniswapV3Pool} from "./interfaces/IUniswapV3Pool.sol"; 14 | import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol"; 15 | import {IWETH} from "./interfaces/IWETH.sol"; 16 | import {IClubCoinDropToken} from "./interfaces/IClubCoinDropToken.sol"; 17 | 18 | contract ClubCoinDropTokenV1 is IClubCoinDropToken, Initializable, ERC20Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable, IERC721Receiver { 19 | using SafeERC20 for IERC20; 20 | 21 | // Constants 22 | uint256 public constant MAX_SUPPLY = 1_000_000e18; // 1M tokens 23 | uint256 public constant LP_TOKEN_PERCENTAGE = 20; // 20% of tokens go to LP pool 24 | uint256 public constant TOKEN_NFT_PERCENTAGE = 80; // 80% of tokens allocated to NFT holders 25 | uint256 public constant MIN_REWARD_AMOUNT = 1e18; // Minimum reward (1 token) 26 | uint256 public constant MAX_REWARD_AMOUNT = 800_000e18; // Maximum reward (800k tokens, 80% of MAX_SUPPLY) 27 | uint24 internal constant LP_FEE = 10000; // 1% fee tier for Uniswap V3 28 | // Sqrt price with 1 ETH = ~$2,000, token = $1 29 | uint160 internal constant POOL_SQRT_PRICE_X96_WETH_0 = 2505414483750479241346919126; 30 | uint160 internal constant POOL_SQRT_PRICE_X96_TOKEN_0 = 79228162514264337593543950336; 31 | 32 | // Contract references 33 | address public nftDropContract; 34 | address public weth; 35 | address public uniswapV3Factory; 36 | address public uniswapV3Router; 37 | address public nonfungiblePositionManager; 38 | 39 | // State variables 40 | address public poolAddress; 41 | bool public liquiditySetup; 42 | uint256 public totalBurnRewards; 43 | uint256 public liquidityPositionId; 44 | 45 | // Events 46 | event RewardMinted(address indexed recipient, uint256 amount); 47 | event LiquiditySetup(address indexed pool, uint256 ethAmount, uint256 tokenAmount); 48 | event PositionFeesCollected(uint256 indexed positionId, uint256 amount0, uint256 amount1); 49 | 50 | // Errors 51 | error NotDropContract(); 52 | error AddressZero(); 53 | // LiquidityAlreadySetup() is now defined in the interface 54 | error MaxSupplyExceeded(); 55 | error LiquidityNotSetup(); 56 | 57 | /** 58 | * @notice Modifier to ensure only the NFT drop contract can call 59 | */ 60 | modifier onlyDropContract() { 61 | if (msg.sender != nftDropContract) revert NotDropContract(); 62 | _; 63 | } 64 | 65 | /** 66 | * @notice Required implementation of IERC721Receiver for receiving Uniswap V3 position NFTs 67 | */ 68 | function onERC721Received( 69 | address, 70 | address, 71 | uint256, 72 | bytes calldata 73 | ) external pure override returns (bytes4) { 74 | return this.onERC721Received.selector; 75 | } 76 | 77 | /** 78 | * @notice Initializes the token contract 79 | * @param _owner The owner of the contract 80 | * @param _tokenName The name of the token 81 | * @param _tokenSymbol The symbol of the token 82 | * @param _nftDropContract The address of the NFT drop contract 83 | * @param _weth The address of the WETH contract 84 | * @param _uniswapV3Factory The address of the Uniswap V3 factory 85 | * @param _uniswapV3Router The address of the Uniswap V3 router 86 | * @param _nonfungiblePositionManager The address of the Uniswap V3 NonfungiblePositionManager 87 | */ 88 | function initialize( 89 | address _owner, 90 | string memory _tokenName, 91 | string memory _tokenSymbol, 92 | address _nftDropContract, 93 | address _weth, 94 | address _uniswapV3Factory, 95 | address _uniswapV3Router, 96 | address _nonfungiblePositionManager 97 | ) external initializer { 98 | if (_owner == address(0)) revert AddressZero(); 99 | if (_nftDropContract == address(0)) revert AddressZero(); 100 | if (_weth == address(0)) revert AddressZero(); 101 | if (_uniswapV3Factory == address(0)) revert AddressZero(); 102 | if (_uniswapV3Router == address(0)) revert AddressZero(); 103 | if (_nonfungiblePositionManager == address(0)) revert AddressZero(); 104 | 105 | // Initialize base contracts 106 | __ERC20_init(_tokenName, _tokenSymbol); 107 | __Ownable_init(_owner); 108 | __ReentrancyGuard_init(); 109 | 110 | // Set addresses 111 | nftDropContract = _nftDropContract; 112 | weth = _weth; 113 | uniswapV3Factory = _uniswapV3Factory; 114 | uniswapV3Router = _uniswapV3Router; 115 | nonfungiblePositionManager = _nonfungiblePositionManager; 116 | 117 | // Initialize state 118 | liquiditySetup = false; 119 | totalBurnRewards = 0; 120 | 121 | // Create Uniswap V3 pool 122 | address token0 = address(this) < weth ? address(this) : weth; 123 | address token1 = address(this) < weth ? weth : address(this); 124 | 125 | // Set initial price based on token ordering 126 | uint160 sqrtPriceX96 = token0 == weth ? 127 | POOL_SQRT_PRICE_X96_WETH_0 : POOL_SQRT_PRICE_X96_TOKEN_0; 128 | 129 | // Create and initialize the pool 130 | poolAddress = INonfungiblePositionManager(nonfungiblePositionManager).createAndInitializePoolIfNecessary( 131 | token0, 132 | token1, 133 | LP_FEE, 134 | sqrtPriceX96 135 | ); 136 | } 137 | 138 | /** 139 | * @notice Mints tokens as rewards for burning NFTs 140 | * @param recipient The address to receive the tokens 141 | * @param amount The amount of tokens to mint 142 | */ 143 | function mintReward(address recipient, uint256 amount) external override onlyDropContract nonReentrant { 144 | if (recipient == address(0)) revert AddressZero(); 145 | 146 | // Apply guardrails to prevent minting too many or too few tokens 147 | if (amount < MIN_REWARD_AMOUNT) { 148 | amount = MIN_REWARD_AMOUNT; 149 | } else if (amount > MAX_REWARD_AMOUNT) { 150 | amount = MAX_REWARD_AMOUNT; 151 | } 152 | 153 | // Ensure we don't exceed max supply 154 | if (totalSupply() + amount > MAX_SUPPLY) revert MaxSupplyExceeded(); 155 | 156 | // Update rewards counter 157 | totalBurnRewards += amount; 158 | 159 | // Mint tokens to recipient 160 | _mint(recipient, amount); 161 | 162 | emit RewardMinted(recipient, amount); 163 | } 164 | 165 | /** 166 | * @notice Sets up liquidity on Uniswap with ETH and tokens 167 | * @dev Can only be called by NFT drop contract 168 | * @dev Follows checks-effects-interactions pattern to prevent reentrancy 169 | */ 170 | function setupLiquidity() external payable override onlyDropContract nonReentrant { 171 | // ===== CHECKS ===== 172 | // Ensure liquidity hasn't been set up yet 173 | if (liquiditySetup) revert LiquidityAlreadySetup(); 174 | 175 | // Verify pool address is set 176 | require(poolAddress != address(0), "Pool address not set"); 177 | 178 | // Calculate token amount for liquidity (20% of max supply) 179 | // But ensure we don't exceed MAX_SUPPLY even if some tokens were already minted 180 | uint256 remainingSupply = MAX_SUPPLY - totalSupply(); 181 | uint256 desiredLiquidityTokens = (MAX_SUPPLY * LP_TOKEN_PERCENTAGE) / 100; 182 | uint256 tokensForLiquidity = desiredLiquidityTokens <= remainingSupply ? 183 | desiredLiquidityTokens : 184 | remainingSupply; 185 | 186 | // Verify we have enough tokens to make liquidity setup worthwhile 187 | require(tokensForLiquidity > 0, "Insufficient remaining supply"); 188 | 189 | // ===== EFFECTS ===== 190 | // Mark liquidity as set up BEFORE any external calls 191 | // This prevents reentrancy attacks 192 | liquiditySetup = true; 193 | 194 | // ===== INTERACTIONS ===== 195 | // Mint tokens for liquidity 196 | _mint(address(this), tokensForLiquidity); 197 | 198 | // Convert ETH to WETH 199 | IWETH(weth).deposit{value: address(this).balance}(); 200 | 201 | // Determine token order for the pool 202 | bool isWethToken0 = weth < address(this); 203 | 204 | // Get the total ETH amount 205 | uint256 ethAmount = IERC20(weth).balanceOf(address(this)); 206 | 207 | // Approve the tokens to the position manager 208 | IERC20(weth).approve(nonfungiblePositionManager, ethAmount); 209 | IERC20(address(this)).approve(nonfungiblePositionManager, tokensForLiquidity); 210 | 211 | // Define parameters for providing liquidity 212 | INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams({ 213 | token0: isWethToken0 ? weth : address(this), 214 | token1: isWethToken0 ? address(this) : weth, 215 | fee: LP_FEE, 216 | tickLower: -887200, // Approx price range 0.1x 217 | tickUpper: 887200, // Approx price range 10x 218 | amount0Desired: isWethToken0 ? ethAmount : tokensForLiquidity, 219 | amount1Desired: isWethToken0 ? tokensForLiquidity : ethAmount, 220 | amount0Min: 0, 221 | amount1Min: 0, 222 | recipient: address(this), 223 | deadline: block.timestamp + 30 minutes 224 | }); 225 | 226 | // Mint the position and get back a tokenId that represents the position 227 | (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = 228 | INonfungiblePositionManager(nonfungiblePositionManager).mint(params); 229 | 230 | // Keep track of the position ID 231 | liquidityPositionId = tokenId; 232 | 233 | // Verify the liquidity position was created successfully 234 | // Both the tokenId and the liquidity amount should be non-zero 235 | if (tokenId == 0 || liquidity == 0) { 236 | revert LiquiditySetupFailed(); 237 | } 238 | 239 | // Verify that tokens were actually transferred (some amount was used) 240 | if (amount0 == 0 && amount1 == 0) { 241 | revert LiquiditySetupFailed(); 242 | } 243 | 244 | // Additional verification - check that the pool exists and matches our stored address 245 | // Get the actual pool from the factory to ensure it matches our stored address 246 | address verifiedPoolAddress = IUniswapV3Factory(uniswapV3Factory).getPool( 247 | isWethToken0 ? weth : address(this), 248 | isWethToken0 ? address(this) : weth, 249 | LP_FEE 250 | ); 251 | 252 | // Verify the pool exists and matches our stored pool address 253 | if (verifiedPoolAddress == address(0) || verifiedPoolAddress != poolAddress) { 254 | revert LiquiditySetupFailed(); 255 | } 256 | 257 | // Verify the pool is initialized by checking its slot0 258 | try IUniswapV3Pool(poolAddress).slot0() returns (IUniswapV3Pool.Slot0 memory slot) { 259 | // Verify the price is non-zero 260 | if (slot.sqrtPriceX96 == 0) { 261 | revert LiquiditySetupFailed(); 262 | } 263 | } catch { 264 | // If we can't call slot0, the pool might not be properly initialized 265 | revert LiquiditySetupFailed(); 266 | } 267 | 268 | emit LiquiditySetup(poolAddress, ethAmount, tokensForLiquidity); 269 | } 270 | 271 | /** 272 | * @notice Allows the owner to collect Uniswap V3 position fees 273 | */ 274 | function collectPositionFees() external onlyOwner nonReentrant { 275 | require(liquidityPositionId > 0, "No liquidity position"); 276 | require(liquiditySetup, "Liquidity not setup"); 277 | 278 | // Setup the collect parameters 279 | INonfungiblePositionManager.CollectParams memory params = INonfungiblePositionManager.CollectParams({ 280 | tokenId: liquidityPositionId, 281 | recipient: owner(), // Send fees directly to owner 282 | amount0Max: type(uint128).max, // Collect all token0 fees 283 | amount1Max: type(uint128).max // Collect all token1 fees 284 | }); 285 | 286 | // Collect the fees 287 | (uint256 amount0, uint256 amount1) = INonfungiblePositionManager(nonfungiblePositionManager).collect(params); 288 | 289 | emit PositionFeesCollected(liquidityPositionId, amount0, amount1); 290 | } 291 | 292 | /** 293 | * @notice Receive function to allow contract to receive ETH 294 | */ 295 | receive() external payable { 296 | // This function is needed to receive ETH from the drop contract 297 | } 298 | } -------------------------------------------------------------------------------- /clubcoin/factory/ClubCoinDropDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; 5 | import {ClubCoinDropV1} from "../ClubCoinDropV1.sol"; 6 | import {ClubCoinDropTokenV1} from "../ClubCoinDropTokenV1.sol"; 7 | 8 | contract ClubCoinDropDeployer { 9 | // Events 10 | event DropCreated( 11 | address indexed creator, 12 | address indexed dropContract, 13 | address indexed tokenContract, 14 | string nftName, 15 | string nftSymbol, 16 | string tokenName, 17 | string tokenSymbol 18 | ); 19 | 20 | // State variables 21 | address public immutable dropImplementation; 22 | address public immutable tokenImplementation; 23 | address public immutable WETH; 24 | address public immutable uniswapV3Factory; 25 | address public immutable uniswapV3Router; 26 | address public immutable nonfungiblePositionManager; 27 | 28 | /** 29 | * @notice Constructor 30 | * @param _dropImplementation The drop implementation contract 31 | * @param _tokenImplementation The token implementation contract 32 | * @param _weth The WETH contract address 33 | * @param _uniswapV3Factory The Uniswap V3 factory address 34 | * @param _uniswapV3Router The Uniswap V3 router address 35 | * @param _nonfungiblePositionManager The Uniswap V3 NonfungiblePositionManager address 36 | */ 37 | constructor( 38 | address _dropImplementation, 39 | address _tokenImplementation, 40 | address _weth, 41 | address _uniswapV3Factory, 42 | address _uniswapV3Router, 43 | address _nonfungiblePositionManager 44 | ) { 45 | require(_dropImplementation != address(0), "Zero address"); 46 | require(_tokenImplementation != address(0), "Zero address"); 47 | require(_weth != address(0), "Zero address"); 48 | require(_uniswapV3Factory != address(0), "Zero address"); 49 | require(_uniswapV3Router != address(0), "Zero address"); 50 | require(_nonfungiblePositionManager != address(0), "Zero address"); 51 | 52 | dropImplementation = _dropImplementation; 53 | tokenImplementation = _tokenImplementation; 54 | WETH = _weth; 55 | uniswapV3Factory = _uniswapV3Factory; 56 | uniswapV3Router = _uniswapV3Router; 57 | nonfungiblePositionManager = _nonfungiblePositionManager; 58 | } 59 | 60 | /** 61 | * @notice Creates a new drop and token pair with atomic initialization 62 | * @param owner The owner of the new contracts 63 | * @param nftName Name of the NFT collection 64 | * @param nftSymbol Symbol of the NFT collection 65 | * @param tokenName Name of the token 66 | * @param tokenSymbol Symbol of the token 67 | * @param dropConfig Configuration for the drop 68 | * @return dropAddress Address of the deployed drop contract 69 | * @return tokenAddress Address of the deployed token contract 70 | */ 71 | function createDrop( 72 | address owner, 73 | string memory nftName, 74 | string memory nftSymbol, 75 | string memory tokenName, 76 | string memory tokenSymbol, 77 | ClubCoinDropV1.DropConfig memory dropConfig 78 | ) external returns (address dropAddress, address tokenAddress) { 79 | // Clone the template contracts 80 | dropAddress = Clones.clone(dropImplementation); 81 | tokenAddress = Clones.clone(tokenImplementation); 82 | 83 | // Validate the addresses 84 | require(dropAddress != address(0), "Drop deployment failed"); 85 | require(tokenAddress != address(0), "Token deployment failed"); 86 | 87 | // Initialize both contracts atomically 88 | // If any initialization fails, the entire transaction will revert 89 | 90 | // Initialize the drop contract 91 | ClubCoinDropV1(payable(dropAddress)).initialize( 92 | owner, 93 | nftName, 94 | nftSymbol, 95 | WETH, 96 | dropConfig 97 | ); 98 | 99 | // Initialize the token contract with a reference to the drop contract 100 | ClubCoinDropTokenV1(payable(tokenAddress)).initialize( 101 | owner, 102 | tokenName, 103 | tokenSymbol, 104 | dropAddress, 105 | WETH, 106 | uniswapV3Factory, 107 | uniswapV3Router, 108 | nonfungiblePositionManager 109 | ); 110 | 111 | // Complete the linking by setting the token contract in the drop contract 112 | ClubCoinDropV1(payable(dropAddress)).setTokenContract(tokenAddress); 113 | 114 | // Validate contracts are properly linked 115 | address registeredToken = ClubCoinDropV1(payable(dropAddress)).clubTokenAddress(); 116 | address registeredDrop = ClubCoinDropTokenV1(payable(tokenAddress)).nftDropContract(); 117 | 118 | require(registeredToken == tokenAddress, "Drop to token link invalid"); 119 | require(registeredDrop == dropAddress, "Token to drop link invalid"); 120 | 121 | emit DropCreated( 122 | owner, 123 | dropAddress, 124 | tokenAddress, 125 | nftName, 126 | nftSymbol, 127 | tokenName, 128 | tokenSymbol 129 | ); 130 | 131 | return (dropAddress, tokenAddress); 132 | } 133 | } -------------------------------------------------------------------------------- /clubcoin/factory/ClubCoinDropFactoryV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 5 | import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 7 | 8 | import {ClubCoinDropV1} from "../ClubCoinDropV1.sol"; 9 | import {ClubCoinDropDeployer} from "./ClubCoinDropDeployer.sol"; 10 | 11 | contract ClubCoinDropFactoryV1 is Initializable, OwnableUpgradeable, UUPSUpgradeable { 12 | // The deployer contract that actually creates the drop and token pairs 13 | ClubCoinDropDeployer public deployer; 14 | 15 | // Default configurations 16 | ClubCoinDropV1.DropConfig public defaultConfig; 17 | 18 | // Errors 19 | error AddressZero(); 20 | error InvalidParameter(); 21 | 22 | /// @custom:oz-upgrades-unsafe-allow constructor 23 | constructor() { 24 | _disableInitializers(); 25 | } 26 | 27 | /** 28 | * @notice Initializes the contract (replaces constructor) 29 | * @param _deployer Address of the deployer contract 30 | */ 31 | function initialize( 32 | ClubCoinDropDeployer _deployer 33 | ) public initializer { 34 | if (address(_deployer) == address(0)) revert AddressZero(); 35 | 36 | __Ownable_init(msg.sender); 37 | __UUPSUpgradeable_init(); 38 | 39 | // Set deployer 40 | deployer = _deployer; 41 | 42 | // Set default configurations directly instead of using library 43 | defaultConfig = ClubCoinDropV1.DropConfig({ 44 | whitelistDuration: 3 days, 45 | publicMintDuration: 4 days, 46 | startingPrice: 0.1 ether, 47 | priceCurveRate: 200, // 2% increase per mint 48 | maxSupply: 1000, 49 | maxMintsPerAddress: 5, 50 | baseURI: "https://build.wield.xyz/clubcoin/drop/metadata/", 51 | vestingDuration: 730 days, // 2 years 52 | initialRedemptionRate: 3000 // 30% 53 | }); 54 | } 55 | 56 | /** 57 | * @notice Creates a new drop with default parameters 58 | * @param nftName Name of the NFT collection 59 | * @param nftSymbol Symbol of the NFT collection 60 | * @param tokenName Name of the token 61 | * @param tokenSymbol Symbol of the token 62 | * @return dropAddress Address of the deployed drop contract 63 | * @return tokenAddress Address of the deployed token contract 64 | */ 65 | function createDrop( 66 | string memory nftName, 67 | string memory nftSymbol, 68 | string memory tokenName, 69 | string memory tokenSymbol 70 | ) external returns (address dropAddress, address tokenAddress) { 71 | return deployer.createDrop( 72 | msg.sender, 73 | nftName, 74 | nftSymbol, 75 | tokenName, 76 | tokenSymbol, 77 | defaultConfig 78 | ); 79 | } 80 | 81 | /** 82 | * @notice Creates a new drop with custom parameters 83 | * @param nftName Name of the NFT collection 84 | * @param nftSymbol Symbol of the NFT collection 85 | * @param tokenName Name of the token 86 | * @param tokenSymbol Symbol of the token 87 | * @param dropConfig Custom configuration for the drop 88 | * @return dropAddress Address of the deployed drop contract 89 | * @return tokenAddress Address of the deployed token contract 90 | */ 91 | function createDropWithConfig( 92 | string memory nftName, 93 | string memory nftSymbol, 94 | string memory tokenName, 95 | string memory tokenSymbol, 96 | ClubCoinDropV1.DropConfig memory dropConfig 97 | ) external returns (address dropAddress, address tokenAddress) { 98 | return deployer.createDrop( 99 | msg.sender, 100 | nftName, 101 | nftSymbol, 102 | tokenName, 103 | tokenSymbol, 104 | dropConfig 105 | ); 106 | } 107 | 108 | /** 109 | * @notice Updates the default parameters 110 | * @param newConfig The new default configuration values 111 | */ 112 | function updateDefaultConfig( 113 | ClubCoinDropV1.DropConfig calldata newConfig 114 | ) external onlyOwner { 115 | // Validate the configuration directly 116 | if ( 117 | newConfig.whitelistDuration < 1 minutes || 118 | newConfig.publicMintDuration < 1 minutes || 119 | newConfig.startingPrice == 0 || 120 | newConfig.priceCurveRate == 0 || 121 | newConfig.maxSupply == 0 || 122 | newConfig.maxMintsPerAddress == 0 || 123 | newConfig.vestingDuration < 1 minutes || 124 | newConfig.initialRedemptionRate >= 10000 125 | ) revert InvalidParameter(); 126 | 127 | // Update defaults 128 | defaultConfig = newConfig; 129 | } 130 | 131 | /** 132 | * @notice Updates the deployer contract 133 | * @dev Only the owner can call this function 134 | * @param newDeployer The new deployer contract 135 | */ 136 | function updateDeployer( 137 | ClubCoinDropDeployer newDeployer 138 | ) external onlyOwner { 139 | if (address(newDeployer) == address(0)) revert AddressZero(); 140 | deployer = newDeployer; 141 | } 142 | 143 | /** 144 | * @notice Authorization function for contract upgrades 145 | * @dev Required by UUPSUpgradeable. Only the owner can upgrade the contract. 146 | * @param newImplementation Address of the new implementation contract 147 | */ 148 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner { 149 | // Authorization logic in onlyOwner modifier 150 | } 151 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/IClubCoinDropToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IClubCoinDropToken { 5 | /** 6 | * @notice Maximum token supply 7 | */ 8 | function MAX_SUPPLY() external view returns (uint256); 9 | 10 | /** 11 | * @notice Percentage of tokens allocated to NFT holders 12 | */ 13 | function TOKEN_NFT_PERCENTAGE() external view returns (uint256); 14 | 15 | /** 16 | * @notice Error thrown when liquidity setup fails verification 17 | */ 18 | error LiquiditySetupFailed(); 19 | 20 | /** 21 | * @notice Error thrown when liquidity is already set up 22 | */ 23 | error LiquidityAlreadySetup(); 24 | 25 | /** 26 | * @notice Mints tokens as a reward for burning NFTs 27 | * @param recipient The address to receive the tokens 28 | * @param amount The amount of tokens to mint 29 | */ 30 | function mintReward(address recipient, uint256 amount) external; 31 | 32 | /** 33 | * @notice Sets up liquidity on Uniswap with ETH and tokens 34 | * @dev Can only be called by the NFT drop contract 35 | * @dev Will revert with LiquiditySetupFailed if verification checks fail 36 | * @dev Will revert with LiquidityAlreadySetup if liquidity is already set up 37 | */ 38 | function setupLiquidity() external payable; 39 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/INonfungiblePositionManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface INonfungiblePositionManager { 5 | struct MintParams { 6 | address token0; 7 | address token1; 8 | uint24 fee; 9 | int24 tickLower; 10 | int24 tickUpper; 11 | uint256 amount0Desired; 12 | uint256 amount1Desired; 13 | uint256 amount0Min; 14 | uint256 amount1Min; 15 | address recipient; 16 | uint256 deadline; 17 | } 18 | 19 | struct CollectParams { 20 | uint256 tokenId; 21 | address recipient; 22 | uint128 amount0Max; 23 | uint128 amount1Max; 24 | } 25 | 26 | /// @notice Creates a new pool if it does not exist, then initializes if not initialized 27 | /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool 28 | /// @param token0 The contract address of token0 of the pool 29 | /// @param token1 The contract address of token1 of the pool 30 | /// @param fee The fee amount of the v3 pool for the specified token pair 31 | /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value 32 | /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary 33 | function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96) 34 | external 35 | payable 36 | returns (address pool); 37 | 38 | /// @notice Creates a new position wrapped in a NFT 39 | /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized 40 | /// a method does not exist, i.e. the pool is assumed to be initialized. 41 | /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata 42 | /// @return tokenId The ID of the token that represents the minted position 43 | /// @return liquidity The amount of liquidity for this position 44 | /// @return amount0 The amount of token0 45 | /// @return amount1 The amount of token1 46 | function mint(MintParams calldata params) 47 | external 48 | payable 49 | returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); 50 | 51 | /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient 52 | /// @param params tokenId The ID of the NFT for which tokens are being collected, 53 | /// recipient The account that should receive the tokens, 54 | /// amount0Max The maximum amount of token0 to collect, 55 | /// amount1Max The maximum amount of token1 to collect 56 | /// @return amount0 The amount of fees collected in token0 57 | /// @return amount1 The amount of fees collected in token1 58 | function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); 59 | 60 | /// @notice Returns the position information associated with a given token ID. 61 | /// @dev Throws if the token ID is not valid. 62 | /// @param tokenId The ID of the token that represents the position 63 | /// @return nonce The nonce for permits 64 | /// @return operator The address that is approved for spending 65 | /// @return token0 The address of the token0 for a specific pool 66 | /// @return token1 The address of the token1 for a specific pool 67 | /// @return fee The fee associated with the pool 68 | /// @return tickLower The lower end of the tick range for the position 69 | /// @return tickUpper The higher end of the tick range for the position 70 | /// @return liquidity The liquidity of the position 71 | /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position 72 | /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position 73 | /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation 74 | /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation 75 | function positions(uint256 tokenId) 76 | external 77 | view 78 | returns ( 79 | uint96 nonce, 80 | address operator, 81 | address token0, 82 | address token1, 83 | uint24 fee, 84 | int24 tickLower, 85 | int24 tickUpper, 86 | uint128 liquidity, 87 | uint256 feeGrowthInside0LastX128, 88 | uint256 feeGrowthInside1LastX128, 89 | uint128 tokensOwed0, 90 | uint128 tokensOwed1 91 | ); 92 | 93 | function approve(address to, uint256 tokenId) external; 94 | 95 | function ownerOf(uint256 tokenId) external view returns (address); 96 | 97 | function transferFrom(address from, address to, uint256 tokenId) external; 98 | 99 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; 100 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/IUniswapV3Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IUniswapV3Factory { 5 | function createPool( 6 | address tokenA, 7 | address tokenB, 8 | uint24 fee 9 | ) external returns (address pool); 10 | 11 | function getPool( 12 | address tokenA, 13 | address tokenB, 14 | uint24 fee 15 | ) external view returns (address pool); 16 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/IUniswapV3Pool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IUniswapV3Pool { 5 | function initialize(uint160 sqrtPriceX96) external; 6 | function swap( 7 | address recipient, 8 | bool zeroForOne, 9 | int256 amountSpecified, 10 | uint160 sqrtPriceLimitX96, 11 | bytes calldata data 12 | ) external returns (int256 amount0, int256 amount1); 13 | function mint( 14 | address recipient, 15 | int24 tickLower, 16 | int24 tickUpper, 17 | uint128 amount, 18 | bytes calldata data 19 | ) external returns (uint256 amount0, uint256 amount1); 20 | function burn( 21 | int24 tickLower, 22 | int24 tickUpper, 23 | uint128 amount, 24 | bytes calldata data 25 | ) external returns (uint256 amount0, uint256 amount1); 26 | function collect( 27 | address recipient, 28 | int24 tickLower, 29 | int24 tickUpper, 30 | uint128 amount0Requested, 31 | uint128 amount1Requested 32 | ) external returns (uint128 amount0, uint128 amount1); 33 | 34 | struct Slot0 { 35 | uint160 sqrtPriceX96; 36 | int24 tick; 37 | uint16 observationIndex; 38 | uint16 observationCardinality; 39 | uint16 observationCardinalityNext; 40 | uint8 feeProtocol; 41 | bool unlocked; 42 | } 43 | 44 | function slot0() external view returns (Slot0 memory); 45 | 46 | // Add for liquidity addition in place of donate 47 | function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; 48 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/IUniswapV3Router.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IUniswapV3SwapCallback { 5 | function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external; 6 | } 7 | 8 | /// @title Router token swapping functionality 9 | /// @notice Functions for swapping tokens via Uniswap V3 10 | interface IUniswapV3Router is IUniswapV3SwapCallback { 11 | struct ExactInputSingleParams { 12 | address tokenIn; 13 | address tokenOut; 14 | uint24 fee; 15 | address recipient; 16 | uint256 deadline; 17 | uint256 amountIn; 18 | uint256 amountOutMinimum; 19 | uint160 sqrtPriceLimitX96; 20 | } 21 | 22 | function exactInputSingle( 23 | ExactInputSingleParams calldata params 24 | ) external payable returns (uint256 amountOut); 25 | 26 | struct ExactInputParams { 27 | bytes path; 28 | address recipient; 29 | uint256 deadline; 30 | uint256 amountIn; 31 | uint256 amountOutMinimum; 32 | } 33 | 34 | function exactInput( 35 | ExactInputParams calldata params 36 | ) external payable returns (uint256 amountOut); 37 | 38 | struct ExactOutputSingleParams { 39 | address tokenIn; 40 | address tokenOut; 41 | uint24 fee; 42 | address recipient; 43 | uint256 deadline; 44 | uint256 amountOut; 45 | uint256 amountInMaximum; 46 | uint160 sqrtPriceLimitX96; 47 | } 48 | 49 | function exactOutputSingle( 50 | ExactOutputSingleParams calldata params 51 | ) external payable returns (uint256 amountIn); 52 | 53 | struct ExactOutputParams { 54 | bytes path; 55 | address recipient; 56 | uint256 deadline; 57 | uint256 amountOut; 58 | uint256 amountInMaximum; 59 | } 60 | 61 | function exactOutput( 62 | ExactOutputParams calldata params 63 | ) external payable returns (uint256 amountIn); 64 | } -------------------------------------------------------------------------------- /clubcoin/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IWETH is IERC20 { 7 | function deposit() external payable; 8 | function withdraw(uint256) external; 9 | } -------------------------------------------------------------------------------- /clubcoin/lib/MerkleWhitelist.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; 5 | 6 | /** 7 | * @title MerkleWhitelist 8 | * @dev Library for handling whitelist verification using Merkle trees 9 | * This is much more efficient than using a mapping for large whitelists 10 | */ 11 | library MerkleWhitelist { 12 | /** 13 | * @notice Verify if an address is whitelisted using a Merkle proof 14 | * @param account The address to verify 15 | * @param proof The Merkle proof for the address 16 | * @param merkleRoot The root of the Merkle tree 17 | * @return Whether the address is whitelisted or not 18 | */ 19 | function verifyAddress( 20 | address account, 21 | bytes32[] calldata proof, 22 | bytes32 merkleRoot 23 | ) internal pure returns (bool) { 24 | bytes32 leaf = keccak256(abi.encodePacked(account)); 25 | return MerkleProof.verify(proof, merkleRoot, leaf); 26 | } 27 | } -------------------------------------------------------------------------------- /domains/README.md: -------------------------------------------------------------------------------- 1 | # `.cast` Details & Contract Deployment 2 | 3 | See the `contracts/` folder for more details on how .cast handles work! 4 | 5 | String names of domains are not stored on a contract level - token uri's are 6 | `sha3` hashes of the lowercase name after `.beb`/`.cast` has been removed. 7 | 8 | To map a token uri to metadata, you query: `build.far.quest/metadata/uri/{uri}`, 9 | e.g. for 10 | [playground.cast](https://build.far.quest/metadata/uri/28351188642621241456184943762989329996148978531966429149720007640204744112723). 11 | 12 | This folder also contains applicable ABIs for .cast handles. 13 | 14 | # Ethereum 15 | 16 | - [OpenSea Collection](https://opensea.io/collection/casthandles) 17 | - **BaseRegistrar** deployed to: 18 | [`0x427b8efEe2d6453Bb1c59849F164C867e4b2B376`](https://etherscan.io/address/0x427b8efEe2d6453Bb1c59849F164C867e4b2B376) 19 | (bebdomains.eth) 20 | - Base Registar = `.beb` TLD. 21 | - **BebRegistryBetaController** deployed to: 22 | [`0x0F08FC2A63F4BfcDDfDa5c38e9896220d5468a64`](https://etherscan.io/address/0x0F08FC2A63F4BfcDDfDa5c38e9896220d5468a64) 23 | 24 | # Optimism 25 | 26 | - [OpenSea Collection](https://opensea.io/collection/castoptimism) 27 | - **BaseRegistrar** deployed to: 28 | [`0xd14005cb9b40a1b7104eacdeae36f7fe112fae5f`](https://optimistic.etherscan.io/address/0xd14005cb9b40a1b7104eacdeae36f7fe112fae5f) 29 | - **OPBebRegistryBetaController** deployed to: 30 | [`0x8db531fe6bea7b474c7735879e9a1000e819bd1d`](https://optimistic.etherscan.io/address/0x8db531fe6bea7b474c7735879e9a1000e819bd1d) 31 | - Base Registar = `.cast` TLD with domains prefixed with `op_` (enforced by `OPBebRegistryBetaController`) 32 | 33 | # Base 34 | 35 | - [OpenSea Collection](https://opensea.io/collection/castbase) 36 | - **BaseRegistrar** deployed to: 37 | [`0xf2b35faadbcded342a1a4f1e6c95b977e85439fa`](https://basescan.org/address/0xf2b35faadbcded342a1a4f1e6c95b977e85439fa) 38 | - **BaseBebRegistryBetaController** deployed to: 39 | [`0xdd7672abb72542fd30307159bd898a273b1a14af`](https://basescan.org/address/0xdd7672abb72542fd30307159bd898a273b1a14af) 40 | - Base Registar = `.cast` TLD with domains prefixed with `base_` (enforced by `BaseBebRegistryBetaController`) 41 | -------------------------------------------------------------------------------- /domains/contracts/BEBRegistry.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IBEBRegistry.sol"; 10 | 11 | /** 12 | * The ENS registry contract. 13 | */ 14 | contract BEBRegistry is IBEBRegistry { 15 | struct Record { 16 | address owner; 17 | uint64 ttl; 18 | } 19 | 20 | mapping(bytes32 => Record) records; 21 | mapping(address => mapping(address => bool)) operators; 22 | 23 | // Permits modifications only by the owner of the specified node. 24 | modifier authorised(bytes32 node) { 25 | address owner = records[node].owner; 26 | require(owner == msg.sender || operators[owner][msg.sender]); 27 | _; 28 | } 29 | 30 | /** 31 | * @dev Constructs a new BEB registry. 32 | */ 33 | constructor() { 34 | records[0x0].owner = msg.sender; 35 | } 36 | 37 | /** 38 | * @dev Sets the record for a node. 39 | * @param node The node to update. 40 | * @param owner The address of the new owner. 41 | * @param ttl The TTL in seconds. 42 | */ 43 | function setRecord( 44 | bytes32 node, 45 | address owner, 46 | uint64 ttl 47 | ) external virtual override { 48 | setOwner(node, owner); 49 | _setTTL(node, ttl); 50 | } 51 | 52 | /** 53 | * @dev Sets the record for a subnode. 54 | * @param node The parent node. 55 | * @param label The hash of the label specifying the subnode. 56 | * @param owner The address of the new owner. 57 | * @param ttl The TTL in seconds. 58 | */ 59 | function setSubnodeRecord( 60 | bytes32 node, 61 | bytes32 label, 62 | address owner, 63 | uint64 ttl 64 | ) external virtual override { 65 | bytes32 subnode = setSubnodeOwner(node, label, owner); 66 | _setTTL(subnode, ttl); 67 | } 68 | 69 | /** 70 | * @dev Transfers ownership of a node to a new address. May only be called by the current owner of the node. 71 | * @param node The node to transfer ownership of. 72 | * @param owner The address of the new owner. 73 | */ 74 | function setOwner(bytes32 node, address owner) 75 | public 76 | virtual 77 | override 78 | authorised(node) 79 | { 80 | _setOwner(node, owner); 81 | emit Transfer(node, owner); 82 | } 83 | 84 | /** 85 | * @dev Transfers ownership of a subnode keccak256(node, label) to a new address. May only be called by the owner of the parent node. 86 | * @param node The parent node. 87 | * @param label The hash of the label specifying the subnode. 88 | * @param owner The address of the new owner. 89 | */ 90 | function setSubnodeOwner( 91 | bytes32 node, 92 | bytes32 label, 93 | address owner 94 | ) public virtual override authorised(node) returns (bytes32) { 95 | bytes32 subnode = keccak256(abi.encodePacked(node, label)); 96 | _setOwner(subnode, owner); 97 | emit NewOwner(node, label, owner); 98 | return subnode; 99 | } 100 | 101 | /** 102 | * @dev Sets the TTL for the specified node. 103 | * @param node The node to update. 104 | * @param ttl The TTL in seconds. 105 | */ 106 | function setTTL(bytes32 node, uint64 ttl) 107 | public 108 | virtual 109 | override 110 | authorised(node) 111 | { 112 | _setTTL(node, ttl); 113 | } 114 | 115 | /** 116 | * @dev Enable or disable approval for a third party ("operator") to manage 117 | * all of `msg.sender`'s BEB records. Emits the ApprovalForAll event. 118 | * @param operator Address to add to the set of authorized operators. 119 | * @param approved True if the operator is approved, false to revoke approval. 120 | */ 121 | function setApprovalForAll(address operator, bool approved) 122 | external 123 | virtual 124 | override 125 | { 126 | operators[msg.sender][operator] = approved; 127 | emit ApprovalForAll(msg.sender, operator, approved); 128 | } 129 | 130 | /** 131 | * @dev Returns the address that owns the specified node. 132 | * @param node The specified node. 133 | * @return address of the owner. 134 | */ 135 | function owner(bytes32 node) 136 | public 137 | view 138 | virtual 139 | override 140 | returns (address) 141 | { 142 | address addr = records[node].owner; 143 | if (addr == address(this)) { 144 | return address(0x0); 145 | } 146 | 147 | return addr; 148 | } 149 | 150 | /** 151 | * @dev Returns the TTL of a node, and any records associated with it. 152 | * @param node The specified node. 153 | * @return ttl of the node. 154 | */ 155 | function ttl(bytes32 node) public view virtual override returns (uint64) { 156 | return records[node].ttl; 157 | } 158 | 159 | /** 160 | * @dev Returns whether a record has been imported to the registry. 161 | * @param node The specified node. 162 | * @return Bool if record exists 163 | */ 164 | function recordExists(bytes32 node) 165 | public 166 | view 167 | virtual 168 | override 169 | returns (bool) 170 | { 171 | return records[node].owner != address(0x0); 172 | } 173 | 174 | /** 175 | * @dev Query if an address is an authorized operator for another address. 176 | * @param owner The address that owns the records. 177 | * @param operator The address that acts on behalf of the owner. 178 | * @return True if `operator` is an approved operator for `owner`, false otherwise. 179 | */ 180 | function isApprovedForAll(address owner, address operator) 181 | external 182 | view 183 | virtual 184 | override 185 | returns (bool) 186 | { 187 | return operators[owner][operator]; 188 | } 189 | 190 | function _setOwner(bytes32 node, address owner) internal virtual { 191 | records[node].owner = owner; 192 | } 193 | 194 | function _setTTL( 195 | bytes32 node, 196 | uint64 ttl 197 | ) internal { 198 | if (ttl != records[node].ttl) { 199 | records[node].ttl = ttl; 200 | emit NewTTL(node, ttl); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /domains/contracts/BaseBebRegistryBulkRegister.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.8.4; 3 | 4 | import "./BaseBebRegistryOneStepController.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract BaseBebRegistryBulkRegister is Ownable { 8 | BaseBebRegistryOneStepController public controller; 9 | 10 | constructor(address _controllerAddress) { 11 | controller = BaseBebRegistryOneStepController(_controllerAddress); 12 | } 13 | 14 | function bulkRegister( 15 | string[] calldata names, 16 | address[] calldata owners, 17 | uint256[] calldata durations 18 | ) external payable { 19 | require( 20 | names.length == owners.length && names.length == durations.length, 21 | "BaseBebRegistryBulkRegister: Input arrays must have the same length" 22 | ); 23 | 24 | uint256 totalCost = calculateTotalPrice(names, durations); 25 | require(msg.value >= totalCost, "BaseBebRegistryBulkRegister: Insufficient funds"); 26 | 27 | for (uint256 i = 0; i < names.length; i++) { 28 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 29 | controller.register{value: price.base + price.premium}(names[i], owners[i], durations[i]); 30 | } 31 | 32 | // Refund excess payment 33 | if (msg.value > totalCost) { 34 | payable(msg.sender).transfer(msg.value - totalCost); 35 | } 36 | } 37 | 38 | function bulkRenew(string[] calldata names, uint256[] calldata durations) external payable { 39 | require( 40 | names.length == durations.length, 41 | "BaseBebRegistryBulkRegister: Input arrays must have the same length" 42 | ); 43 | 44 | uint256 totalCost = calculateTotalPrice(names, durations); 45 | require(msg.value >= totalCost, "BaseBebRegistryBulkRegister: Insufficient funds"); 46 | 47 | for (uint256 i = 0; i < names.length; i++) { 48 | controller.renew{value: controller.rentPrice(names[i], durations[i]).base + controller.rentPrice(names[i], durations[i]).premium}(names[i], durations[i]); 49 | } 50 | 51 | // Refund excess payment 52 | if (msg.value > totalCost) { 53 | payable(msg.sender).transfer(msg.value - totalCost); 54 | } 55 | } 56 | 57 | function calculateTotalPrice(string[] calldata names, uint256[] calldata durations) public view returns (uint256) { 58 | require(names.length == durations.length, "Input arrays must have the same length"); 59 | 60 | uint256 totalCost = 0; 61 | uint256 length = names.length; 62 | 63 | for (uint256 i = 0; i < length;) { 64 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 65 | totalCost += price.base + price.premium; 66 | 67 | unchecked { 68 | ++i; 69 | } 70 | } 71 | 72 | return totalCost; 73 | } 74 | 75 | function withdraw() external onlyOwner { 76 | payable(owner()).transfer(address(this).balance); 77 | } 78 | 79 | receive() external payable {} 80 | } -------------------------------------------------------------------------------- /domains/contracts/BaseBebRegistryOneStepController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-only 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./BaseRegistrar.sol"; 10 | import "./StringUtils.sol"; 11 | import "./IBebRegistryOneStepController.sol"; 12 | 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Address.sol"; 15 | 16 | /** 17 | * @dev A registrar controller for registering and renewing names at fixed cost. 18 | */ 19 | contract BaseBebRegistryOneStepController is Ownable, IBebRegistryOneStepController { 20 | using StringUtils for *; 21 | using Address for address; 22 | 23 | uint256 public constant MIN_REGISTRATION_DURATION = 28 days; 24 | 25 | BaseRegistrar immutable base; 26 | IPriceOracle public immutable prices; 27 | 28 | event NameRegistered( 29 | string name, 30 | bytes32 indexed label, 31 | address indexed owner, 32 | uint256 duration, 33 | uint256 baseCost, 34 | uint256 premium, 35 | uint256 expires 36 | ); 37 | event NameRenewed( 38 | string name, 39 | bytes32 indexed label, 40 | uint256 cost, 41 | uint256 expires 42 | ); 43 | 44 | constructor( 45 | BaseRegistrar _base, 46 | IPriceOracle _prices 47 | ) { 48 | base = _base; 49 | prices = _prices; 50 | } 51 | 52 | function rentPrice(string memory name, uint256 duration) 53 | public 54 | view 55 | override 56 | returns (IPriceOracle.Price memory price) 57 | { 58 | bytes32 label = keccak256(bytes(name)); 59 | return prices.price(name, base.nameExpires(uint256(label)), duration); 60 | } 61 | 62 | function _compareStrings(string memory a, string memory b) internal pure returns(bool) { 63 | return (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); 64 | } 65 | 66 | function _trim(string memory str, uint start, uint end) internal pure returns(string memory) { 67 | // Warning: make sure the string is at least `end` characters long! 68 | bytes memory strBytes = bytes(str); 69 | bytes memory result = new bytes(end - start); 70 | for(uint i = start; i < end; i++) { 71 | result[i - start] = strBytes[i]; 72 | } 73 | return string(result); 74 | } 75 | 76 | 77 | function valid(string memory name) public pure returns (bool) { 78 | return name.strlen() >= 10 && _compareStrings(_trim(name, 0, 5), "base_"); 79 | } 80 | 81 | function available(string memory name) public view override returns (bool) { 82 | bytes32 label = keccak256(bytes(name)); 83 | return valid(name) && base.available(uint256(label)); 84 | } 85 | 86 | function register( 87 | string calldata name, 88 | address owner, 89 | uint256 duration 90 | ) public payable override { 91 | require( 92 | valid(name), 93 | "BaseBebRegistryOneStepController: Invalid name!" 94 | ); 95 | bytes32 label = keccak256(bytes(name)); 96 | IPriceOracle.Price memory price = rentPrice(name, duration); 97 | require( 98 | msg.value >= (price.base + price.premium), 99 | "BaseBebRegistryOneStepController: Not enough ether provided" 100 | ); 101 | require(duration >= MIN_REGISTRATION_DURATION); 102 | 103 | uint256 tokenId = uint256(label); 104 | uint256 expires = base.register(tokenId, owner, duration); 105 | 106 | emit NameRegistered( 107 | name, 108 | label, 109 | owner, 110 | duration, 111 | price.base, 112 | price.premium, 113 | expires 114 | ); 115 | 116 | if (msg.value > (price.base + price.premium)) { 117 | payable(msg.sender).transfer( 118 | msg.value - (price.base + price.premium) 119 | ); 120 | } 121 | } 122 | 123 | function renew(string calldata name, uint256 duration) 124 | external 125 | payable 126 | override 127 | { 128 | bytes32 label = keccak256(bytes(name)); 129 | IPriceOracle.Price memory price = rentPrice(name, duration); 130 | require( 131 | msg.value >= (price.base + price.premium), 132 | "BaseBebRegistryOneStepController: Not enough Ether provided for renewal" 133 | ); 134 | 135 | uint256 expires = base.renew(uint256(label), duration); 136 | 137 | if (msg.value > (price.base + price.premium)) { 138 | payable(msg.sender).transfer(msg.value - (price.base + price.premium)); 139 | } 140 | 141 | emit NameRenewed(name, label, price.base + price.premium, expires); 142 | } 143 | 144 | function withdraw() public { 145 | payable(owner()).transfer(address(this).balance); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /domains/contracts/BaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IBaseRegistrar.sol"; 10 | import "./IBEBRegistry.sol"; 11 | import "./IRoyaltyController.sol"; 12 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | 15 | contract BaseRegistrar is ERC721, IBaseRegistrar, Ownable { 16 | // A map of expiry times 17 | mapping(uint256=>uint) expiries; 18 | 19 | // The BEB registry 20 | IBEBRegistry public bebRegistry; 21 | // The Royalty Controller 22 | IRoyaltyController public royaltyController; 23 | // The namehash of the TLD this registrar owns (eg, .beb) 24 | bytes32 public baseNode; 25 | 26 | // A map of addresses that are authorised to register and renew names. 27 | mapping(address => bool) public controllers; 28 | 29 | // the grace period where the owner can renew but not reclaim the name 30 | uint256 public constant GRACE_PERIOD = 90 days; 31 | 32 | // The base metadata uri for the registrar 33 | string public baseURI; 34 | 35 | /** 36 | * v2.1.3 version of _isApprovedOrOwner which calls ownerOf(tokenId) and takes grace period into consideration instead of ERC721.ownerOf(tokenId); 37 | * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.3/contracts/token/ERC721/ERC721.sol#L187 38 | * @dev Returns whether the given spender can transfer a given token ID 39 | * @param spender address of the spender to query 40 | * @param tokenId uint256 ID of the token to be transferred 41 | * @return bool whether the msg.sender is approved for the given token ID, 42 | * is an operator of the owner, or is the owner of the token 43 | */ 44 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view override returns (bool) { 45 | address owner = ownerOf(tokenId); 46 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 47 | } 48 | 49 | // // control the royalty information 50 | // function _beforeTokenTransfer(address from, address to, uint256 tokenId) 51 | // internal virtual override 52 | // { 53 | // (address royaltyAddress, uint256 royaltyAmount) = this.royaltyInfo(tokenId, msg.value); 54 | // if (from == address(0)) { 55 | // // if the from address is 0, then this is a transfer from the contract itself 56 | // // so we don't need to do anything 57 | // return; 58 | // } 59 | // payable(royaltyAddress).transfer(royaltyAmount); // transfer the royalty to the royalty receiver 60 | // payable(to).transfer(msg.value - royaltyAmount); // transfer the rest to the receiver 61 | // super._beforeTokenTransfer(from, to, tokenId); // Call parent hook; 62 | // } 63 | 64 | constructor(IBEBRegistry _bebRegistry, bytes32 _baseNode) ERC721("BEB","BEB") { 65 | bebRegistry = _bebRegistry; 66 | baseNode = _baseNode; 67 | } 68 | 69 | /** 70 | * @dev make sure the owner of base TLD (i.e .beb) is of this registrar in the BEB registry 71 | */ 72 | modifier live() { 73 | require(bebRegistry.owner(baseNode) == address(this)); 74 | _; 75 | } 76 | 77 | 78 | modifier onlyController { 79 | require(controllers[msg.sender]); 80 | _; 81 | } 82 | 83 | /** 84 | * @dev Gets the owner of the specified token ID. Names become unowned 85 | * when their registration expires. 86 | * @param tokenId uint256 ID of the token to query the owner of 87 | * @return address currently marked as the owner of the given token ID 88 | */ 89 | function ownerOf(uint256 tokenId) public view override(IERC721, ERC721) returns (address) { 90 | require(expiries[tokenId] > block.timestamp); 91 | return super.ownerOf(tokenId); 92 | } 93 | 94 | // Authorises a controller, who can register and renew domains. 95 | function addController(address controller) external override onlyOwner { 96 | controllers[controller] = true; 97 | } 98 | 99 | // Revoke controller permission for an address. 100 | function removeController(address controller) external override onlyOwner { 101 | controllers[controller] = false; 102 | } 103 | 104 | // Returns the expiration timestamp of the specified id. 105 | function nameExpires(uint256 id) external view override returns(uint) { 106 | return expiries[id]; 107 | } 108 | 109 | // Returns true iff the specified name is available for registration. 110 | function available(uint256 id) public view override returns(bool) { 111 | // Not available if it's registered here or in its grace period. 112 | return expiries[id] + GRACE_PERIOD < block.timestamp; 113 | } 114 | 115 | /** 116 | * @dev Register a name. 117 | * @param id The token ID (keccak256 of the label). 118 | * @param owner The address that should own the registration. 119 | * @param duration Duration in seconds for the registration. 120 | */ 121 | function register( 122 | uint256 id, 123 | address owner, 124 | uint256 duration 125 | ) external override returns (uint256) { 126 | return _register(id, owner, duration, true); 127 | } 128 | 129 | /** 130 | * @dev Register a name, without modifying the registry. 131 | * @param id The token ID (keccak256 of the label). 132 | * @param owner The address that should own the registration. 133 | * @param duration Duration in seconds for the registration. 134 | */ 135 | function registerOnly( 136 | uint256 id, 137 | address owner, 138 | uint256 duration 139 | ) external returns (uint256) { 140 | return _register(id, owner, duration, false); 141 | } 142 | 143 | function renew(uint256 id, uint duration) external override onlyController returns(uint) { 144 | require(expiries[id] + GRACE_PERIOD >= block.timestamp); // Name must be registered here or in grace period 145 | require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow 146 | 147 | expiries[id] += duration; 148 | emit NameRenewed(id, expiries[id]); 149 | return expiries[id]; 150 | } 151 | 152 | /** 153 | * Internal functions 154 | */ 155 | 156 | function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) { 157 | require(available(id)); 158 | require(block.timestamp + duration + GRACE_PERIOD > block.timestamp + GRACE_PERIOD); // Prevent future overflow 159 | 160 | expiries[id] = block.timestamp + duration; 161 | if(_exists(id)) { 162 | // Name was previously owned, and expired 163 | _burn(id); 164 | } 165 | _mint(owner, id); 166 | if (updateRegistry) { 167 | bebRegistry.setSubnodeOwner(baseNode, bytes32(id), owner); 168 | } 169 | 170 | emit NameRegistered(id, owner, block.timestamp + duration); 171 | 172 | return block.timestamp + duration; 173 | } 174 | 175 | /** 176 | * @dev Reclaim ownership of a name in ENS, if you own it in the registrar. 177 | */ 178 | function reclaim(uint256 id, address owner) external live { 179 | require(_isApprovedOrOwner(msg.sender, id)); 180 | bebRegistry.setSubnodeOwner(baseNode, bytes32(id), owner); 181 | } 182 | 183 | /** 184 | * @dev Modify the baseURI. 185 | * @param uri The new baseURI. 186 | */ 187 | function setBaseURI(string calldata uri) external { 188 | return _setBaseURI(uri); 189 | } 190 | 191 | function _setBaseURI(string calldata uri) internal onlyOwner { 192 | baseURI = uri; 193 | } 194 | 195 | function _baseURI() internal view override returns(string memory) { 196 | return baseURI; 197 | } 198 | 199 | /** 200 | * @dev Modify the royaltyController. 201 | * @param _royaltyController The new royaltyController. 202 | */ 203 | function setRoyaltyController(IRoyaltyController _royaltyController) external { 204 | return _setRoyaltyController(_royaltyController); 205 | } 206 | 207 | function _setRoyaltyController(IRoyaltyController _royaltyController) internal onlyOwner { 208 | royaltyController = _royaltyController; 209 | } 210 | 211 | function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount) { 212 | return royaltyController.royaltyInfo(tokenId, salePrice); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /domains/contracts/Beb.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.7.0 <0.9.0; 8 | 9 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 10 | import "@openzeppelin/contracts/access/AccessControl.sol"; 11 | import "@openzeppelin/contracts/access/Ownable.sol"; 12 | 13 | contract Beb is ERC1155, Ownable, AccessControl { 14 | bytes32 public constant GRANTER_ROLE = keccak256("GRANTER_ROLE"); 15 | string public contractUri = "https://bebverse-public.s3.us-west-1.amazonaws.com/contract.json"; 16 | 17 | constructor(string memory uri_, address granter) ERC1155(uri_) { 18 | _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); 19 | _setupRole(GRANTER_ROLE, msg.sender); 20 | _setupRole(GRANTER_ROLE, granter); 21 | } 22 | 23 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, AccessControl) returns (bool) { 24 | return super.supportsInterface(interfaceId); 25 | } 26 | 27 | function _beforeTokenTransfer(address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) 28 | internal virtual override 29 | { 30 | super._beforeTokenTransfer(operator, from, to, ids, amounts, data); // Call parent hook 31 | require(from == address(0), "BEB: soulbound, non-transferable"); 32 | } 33 | 34 | function setTokenUri(string memory uri) 35 | public onlyRole(DEFAULT_ADMIN_ROLE) 36 | { 37 | super._setURI(uri); // Call parent hook 38 | } 39 | function setContractUri(string memory uri) 40 | public onlyRole(DEFAULT_ADMIN_ROLE) 41 | { 42 | contractUri = uri; 43 | } 44 | 45 | function grantToken(address to, uint256 id, uint256 amount, bytes memory data) 46 | public onlyRole(GRANTER_ROLE) 47 | { 48 | super._mint(to,id,amount,data); 49 | } 50 | 51 | function grantTokens(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) 52 | public onlyRole(GRANTER_ROLE) 53 | { 54 | super._mintBatch(to,ids,amounts,data); 55 | } 56 | 57 | function contractURI() public view returns (string memory) { 58 | return contractUri; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /domains/contracts/BebRegistryBetaController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./BaseRegistrar.sol"; 10 | import "./StringUtils.sol"; 11 | import "./IBebRegistryBetaController.sol"; 12 | 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; 15 | import "@openzeppelin/contracts/utils/Address.sol"; 16 | 17 | /** 18 | * @dev A registrar controller for registering and renewing names at fixed cost. 19 | */ 20 | contract BebRegistryBetaController is Ownable, IBebRegistryBetaController { 21 | using StringUtils for *; 22 | using Address for address; 23 | 24 | uint256 public constant MIN_REGISTRATION_DURATION = 28 days; 25 | 26 | BaseRegistrar immutable base; 27 | IPriceOracle public immutable prices; 28 | uint256 public immutable minCommitmentAge; 29 | uint256 public immutable maxCommitmentAge; 30 | 31 | mapping(bytes32 => uint256) public commitments; 32 | 33 | event NameRegistered( 34 | string name, 35 | bytes32 indexed label, 36 | address indexed owner, 37 | uint256 duration, 38 | uint256 baseCost, 39 | uint256 premium, 40 | uint256 expires 41 | ); 42 | event NameRenewed( 43 | string name, 44 | bytes32 indexed label, 45 | uint256 cost, 46 | uint256 expires 47 | ); 48 | 49 | constructor( 50 | BaseRegistrar _base, 51 | IPriceOracle _prices, 52 | uint256 _minCommitmentAge, 53 | uint256 _maxCommitmentAge 54 | ) { 55 | require(_maxCommitmentAge > _minCommitmentAge); 56 | 57 | base = _base; 58 | prices = _prices; 59 | minCommitmentAge = _minCommitmentAge; 60 | maxCommitmentAge = _maxCommitmentAge; 61 | } 62 | 63 | function rentPrice(string memory name, uint256 duration) 64 | public 65 | view 66 | override 67 | returns (IPriceOracle.Price memory price) 68 | { 69 | bytes32 label = keccak256(bytes(name)); 70 | return prices.price(name, base.nameExpires(uint256(label)), duration); 71 | } 72 | 73 | function valid(string memory name) public pure returns (bool) { 74 | return name.strlen() >= 1; 75 | } 76 | 77 | function available(string memory name) public view override returns (bool) { 78 | bytes32 label = keccak256(bytes(name)); 79 | return valid(name) && base.available(uint256(label)); 80 | } 81 | 82 | function makeCommitment( 83 | string memory name, 84 | address owner, 85 | uint256 duration, 86 | bytes32 secret 87 | ) public pure override returns (bytes32) { 88 | bytes32 label = keccak256(bytes(name)); 89 | return 90 | keccak256( 91 | abi.encode( 92 | label, 93 | owner, 94 | duration, 95 | secret 96 | ) 97 | ); 98 | } 99 | 100 | function commit(bytes32 commitment) public override { 101 | require(commitments[commitment] + maxCommitmentAge < block.timestamp); 102 | commitments[commitment] = block.timestamp; 103 | } 104 | 105 | function register( 106 | string calldata name, 107 | address owner, 108 | uint256 duration, 109 | bytes32 secret 110 | ) public payable override { 111 | bytes32 label = keccak256(bytes(name)); 112 | IPriceOracle.Price memory price = rentPrice(name, duration); 113 | require( 114 | msg.value >= (price.base + price.premium), 115 | "BebRegistryBetaController: Not enough ether provided" 116 | ); 117 | 118 | _consumeCommitment( 119 | name, 120 | duration, 121 | makeCommitment( 122 | name, 123 | owner, 124 | duration, 125 | secret 126 | ) 127 | ); 128 | 129 | uint256 tokenId = uint256(label); 130 | uint256 expires = base.register(tokenId, owner, duration); 131 | 132 | emit NameRegistered( 133 | name, 134 | label, 135 | owner, 136 | duration, 137 | price.base, 138 | price.premium, 139 | expires 140 | ); 141 | 142 | if (msg.value > (price.base + price.premium)) { 143 | payable(msg.sender).transfer( 144 | msg.value - (price.base + price.premium) 145 | ); 146 | } 147 | } 148 | 149 | function renew(string calldata name, uint256 duration) 150 | external 151 | payable 152 | override 153 | { 154 | bytes32 label = keccak256(bytes(name)); 155 | IPriceOracle.Price memory price = rentPrice(name, duration); 156 | require( 157 | msg.value >= (price.base + price.premium), 158 | "BebRegistryBetaController: Not enough Ether provided for renewal" 159 | ); 160 | 161 | uint256 expires = base.renew(uint256(label), duration); 162 | 163 | if (msg.value > (price.base + price.premium)) { 164 | payable(msg.sender).transfer(msg.value - (price.base + price.premium)); 165 | } 166 | 167 | emit NameRenewed(name, label, price.base + price.premium, expires); 168 | } 169 | 170 | function withdraw() public { 171 | payable(owner()).transfer(address(this).balance); 172 | } 173 | 174 | /* Internal functions */ 175 | 176 | function _consumeCommitment( 177 | string memory name, 178 | uint256 duration, 179 | bytes32 commitment 180 | ) internal { 181 | // Require a valid commitment (is old enough and is committed) 182 | require( 183 | commitments[commitment] + minCommitmentAge <= block.timestamp, 184 | "BebRegistryBetaController: Commitment is not valid" 185 | ); 186 | 187 | // If the commitment is too old, or the name is registered, stop 188 | require( 189 | commitments[commitment] + maxCommitmentAge > block.timestamp, 190 | "BebRegistryBetaController: Commitment has expired" 191 | ); 192 | require(available(name), "BebRegistryBetaController: Name is unavailable"); 193 | 194 | delete(commitments[commitment]); 195 | 196 | require(duration >= MIN_REGISTRATION_DURATION); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /domains/contracts/BebRegistryBulkRegister.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity >=0.8.4; 3 | 4 | import "./BebRegistryOneStepController.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract BebRegistryBulkRegister is Ownable { 8 | BebRegistryOneStepController public controller; 9 | 10 | constructor(address _controllerAddress) { 11 | controller = BebRegistryOneStepController(_controllerAddress); 12 | } 13 | 14 | function bulkRegister( 15 | string[] calldata names, 16 | address[] calldata owners, 17 | uint256[] calldata durations 18 | ) external payable { 19 | require( 20 | names.length == owners.length && names.length == durations.length, 21 | "BebRegistryBulkRegister: Input arrays must have the same length" 22 | ); 23 | 24 | uint256 totalCost = calculateTotalPrice(names, durations); 25 | require(msg.value >= totalCost, "BebRegistryBulkRegister: Insufficient funds"); 26 | 27 | for (uint256 i = 0; i < names.length; i++) { 28 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 29 | controller.register{value: price.base + price.premium}(names[i], owners[i], durations[i]); 30 | } 31 | 32 | // Refund excess payment 33 | if (msg.value > totalCost) { 34 | payable(msg.sender).transfer(msg.value - totalCost); 35 | } 36 | } 37 | 38 | function bulkRenew(string[] calldata names, uint256[] calldata durations) external payable { 39 | require( 40 | names.length == durations.length, 41 | "BebRegistryBulkRegister: Input arrays must have the same length" 42 | ); 43 | 44 | uint256 totalCost = calculateTotalPrice(names, durations); 45 | require(msg.value >= totalCost, "BebRegistryBulkRegister: Insufficient funds"); 46 | 47 | for (uint256 i = 0; i < names.length; i++) { 48 | controller.renew{value: controller.rentPrice(names[i], durations[i]).base + controller.rentPrice(names[i], durations[i]).premium}(names[i], durations[i]); 49 | } 50 | 51 | // Refund excess payment 52 | if (msg.value > totalCost) { 53 | payable(msg.sender).transfer(msg.value - totalCost); 54 | } 55 | } 56 | 57 | function calculateTotalPrice(string[] calldata names, uint256[] calldata durations) public view returns (uint256) { 58 | require(names.length == durations.length, "Input arrays must have the same length"); 59 | 60 | uint256 totalCost = 0; 61 | uint256 length = names.length; 62 | 63 | for (uint256 i = 0; i < length;) { 64 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 65 | totalCost += price.base + price.premium; 66 | 67 | unchecked { 68 | ++i; 69 | } 70 | } 71 | 72 | return totalCost; 73 | } 74 | 75 | function withdraw() external onlyOwner { 76 | payable(owner()).transfer(address(this).balance); 77 | } 78 | 79 | receive() external payable {} 80 | } -------------------------------------------------------------------------------- /domains/contracts/BebRegistryOneStepController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./BaseRegistrar.sol"; 10 | import "./StringUtils.sol"; 11 | import "./IBebRegistryOneStepController.sol"; 12 | 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Address.sol"; 15 | 16 | /** 17 | * @dev A registrar controller for registering and renewing names at fixed cost. 18 | */ 19 | contract BebRegistryOneStepController is Ownable, IBebRegistryOneStepController { 20 | using StringUtils for *; 21 | using Address for address; 22 | 23 | uint256 public constant MIN_REGISTRATION_DURATION = 28 days; 24 | 25 | BaseRegistrar immutable base; 26 | IPriceOracle public immutable prices; 27 | 28 | event NameRegistered( 29 | string name, 30 | bytes32 indexed label, 31 | address indexed owner, 32 | uint256 duration, 33 | uint256 baseCost, 34 | uint256 premium, 35 | uint256 expires 36 | ); 37 | event NameRenewed( 38 | string name, 39 | bytes32 indexed label, 40 | uint256 cost, 41 | uint256 expires 42 | ); 43 | 44 | constructor( 45 | BaseRegistrar _base, 46 | IPriceOracle _prices 47 | ) { 48 | base = _base; 49 | prices = _prices; 50 | } 51 | 52 | function rentPrice(string memory name, uint256 duration) 53 | public 54 | view 55 | override 56 | returns (IPriceOracle.Price memory price) 57 | { 58 | bytes32 label = keccak256(bytes(name)); 59 | return prices.price(name, base.nameExpires(uint256(label)), duration); 60 | } 61 | 62 | function valid(string memory name) public pure returns (bool) { 63 | return name.strlen() >= 1; 64 | } 65 | 66 | function available(string memory name) public view override returns (bool) { 67 | bytes32 label = keccak256(bytes(name)); 68 | return valid(name) && base.available(uint256(label)); 69 | } 70 | 71 | function register( 72 | string calldata name, 73 | address owner, 74 | uint256 duration 75 | ) public payable override { 76 | bytes32 label = keccak256(bytes(name)); 77 | IPriceOracle.Price memory price = rentPrice(name, duration); 78 | require( 79 | msg.value >= (price.base + price.premium), 80 | "BebRegistryBetaController: Not enough ether provided" 81 | ); 82 | require(duration >= MIN_REGISTRATION_DURATION); 83 | 84 | uint256 tokenId = uint256(label); 85 | uint256 expires = base.register(tokenId, owner, duration); 86 | 87 | emit NameRegistered( 88 | name, 89 | label, 90 | owner, 91 | duration, 92 | price.base, 93 | price.premium, 94 | expires 95 | ); 96 | 97 | if (msg.value > (price.base + price.premium)) { 98 | payable(msg.sender).transfer( 99 | msg.value - (price.base + price.premium) 100 | ); 101 | } 102 | } 103 | 104 | function renew(string calldata name, uint256 duration) 105 | external 106 | payable 107 | override 108 | { 109 | bytes32 label = keccak256(bytes(name)); 110 | IPriceOracle.Price memory price = rentPrice(name, duration); 111 | require( 112 | msg.value >= (price.base + price.premium), 113 | "BebRegistryBetaController: Not enough Ether provided for renewal" 114 | ); 115 | 116 | uint256 expires = base.renew(uint256(label), duration); 117 | 118 | if (msg.value > (price.base + price.premium)) { 119 | payable(msg.sender).transfer(msg.value - (price.base + price.premium)); 120 | } 121 | 122 | emit NameRenewed(name, label, price.base + price.premium, expires); 123 | } 124 | 125 | function withdraw() public { 126 | payable(owner()).transfer(address(this).balance); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /domains/contracts/IBEBRegistry.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | interface IBEBRegistry { 10 | // Logged when the owner of a node assigns a new owner to a subnode. 11 | event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); 12 | 13 | // Logged when the owner of a node transfers ownership to a new account. 14 | event Transfer(bytes32 indexed node, address owner); 15 | 16 | // Logged when the TTL of a node changes 17 | event NewTTL(bytes32 indexed node, uint64 ttl); 18 | 19 | // Logged when an operator is added or removed. 20 | event ApprovalForAll( 21 | address indexed owner, 22 | address indexed operator, 23 | bool approved 24 | ); 25 | 26 | function setRecord( 27 | bytes32 node, 28 | address owner, 29 | uint64 ttl 30 | ) external; 31 | 32 | function setSubnodeRecord( 33 | bytes32 node, 34 | bytes32 label, 35 | address owner, 36 | uint64 ttl 37 | ) external; 38 | 39 | function setSubnodeOwner( 40 | bytes32 node, 41 | bytes32 label, 42 | address owner 43 | ) external returns (bytes32); 44 | 45 | function setOwner(bytes32 node, address owner) external; 46 | 47 | function setTTL(bytes32 node, uint64 ttl) external; 48 | 49 | function setApprovalForAll(address operator, bool approved) external; 50 | 51 | function owner(bytes32 node) external view returns (address); 52 | 53 | function ttl(bytes32 node) external view returns (uint64); 54 | 55 | function recordExists(bytes32 node) external view returns (bool); 56 | 57 | function isApprovedForAll(address owner, address operator) 58 | external 59 | view 60 | returns (bool); 61 | } 62 | -------------------------------------------------------------------------------- /domains/contracts/IBaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IBaseRegistrar.sol"; 10 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 11 | 12 | interface IBaseRegistrar is IERC721 { 13 | 14 | event ControllerAdded(address indexed controller); 15 | event ControllerRemoved(address indexed controller); 16 | event NameMigrated( 17 | uint256 indexed id, 18 | address indexed owner, 19 | uint256 expires 20 | ); 21 | event NameRegistered( 22 | uint256 indexed id, 23 | address indexed owner, 24 | uint256 expires 25 | ); 26 | event NameRenewed(uint256 indexed id, uint256 expires); 27 | // Authorises a controller, who can register and renew domains. 28 | function addController(address controller) external; 29 | 30 | // Revoke controller permission for an address. 31 | function removeController(address controller) external; 32 | 33 | // Returns the expiration timestamp of the specified label hash. 34 | function nameExpires(uint256 id) external view returns (uint256); 35 | 36 | // Returns true iff the specified name is available for registration. 37 | function available(uint256 id) external view returns (bool); 38 | 39 | /** 40 | * @dev Register a name. 41 | */ 42 | function register( 43 | uint256 id, 44 | address owner, 45 | uint256 duration 46 | ) external returns (uint256); 47 | 48 | function renew(uint256 id, uint256 duration) external returns (uint256); 49 | } 50 | -------------------------------------------------------------------------------- /domains/contracts/IBebRegistryBetaController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IPriceOracle.sol"; 10 | 11 | interface IBebRegistryBetaController { 12 | function rentPrice(string memory, uint256) 13 | external 14 | returns (IPriceOracle.Price memory); 15 | 16 | function available(string memory) external returns (bool); 17 | 18 | function makeCommitment( 19 | string memory, 20 | address, 21 | uint256, 22 | bytes32 23 | ) external returns (bytes32); 24 | 25 | function commit(bytes32) external; 26 | 27 | function register( 28 | string calldata, 29 | address, 30 | uint256, 31 | bytes32 32 | ) external payable; 33 | 34 | function renew(string calldata, uint256) external payable; 35 | } 36 | -------------------------------------------------------------------------------- /domains/contracts/IBebRegistryOneStepController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IPriceOracle.sol"; 10 | 11 | interface IBebRegistryOneStepController { 12 | function rentPrice(string memory, uint256) 13 | external 14 | returns (IPriceOracle.Price memory); 15 | 16 | function available(string memory) external returns (bool); 17 | 18 | function register( 19 | string calldata, 20 | address, 21 | uint256 22 | ) external payable; 23 | 24 | function renew(string calldata, uint256) external payable; 25 | } 26 | -------------------------------------------------------------------------------- /domains/contracts/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | interface IPriceOracle { 10 | struct Price { 11 | uint256 base; 12 | uint256 premium; 13 | } 14 | 15 | /** 16 | * @dev Returns the price to register or renew a name. 17 | * @param name The name being registered or renewed. 18 | * @param expires When the name presently expires (0 if this is a new registration). 19 | * @param duration How long the name is being registered or extended for, in seconds. 20 | * @return base premium tuple of base price + premium price 21 | */ 22 | function price( 23 | string calldata name, 24 | uint256 expires, 25 | uint256 duration 26 | ) external view returns (Price calldata); 27 | } 28 | -------------------------------------------------------------------------------- /domains/contracts/IRoyaltyController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | import "@openzeppelin/contracts/interfaces/IERC2981.sol"; 8 | 9 | pragma solidity >=0.8.4; 10 | 11 | interface IRoyaltyController is IERC2981 {} 12 | -------------------------------------------------------------------------------- /domains/contracts/OPBebRegistryOneStepController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./BaseRegistrar.sol"; 10 | import "./StringUtils.sol"; 11 | import "./IBebRegistryOneStepController.sol"; 12 | 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Address.sol"; 15 | 16 | /** 17 | * @dev A registrar controller for registering and renewing names at fixed cost. 18 | */ 19 | contract OPBebRegistryOneStepController is Ownable, IBebRegistryOneStepController { 20 | using StringUtils for *; 21 | using Address for address; 22 | 23 | uint256 public constant MIN_REGISTRATION_DURATION = 28 days; 24 | 25 | BaseRegistrar immutable base; 26 | IPriceOracle public immutable prices; 27 | 28 | event NameRegistered( 29 | string name, 30 | bytes32 indexed label, 31 | address indexed owner, 32 | uint256 duration, 33 | uint256 baseCost, 34 | uint256 premium, 35 | uint256 expires 36 | ); 37 | event NameRenewed( 38 | string name, 39 | bytes32 indexed label, 40 | uint256 cost, 41 | uint256 expires 42 | ); 43 | 44 | constructor( 45 | BaseRegistrar _base, 46 | IPriceOracle _prices 47 | ) { 48 | base = _base; 49 | prices = _prices; 50 | } 51 | 52 | function rentPrice(string memory name, uint256 duration) 53 | public 54 | view 55 | override 56 | returns (IPriceOracle.Price memory price) 57 | { 58 | bytes32 label = keccak256(bytes(name)); 59 | return prices.price(name, base.nameExpires(uint256(label)), duration); 60 | } 61 | 62 | function _compareStrings(string memory a, string memory b) internal pure returns(bool) { 63 | return (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); 64 | } 65 | 66 | function _trim(string memory str, uint start, uint end) internal pure returns(string memory) { 67 | // Warning: make sure the string is at least `end` characters long! 68 | bytes memory strBytes = bytes(str); 69 | bytes memory result = new bytes(end - start); 70 | for(uint i = start; i < end; i++) { 71 | result[i - start] = strBytes[i]; 72 | } 73 | return string(result); 74 | } 75 | 76 | 77 | function valid(string memory name) public pure returns (bool) { 78 | return name.strlen() >= 10 && _compareStrings(_trim(name, 0, 3), "op_"); 79 | } 80 | 81 | function available(string memory name) public view override returns (bool) { 82 | bytes32 label = keccak256(bytes(name)); 83 | return valid(name) && base.available(uint256(label)); 84 | } 85 | 86 | function register( 87 | string calldata name, 88 | address owner, 89 | uint256 duration 90 | ) public payable override { 91 | require( 92 | valid(name), 93 | "OPBebRegistryOneStepController: Invalid name!" 94 | ); 95 | bytes32 label = keccak256(bytes(name)); 96 | IPriceOracle.Price memory price = rentPrice(name, duration); 97 | require( 98 | msg.value >= (price.base + price.premium), 99 | "OPBebRegistryOneStepController: Not enough ether provided" 100 | ); 101 | require(duration >= MIN_REGISTRATION_DURATION); 102 | 103 | uint256 tokenId = uint256(label); 104 | uint256 expires = base.register(tokenId, owner, duration); 105 | 106 | emit NameRegistered( 107 | name, 108 | label, 109 | owner, 110 | duration, 111 | price.base, 112 | price.premium, 113 | expires 114 | ); 115 | 116 | if (msg.value > (price.base + price.premium)) { 117 | payable(msg.sender).transfer( 118 | msg.value - (price.base + price.premium) 119 | ); 120 | } 121 | } 122 | 123 | function renew(string calldata name, uint256 duration) 124 | external 125 | payable 126 | override 127 | { 128 | bytes32 label = keccak256(bytes(name)); 129 | IPriceOracle.Price memory price = rentPrice(name, duration); 130 | require( 131 | msg.value >= (price.base + price.premium), 132 | "OPBebRegistryOneStepController: Not enough Ether provided for renewal" 133 | ); 134 | 135 | uint256 expires = base.renew(uint256(label), duration); 136 | 137 | if (msg.value > (price.base + price.premium)) { 138 | payable(msg.sender).transfer(msg.value - (price.base + price.premium)); 139 | } 140 | 141 | emit NameRenewed(name, label, price.base + price.premium, expires); 142 | } 143 | 144 | function withdraw() public { 145 | payable(owner()).transfer(address(this).balance); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /domains/contracts/OPCastRegistryOneStepController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./BaseRegistrar.sol"; 10 | import "./StringUtils.sol"; 11 | import "./IBebRegistryOneStepController.sol"; 12 | 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | import "@openzeppelin/contracts/utils/Address.sol"; 15 | 16 | /** 17 | * @dev A registrar controller for registering and renewing names at fixed cost. 18 | */ 19 | contract OPCastRegistryOneStepController is Ownable, IBebRegistryOneStepController { 20 | using StringUtils for *; 21 | using Address for address; 22 | 23 | uint256 public constant MIN_REGISTRATION_DURATION = 28 days; 24 | 25 | BaseRegistrar immutable base; 26 | IPriceOracle public immutable prices; 27 | 28 | event NameRegistered( 29 | string name, 30 | bytes32 indexed label, 31 | address indexed owner, 32 | uint256 duration, 33 | uint256 baseCost, 34 | uint256 premium, 35 | uint256 expires 36 | ); 37 | event NameRenewed( 38 | string name, 39 | bytes32 indexed label, 40 | uint256 cost, 41 | uint256 expires 42 | ); 43 | 44 | constructor( 45 | BaseRegistrar _base, 46 | IPriceOracle _prices 47 | ) { 48 | base = _base; 49 | prices = _prices; 50 | } 51 | 52 | function rentPrice(string memory name, uint256 duration) 53 | public 54 | view 55 | override 56 | returns (IPriceOracle.Price memory price) 57 | { 58 | bytes32 label = keccak256(bytes(name)); 59 | return prices.price(name, base.nameExpires(uint256(label)), duration); 60 | } 61 | 62 | function _compareStrings(string memory a, string memory b) internal pure returns(bool) { 63 | return (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); 64 | } 65 | 66 | function _trim(string memory str, uint start, uint end) internal pure returns(string memory) { 67 | // Warning: make sure the string is at least `end` characters long! 68 | bytes memory strBytes = bytes(str); 69 | bytes memory result = new bytes(end - start); 70 | for(uint i = start; i < end; i++) { 71 | result[i - start] = strBytes[i]; 72 | } 73 | return string(result); 74 | } 75 | 76 | 77 | function valid(string memory name) public pure returns (bool) { 78 | return name.strlen() >= 4 && _compareStrings(_trim(name, 0, 3), "op_"); 79 | } 80 | 81 | function available(string memory name) public view override returns (bool) { 82 | bytes32 label = keccak256(bytes(name)); 83 | return valid(name) && base.available(uint256(label)); 84 | } 85 | 86 | function register( 87 | string calldata name, 88 | address owner, 89 | uint256 duration 90 | ) public payable override { 91 | require( 92 | valid(name), 93 | "OPCastRegistryOneStepController: Invalid name!" 94 | ); 95 | bytes32 label = keccak256(bytes(name)); 96 | IPriceOracle.Price memory price = rentPrice(name, duration); 97 | require( 98 | msg.value >= (price.base + price.premium), 99 | "OPCastRegistryOneStepController: Not enough ether provided" 100 | ); 101 | require(duration >= MIN_REGISTRATION_DURATION); 102 | 103 | uint256 tokenId = uint256(label); 104 | uint256 expires = base.register(tokenId, owner, duration); 105 | 106 | emit NameRegistered( 107 | name, 108 | label, 109 | owner, 110 | duration, 111 | price.base, 112 | price.premium, 113 | expires 114 | ); 115 | 116 | if (msg.value > (price.base + price.premium)) { 117 | payable(msg.sender).transfer( 118 | msg.value - (price.base + price.premium) 119 | ); 120 | } 121 | } 122 | 123 | function renew(string calldata name, uint256 duration) 124 | external 125 | payable 126 | override 127 | { 128 | bytes32 label = keccak256(bytes(name)); 129 | IPriceOracle.Price memory price = rentPrice(name, duration); 130 | require( 131 | msg.value >= (price.base + price.premium), 132 | "OPCastRegistryOneStepController: Not enough Ether provided for renewal" 133 | ); 134 | 135 | uint256 expires = base.renew(uint256(label), duration); 136 | 137 | if (msg.value > (price.base + price.premium)) { 138 | payable(msg.sender).transfer(msg.value - (price.base + price.premium)); 139 | } 140 | 141 | emit NameRenewed(name, label, price.base + price.premium, expires); 142 | } 143 | 144 | function withdraw() public { 145 | payable(owner()).transfer(address(this).balance); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /domains/contracts/OpBebRegistryBulkRegister.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | pragma solidity >=0.8.4; 7 | 8 | import "./OPBebRegistryOneStepController.sol"; 9 | import "@openzeppelin/contracts/access/Ownable.sol"; 10 | 11 | contract OpBebRegistryBulkRegister is Ownable { 12 | OPBebRegistryOneStepController public controller; 13 | 14 | constructor(address _controllerAddress) { 15 | controller = OPBebRegistryOneStepController(_controllerAddress); 16 | } 17 | 18 | function bulkRegister( 19 | string[] calldata names, 20 | address[] calldata owners, 21 | uint256[] calldata durations 22 | ) external payable { 23 | require( 24 | names.length == owners.length && names.length == durations.length, 25 | "OpBebRegistryBulkRegister: Input arrays must have the same length" 26 | ); 27 | 28 | uint256 totalCost = calculateTotalPrice(names, durations); 29 | require(msg.value >= totalCost, "OpBebRegistryBulkRegister: Insufficient funds"); 30 | 31 | for (uint256 i = 0; i < names.length; i++) { 32 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 33 | controller.register{value: price.base + price.premium}(names[i], owners[i], durations[i]); 34 | } 35 | 36 | // Refund excess payment 37 | if (msg.value > totalCost) { 38 | payable(msg.sender).transfer(msg.value - totalCost); 39 | } 40 | } 41 | 42 | function bulkRenew(string[] calldata names, uint256[] calldata durations) external payable { 43 | require( 44 | names.length == durations.length, 45 | "OpBebRegistryBulkRegister: Input arrays must have the same length" 46 | ); 47 | 48 | uint256 totalCost = calculateTotalPrice(names, durations); 49 | require(msg.value >= totalCost, "OpBebRegistryBulkRegister: Insufficient funds"); 50 | 51 | for (uint256 i = 0; i < names.length; i++) { 52 | controller.renew{value: controller.rentPrice(names[i], durations[i]).base + controller.rentPrice(names[i], durations[i]).premium}(names[i], durations[i]); 53 | } 54 | 55 | // Refund excess payment 56 | if (msg.value > totalCost) { 57 | payable(msg.sender).transfer(msg.value - totalCost); 58 | } 59 | } 60 | 61 | function calculateTotalPrice(string[] calldata names, uint256[] calldata durations) public view returns (uint256) { 62 | require(names.length == durations.length, "Input arrays must have the same length"); 63 | 64 | uint256 totalCost = 0; 65 | uint256 length = names.length; 66 | 67 | for (uint256 i = 0; i < length;) { 68 | IPriceOracle.Price memory price = controller.rentPrice(names[i], durations[i]); 69 | totalCost += price.base + price.premium; 70 | 71 | unchecked { 72 | ++i; 73 | } 74 | } 75 | 76 | return totalCost; 77 | } 78 | 79 | function withdraw() external onlyOwner { 80 | payable(owner()).transfer(address(this).balance); 81 | } 82 | 83 | receive() external payable {} 84 | } -------------------------------------------------------------------------------- /domains/contracts/PriceOracle.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IPriceOracle.sol"; 10 | import "./StringUtils.sol"; 11 | import "@openzeppelin/contracts/access/Ownable.sol"; 12 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 13 | import "@openzeppelin/contracts/access/AccessControl.sol"; 14 | 15 | 16 | // StablePriceOracle sets a price in USD, based on an oracle. 17 | contract PriceOracle is IPriceOracle, Ownable, AccessControl { 18 | using SafeMath for *; 19 | using StringUtils for *; 20 | 21 | // Rent in base price units by length, in WEI 22 | uint256 public basePrice; 23 | 24 | // Rent in premium, in WEI 25 | uint256 public premiumPriceBase; 26 | 27 | // Granter address 28 | bytes32 public constant GRANTER_ROLE = keccak256("GRANTER_ROLE"); 29 | 30 | constructor(uint256 _basePrice, uint256 _premiumPriceBase, address granter) { 31 | basePrice = _basePrice; 32 | premiumPriceBase = _premiumPriceBase; 33 | _setupRole(GRANTER_ROLE, granter); 34 | } 35 | 36 | function price( 37 | string calldata name, 38 | uint256 expires, 39 | uint256 duration 40 | ) external view override returns (IPriceOracle.Price memory) { 41 | return 42 | IPriceOracle.Price({ 43 | base: basePrice.mul(duration), 44 | premium: (_premium(name, expires, duration)).mul(duration) 45 | }); 46 | } 47 | 48 | /** 49 | * @dev Returns the pricing premium in wei. 50 | */ 51 | function premium( 52 | string calldata name, 53 | uint256 expires, 54 | uint256 duration 55 | ) external view returns (uint256) { 56 | return _premium(name, expires, duration); 57 | } 58 | 59 | /** 60 | * @dev Returns the pricing premium in internal base units. 61 | */ 62 | function _premium( 63 | string memory name, 64 | uint256 expires, 65 | uint256 duration 66 | ) internal view virtual returns (uint256) { 67 | uint256 len = name.strlen(); 68 | require(len >= 1, "PriceOracle: Name must be at least 1 character long"); 69 | if (len >= 10 || hasRole(GRANTER_ROLE, msg.sender)) { 70 | return 0; 71 | } 72 | return (premiumPriceBase == 0) ? 0 : (premiumPriceBase).div(len); 73 | } 74 | 75 | function toWei(uint256 amount) internal view returns (uint256) { 76 | uint256 weiAmount = (1e8).mul(amount); 77 | return weiAmount; 78 | } 79 | 80 | function changeBasePrice(uint256 newBasePrice) public onlyOwner { 81 | basePrice = newBasePrice; 82 | } 83 | 84 | function changePremiumPrice(uint256 newPremiumPriceBase) public onlyOwner { 85 | premiumPriceBase = newPremiumPriceBase; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /domains/contracts/RoyaltyController.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "@openzeppelin/contracts/access/Ownable.sol"; 10 | import "@openzeppelin/contracts/token/common/ERC2981.sol"; 11 | 12 | contract RoyaltyController is Ownable, ERC2981 { 13 | constructor(address receiver, uint96 feeNumerator) { 14 | _setDefaultRoyalty(receiver, feeNumerator); 15 | } 16 | 17 | function setDefaultRoyalty(address receiver, uint96 feeNumerator) public onlyOwner { 18 | _setDefaultRoyalty(receiver, feeNumerator); 19 | } 20 | 21 | function setTokenRoyalty(uint256 tokenId, address receiver, uint96 feeNumerator) public onlyOwner { 22 | _setTokenRoyalty(tokenId, receiver, feeNumerator); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /domains/contracts/StringUtils.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | library StringUtils { 10 | /** 11 | * @dev Returns the length of a given string 12 | * 13 | * @param s The string to measure the length of 14 | * @return The length of the input string 15 | */ 16 | function strlen(string memory s) internal pure returns (uint) { 17 | uint len; 18 | uint i = 0; 19 | uint bytelength = bytes(s).length; 20 | for(len = 0; i < bytelength; len++) { 21 | bytes1 b = bytes(s)[i]; 22 | if(b < 0x80) { 23 | i += 1; 24 | } else if (b < 0xE0) { 25 | i += 2; 26 | } else if (b < 0xF0) { 27 | i += 3; 28 | } else if (b < 0xF8) { 29 | i += 4; 30 | } else if (b < 0xFC) { 31 | i += 5; 32 | } else { 33 | i += 6; 34 | } 35 | } 36 | return len; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /domains/contracts/VibeBaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ///////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IBaseRegistrar.sol"; 10 | import "./IBEBRegistry.sol"; 11 | import "./IRoyaltyController.sol"; 12 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 13 | import "@openzeppelin/contracts/access/Ownable.sol"; 14 | 15 | contract VibeBaseRegistrar is ERC721, IBaseRegistrar, Ownable { 16 | // A map of expiry times 17 | mapping(uint256=>uint) expiries; 18 | 19 | // The BEB registry 20 | IBEBRegistry public bebRegistry; 21 | // The Royalty Controller 22 | IRoyaltyController public royaltyController; 23 | // The namehash of the TLD this registrar owns (eg, .beb) 24 | bytes32 public baseNode; 25 | 26 | // A map of addresses that are authorised to register and renew names. 27 | mapping(address => bool) public controllers; 28 | 29 | // the grace period where the owner can renew but not reclaim the name 30 | uint256 public constant GRACE_PERIOD = 90 days; 31 | 32 | // The base metadata uri for the registrar 33 | string public baseURI; 34 | 35 | /** 36 | * v2.1.3 version of _isApprovedOrOwner which calls ownerOf(tokenId) and takes grace period into consideration instead of ERC721.ownerOf(tokenId); 37 | * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.1.3/contracts/token/ERC721/ERC721.sol#L187 38 | * @dev Returns whether the given spender can transfer a given token ID 39 | * @param spender address of the spender to query 40 | * @param tokenId uint256 ID of the token to be transferred 41 | * @return bool whether the msg.sender is approved for the given token ID, 42 | * is an operator of the owner, or is the owner of the token 43 | */ 44 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view override returns (bool) { 45 | address owner = ownerOf(tokenId); 46 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 47 | } 48 | 49 | // // control the royalty information 50 | // function _beforeTokenTransfer(address from, address to, uint256 tokenId) 51 | // internal virtual override 52 | // { 53 | // (address royaltyAddress, uint256 royaltyAmount) = this.royaltyInfo(tokenId, msg.value); 54 | // if (from == address(0)) { 55 | // // if the from address is 0, then this is a transfer from the contract itself 56 | // // so we don't need to do anything 57 | // return; 58 | // } 59 | // payable(royaltyAddress).transfer(royaltyAmount); // transfer the royalty to the royalty receiver 60 | // payable(to).transfer(msg.value - royaltyAmount); // transfer the rest to the receiver 61 | // super._beforeTokenTransfer(from, to, tokenId); // Call parent hook; 62 | // } 63 | 64 | constructor(IBEBRegistry _bebRegistry, bytes32 _baseNode) ERC721("VIBE","VIBE") { 65 | bebRegistry = _bebRegistry; 66 | baseNode = _baseNode; 67 | } 68 | 69 | /** 70 | * @dev make sure the owner of base TLD (i.e .beb) is of this registrar in the BEB registry 71 | */ 72 | modifier live() { 73 | require(bebRegistry.owner(baseNode) == address(this)); 74 | _; 75 | } 76 | 77 | 78 | modifier onlyController { 79 | require(controllers[msg.sender]); 80 | _; 81 | } 82 | 83 | /** 84 | * @dev Gets the owner of the specified token ID. Names become unowned 85 | * when their registration expires. 86 | * @param tokenId uint256 ID of the token to query the owner of 87 | * @return address currently marked as the owner of the given token ID 88 | */ 89 | function ownerOf(uint256 tokenId) public view override(IERC721, ERC721) returns (address) { 90 | require(expiries[tokenId] > block.timestamp); 91 | return super.ownerOf(tokenId); 92 | } 93 | 94 | // Authorises a controller, who can register and renew domains. 95 | function addController(address controller) external override onlyOwner { 96 | controllers[controller] = true; 97 | } 98 | 99 | // Revoke controller permission for an address. 100 | function removeController(address controller) external override onlyOwner { 101 | controllers[controller] = false; 102 | } 103 | 104 | // Returns the expiration timestamp of the specified id. 105 | function nameExpires(uint256 id) external view override returns(uint) { 106 | return expiries[id]; 107 | } 108 | 109 | // Returns true iff the specified name is available for registration. 110 | function available(uint256 id) public view override returns(bool) { 111 | // Not available if it's registered here or in its grace period. 112 | return expiries[id] + GRACE_PERIOD < block.timestamp; 113 | } 114 | 115 | /** 116 | * @dev Register a name. 117 | * @param id The token ID (keccak256 of the label). 118 | * @param owner The address that should own the registration. 119 | * @param duration Duration in seconds for the registration. 120 | */ 121 | function register( 122 | uint256 id, 123 | address owner, 124 | uint256 duration 125 | ) external override returns (uint256) { 126 | return _register(id, owner, duration, true); 127 | } 128 | 129 | /** 130 | * @dev Register a name, without modifying the registry. 131 | * @param id The token ID (keccak256 of the label). 132 | * @param owner The address that should own the registration. 133 | * @param duration Duration in seconds for the registration. 134 | */ 135 | function registerOnly( 136 | uint256 id, 137 | address owner, 138 | uint256 duration 139 | ) external returns (uint256) { 140 | return _register(id, owner, duration, false); 141 | } 142 | 143 | function renew(uint256 id, uint duration) external override onlyController returns(uint) { 144 | require(expiries[id] + GRACE_PERIOD >= block.timestamp); // Name must be registered here or in grace period 145 | require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow 146 | 147 | expiries[id] += duration; 148 | emit NameRenewed(id, expiries[id]); 149 | return expiries[id]; 150 | } 151 | 152 | /** 153 | * Internal functions 154 | */ 155 | 156 | function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) { 157 | require(available(id)); 158 | require(block.timestamp + duration + GRACE_PERIOD > block.timestamp + GRACE_PERIOD); // Prevent future overflow 159 | 160 | expiries[id] = block.timestamp + duration; 161 | if(_exists(id)) { 162 | // Name was previously owned, and expired 163 | _burn(id); 164 | } 165 | _mint(owner, id); 166 | if (updateRegistry) { 167 | bebRegistry.setSubnodeOwner(baseNode, bytes32(id), owner); 168 | } 169 | 170 | emit NameRegistered(id, owner, block.timestamp + duration); 171 | 172 | return block.timestamp + duration; 173 | } 174 | 175 | /** 176 | * @dev Reclaim ownership of a name in ENS, if you own it in the registrar. 177 | */ 178 | function reclaim(uint256 id, address owner) external live { 179 | require(_isApprovedOrOwner(msg.sender, id)); 180 | bebRegistry.setSubnodeOwner(baseNode, bytes32(id), owner); 181 | } 182 | 183 | /** 184 | * @dev Modify the baseURI. 185 | * @param uri The new baseURI. 186 | */ 187 | function setBaseURI(string calldata uri) external { 188 | return _setBaseURI(uri); 189 | } 190 | 191 | function _setBaseURI(string calldata uri) internal onlyOwner { 192 | baseURI = uri; 193 | } 194 | 195 | function _baseURI() internal view override returns(string memory) { 196 | return baseURI; 197 | } 198 | 199 | /** 200 | * @dev Modify the royaltyController. 201 | * @param _royaltyController The new royaltyController. 202 | */ 203 | function setRoyaltyController(IRoyaltyController _royaltyController) external { 204 | return _setRoyaltyController(_royaltyController); 205 | } 206 | 207 | function _setRoyaltyController(IRoyaltyController _royaltyController) internal onlyOwner { 208 | royaltyController = _royaltyController; 209 | } 210 | 211 | function royaltyInfo(uint256 tokenId, uint256 salePrice) external view returns (address receiver, uint256 royaltyAmount) { 212 | return royaltyController.royaltyInfo(tokenId, salePrice); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /domains/contracts/VibePriceOracle.sol: -------------------------------------------------------------------------------- 1 | ///////// 2 | // Wield 3 | // https://wield.xyz 4 | // SPDX-License-Identifier: AGPL-3.0-or-later 5 | ////////////////// 6 | 7 | pragma solidity >=0.8.4; 8 | 9 | import "./IPriceOracle.sol"; 10 | import "./StringUtils.sol"; 11 | import "@openzeppelin/contracts/access/Ownable.sol"; 12 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 13 | import "@openzeppelin/contracts/access/AccessControl.sol"; 14 | 15 | 16 | // StablePriceOracle sets a price in USD, based on an oracle. 17 | contract VibePriceOracle is IPriceOracle, Ownable, AccessControl { 18 | using SafeMath for *; 19 | using StringUtils for *; 20 | 21 | // Rent in base price units by length, in WEI 22 | uint256 public basePrice; 23 | 24 | // Rent in premium, in WEI 25 | uint256 public premiumPriceBase; 26 | 27 | // Granter address 28 | bytes32 public constant GRANTER_ROLE = keccak256("GRANTER_ROLE"); 29 | 30 | constructor(uint256 _basePrice, uint256 _premiumPriceBase, address granter) { 31 | basePrice = _basePrice; 32 | premiumPriceBase = _premiumPriceBase; 33 | _setupRole(GRANTER_ROLE, granter); 34 | } 35 | 36 | function price( 37 | string calldata name, 38 | uint256 expires, 39 | uint256 duration 40 | ) external view override returns (IPriceOracle.Price memory) { 41 | return 42 | IPriceOracle.Price({ 43 | base: basePrice.mul(duration), 44 | premium: (_premium(name, expires, duration)).mul(duration) 45 | }); 46 | } 47 | 48 | /** 49 | * @dev Returns the pricing premium in wei. 50 | */ 51 | function premium( 52 | string calldata name, 53 | uint256 expires, 54 | uint256 duration 55 | ) external view returns (uint256) { 56 | return _premium(name, expires, duration); 57 | } 58 | 59 | /** 60 | * @dev Returns the pricing premium in internal base units. 61 | */ 62 | function _premium( 63 | string memory name, 64 | uint256 expires, 65 | uint256 duration 66 | ) internal view virtual returns (uint256) { 67 | uint256 len = name.strlen(); 68 | require(len >= 1, "VibePriceOracle: Name must be at least 1 character long"); 69 | if (len >= 10 || hasRole(GRANTER_ROLE, msg.sender)) { 70 | return 0; 71 | } 72 | return (premiumPriceBase == 0) ? 0 : (premiumPriceBase).div(len); 73 | } 74 | 75 | function toWei(uint256 amount) internal view returns (uint256) { 76 | uint256 weiAmount = (1e8).mul(amount); 77 | return weiAmount; 78 | } 79 | 80 | function changeBasePrice(uint256 newBasePrice) public onlyOwner { 81 | basePrice = newBasePrice; 82 | } 83 | 84 | function changePremiumPrice(uint256 newPremiumPriceBase) public onlyOwner { 85 | premiumPriceBase = newPremiumPriceBase; 86 | } 87 | } -------------------------------------------------------------------------------- /fartoken/BondingCurveV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol"; 5 | 6 | contract BondingCurveV2 { 7 | using FixedPointMathLib for uint256; 8 | using FixedPointMathLib for int256; 9 | 10 | error InsufficientLiquidity(); 11 | 12 | uint256 public immutable B = 4379701787; 13 | 14 | constructor() {} 15 | 16 | /** 17 | * @notice Computes A dynamically given the stored desiredRaise and a provided allocatedSupply. 18 | * @param allocatedSupply The number of tokens allocated to the bonding curve. 19 | * @param desiredRaise The desired raise for the bonding curve. 20 | */ 21 | function _computeA(uint256 allocatedSupply, uint256 desiredRaise) internal view returns (uint256) { 22 | uint256 b_times_alloc = B.mulWad(allocatedSupply); 23 | 24 | uint256 exp_b_alloc = uint256((int256(b_times_alloc)).expWad()); 25 | require(exp_b_alloc > 1e18, "exp too low, adjust parameters"); 26 | 27 | uint256 numerator = desiredRaise.mulWad(B); 28 | uint256 denominator = exp_b_alloc - 1e18; 29 | 30 | return numerator.divWad(denominator); 31 | } 32 | 33 | function getEthBuyQuote(uint256 currentSupply, uint256 allocatedSupply, uint256 ethOrderSize, uint256 desiredRaise) 34 | external view returns (uint256) 35 | { 36 | require(currentSupply < allocatedSupply, "Supply exceeds allocation"); 37 | require(ethOrderSize > 0, "Invalid order size"); 38 | 39 | uint256 A_local = _computeA(allocatedSupply, desiredRaise); 40 | uint256 x0 = currentSupply; 41 | uint256 deltaY = ethOrderSize; 42 | 43 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 44 | uint256 scaled_term = deltaY.mulWad(B).divWad(A_local); 45 | uint256 exp_b_x1 = exp_b_x0 + scaled_term; 46 | 47 | uint256 deltaX = uint256(int256(exp_b_x1).lnWad()).divWad(B) - x0; 48 | return deltaX; 49 | } 50 | 51 | function getTokenBuyQuote(uint256 currentSupply, uint256 allocatedSupply, uint256 tokenOrderSize, uint256 desiredRaise) 52 | external view returns (uint256) 53 | { 54 | uint256 A_local = _computeA(allocatedSupply, desiredRaise); 55 | uint256 x0 = currentSupply; 56 | uint256 x1 = x0 + tokenOrderSize; 57 | 58 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 59 | uint256 exp_b_x1 = uint256((int256(B.mulWad(x1))).expWad()); 60 | 61 | uint256 deltaY = (exp_b_x1 - exp_b_x0).fullMulDiv(A_local, B); 62 | return deltaY; 63 | } 64 | 65 | function getEthSellQuote(uint256 currentSupply, uint256 allocatedSupply, uint256 ethOrderSize, uint256 desiredRaise) 66 | external view returns (uint256) 67 | { 68 | uint256 A_local = _computeA(allocatedSupply, desiredRaise); 69 | 70 | uint256 deltaY = ethOrderSize; 71 | uint256 x0 = currentSupply; 72 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 73 | 74 | uint256 exp_b_x1 = exp_b_x0 - deltaY.fullMulDiv(B, A_local); 75 | if (exp_b_x1 < 1) revert InsufficientLiquidity(); 76 | 77 | uint256 x1 = uint256(int256(exp_b_x1).lnWad()).divWad(B); 78 | uint256 tokensToSell = x0 - x1; 79 | 80 | return tokensToSell; 81 | } 82 | 83 | function getTokenSellQuote(uint256 currentSupply, uint256 allocatedSupply, uint256 tokensToSell, uint256 desiredRaise) 84 | external view returns (uint256) 85 | { 86 | if (currentSupply < tokensToSell) revert InsufficientLiquidity(); 87 | 88 | uint256 A_local = _computeA(allocatedSupply, desiredRaise); 89 | 90 | uint256 x0 = currentSupply; 91 | uint256 x1 = x0 - tokensToSell; 92 | 93 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 94 | uint256 exp_b_x1 = uint256((int256(B.mulWad(x1))).expWad()); 95 | 96 | uint256 deltaY = (exp_b_x0 - exp_b_x1).fullMulDiv(A_local, B); 97 | 98 | return deltaY; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /fartoken/FarAgentTips.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 5 | import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 8 | import {EIP712} from "../lib/EIP712.sol"; 9 | import {IFarAgentTips} from "./interfaces/IFarAgentTips.sol"; 10 | 11 | abstract contract Pausable { 12 | bool private _paused; 13 | 14 | event Paused(); 15 | event Unpaused(); 16 | 17 | error EnforcedPause(); 18 | error ExpectedPause(); 19 | 20 | constructor() { 21 | _paused = false; 22 | } 23 | 24 | modifier whenNotPaused() { 25 | _requireNotPaused(); 26 | _; 27 | } 28 | 29 | modifier whenPaused() { 30 | _requirePaused(); 31 | _; 32 | } 33 | 34 | function paused() public view virtual returns (bool) { 35 | return _paused; 36 | } 37 | 38 | function _requireNotPaused() internal view virtual { 39 | if (paused()) { 40 | revert EnforcedPause(); 41 | } 42 | } 43 | 44 | function _requirePaused() internal view virtual { 45 | if (!paused()) { 46 | revert ExpectedPause(); 47 | } 48 | } 49 | 50 | function _pause() internal virtual whenNotPaused { 51 | _paused = true; 52 | emit Paused(); 53 | } 54 | 55 | function _unpause() internal virtual whenPaused { 56 | _paused = false; 57 | emit Unpaused(); 58 | } 59 | } 60 | 61 | abstract contract Nonces { 62 | error InvalidAccountNonce(address account, uint256 currentNonce); 63 | 64 | mapping(address account => uint256) private _nonces; 65 | 66 | function nonces(address owner) public view virtual returns (uint256) { 67 | return _nonces[owner]; 68 | } 69 | 70 | function _useNonce(address owner) internal virtual returns (uint256) { 71 | unchecked { 72 | return _nonces[owner]++; 73 | } 74 | } 75 | 76 | function _useCheckedNonce(address owner, uint256 nonce) internal virtual { 77 | uint256 current = _useNonce(owner); 78 | if (nonce != current) { 79 | revert InvalidAccountNonce(owner, current); 80 | } 81 | } 82 | } 83 | 84 | contract FarAgentTips is IFarAgentTips, Initializable, EIP712, Nonces, Pausable { 85 | using SafeERC20 for IERC20; 86 | 87 | bytes32 public constant WITHDRAW_FROM_RESERVE_TYPEHASH = keccak256( 88 | "Withdraw(address to,uint256 amount,uint256 nonce,uint256 deadline)" 89 | ); 90 | 91 | bytes32 public constant ADD_TO_RESERVE_TYPEHASH = keccak256( 92 | "Add(address from,uint256 amount,uint256 nonce,uint256 deadline)" 93 | ); 94 | 95 | address public tokenCreator; 96 | address public operator; 97 | address public tokenAddress; 98 | uint256 public reservedSupply; 99 | 100 | modifier onlyTokenCreator() { 101 | if (msg.sender != tokenCreator) revert InvalidTokenCreator(); 102 | _; 103 | } 104 | 105 | constructor() initializer EIP712("FarAgentTips", "1") {} 106 | 107 | function initialize( 108 | address _tokenCreator, 109 | address _operator, 110 | address _tokenAddress 111 | ) public initializer { 112 | if (_tokenCreator == address(0)) revert AddressZero(); 113 | if (_operator == address(0)) revert AddressZero(); 114 | if (_tokenAddress == address(0)) revert AddressZero(); 115 | 116 | tokenCreator = _tokenCreator; 117 | operator = _operator; 118 | tokenAddress = _tokenAddress; 119 | } 120 | 121 | function _verifySig(bytes32 digest, address signer, uint256 deadline, bytes memory sig) internal view { 122 | if (block.timestamp >= deadline) revert SignatureExpired(); 123 | if (!SignatureChecker.isValidSignatureNow(signer, digest, sig)) { 124 | revert InvalidSignature(); 125 | } 126 | } 127 | 128 | function pause() external onlyTokenCreator { 129 | _pause(); 130 | } 131 | 132 | function unpause() external onlyTokenCreator { 133 | _unpause(); 134 | } 135 | 136 | function setOperator(address _operator) external onlyTokenCreator { 137 | if (_operator == address(0)) revert AddressZero(); 138 | 139 | address oldOperator = operator; 140 | operator = _operator; 141 | 142 | emit FarAgentOperatorUpdated(tokenCreator, oldOperator, _operator); 143 | } 144 | 145 | function withdrawFromReserve(uint256 amount, address to) external whenNotPaused onlyTokenCreator { 146 | if (amount == 0) revert InvalidAmount(); 147 | if (amount > reservedSupply) revert InvalidAmount(); 148 | 149 | reservedSupply -= amount; 150 | IERC20(tokenAddress).safeTransfer(to, amount); 151 | 152 | emit FarAgentWithdrawFromReserve(to, amount); 153 | } 154 | 155 | function withdrawFromReserveWithSig(uint256 amount, address to, uint256 deadline, bytes memory signature) external whenNotPaused { 156 | if (amount == 0) revert InvalidAmount(); 157 | if (amount > reservedSupply) revert InvalidAmount(); 158 | 159 | _verifySig( 160 | _hashTypedDataV4(keccak256(abi.encode(WITHDRAW_FROM_RESERVE_TYPEHASH, to, amount, _useNonce(to), deadline))), 161 | operator, 162 | deadline, 163 | signature 164 | ); 165 | 166 | reservedSupply -= amount; 167 | IERC20(tokenAddress).safeTransfer(to, amount); 168 | 169 | emit FarAgentWithdrawFromReserve(to, amount); 170 | } 171 | 172 | function addToReserve(uint256 amount) external { 173 | if (amount == 0) revert InvalidAmount(); 174 | 175 | IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), amount); 176 | reservedSupply += amount; 177 | 178 | emit FarAgentAddToReserve(msg.sender, amount); 179 | } 180 | 181 | function addToReserveWithSig(uint256 amount, address from, uint256 deadline, bytes memory signature) external { 182 | if (from == address(0)) revert AddressZero(); 183 | if (amount == 0) revert InvalidAmount(); 184 | 185 | _verifySig( 186 | _hashTypedDataV4(keccak256(abi.encode(ADD_TO_RESERVE_TYPEHASH, from, amount, _useNonce(from), deadline))), 187 | from, 188 | deadline, 189 | signature 190 | ); 191 | 192 | IERC20(tokenAddress).safeTransferFrom(from, address(this), amount); 193 | reservedSupply += amount; 194 | 195 | emit FarAgentAddToReserve(from, amount); 196 | } 197 | } -------------------------------------------------------------------------------- /fartoken/FarAgentTipsFactoryImpl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 7 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 8 | import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; 9 | import {IFarAgentTipsFactory} from "./interfaces/IFarAgentTipsFactory.sol"; 10 | import {FarAgentTips} from "./FarAgentTips.sol"; 11 | 12 | contract FarAgentTipsFactoryImpl is IFarAgentTipsFactory, UUPSUpgradeable, ReentrancyGuardUpgradeable, OwnableUpgradeable { 13 | address public immutable tipsImplementation; 14 | 15 | constructor(address _tipsImplementation) { 16 | if (_tipsImplementation == address(0)) revert("Zero address"); 17 | tipsImplementation = _tipsImplementation; 18 | } 19 | 20 | /// @notice Initializes the factory proxy contract 21 | /// @param _owner Address of the contract owner 22 | /// @dev Can only be called once due to initializer modifier 23 | function initialize(address _owner) external initializer { 24 | __UUPSUpgradeable_init(); 25 | __ReentrancyGuard_init(); 26 | __Ownable_init(_owner); 27 | } 28 | 29 | /// @notice Deploys a new FarAgentTips contract 30 | /// @param _tokenCreator The address of the token creator 31 | /// @param _operator The address of the operator 32 | /// @param _tokenAddress The address of the token to manage 33 | function deploy( 34 | address _tokenCreator, 35 | address _operator, 36 | address _tokenAddress 37 | ) external nonReentrant returns (address) { 38 | bytes32 salt = _generateSalt(_tokenCreator, _tokenAddress); 39 | 40 | FarAgentTips tips = FarAgentTips(Clones.cloneDeterministic(tipsImplementation, salt)); 41 | 42 | tips.initialize( 43 | _tokenCreator, 44 | _operator, 45 | _tokenAddress 46 | ); 47 | 48 | emit FarAgentTipsCreated( 49 | address(this), 50 | _tokenCreator, 51 | _operator, 52 | _tokenAddress, 53 | address(tips) 54 | ); 55 | 56 | return address(tips); 57 | } 58 | 59 | /// @dev Generates a unique salt for deterministic deployment 60 | function _generateSalt(address _tokenCreator, address _tokenAddress) internal view returns (bytes32) { 61 | return keccak256( 62 | abi.encodePacked( 63 | msg.sender, 64 | _tokenCreator, 65 | _tokenAddress, 66 | block.coinbase, 67 | block.number, 68 | block.prevrandao, 69 | block.timestamp, 70 | tx.gasprice, 71 | tx.origin 72 | ) 73 | ); 74 | } 75 | 76 | /// @notice The implementation address of the factory contract 77 | function implementation() external view returns (address) { 78 | return ERC1967Utils.getImplementation(); 79 | } 80 | 81 | /// @dev Authorizes an upgrade to a new implementation 82 | /// @param _newImpl The new implementation address 83 | function _authorizeUpgrade(address _newImpl) internal override onlyOwner {} 84 | } -------------------------------------------------------------------------------- /fartoken/FarTokenV2TokenFactoryImpl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 7 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 8 | import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; 9 | import {IFarTokenV2Factory} from "./interfaces/IFarTokenV2Factory.sol"; 10 | import {FarTokenV2} from "./FarTokenV2.sol"; 11 | 12 | contract FarTokenV2FactoryImpl is IFarTokenV2Factory, UUPSUpgradeable, ReentrancyGuardUpgradeable, OwnableUpgradeable { 13 | address public immutable tokenImplementation; 14 | address public immutable bondingCurve; 15 | 16 | constructor(address _tokenImplementation, address _bondingCurve) { 17 | tokenImplementation = _tokenImplementation; 18 | bondingCurve = _bondingCurve; 19 | } 20 | 21 | /// @notice Initializes the factory proxy contract 22 | /// @param _owner Address of the contract owner 23 | /// @dev Can only be called once due to initializer modifier 24 | function initialize(address _owner) external initializer { 25 | __UUPSUpgradeable_init(); 26 | __ReentrancyGuard_init(); 27 | __Ownable_init(_owner); 28 | } 29 | 30 | /// @notice Creates a Far token with bonding curve mechanics that graduates to Uniswap V3 31 | /// @param _tokenCreator The address of the token creator. Must be the owner of the fid 32 | /// @param _operator The address of the operator. Must be the owner of the fid 33 | /// @param _platformReferrer The address of the platform referrer 34 | /// @param _tokenURI The ERC20z token URI 35 | /// @param _name The ERC20 token name 36 | /// @param _symbol The ERC20 token symbol 37 | /// @param _platformReferrerFeeBps The platform referrer fee in BPS 38 | /// @param _orderReferrerFeeBps The order referrer fee in BPS 39 | /// @param _allocatedSupply The number of tokens allocated to the bonding curve. The portion allocated to the creator is PRIMARY_MARKET_SUPPLY - allocatedSupply 40 | /// @param _desiredRaise The desired raise for the bonding curve 41 | function deploy( 42 | address _tokenCreator, 43 | address _operator, 44 | address _platformReferrer, 45 | string memory _tokenURI, 46 | string memory _name, 47 | string memory _symbol, 48 | uint256 _platformReferrerFeeBps, 49 | uint256 _orderReferrerFeeBps, 50 | uint256 _allocatedSupply, 51 | uint256 _desiredRaise 52 | ) external payable nonReentrant returns (address) { 53 | bytes32 salt = _generateSalt(_tokenCreator, _tokenURI); 54 | 55 | FarTokenV2 token = FarTokenV2(payable(Clones.cloneDeterministic(tokenImplementation, salt))); 56 | 57 | token.initialize{value: msg.value}(_tokenCreator, _operator, _platformReferrer, bondingCurve, _tokenURI, _name, _symbol, _platformReferrerFeeBps, _orderReferrerFeeBps, _allocatedSupply, _desiredRaise); 58 | 59 | emit FarTokenCreated( 60 | address(this), 61 | _tokenCreator, 62 | _operator, 63 | _platformReferrer, 64 | token.protocolFeeRecipient(), 65 | bondingCurve, 66 | _tokenURI, 67 | _name, 68 | _symbol, 69 | address(token), 70 | token.poolAddress(), 71 | _platformReferrerFeeBps, 72 | _orderReferrerFeeBps, 73 | _allocatedSupply, 74 | _desiredRaise 75 | ); 76 | 77 | return address(token); 78 | } 79 | 80 | /// @dev Generates a unique salt for deterministic deployment 81 | function _generateSalt(address _tokenCreator, string memory _tokenURI) internal view returns (bytes32) { 82 | return keccak256( 83 | abi.encodePacked( 84 | msg.sender, 85 | _tokenCreator, 86 | keccak256(abi.encodePacked(_tokenURI)), 87 | block.coinbase, 88 | block.number, 89 | block.prevrandao, 90 | block.timestamp, 91 | tx.gasprice, 92 | tx.origin 93 | ) 94 | ); 95 | } 96 | 97 | /// @notice The implementation address of the factory contract 98 | function implementation() external view returns (address) { 99 | return ERC1967Utils.getImplementation(); 100 | } 101 | 102 | /// @dev Authorizes an upgrade to a new implementation 103 | /// @param _newImpl The new implementation address 104 | function _authorizeUpgrade(address _newImpl) internal override onlyOwner {} 105 | } 106 | 107 | // Inspired by the open-source Wow Protocol -------------------------------------------------------------------------------- /fartoken/interfaces/IFarAgentTips.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IFarAgentTips { 5 | /// @notice Thrown when an operation is attempted with a zero address 6 | error AddressZero(); 7 | 8 | /// @notice Thrown when an invalid token creator attempts an operation 9 | error InvalidTokenCreator(); 10 | 11 | /// @notice Thrown when an invalid signature is specified 12 | error InvalidSignature(); 13 | 14 | /// @notice Thrown when a signature deadline is expired 15 | error SignatureExpired(); 16 | 17 | /// @notice Thrown when an invalid amount is specified 18 | error InvalidAmount(); 19 | 20 | event FarAgentOperatorUpdated( 21 | address indexed tokenCreator, 22 | address indexed oldOperator, 23 | address indexed newOperator 24 | ); 25 | 26 | event FarAgentWithdrawFromReserve( 27 | address indexed to, 28 | uint256 amount 29 | ); 30 | 31 | event FarAgentAddToReserve( 32 | address indexed from, 33 | uint256 amount 34 | ); 35 | 36 | function WITHDRAW_FROM_RESERVE_TYPEHASH() external view returns (bytes32); 37 | function ADD_TO_RESERVE_TYPEHASH() external view returns (bytes32); 38 | 39 | function initialize( 40 | address _tokenCreator, 41 | address _operator, 42 | address _tokenAddress 43 | ) external; 44 | 45 | function pause() external; 46 | function unpause() external; 47 | 48 | function tokenCreator() external view returns (address); 49 | function operator() external view returns (address); 50 | function tokenAddress() external view returns (address); 51 | function reservedSupply() external view returns (uint256); 52 | 53 | function setOperator(address _operator) external; 54 | /// @notice withdraws tokens from the reserve to the to address 55 | /// @dev Requires msg.sender to be the token creator 56 | /// @param amount The amount of tokens to withdraw 57 | /// @param to The address to transfer tokens to 58 | function withdrawFromReserve(uint256 amount, address to) external; 59 | /// @notice withdraws tokens from the reserve to the to address with a signature 60 | /// @dev Requires signature to be valid and the operator to be the signer 61 | /// @param amount The amount of tokens to withdraw 62 | /// @param to The address to transfer tokens to 63 | /// @param deadline The deadline for the signature to be valid 64 | /// @param signature The signature authorizing the transfer 65 | function withdrawFromReserveWithSig(uint256 amount, address to, uint256 deadline, bytes memory signature) external; 66 | /// @notice Adds tokens to the reserve from msg.sender 67 | /// @dev Requires msg.sender to approve this contract to spend their tokens first 68 | /// @param amount The amount of tokens to add to reserve 69 | function addToReserve(uint256 amount) external; 70 | /// @notice Adds tokens to the reserve with a signature 71 | /// @dev Requires the from address to approve this contract to spend their tokens first 72 | /// @param amount The amount of tokens to add to reserve 73 | /// @param from The address to transfer tokens from 74 | /// @param deadline The deadline for the signature to be valid 75 | /// @param signature The signature authorizing the transfer 76 | function addToReserveWithSig( 77 | uint256 amount, 78 | address from, 79 | uint256 deadline, 80 | bytes memory signature 81 | ) external; 82 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IFarAgentTipsFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IFarAgentTipsFactory { 5 | event FarAgentTipsCreated( 6 | address indexed factory, 7 | address indexed tokenCreator, 8 | address indexed operator, 9 | address tokenAddress, 10 | address agentTips 11 | ); 12 | 13 | /// @notice Deploys a new FarAgentTips contract 14 | /// @param _tokenCreator The address of the token creator 15 | /// @param _operator The address of the operator 16 | /// @param _tokenAddress The address of the token to manage 17 | /// @return The address of the deployed FarAgentTips contract 18 | function deploy( 19 | address _tokenCreator, 20 | address _operator, 21 | address _tokenAddress 22 | ) external returns (address); 23 | 24 | /// @notice The implementation address of the factory contract 25 | function implementation() external view returns (address); 26 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IFarTokenV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | /* 5 | !!! !!! !!! 6 | !!! !!! !!! 7 | !!! !!! !!! 8 | !!! !!! !!! 9 | !!! !!! !!! 10 | !!! !!! !!! 11 | 12 | FAR FAR FAR 13 | */ 14 | interface IFarTokenV2 { 15 | /// @notice Thrown when an operation is attempted with a zero address 16 | error AddressZero(); 17 | 18 | /// @notice Thrown when an invalid market type is specified 19 | error InvalidMarketType(); 20 | 21 | /// @notice Thrown when there are insufficient funds for an operation 22 | error InsufficientFunds(); 23 | 24 | /// @notice Thrown when there is insufficient liquidity for a transaction 25 | error InsufficientLiquidity(); 26 | 27 | /// @notice Thrown when the slippage bounds are exceeded during a transaction 28 | error SlippageBoundsExceeded(); 29 | 30 | /// @notice Thrown when the initial order size is too large 31 | error InitialOrderSizeTooLarge(); 32 | 33 | /// @notice Thrown when the ETH amount is too small for a transaction 34 | error EthAmountTooSmall(); 35 | 36 | /// @notice Thrown when an ETH transfer fails 37 | error EthTransferFailed(); 38 | 39 | /// @notice Thrown when an operation is attempted by an entity other than the pool 40 | error OnlyPool(); 41 | 42 | /// @notice Thrown when an operation is attempted by an entity other than WETH 43 | error OnlyWeth(); 44 | 45 | /// @notice Thrown when a market is not yet graduated 46 | error MarketNotGraduated(); 47 | 48 | /// @notice Thrown when a market is already graduated 49 | error MarketAlreadyGraduated(); 50 | 51 | /// @notice Thrown when a platform referrer fee is set to higher than max allowed 52 | error PlatformReferrerFeeTooHigh(); 53 | 54 | /// @notice Thrown when an order referrer fee is set to higher than max allowed 55 | error OrderReferrerFeeTooHigh(); 56 | 57 | /// @notice Thrown when an invalid allocated supply is specified 58 | error InvalidAllocatedSupply(); 59 | 60 | /// @notice Thrown when an invalid token creator is not the owner of the fid 61 | error InvalidTokenCreator(); 62 | 63 | /// @notice Thrown when an invalid amount is specified 64 | error InvalidAmount(); 65 | 66 | /// @notice Thrown when an invalid signature is specified 67 | error InvalidSignature(); 68 | 69 | /// @notice Thrown when a signature deadline is expired 70 | error SignatureExpired(); 71 | 72 | /// @notice Represents the type of market 73 | enum MarketType { 74 | BONDING_CURVE, 75 | UNISWAP_POOL 76 | } 77 | 78 | /// @notice Represents the state of the market 79 | struct MarketState { 80 | MarketType marketType; 81 | address marketAddress; 82 | } 83 | 84 | function ADD_TO_RESERVE_TYPEHASH() external view returns (bytes32); 85 | 86 | function WITHDRAW_FROM_RESERVE_TYPEHASH() external view returns (bytes32); 87 | 88 | event FarTokenBuy( 89 | address indexed buyer, 90 | address indexed recipient, 91 | address indexed orderReferrer, 92 | uint256 totalEth, 93 | uint256 ethFee, 94 | uint256 ethSold, 95 | uint256 tokensBought, 96 | uint256 buyerTokenBalance, 97 | string comment, 98 | uint256 totalSupply, 99 | MarketType marketType 100 | ); 101 | 102 | event FarTokenSell( 103 | address indexed seller, 104 | address indexed recipient, 105 | address indexed orderReferrer, 106 | uint256 totalEth, 107 | uint256 ethFee, 108 | uint256 ethBought, 109 | uint256 tokensSold, 110 | uint256 sellerTokenBalance, 111 | string comment, 112 | uint256 totalSupply, 113 | MarketType marketType 114 | ); 115 | 116 | event FarTokenTransfer( 117 | address indexed from, 118 | address indexed to, 119 | uint256 amount, 120 | uint256 fromTokenBalance, 121 | uint256 toTokenBalance, 122 | uint256 totalSupply 123 | ); 124 | 125 | event FarTokenFees( 126 | address indexed tokenCreator, 127 | address indexed platformReferrer, 128 | address indexed orderReferrer, 129 | address protocolFeeRecipient, 130 | uint256 tokenCreatorFee, 131 | uint256 platformReferrerFee, 132 | uint256 orderReferrerFee, 133 | uint256 protocolFee 134 | ); 135 | 136 | event FarTokenMarketGraduated( 137 | address indexed tokenAddress, 138 | address indexed poolAddress, 139 | uint256 totalEthLiquidity, 140 | uint256 totalTokenLiquidity, 141 | uint256 lpPositionId, 142 | MarketType marketType 143 | ); 144 | 145 | event FarTokenWithdrawFromReserve( 146 | address indexed to, 147 | uint256 amount 148 | ); 149 | 150 | event FarTokenAddToReserve( 151 | address indexed from, 152 | uint256 amount 153 | ); 154 | 155 | function buy( 156 | address recipient, 157 | address refundRecipient, 158 | address orderReferrer, 159 | string memory comment, 160 | MarketType expectedMarketType, 161 | uint256 minOrderSize, 162 | uint160 sqrtPriceLimitX96 163 | ) external payable returns (uint256); 164 | 165 | function sell( 166 | uint256 tokensToSell, 167 | address recipient, 168 | address orderReferrer, 169 | string memory comment, 170 | MarketType expectedMarketType, 171 | uint256 minPayoutSize, 172 | uint160 sqrtPriceLimitX96 173 | ) external returns (uint256); 174 | 175 | function burn(uint256 tokensToBurn) external; 176 | 177 | function getEthBuyQuote(uint256 amount) external view returns (uint256); 178 | 179 | function getTokenSellQuote(uint256 amount) external view returns (uint256); 180 | 181 | function state() external view returns (MarketState memory); 182 | 183 | function tokenURI() external view returns (string memory); 184 | 185 | function tokenCreator() external view returns (address); 186 | 187 | function platformReferrer() external view returns (address); 188 | 189 | function addToReserve(uint256 amount) external; 190 | 191 | function withdrawFromReserve(uint256 amount, address to) external; 192 | 193 | function withdrawFromReserveWithSig(uint256 amount, address to, uint256 deadline, bytes memory signature) external; 194 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IFarTokenV2Factory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IFarTokenV2Factory { 5 | event FarTokenCreated( 6 | address indexed factoryAddress, 7 | address indexed tokenCreator, 8 | address operator, 9 | address platformReferrer, 10 | address protocolFeeRecipient, 11 | address bondingCurve, 12 | string tokenURI, 13 | string name, 14 | string symbol, 15 | address tokenAddress, 16 | address poolAddress, 17 | uint256 platformReferrerFeeBps, 18 | uint256 orderReferrerFeeBps, 19 | uint256 allocatedSupply, 20 | uint256 desiredRaise 21 | ); 22 | 23 | function deploy( 24 | address _tokenCreator, 25 | address _operator, 26 | address _platformReferrer, 27 | string memory _tokenURI, 28 | string memory _name, 29 | string memory _symbol, 30 | uint256 _platformReferrerFeeBps, 31 | uint256 _orderReferrerFeeBps, 32 | uint256 _allocatedSupply, 33 | uint256 _desiredRaise 34 | ) external payable returns (address); 35 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IGuardian.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.21; 3 | 4 | interface IGuardians { 5 | /*////////////////////////////////////////////////////////////// 6 | EVENTS 7 | //////////////////////////////////////////////////////////////*/ 8 | 9 | /** 10 | * @dev Emit an event when owner adds a new guardian address. 11 | * 12 | * @param guardian Address of the added guardian. 13 | */ 14 | event Add(address indexed guardian); 15 | 16 | /** 17 | * @dev Emit an event when owner removes a guardian address. 18 | * 19 | * @param guardian Address of the removed guardian. 20 | */ 21 | event Remove(address indexed guardian); 22 | 23 | /*////////////////////////////////////////////////////////////// 24 | ERRORS 25 | //////////////////////////////////////////////////////////////*/ 26 | 27 | /// @dev Revert if an unauthorized caller calls a protected function. 28 | error OnlyGuardian(); 29 | 30 | /*////////////////////////////////////////////////////////////// 31 | PERMISSIONED FUNCTIONS 32 | //////////////////////////////////////////////////////////////*/ 33 | 34 | /** 35 | * @notice Add an address as a guardian. Only callable by owner. 36 | * 37 | * @param guardian Address of the guardian. 38 | */ 39 | function addGuardian(address guardian) external; 40 | 41 | /** 42 | * @notice Remove a guardian. Only callable by owner. 43 | * 44 | * @param guardian Address of the guardian. 45 | */ 46 | function removeGuardian(address guardian) external; 47 | 48 | /** 49 | * @notice Pause the contract. Only callable by owner or a guardian. 50 | */ 51 | function pause() external; 52 | 53 | /** 54 | * @notice Unpause the contract. Only callable by owner. 55 | */ 56 | function unpause() external; 57 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IIdRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IIdRegistry { 5 | /*////////////////////////////////////////////////////////////// 6 | CONSTANTS 7 | //////////////////////////////////////////////////////////////*/ 8 | 9 | /** 10 | * @notice Defined for compatibility with tools like Etherscan that detect fid 11 | * transfers as token transfers. This is intentionally lowercased. 12 | */ 13 | function name() external view returns (string memory); 14 | 15 | /** 16 | * @notice Contract version specified in the Farcaster protocol version scheme. 17 | */ 18 | function VERSION() external view returns (string memory); 19 | 20 | /** 21 | * @notice EIP-712 typehash for Register signatures. 22 | */ 23 | function REGISTER_TYPEHASH() external view returns (bytes32); 24 | 25 | /** 26 | * @notice EIP-712 typehash for Transfer signatures. 27 | */ 28 | function TRANSFER_TYPEHASH() external view returns (bytes32); 29 | 30 | /** 31 | * @notice EIP-712 typehash for ChangeRecoveryAddress signatures. 32 | */ 33 | function CHANGE_RECOVERY_ADDRESS_TYPEHASH() external view returns (bytes32); 34 | 35 | /*////////////////////////////////////////////////////////////// 36 | STORAGE 37 | //////////////////////////////////////////////////////////////*/ 38 | 39 | /** 40 | * @notice The last Farcaster id that was issued. 41 | */ 42 | function idCounter() external view returns (uint256); 43 | 44 | /** 45 | * @notice Maps each address to an fid, or zero if it does not own an fid. 46 | */ 47 | function idOf(address owner) external view returns (uint256 fid); 48 | 49 | /** 50 | * @notice Maps each fid to an address that can initiate a recovery. 51 | */ 52 | function recoveryOf(uint256 fid) external view returns (address recovery); 53 | 54 | /*////////////////////////////////////////////////////////////// 55 | REGISTRATION LOGIC 56 | //////////////////////////////////////////////////////////////*/ 57 | 58 | /** 59 | * @notice Register a new Farcaster ID (fid) to the caller. The caller must not have an fid. 60 | * The contract must not be in the Registrable (trustedOnly = 0) state. 61 | * 62 | * @param recovery Address which can recover the fid. Set to zero to disable recovery. 63 | * 64 | * @return fid registered FID. 65 | */ 66 | function register(address recovery) external returns (uint256 fid); 67 | 68 | /** 69 | * @notice Register a new Farcaster ID (fid) to any address. A signed message from the address 70 | * must be provided which approves both the to and the recovery. The address must not 71 | * have an fid. The contract must be in the Registrable (trustedOnly = 0) state. 72 | * 73 | * @param to Address which will own the fid. 74 | * @param recovery Address which can recover the fid. Set to zero to disable recovery. 75 | * @param deadline Expiration timestamp of the signature. 76 | * @param sig EIP-712 Register signature signed by the to address. 77 | * 78 | * @return fid registered FID. 79 | */ 80 | function registerFor( 81 | address to, 82 | address recovery, 83 | uint256 deadline, 84 | bytes calldata sig 85 | ) external returns (uint256 fid); 86 | 87 | /*////////////////////////////////////////////////////////////// 88 | TRANSFER LOGIC 89 | //////////////////////////////////////////////////////////////*/ 90 | 91 | /** 92 | * @notice Transfer the fid owned by this address to another address that does not have an fid. 93 | * A signed Transfer message from the destination address must be provided. 94 | * 95 | * @param to The address to transfer the fid to. 96 | * @param deadline Expiration timestamp of the signature. 97 | * @param sig EIP-712 Transfer signature signed by the to address. 98 | */ 99 | function transfer(address to, uint256 deadline, bytes calldata sig) external; 100 | 101 | /** 102 | * @notice Transfer the fid owned by the from address to another address that does not 103 | * have an fid. Caller must provide two signed Transfer messages: one signed by 104 | * the from address and one signed by the to address. 105 | * 106 | * @param from The owner address of the fid to transfer. 107 | * @param to The address to transfer the fid to. 108 | * @param fromDeadline Expiration timestamp of the from signature. 109 | * @param fromSig EIP-712 Transfer signature signed by the from address. 110 | * @param toDeadline Expiration timestamp of the to signature. 111 | * @param toSig EIP-712 Transfer signature signed by the to address. 112 | */ 113 | function transferFor( 114 | address from, 115 | address to, 116 | uint256 fromDeadline, 117 | bytes calldata fromSig, 118 | uint256 toDeadline, 119 | bytes calldata toSig 120 | ) external; 121 | 122 | /*////////////////////////////////////////////////////////////// 123 | RECOVERY LOGIC 124 | //////////////////////////////////////////////////////////////*/ 125 | 126 | /** 127 | * @notice Change the recovery address of the fid owned by the caller. 128 | * 129 | * @param recovery The address which can recover the fid. Set to 0x0 to disable recovery. 130 | */ 131 | function changeRecoveryAddress(address recovery) external; 132 | 133 | /** 134 | * @notice Change the recovery address of fid owned by the owner. Caller must provide an 135 | * EIP-712 ChangeRecoveryAddress message signed by the owner. 136 | * 137 | * @param owner Custody address of the fid whose recovery address will be changed. 138 | * @param recovery The address which can recover the fid. Set to 0x0 to disable recovery. 139 | * @param deadline Expiration timestamp of the ChangeRecoveryAddress signature. 140 | * @param sig EIP-712 ChangeRecoveryAddress message signed by the owner address. 141 | */ 142 | function changeRecoveryAddressFor(address owner, address recovery, uint256 deadline, bytes calldata sig) external; 143 | 144 | /** 145 | * @notice Transfer the fid from the from address to the to address. Must be called by the 146 | * recovery address. A signed message from the to address must be provided. 147 | * 148 | * @param from The address that currently owns the fid. 149 | * @param to The address to transfer the fid to. 150 | * @param deadline Expiration timestamp of the signature. 151 | * @param sig EIP-712 Transfer signature signed by the to address. 152 | */ 153 | function recover(address from, address to, uint256 deadline, bytes calldata sig) external; 154 | 155 | /** 156 | * @notice Transfer the fid owned by the from address to another address that does not 157 | * have an fid. Caller must provide two signed Transfer messages: one signed by 158 | * the recovery address and one signed by the to address. 159 | * 160 | * @param from The owner address of the fid to transfer. 161 | * @param to The address to transfer the fid to. 162 | * @param recoveryDeadline Expiration timestamp of the recovery signature. 163 | * @param recoverySig EIP-712 Transfer signature signed by the recovery address. 164 | * @param toDeadline Expiration timestamp of the to signature. 165 | * @param toSig EIP-712 Transfer signature signed by the to address. 166 | */ 167 | function recoverFor( 168 | address from, 169 | address to, 170 | uint256 recoveryDeadline, 171 | bytes calldata recoverySig, 172 | uint256 toDeadline, 173 | bytes calldata toSig 174 | ) external; 175 | 176 | /*////////////////////////////////////////////////////////////// 177 | VIEWS 178 | //////////////////////////////////////////////////////////////*/ 179 | 180 | /** 181 | * @notice Verify that a signature was produced by the custody address that owns an fid. 182 | * 183 | * @param custodyAddress The address to check the signature of. 184 | * @param fid The fid to check the signature of. 185 | * @param digest The digest that was signed. 186 | * @param sig The signature to check. 187 | * 188 | * @return isValid Whether provided signature is valid. 189 | */ 190 | function verifyFidSignature( 191 | address custodyAddress, 192 | uint256 fid, 193 | bytes32 digest, 194 | bytes calldata sig 195 | ) external view returns (bool isValid); 196 | 197 | /*////////////////////////////////////////////////////////////// 198 | PERMISSIONED ACTIONS 199 | //////////////////////////////////////////////////////////////*/ 200 | 201 | /** 202 | * @notice Register a new Farcaster ID (fid) to any address. The address must not have an fid. 203 | * The contract must be in the Seedable (trustedOnly = 1) state. 204 | * Can only be called by the trustedCaller. 205 | * 206 | * @param to The address which will own the fid. 207 | * @param recovery The address which can recover the fid. 208 | * 209 | * @return fid registered FID. 210 | */ 211 | function trustedRegister(address to, address recovery) external returns (uint256 fid); 212 | 213 | /** 214 | * @notice Pause registration, transfer, and recovery. 215 | * Must be called by the owner. 216 | */ 217 | function pause() external; 218 | 219 | /** 220 | * @notice Unpause registration, transfer, and recovery. 221 | * Must be called by the owner. 222 | */ 223 | function unpause() external; 224 | 225 | function nonces( 226 | address owner 227 | ) external view returns (uint256); 228 | 229 | } 230 | -------------------------------------------------------------------------------- /fartoken/interfaces/IKeyGateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {IKeyRegistry} from "./IKeyRegistry.sol"; 5 | 6 | interface IKeyGateway { 7 | /*////////////////////////////////////////////////////////////// 8 | CONSTANTS 9 | //////////////////////////////////////////////////////////////*/ 10 | 11 | /** 12 | * @notice Contract version specified in the Farcaster protocol version scheme. 13 | */ 14 | function VERSION() external view returns (string memory); 15 | 16 | /** 17 | * @notice EIP-712 typehash for Add signatures. 18 | */ 19 | function ADD_TYPEHASH() external view returns (bytes32); 20 | 21 | /*////////////////////////////////////////////////////////////// 22 | STORAGE 23 | //////////////////////////////////////////////////////////////*/ 24 | 25 | /** 26 | * @notice The KeyRegistry contract. 27 | */ 28 | function keyRegistry() external view returns (IKeyRegistry); 29 | 30 | /*////////////////////////////////////////////////////////////// 31 | REGISTRATION 32 | //////////////////////////////////////////////////////////////*/ 33 | 34 | /** 35 | * @notice Add a key associated with the caller's fid, setting the key state to ADDED. 36 | * 37 | * @param keyType The key's numeric keyType. 38 | * @param key Bytes of the key to add. 39 | * @param metadataType Metadata type ID. 40 | * @param metadata Metadata about the key, which is not stored and only emitted in an event. 41 | */ 42 | function add(uint32 keyType, bytes calldata key, uint8 metadataType, bytes calldata metadata) external; 43 | 44 | /** 45 | * @notice Add a key on behalf of another fid owner, setting the key state to ADDED. 46 | * caller must supply a valid EIP-712 Add signature from the fid owner. 47 | * 48 | * @param fidOwner The fid owner address. 49 | * @param keyType The key's numeric keyType. 50 | * @param key Bytes of the key to add. 51 | * @param metadataType Metadata type ID. 52 | * @param metadata Metadata about the key, which is not stored and only emitted in an event. 53 | * @param deadline Deadline after which the signature expires. 54 | * @param sig EIP-712 Add signature generated by fid owner. 55 | */ 56 | function addFor( 57 | address fidOwner, 58 | uint32 keyType, 59 | bytes calldata key, 60 | uint8 metadataType, 61 | bytes calldata metadata, 62 | uint256 deadline, 63 | bytes calldata sig 64 | ) external; 65 | } -------------------------------------------------------------------------------- /fartoken/interfaces/IMigrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.21; 3 | 4 | interface IMigration { 5 | /*////////////////////////////////////////////////////////////// 6 | ERRORS 7 | //////////////////////////////////////////////////////////////*/ 8 | 9 | /// @dev Revert if the caller is not the migrator. 10 | error OnlyMigrator(); 11 | 12 | /// @dev Revert if the migrator calls a migration function after the grace period. 13 | error PermissionRevoked(); 14 | 15 | /// @dev Revert if the migrator calls migrate more than once. 16 | error AlreadyMigrated(); 17 | 18 | /*////////////////////////////////////////////////////////////// 19 | EVENTS 20 | //////////////////////////////////////////////////////////////*/ 21 | 22 | /** 23 | * @dev Emit an event when the admin calls migrate(). Used to migrate 24 | * Hubs from reading events from one contract to another. 25 | * 26 | * @param migratedAt The timestamp at which the migration occurred. 27 | */ 28 | event Migrated(uint256 indexed migratedAt); 29 | 30 | /** 31 | * @notice Emit an event when the owner changes the migrator address. 32 | * 33 | * @param oldMigrator The address of the previous migrator. 34 | * @param newMigrator The address of the new migrator. 35 | */ 36 | event SetMigrator(address oldMigrator, address newMigrator); 37 | 38 | /*////////////////////////////////////////////////////////////// 39 | IMMUTABLES 40 | //////////////////////////////////////////////////////////////*/ 41 | 42 | /** 43 | * @notice Period in seconds after migration during which admin can continue to call protected 44 | * migration functions. Admins can make corrections to the migrated data during the 45 | * grace period if necessary, but cannot make changes after it expires. 46 | */ 47 | function gracePeriod() external view returns (uint24); 48 | 49 | /*////////////////////////////////////////////////////////////// 50 | STORAGE 51 | //////////////////////////////////////////////////////////////*/ 52 | 53 | /** 54 | * @notice Migration admin address. 55 | */ 56 | function migrator() external view returns (address); 57 | 58 | /** 59 | * @notice Timestamp at which data is migrated. Hubs will cut over to use this contract as their 60 | * source of truth after this timestamp. 61 | */ 62 | function migratedAt() external view returns (uint40); 63 | 64 | /*////////////////////////////////////////////////////////////// 65 | VIEWS 66 | //////////////////////////////////////////////////////////////*/ 67 | 68 | /** 69 | * @notice Check if the contract has been migrated. 70 | * 71 | * @return true if the contract has been migrated, false otherwise. 72 | */ 73 | function isMigrated() external view returns (bool); 74 | 75 | /*////////////////////////////////////////////////////////////// 76 | PERMISSIONED ACTIONS 77 | //////////////////////////////////////////////////////////////*/ 78 | 79 | /** 80 | * @notice Set the time of the migration and emit an event. Hubs will watch this event and 81 | * cut over to use this contract as their source of truth after this timestamp. 82 | * Only callable by the migrator. 83 | */ 84 | function migrate() external; 85 | 86 | /** 87 | * @notice Set the migrator address. Only callable by owner. 88 | * 89 | * @param _migrator Migrator address. 90 | */ 91 | function setMigrator(address _migrator) external; 92 | } -------------------------------------------------------------------------------- /fartoken/interfaces/INonfungiblePositionManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface INonfungiblePositionManager { 5 | struct MintParams { 6 | address token0; 7 | address token1; 8 | uint24 fee; 9 | int24 tickLower; 10 | int24 tickUpper; 11 | uint256 amount0Desired; 12 | uint256 amount1Desired; 13 | uint256 amount0Min; 14 | uint256 amount1Min; 15 | address recipient; 16 | uint256 deadline; 17 | } 18 | 19 | struct CollectParams { 20 | uint256 tokenId; 21 | address recipient; 22 | uint128 amount0Max; 23 | uint128 amount1Max; 24 | } 25 | 26 | /// @notice Creates a new pool if it does not exist, then initializes if not initialized 27 | /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool 28 | /// @param token0 The contract address of token0 of the pool 29 | /// @param token1 The contract address of token1 of the pool 30 | /// @param fee The fee amount of the v3 pool for the specified token pair 31 | /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value 32 | /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary 33 | function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96) 34 | external 35 | payable 36 | returns (address pool); 37 | 38 | /// @notice Creates a new position wrapped in a NFT 39 | /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized 40 | /// a method does not exist, i.e. the pool is assumed to be initialized. 41 | /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata 42 | /// @return tokenId The ID of the token that represents the minted position 43 | /// @return liquidity The amount of liquidity for this position 44 | /// @return amount0 The amount of token0 45 | /// @return amount1 The amount of token1 46 | function mint(MintParams calldata params) 47 | external 48 | payable 49 | returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); 50 | 51 | /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient 52 | /// @param params tokenId The ID of the NFT for which tokens are being collected, 53 | /// recipient The account that should receive the tokens, 54 | /// amount0Max The maximum amount of token0 to collect, 55 | /// amount1Max The maximum amount of token1 to collect 56 | /// @return amount0 The amount of fees collected in token0 57 | /// @return amount1 The amount of fees collected in token1 58 | function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); 59 | 60 | /// @notice Returns the position information associated with a given token ID. 61 | /// @dev Throws if the token ID is not valid. 62 | /// @param tokenId The ID of the token that represents the position 63 | /// @return nonce The nonce for permits 64 | /// @return operator The address that is approved for spending 65 | /// @return token0 The address of the token0 for a specific pool 66 | /// @return token1 The address of the token1 for a specific pool 67 | /// @return fee The fee associated with the pool 68 | /// @return tickLower The lower end of the tick range for the position 69 | /// @return tickUpper The higher end of the tick range for the position 70 | /// @return liquidity The liquidity of the position 71 | /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position 72 | /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position 73 | /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation 74 | /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation 75 | function positions(uint256 tokenId) 76 | external 77 | view 78 | returns ( 79 | uint96 nonce, 80 | address operator, 81 | address token0, 82 | address token1, 83 | uint24 fee, 84 | int24 tickLower, 85 | int24 tickUpper, 86 | uint128 liquidity, 87 | uint256 feeGrowthInside0LastX128, 88 | uint256 feeGrowthInside1LastX128, 89 | uint128 tokensOwed0, 90 | uint128 tokensOwed1 91 | ); 92 | 93 | function approve(address to, uint256 tokenId) external; 94 | 95 | function ownerOf(uint256 tokenId) external view returns (address); 96 | 97 | function transferFrom(address from, address to, uint256 tokenId) external; 98 | 99 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; 100 | 101 | /// @notice Emitted when tokens are collected for a position NFT 102 | /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior 103 | /// @param tokenId The ID of the token for which underlying tokens were collected 104 | /// @param recipient The address of the account that received the collected tokens 105 | /// @param amount0 The amount of token0 owed to the position that was collected 106 | /// @param amount1 The amount of token1 owed to the position that was collected 107 | event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); 108 | } 109 | -------------------------------------------------------------------------------- /fartoken/interfaces/IProtocolRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IProtocolRewards { 5 | event Deposit(address indexed from, address indexed to, bytes4 indexed reason, uint256 amount, string comment); 6 | event Withdraw(address indexed from, address indexed to, uint256 amount); 7 | event RewardsDeposit( 8 | address indexed creator, 9 | address indexed createReferral, 10 | address indexed mintReferral, 11 | address firstMinter, 12 | address zora, 13 | address caller, 14 | uint256 creatorReward, 15 | uint256 createReferralReward, 16 | uint256 mintReferralReward, 17 | uint256 firstMinterReward, 18 | uint256 zoraReward 19 | ); 20 | 21 | function balanceOf(address account) external view returns (uint256); 22 | 23 | function deposit(address to, bytes4 why, string calldata comment) external payable; 24 | 25 | function depositBatch( 26 | address[] calldata recipients, 27 | uint256[] calldata amounts, 28 | bytes4[] calldata reasons, 29 | string calldata comment 30 | ) external payable; 31 | } 32 | -------------------------------------------------------------------------------- /fartoken/interfaces/ISignature.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.21; 2 | 3 | interface ISignatures { 4 | /*////////////////////////////////////////////////////////////// 5 | ERRORS 6 | //////////////////////////////////////////////////////////////*/ 7 | 8 | /// @dev Revert when the signature provided is invalid. 9 | error InvalidSignature(); 10 | 11 | /// @dev Revert when the block.timestamp is ahead of the signature deadline. 12 | error SignatureExpired(); 13 | } -------------------------------------------------------------------------------- /fartoken/interfaces/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {IUniswapV3SwapCallback} from "./IUniswapV3SwapCallback.sol"; 5 | 6 | /// @title Router token swapping functionality 7 | /// @notice Functions for swapping tokens via Uniswap V3 8 | interface ISwapRouter is IUniswapV3SwapCallback { 9 | struct ExactInputSingleParams { 10 | address tokenIn; 11 | address tokenOut; 12 | uint24 fee; 13 | address recipient; 14 | uint256 amountIn; 15 | uint256 amountOutMinimum; 16 | uint160 sqrtPriceLimitX96; 17 | } 18 | 19 | struct ExactOutputSingleParams { 20 | address tokenIn; 21 | address tokenOut; 22 | uint24 fee; 23 | address recipient; 24 | uint256 amountOut; 25 | uint256 amountInMaximum; 26 | uint160 sqrtPriceLimitX96; 27 | } 28 | 29 | /// @notice Swaps `amountIn` of one token for as much as possible of another token 30 | /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata 31 | /// @return amountOut The amount of the received token 32 | function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); 33 | 34 | /// @notice Swaps as little as possible of one token for `amountOut` of another token 35 | /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata 36 | /// @return amountIn The amount of the input token 37 | function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); 38 | } 39 | -------------------------------------------------------------------------------- /fartoken/interfaces/IUniswapV3Pool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IUniswapV3Pool { 5 | /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool 6 | /// @dev This value can overflow the uint256 7 | function feeGrowthGlobal0X128() external view returns (uint256); 8 | 9 | /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool 10 | /// @dev This value can overflow the uint256 11 | function feeGrowthGlobal1X128() external view returns (uint256); 12 | 13 | function swap( 14 | address recipient, 15 | bool zeroForOne, 16 | int256 amountSpecified, 17 | uint160 sqrtPriceLimitX96, 18 | bytes memory data 19 | ) external returns (int256 amount0, int256 amount1); 20 | 21 | function token0() external returns (address); 22 | function token1() external returns (address); 23 | 24 | struct Slot0 { 25 | // the current price 26 | uint160 sqrtPriceX96; 27 | // the current tick 28 | int24 tick; 29 | // the most-recently updated index of the observations array 30 | uint16 observationIndex; 31 | // the current maximum number of observations that are being stored 32 | uint16 observationCardinality; 33 | // the next maximum number of observations to store, triggered in observations.write 34 | uint16 observationCardinalityNext; 35 | // the current protocol fee as a percentage of the swap fee taken on withdrawal 36 | // represented as an integer denominator (1/x)% 37 | uint8 feeProtocol; 38 | // whether the pool is locked 39 | bool unlocked; 40 | } 41 | 42 | function slot0() external view returns (Slot0 memory slot0); 43 | } 44 | -------------------------------------------------------------------------------- /fartoken/interfaces/IUniswapV3SwapCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | /// @title Callback for IUniswapV3PoolActions#swap 5 | /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface 6 | interface IUniswapV3SwapCallback { 7 | /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. 8 | /// @dev In the implementation you must pay the pool tokens owed for the swap. 9 | /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. 10 | /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. 11 | /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by 12 | /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. 13 | /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by 14 | /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. 15 | /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call 16 | function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external; 17 | } 18 | -------------------------------------------------------------------------------- /fartoken/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IWETH { 5 | function deposit() external payable; 6 | 7 | function withdraw(uint256 wad) external; 8 | 9 | function approve(address guy, uint256 wad) external returns (bool); 10 | 11 | function transfer(address dst, uint256 wad) external returns (bool); 12 | 13 | function transferFrom(address src, address dst, uint256 wad) external returns (bool); 14 | 15 | function balanceOf(address guy) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /fartoken/lib/EIP712.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {EIP712 as EIP712Base} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; 5 | 6 | abstract contract EIP712 is EIP712Base { 7 | constructor(string memory name, string memory version) EIP712Base(name, version) {} 8 | 9 | /*////////////////////////////////////////////////////////////// 10 | EIP-712 HELPERS 11 | //////////////////////////////////////////////////////////////*/ 12 | 13 | /** 14 | * @notice Helper view to read EIP-712 domain separator. 15 | * 16 | * @return bytes32 domain separator hash. 17 | */ 18 | function domainSeparatorV4() external view returns (bytes32) { 19 | return _domainSeparatorV4(); 20 | } 21 | 22 | /** 23 | * @notice Helper view to hash EIP-712 typed data onchain. 24 | * 25 | * @param structHash EIP-712 typed data hash. 26 | * 27 | * @return bytes32 EIP-712 message digest. 28 | */ 29 | function hashTypedDataV4(bytes32 structHash) external view returns (bytes32) { 30 | return _hashTypedDataV4(structHash); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fartoken/lib/Signatures.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; 5 | 6 | abstract contract Signatures { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | 11 | /// @dev Revert when the signature provided is invalid. 12 | error InvalidSignature(); 13 | 14 | /// @dev Revert when the block.timestamp is ahead of the signature deadline. 15 | error SignatureExpired(); 16 | 17 | /*////////////////////////////////////////////////////////////// 18 | SIGNATURE VERIFICATION HELPERS 19 | //////////////////////////////////////////////////////////////*/ 20 | 21 | function _verifySig(bytes32 digest, address signer, uint256 deadline, bytes memory sig) internal view { 22 | if (block.timestamp >= deadline) revert SignatureExpired(); 23 | if (!SignatureChecker.isValidSignatureNow(signer, digest, sig)) { 24 | revert InvalidSignature(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /fartoken/lib/TransferHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | library TransferHelper { 5 | /// @dev Revert when a native token transfer fails. 6 | error CallFailed(); 7 | 8 | /** 9 | * @dev Native token transfer helper. 10 | */ 11 | function sendNative(address to, uint256 amount) internal { 12 | bool success; 13 | 14 | // solhint-disable-next-line no-inline-assembly 15 | assembly { 16 | // Transfer the native token and store if it succeeded or not. 17 | success := call(gas(), to, amount, 0, 0, 0, 0) 18 | } 19 | 20 | if (!success) revert CallFailed(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fartoken/lib/TrustedCaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; 5 | 6 | abstract contract TrustedCaller is Ownable2Step { 7 | /*////////////////////////////////////////////////////////////// 8 | ERRORS 9 | //////////////////////////////////////////////////////////////*/ 10 | 11 | /// @dev Revert when an unauthorized caller calls a trusted function. 12 | error OnlyTrustedCaller(); 13 | 14 | /// @dev Revert if trustedRegister is invoked after trustedCallerOnly is disabled. 15 | error Registrable(); 16 | 17 | /// @dev Revert if register is invoked before trustedCallerOnly is disabled. 18 | error Seedable(); 19 | 20 | /// @dev Revert when an invalid address is provided as input. 21 | error InvalidAddress(); 22 | 23 | /*////////////////////////////////////////////////////////////// 24 | EVENTS 25 | //////////////////////////////////////////////////////////////*/ 26 | 27 | /** 28 | * @dev Emit an event when the trusted caller is modified. 29 | * 30 | * @param oldCaller The address of the old trusted caller. 31 | * @param newCaller The address of the new trusted caller. 32 | * @param owner The address of the owner setting the new caller. 33 | */ 34 | event SetTrustedCaller(address indexed oldCaller, address indexed newCaller, address owner); 35 | 36 | /** 37 | * @dev Emit an event when the trustedOnly state is disabled. 38 | */ 39 | event DisableTrustedOnly(); 40 | 41 | /*////////////////////////////////////////////////////////////// 42 | STORAGE 43 | //////////////////////////////////////////////////////////////*/ 44 | 45 | /** 46 | * @dev The privileged address that is allowed to call trusted functions. 47 | */ 48 | address public trustedCaller; 49 | 50 | /** 51 | * @dev Allows calling trusted functions when set 1, and disables trusted 52 | * functions when set to 0. The value is set to 1 and can be changed to 0, 53 | * but never back to 1. 54 | */ 55 | uint256 public trustedOnly = 1; 56 | 57 | /*////////////////////////////////////////////////////////////// 58 | MODIFIERS 59 | //////////////////////////////////////////////////////////////*/ 60 | 61 | /** 62 | * @dev Allow only the trusted caller to call the modified function. 63 | */ 64 | modifier onlyTrustedCaller() { 65 | if (trustedOnly == 0) revert Registrable(); 66 | if (msg.sender != trustedCaller) revert OnlyTrustedCaller(); 67 | _; 68 | } 69 | 70 | /** 71 | * @dev Prevent calling the modified function in trustedOnly mode. 72 | */ 73 | modifier whenNotTrusted() { 74 | if (trustedOnly == 1) revert Seedable(); 75 | _; 76 | } 77 | 78 | /*////////////////////////////////////////////////////////////// 79 | CONSTRUCTOR 80 | //////////////////////////////////////////////////////////////*/ 81 | 82 | /** 83 | * @param _initialOwner Initial contract owner address. 84 | */ 85 | constructor(address _initialOwner) { 86 | _transferOwnership(_initialOwner); 87 | } 88 | 89 | /*////////////////////////////////////////////////////////////// 90 | PERMISSIONED ACTIONS 91 | //////////////////////////////////////////////////////////////*/ 92 | 93 | /** 94 | * @notice Change the trusted caller by calling this from the contract's owner. 95 | * 96 | * @param _trustedCaller The address of the new trusted caller 97 | */ 98 | function setTrustedCaller(address _trustedCaller) public onlyOwner { 99 | _setTrustedCaller(_trustedCaller); 100 | } 101 | 102 | /** 103 | * @notice Disable trustedOnly mode. Must be called by the contract's owner. 104 | */ 105 | function disableTrustedOnly() external onlyOwner { 106 | delete trustedOnly; 107 | emit DisableTrustedOnly(); 108 | } 109 | 110 | /*////////////////////////////////////////////////////////////// 111 | INTERNAL FUNCTIONS 112 | //////////////////////////////////////////////////////////////*/ 113 | 114 | /** 115 | * @dev Internal helper to set trusted caller. Can be used internally 116 | * to set the trusted caller at construction time. 117 | */ 118 | function _setTrustedCaller(address _trustedCaller) internal { 119 | if (_trustedCaller == address(0)) revert InvalidAddress(); 120 | 121 | emit SetTrustedCaller(trustedCaller, _trustedCaller, msg.sender); 122 | trustedCaller = _trustedCaller; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /fartoken/v1/BondingCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | import {FixedPointMathLib} from "solady/src/utils/FixedPointMathLib.sol"; 5 | 6 | contract BondingCurve { 7 | using FixedPointMathLib for uint256; 8 | using FixedPointMathLib for int256; 9 | 10 | error InsufficientLiquidity(); 11 | 12 | // y = A*e^(Bx) 13 | uint256 public immutable A = 1060848709; 14 | uint256 public immutable B = 4379701787; 15 | 16 | function getEthSellQuote(uint256 currentSupply, uint256 ethOrderSize) external pure returns (uint256) { 17 | uint256 deltaY = ethOrderSize; 18 | uint256 x0 = currentSupply; 19 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 20 | 21 | uint256 exp_b_x1 = exp_b_x0 - deltaY.fullMulDiv(B, A); 22 | uint256 x1 = uint256(int256(exp_b_x1).lnWad()).divWad(B); 23 | uint256 tokensToSell = x0 - x1; 24 | 25 | return tokensToSell; 26 | } 27 | 28 | function getTokenSellQuote(uint256 currentSupply, uint256 tokensToSell) external pure returns (uint256) { 29 | if (currentSupply < tokensToSell) revert InsufficientLiquidity(); 30 | uint256 x0 = currentSupply; 31 | uint256 x1 = x0 - tokensToSell; 32 | 33 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 34 | uint256 exp_b_x1 = uint256((int256(B.mulWad(x1))).expWad()); 35 | 36 | // calculate deltaY = (a/b)*(exp(b*x0) - exp(b*x1)) 37 | uint256 deltaY = (exp_b_x0 - exp_b_x1).fullMulDiv(A, B); 38 | 39 | return deltaY; 40 | } 41 | 42 | function getEthBuyQuote(uint256 currentSupply, uint256 ethOrderSize) external pure returns (uint256) { 43 | uint256 x0 = currentSupply; 44 | uint256 deltaY = ethOrderSize; 45 | 46 | // calculate exp(b*x0) 47 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 48 | 49 | // calculate exp(b*x0) + (dy*b/a) 50 | uint256 exp_b_x1 = exp_b_x0 + deltaY.fullMulDiv(B, A); 51 | 52 | uint256 deltaX = uint256(int256(exp_b_x1).lnWad()).divWad(B) - x0; 53 | 54 | return deltaX; 55 | } 56 | 57 | function getTokenBuyQuote(uint256 currentSupply, uint256 tokenOrderSize) external pure returns (uint256) { 58 | uint256 x0 = currentSupply; 59 | uint256 x1 = tokenOrderSize + currentSupply; 60 | 61 | uint256 exp_b_x0 = uint256((int256(B.mulWad(x0))).expWad()); 62 | uint256 exp_b_x1 = uint256((int256(B.mulWad(x1))).expWad()); 63 | 64 | uint256 deltaY = (exp_b_x1 - exp_b_x0).fullMulDiv(A, B); 65 | 66 | return deltaY; 67 | } 68 | } 69 | 70 | // Inspired by the open-source Wow Protocol -------------------------------------------------------------------------------- /fartoken/v1/FarTokenFactoryImpl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; 5 | import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 6 | import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; 7 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 8 | import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; 9 | 10 | import {IFarTokenFactory} from "./interfaces/IFarTokenFactory.sol"; 11 | import {FarToken} from "./FarToken.sol"; 12 | 13 | contract FarTokenFactoryImpl is IFarTokenFactory, UUPSUpgradeable, ReentrancyGuardUpgradeable, OwnableUpgradeable { 14 | address public immutable tokenImplementation; 15 | address public immutable bondingCurve; 16 | 17 | constructor(address _tokenImplementation, address _bondingCurve) { 18 | tokenImplementation = _tokenImplementation; 19 | bondingCurve = _bondingCurve; 20 | } 21 | 22 | /// @notice Initializes the factory proxy contract 23 | /// @param _owner Address of the contract owner 24 | /// @dev Can only be called once due to initializer modifier 25 | function initialize(address _owner) external initializer { 26 | __UUPSUpgradeable_init(); 27 | __ReentrancyGuard_init(); 28 | __Ownable_init(_owner); 29 | } 30 | 31 | /// @notice Creates a Far token with bonding curve mechanics that graduates to Uniswap V3 32 | /// @param _tokenCreator The address of the token creator 33 | /// @param _platformReferrer The address of the platform referrer 34 | /// @param _tokenURI The ERC20z token URI 35 | /// @param _name The ERC20 token name 36 | /// @param _symbol The ERC20 token symbol 37 | /// @param _platformReferrerFeeBps The platform referrer fee in BPS 38 | /// @param _orderReferrerFeeBps The order referrer fee in BPS 39 | function deploy( 40 | address _tokenCreator, 41 | address _platformReferrer, 42 | string memory _tokenURI, 43 | string memory _name, 44 | string memory _symbol, 45 | uint256 _platformReferrerFeeBps, 46 | uint256 _orderReferrerFeeBps 47 | ) external payable nonReentrant returns (address) { 48 | bytes32 salt = _generateSalt(_tokenCreator, _tokenURI); 49 | 50 | FarToken token = FarToken(payable(Clones.cloneDeterministic(tokenImplementation, salt))); 51 | 52 | token.initialize{value: msg.value}(_tokenCreator, _platformReferrer, bondingCurve, _tokenURI, _name, _symbol, _platformReferrerFeeBps, _orderReferrerFeeBps); 53 | 54 | emit FarTokenCreated( 55 | address(this), 56 | _tokenCreator, 57 | _platformReferrer, 58 | token.protocolFeeRecipient(), 59 | bondingCurve, 60 | _tokenURI, 61 | _name, 62 | _symbol, 63 | address(token), 64 | token.poolAddress(), 65 | _platformReferrerFeeBps, 66 | _orderReferrerFeeBps 67 | ); 68 | 69 | return address(token); 70 | } 71 | 72 | /// @dev Generates a unique salt for deterministic deployment 73 | function _generateSalt(address _tokenCreator, string memory _tokenURI) internal view returns (bytes32) { 74 | return keccak256( 75 | abi.encodePacked( 76 | msg.sender, 77 | _tokenCreator, 78 | keccak256(abi.encodePacked(_tokenURI)), 79 | block.coinbase, 80 | block.number, 81 | block.prevrandao, 82 | block.timestamp, 83 | tx.gasprice, 84 | tx.origin 85 | ) 86 | ); 87 | } 88 | 89 | /// @notice The implementation address of the factory contract 90 | function implementation() external view returns (address) { 91 | return ERC1967Utils.getImplementation(); 92 | } 93 | 94 | /// @dev Authorizes an upgrade to a new implementation 95 | /// @param _newImpl The new implementation address 96 | function _authorizeUpgrade(address _newImpl) internal override onlyOwner {} 97 | } 98 | 99 | // Inspired by the open-source Wow Protocol -------------------------------------------------------------------------------- /fartoken/v1/ProtocolRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | 5 | import {EIP712} from "./lib/EIP712.sol"; 6 | import {IProtocolRewards} from "./interfaces/IProtocolRewards.sol"; 7 | 8 | /// @title ProtocolRewards 9 | /// @notice Manager of deposits & withdrawals for protocol rewards 10 | contract ProtocolRewards is IProtocolRewards, EIP712 { 11 | /// @notice Custom errors 12 | error ADDRESS_ZERO(); 13 | error ARRAY_LENGTH_MISMATCH(); 14 | error INVALID_DEPOSIT(); 15 | error INVALID_WITHDRAW(); 16 | error TRANSFER_FAILED(); 17 | error SIGNATURE_DEADLINE_EXPIRED(); 18 | error INVALID_SIGNATURE(); 19 | 20 | /// @notice The EIP-712 typehash for gasless withdraws 21 | bytes32 public constant WITHDRAW_TYPEHASH = keccak256("Withdraw(address from,address to,uint256 amount,uint256 nonce,uint256 deadline)"); 22 | 23 | /// @notice An account's balance 24 | mapping(address => uint256) public balanceOf; 25 | 26 | /// @notice An account's nonce for gasless withdraws 27 | mapping(address => uint256) public nonces; 28 | 29 | constructor() payable EIP712("ProtocolRewards", "1") {} 30 | 31 | /// @notice The total amount of ETH held in the contract 32 | function totalSupply() external view returns (uint256) { 33 | return address(this).balance; 34 | } 35 | 36 | /// @notice Generic function to deposit ETH for a recipient, with an optional comment 37 | /// @param to Address to deposit to 38 | /// @param to Reason system reason for deposit (used for indexing) 39 | /// @param comment Optional comment as reason for deposit 40 | function deposit(address to, bytes4 reason, string calldata comment) external payable { 41 | if (to == address(0)) { 42 | revert ADDRESS_ZERO(); 43 | } 44 | 45 | balanceOf[to] += msg.value; 46 | 47 | emit Deposit(msg.sender, to, reason, msg.value, comment); 48 | } 49 | 50 | /// @notice Generic function to deposit ETH for multiple recipients, with an optional comment 51 | /// @param recipients recipients to send the amount to, array aligns with amounts 52 | /// @param amounts amounts to send to each recipient, array aligns with recipients 53 | /// @param reasons optional bytes4 hash for indexing 54 | /// @param comment Optional comment to include with mint 55 | function depositBatch(address[] calldata recipients, uint256[] calldata amounts, bytes4[] calldata reasons, string calldata comment) external payable { 56 | uint256 numRecipients = recipients.length; 57 | 58 | if (numRecipients != amounts.length || numRecipients != reasons.length) { 59 | revert ARRAY_LENGTH_MISMATCH(); 60 | } 61 | 62 | uint256 expectedTotalValue; 63 | 64 | for (uint256 i; i < numRecipients; ) { 65 | expectedTotalValue += amounts[i]; 66 | 67 | unchecked { 68 | ++i; 69 | } 70 | } 71 | 72 | if (msg.value != expectedTotalValue) { 73 | revert INVALID_DEPOSIT(); 74 | } 75 | 76 | address currentRecipient; 77 | uint256 currentAmount; 78 | 79 | for (uint256 i; i < numRecipients; ) { 80 | currentRecipient = recipients[i]; 81 | currentAmount = amounts[i]; 82 | 83 | if (currentRecipient == address(0)) { 84 | revert ADDRESS_ZERO(); 85 | } 86 | 87 | balanceOf[currentRecipient] += currentAmount; 88 | 89 | emit Deposit(msg.sender, currentRecipient, reasons[i], currentAmount, comment); 90 | 91 | unchecked { 92 | ++i; 93 | } 94 | } 95 | } 96 | 97 | /// @notice Used by Zora ERC-721 & ERC-1155 contracts to deposit protocol rewards 98 | /// @param creator Creator for NFT rewards 99 | /// @param creatorReward Creator reward amount 100 | /// @param createReferral Creator referral 101 | /// @param createReferralReward Creator referral reward 102 | /// @param mintReferral Mint referral user 103 | /// @param mintReferralReward Mint referral amount 104 | /// @param firstMinter First minter reward 105 | /// @param firstMinterReward First minter reward amount 106 | /// @param zora ZORA recipient 107 | /// @param zoraReward ZORA amount 108 | function depositRewards( 109 | address creator, 110 | uint256 creatorReward, 111 | address createReferral, 112 | uint256 createReferralReward, 113 | address mintReferral, 114 | uint256 mintReferralReward, 115 | address firstMinter, 116 | uint256 firstMinterReward, 117 | address zora, 118 | uint256 zoraReward 119 | ) external payable { 120 | if (msg.value != (creatorReward + createReferralReward + mintReferralReward + firstMinterReward + zoraReward)) { 121 | revert INVALID_DEPOSIT(); 122 | } 123 | 124 | unchecked { 125 | if (creator != address(0)) { 126 | balanceOf[creator] += creatorReward; 127 | } 128 | if (createReferral != address(0)) { 129 | balanceOf[createReferral] += createReferralReward; 130 | } 131 | if (mintReferral != address(0)) { 132 | balanceOf[mintReferral] += mintReferralReward; 133 | } 134 | if (firstMinter != address(0)) { 135 | balanceOf[firstMinter] += firstMinterReward; 136 | } 137 | if (zora != address(0)) { 138 | balanceOf[zora] += zoraReward; 139 | } 140 | } 141 | 142 | emit RewardsDeposit( 143 | creator, 144 | createReferral, 145 | mintReferral, 146 | firstMinter, 147 | zora, 148 | msg.sender, 149 | creatorReward, 150 | createReferralReward, 151 | mintReferralReward, 152 | firstMinterReward, 153 | zoraReward 154 | ); 155 | } 156 | 157 | /// @notice Withdraw protocol rewards 158 | /// @param to Withdraws from msg.sender to this address 159 | /// @param amount Amount to withdraw (0 for total balance) 160 | function withdraw(address to, uint256 amount) external { 161 | if (to == address(0)) { 162 | revert ADDRESS_ZERO(); 163 | } 164 | 165 | address owner = msg.sender; 166 | 167 | if (amount > balanceOf[owner]) { 168 | revert INVALID_WITHDRAW(); 169 | } 170 | 171 | if (amount == 0) { 172 | amount = balanceOf[owner]; 173 | } 174 | 175 | balanceOf[owner] -= amount; 176 | 177 | emit Withdraw(owner, to, amount); 178 | 179 | (bool success, ) = to.call{value: amount}(""); 180 | 181 | if (!success) { 182 | revert TRANSFER_FAILED(); 183 | } 184 | } 185 | 186 | /// @notice Withdraw rewards on behalf of an address 187 | /// @param to The address to withdraw for 188 | /// @param amount The amount to withdraw (0 for total balance) 189 | function withdrawFor(address to, uint256 amount) external { 190 | if (to == address(0)) { 191 | revert ADDRESS_ZERO(); 192 | } 193 | 194 | if (amount > balanceOf[to]) { 195 | revert INVALID_WITHDRAW(); 196 | } 197 | 198 | if (amount == 0) { 199 | amount = balanceOf[to]; 200 | } 201 | 202 | balanceOf[to] -= amount; 203 | 204 | emit Withdraw(to, to, amount); 205 | 206 | (bool success, ) = to.call{value: amount}(""); 207 | 208 | if (!success) { 209 | revert TRANSFER_FAILED(); 210 | } 211 | } 212 | 213 | /// @notice Execute a withdraw of protocol rewards via signature 214 | /// @param from Withdraw from this address 215 | /// @param to Withdraw to this address 216 | /// @param amount Amount to withdraw (0 for total balance) 217 | /// @param deadline Deadline for the signature to be valid 218 | /// @param v V component of signature 219 | /// @param r R component of signature 220 | /// @param s S component of signature 221 | function withdrawWithSig(address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external { 222 | if (block.timestamp > deadline) { 223 | revert SIGNATURE_DEADLINE_EXPIRED(); 224 | } 225 | 226 | bytes32 withdrawHash; 227 | 228 | unchecked { 229 | withdrawHash = keccak256(abi.encode(WITHDRAW_TYPEHASH, from, to, amount, nonces[from]++, deadline)); 230 | } 231 | 232 | bytes32 digest = _hashTypedDataV4(withdrawHash); 233 | 234 | address recoveredAddress = ecrecover(digest, v, r, s); 235 | 236 | if (recoveredAddress == address(0) || recoveredAddress != from) { 237 | revert INVALID_SIGNATURE(); 238 | } 239 | 240 | if (to == address(0)) { 241 | revert ADDRESS_ZERO(); 242 | } 243 | 244 | if (amount > balanceOf[from]) { 245 | revert INVALID_WITHDRAW(); 246 | } 247 | 248 | if (amount == 0) { 249 | amount = balanceOf[from]; 250 | } 251 | 252 | balanceOf[from] -= amount; 253 | 254 | emit Withdraw(from, to, amount); 255 | 256 | (bool success, ) = to.call{value: amount}(""); 257 | 258 | if (!success) { 259 | revert TRANSFER_FAILED(); 260 | } 261 | } 262 | } 263 | 264 | // Inspired by the open-source Wow Protocol -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IFarToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | /* 5 | !!! !!! !!! 6 | !!! !!! !!! 7 | !!! !!! !!! 8 | !!! !!! !!! 9 | !!! !!! !!! 10 | !!! !!! !!! 11 | 12 | FAR FAR FAR 13 | */ 14 | interface IFarToken { 15 | /// @notice Thrown when an operation is attempted with a zero address 16 | error AddressZero(); 17 | 18 | /// @notice Thrown when an invalid market type is specified 19 | error InvalidMarketType(); 20 | 21 | /// @notice Thrown when there are insufficient funds for an operation 22 | error InsufficientFunds(); 23 | 24 | /// @notice Thrown when there is insufficient liquidity for a transaction 25 | error InsufficientLiquidity(); 26 | 27 | /// @notice Thrown when the slippage bounds are exceeded during a transaction 28 | error SlippageBoundsExceeded(); 29 | 30 | /// @notice Thrown when the initial order size is too large 31 | error InitialOrderSizeTooLarge(); 32 | 33 | /// @notice Thrown when the ETH amount is too small for a transaction 34 | error EthAmountTooSmall(); 35 | 36 | /// @notice Thrown when an ETH transfer fails 37 | error EthTransferFailed(); 38 | 39 | /// @notice Thrown when an operation is attempted by an entity other than the pool 40 | error OnlyPool(); 41 | 42 | /// @notice Thrown when an operation is attempted by an entity other than WETH 43 | error OnlyWeth(); 44 | 45 | /// @notice Thrown when a market is not yet graduated 46 | error MarketNotGraduated(); 47 | 48 | /// @notice Thrown when a market is already graduated 49 | error MarketAlreadyGraduated(); 50 | 51 | /// @notice Thrown when a platform referrer fee is set to higher than max allowed 52 | error PlatformReferrerFeeTooHigh(); 53 | 54 | /// @notice Thrown when an order referrer fee is set to higher than max allowed 55 | error OrderReferrerFeeTooHigh(); 56 | 57 | /// @notice Represents the type of market 58 | enum MarketType { 59 | BONDING_CURVE, 60 | UNISWAP_POOL 61 | } 62 | 63 | /// @notice Represents the state of the market 64 | struct MarketState { 65 | MarketType marketType; 66 | address marketAddress; 67 | } 68 | 69 | event FarTokenBuy( 70 | address indexed buyer, 71 | address indexed recipient, 72 | address indexed orderReferrer, 73 | uint256 totalEth, 74 | uint256 ethFee, 75 | uint256 ethSold, 76 | uint256 tokensBought, 77 | uint256 buyerTokenBalance, 78 | string comment, 79 | uint256 totalSupply, 80 | MarketType marketType 81 | ); 82 | 83 | event FarTokenSell( 84 | address indexed seller, 85 | address indexed recipient, 86 | address indexed orderReferrer, 87 | uint256 totalEth, 88 | uint256 ethFee, 89 | uint256 ethBought, 90 | uint256 tokensSold, 91 | uint256 sellerTokenBalance, 92 | string comment, 93 | uint256 totalSupply, 94 | MarketType marketType 95 | ); 96 | 97 | event FarTokenTransfer( 98 | address indexed from, 99 | address indexed to, 100 | uint256 amount, 101 | uint256 fromTokenBalance, 102 | uint256 toTokenBalance, 103 | uint256 totalSupply 104 | ); 105 | 106 | event FarTokenFees( 107 | address indexed tokenCreator, 108 | address indexed platformReferrer, 109 | address indexed orderReferrer, 110 | address protocolFeeRecipient, 111 | uint256 tokenCreatorFee, 112 | uint256 platformReferrerFee, 113 | uint256 orderReferrerFee, 114 | uint256 protocolFee 115 | ); 116 | 117 | event FarTokenMarketGraduated( 118 | address indexed tokenAddress, 119 | address indexed poolAddress, 120 | uint256 totalEthLiquidity, 121 | uint256 totalTokenLiquidity, 122 | uint256 lpPositionId, 123 | MarketType marketType 124 | ); 125 | 126 | function buy( 127 | address recipient, 128 | address refundRecipient, 129 | address orderReferrer, 130 | string memory comment, 131 | MarketType expectedMarketType, 132 | uint256 minOrderSize, 133 | uint160 sqrtPriceLimitX96 134 | ) external payable returns (uint256); 135 | 136 | function sell( 137 | uint256 tokensToSell, 138 | address recipient, 139 | address orderReferrer, 140 | string memory comment, 141 | MarketType expectedMarketType, 142 | uint256 minPayoutSize, 143 | uint160 sqrtPriceLimitX96 144 | ) external returns (uint256); 145 | 146 | function burn(uint256 tokensToBurn) external; 147 | 148 | function getEthBuyQuote(uint256 amount) external view returns (uint256); 149 | 150 | function getTokenSellQuote(uint256 amount) external view returns (uint256); 151 | 152 | function state() external view returns (MarketState memory); 153 | 154 | function tokenURI() external view returns (string memory); 155 | 156 | function tokenCreator() external view returns (address); 157 | 158 | function platformReferrer() external view returns (address); 159 | } -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IFarTokenFactory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | interface IFarTokenFactory { 5 | event FarTokenCreated( 6 | address indexed factoryAddress, 7 | address indexed tokenCreator, 8 | address platformReferrer, 9 | address protocolFeeRecipient, 10 | address bondingCurve, 11 | string tokenURI, 12 | string name, 13 | string symbol, 14 | address tokenAddress, 15 | address poolAddress, 16 | uint256 platformReferrerFeeBps, 17 | uint256 orderReferrerFeeBps 18 | ); 19 | 20 | function deploy( 21 | address _tokenCreator, 22 | address _platformReferrer, 23 | string memory _tokenURI, 24 | string memory _name, 25 | string memory _symbol, 26 | uint256 _platformReferrerFeeBps, 27 | uint256 _orderReferrerFeeBps 28 | ) external payable returns (address); 29 | } -------------------------------------------------------------------------------- /fartoken/v1/interfaces/INonfungiblePositionManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface INonfungiblePositionManager { 5 | struct MintParams { 6 | address token0; 7 | address token1; 8 | uint24 fee; 9 | int24 tickLower; 10 | int24 tickUpper; 11 | uint256 amount0Desired; 12 | uint256 amount1Desired; 13 | uint256 amount0Min; 14 | uint256 amount1Min; 15 | address recipient; 16 | uint256 deadline; 17 | } 18 | 19 | struct CollectParams { 20 | uint256 tokenId; 21 | address recipient; 22 | uint128 amount0Max; 23 | uint128 amount1Max; 24 | } 25 | 26 | /// @notice Creates a new pool if it does not exist, then initializes if not initialized 27 | /// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool 28 | /// @param token0 The contract address of token0 of the pool 29 | /// @param token1 The contract address of token1 of the pool 30 | /// @param fee The fee amount of the v3 pool for the specified token pair 31 | /// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value 32 | /// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary 33 | function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96) 34 | external 35 | payable 36 | returns (address pool); 37 | 38 | /// @notice Creates a new position wrapped in a NFT 39 | /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized 40 | /// a method does not exist, i.e. the pool is assumed to be initialized. 41 | /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata 42 | /// @return tokenId The ID of the token that represents the minted position 43 | /// @return liquidity The amount of liquidity for this position 44 | /// @return amount0 The amount of token0 45 | /// @return amount1 The amount of token1 46 | function mint(MintParams calldata params) 47 | external 48 | payable 49 | returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); 50 | 51 | /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient 52 | /// @param params tokenId The ID of the NFT for which tokens are being collected, 53 | /// recipient The account that should receive the tokens, 54 | /// amount0Max The maximum amount of token0 to collect, 55 | /// amount1Max The maximum amount of token1 to collect 56 | /// @return amount0 The amount of fees collected in token0 57 | /// @return amount1 The amount of fees collected in token1 58 | function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); 59 | 60 | /// @notice Returns the position information associated with a given token ID. 61 | /// @dev Throws if the token ID is not valid. 62 | /// @param tokenId The ID of the token that represents the position 63 | /// @return nonce The nonce for permits 64 | /// @return operator The address that is approved for spending 65 | /// @return token0 The address of the token0 for a specific pool 66 | /// @return token1 The address of the token1 for a specific pool 67 | /// @return fee The fee associated with the pool 68 | /// @return tickLower The lower end of the tick range for the position 69 | /// @return tickUpper The higher end of the tick range for the position 70 | /// @return liquidity The liquidity of the position 71 | /// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position 72 | /// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position 73 | /// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation 74 | /// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation 75 | function positions(uint256 tokenId) 76 | external 77 | view 78 | returns ( 79 | uint96 nonce, 80 | address operator, 81 | address token0, 82 | address token1, 83 | uint24 fee, 84 | int24 tickLower, 85 | int24 tickUpper, 86 | uint128 liquidity, 87 | uint256 feeGrowthInside0LastX128, 88 | uint256 feeGrowthInside1LastX128, 89 | uint128 tokensOwed0, 90 | uint128 tokensOwed1 91 | ); 92 | 93 | function approve(address to, uint256 tokenId) external; 94 | 95 | function ownerOf(uint256 tokenId) external view returns (address); 96 | 97 | function transferFrom(address from, address to, uint256 tokenId) external; 98 | 99 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; 100 | 101 | /// @notice Emitted when tokens are collected for a position NFT 102 | /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior 103 | /// @param tokenId The ID of the token for which underlying tokens were collected 104 | /// @param recipient The address of the account that received the collected tokens 105 | /// @param amount0 The amount of token0 owed to the position that was collected 106 | /// @param amount1 The amount of token1 owed to the position that was collected 107 | event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); 108 | } 109 | -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IProtocolRewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | interface IProtocolRewards { 5 | event Deposit(address indexed from, address indexed to, bytes4 indexed reason, uint256 amount, string comment); 6 | event Withdraw(address indexed from, address indexed to, uint256 amount); 7 | event RewardsDeposit( 8 | address indexed creator, 9 | address indexed createReferral, 10 | address indexed mintReferral, 11 | address firstMinter, 12 | address zora, 13 | address caller, 14 | uint256 creatorReward, 15 | uint256 createReferralReward, 16 | uint256 mintReferralReward, 17 | uint256 firstMinterReward, 18 | uint256 zoraReward 19 | ); 20 | 21 | function balanceOf(address account) external view returns (uint256); 22 | 23 | function deposit(address to, bytes4 why, string calldata comment) external payable; 24 | 25 | function depositBatch( 26 | address[] calldata recipients, 27 | uint256[] calldata amounts, 28 | bytes4[] calldata reasons, 29 | string calldata comment 30 | ) external payable; 31 | } 32 | -------------------------------------------------------------------------------- /fartoken/v1/interfaces/ISwapRouter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | import {IUniswapV3SwapCallback} from "./IUniswapV3SwapCallback.sol"; 5 | 6 | /// @title Router token swapping functionality 7 | /// @notice Functions for swapping tokens via Uniswap V3 8 | interface ISwapRouter is IUniswapV3SwapCallback { 9 | struct ExactInputSingleParams { 10 | address tokenIn; 11 | address tokenOut; 12 | uint24 fee; 13 | address recipient; 14 | uint256 amountIn; 15 | uint256 amountOutMinimum; 16 | uint160 sqrtPriceLimitX96; 17 | } 18 | 19 | struct ExactOutputSingleParams { 20 | address tokenIn; 21 | address tokenOut; 22 | uint24 fee; 23 | address recipient; 24 | uint256 amountOut; 25 | uint256 amountInMaximum; 26 | uint160 sqrtPriceLimitX96; 27 | } 28 | 29 | /// @notice Swaps `amountIn` of one token for as much as possible of another token 30 | /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata 31 | /// @return amountOut The amount of the received token 32 | function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); 33 | 34 | /// @notice Swaps as little as possible of one token for `amountOut` of another token 35 | /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata 36 | /// @return amountIn The amount of the input token 37 | function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); 38 | } 39 | -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IUniswapV3Pool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | interface IUniswapV3Pool { 5 | /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool 6 | /// @dev This value can overflow the uint256 7 | function feeGrowthGlobal0X128() external view returns (uint256); 8 | 9 | /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool 10 | /// @dev This value can overflow the uint256 11 | function feeGrowthGlobal1X128() external view returns (uint256); 12 | 13 | function swap( 14 | address recipient, 15 | bool zeroForOne, 16 | int256 amountSpecified, 17 | uint160 sqrtPriceLimitX96, 18 | bytes memory data 19 | ) external returns (int256 amount0, int256 amount1); 20 | 21 | function token0() external returns (address); 22 | function token1() external returns (address); 23 | 24 | struct Slot0 { 25 | // the current price 26 | uint160 sqrtPriceX96; 27 | // the current tick 28 | int24 tick; 29 | // the most-recently updated index of the observations array 30 | uint16 observationIndex; 31 | // the current maximum number of observations that are being stored 32 | uint16 observationCardinality; 33 | // the next maximum number of observations to store, triggered in observations.write 34 | uint16 observationCardinalityNext; 35 | // the current protocol fee as a percentage of the swap fee taken on withdrawal 36 | // represented as an integer denominator (1/x)% 37 | uint8 feeProtocol; 38 | // whether the pool is locked 39 | bool unlocked; 40 | } 41 | 42 | function slot0() external view returns (Slot0 memory slot0); 43 | } 44 | -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IUniswapV3SwapCallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | pragma solidity ^0.8.27; 3 | 4 | /// @title Callback for IUniswapV3PoolActions#swap 5 | /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface 6 | interface IUniswapV3SwapCallback { 7 | /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. 8 | /// @dev In the implementation you must pay the pool tokens owed for the swap. 9 | /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. 10 | /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. 11 | /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by 12 | /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. 13 | /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by 14 | /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. 15 | /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call 16 | function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external; 17 | } 18 | -------------------------------------------------------------------------------- /fartoken/v1/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.27; 3 | 4 | interface IWETH { 5 | function deposit() external payable; 6 | 7 | function withdraw(uint256 wad) external; 8 | 9 | function approve(address guy, uint256 wad) external returns (bool); 10 | 11 | function transfer(address dst, uint256 wad) external returns (bool); 12 | 13 | function transferFrom(address src, address dst, uint256 wad) external returns (bool); 14 | 15 | function balanceOf(address guy) external view returns (uint256); 16 | } 17 | -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x182f5a5c04ccdc4739ee9f362af72d7a0f55d6b410cc9dc2b52dc7f99f06f3f9" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /marketplace/README.md: -------------------------------------------------------------------------------- 1 | # [FarMarket](https://far.quest/market) details 2 | 3 | ### FarMarket is deployed to [`0x57ce6c12a101c41e790744413f4f5408ac64d8c6`](https://optimistic.etherscan.io/address/0x57ce6c12a101c41e790744413f4f5408ac64d8c6). 4 | --------------------------------------------------------------------------------