FriendKey is a social token platform built on Ethereum that allows creators to issue their own tokenized social shares using a bonding curve mechanism. Users can buy and sell shares associated with creators, creating a social marketplace with economic incentives.
This project implements an ERC-1155 based token contract with the following key features:
- Bonding Curve Pricing: Token prices are determined by a bonding curve that increases with supply
- Creator Shares: Each creator has a unique token ID representing their "shares"
- Fee Structure: Includes dev fees, creator fees, and trading pool fees
- Upgradeable Contract: Uses the UUPS proxy pattern for future upgrades
The project consists of four main contracts:
The main contract that inherits from several OpenZeppelin contracts:
ERC1155Upgradeable: Base token standardERC1155BurnableUpgradeable: Allows tokens to be burnedERC1155SupplyUpgradeable: Tracks token supplyOwnableUpgradeable: Access controlUUPSUpgradeable: Upgradeable proxy pattern- Supports both Trading and Social room types with different tiers
A staking contract that allows users to stake their FriendKey tokens to earn rewards:
- Token Staking: Users can stake their FriendKey tokens for specific creators
- Reward Distribution: Distributes rewards to stakers based on their stake
- Lock Mechanism: Supports time-locked staking periods
- Upgradeable: Uses OpenZeppelin's upgradeable contracts pattern
A pool contract that manages reserves and cross-chain functionality:
- Reserve Management: Manages bonding curve reserves for each creator
- Cross-chain Integration: Integrates with DLN (deBridge Liquidity Network) for cross-chain operations
- Fund Dispatching: Allows authorized dispatching of funds across chains
- Upgradeable: Uses UUPS proxy pattern for future upgrades
A manager contract that handles room creation limits and configuration:
- Room Limit Enforcement: Controls how many rooms a creator can register per tier and type
- Fee Management: fee configuration for trading and social rooms
- Bonding Curve Config: Manages divisors and parameters for different room types
- Room Type Support: Enables/disables Trading and Social room creation
- Upgradeable: Uses UUPS proxy pattern for future upgrades
buyShares(uint256 tokenId, uint256 amount, uint256 maxSpend): Purchase shares of a creator with slippage protectionsellShares(uint256 tokenId, uint256 amount, uint256 minReceive): Sell shares of a creator with slippage protectiongetBuyPrice(uint256 id, uint256 amount): Calculate purchase price before feesgetSellPrice(uint256 id, uint256 amount): Calculate sell price before feesregisterCreator(RoomTier tier, uint256 additionalKeys, string metadata, bytes signature): Register as a trading room creatorregisterSocialCreator(RoomTier tier, uint256 additionalKeys, string metadata, bytes signature): Register as a social room creator (no staking/trading pool)stake(uint256 tokenId, uint256 amount): Stake tokens in the creator's poolunstake(uint256 tokenId, uint256 amount): Unstake tokens from the creator's pool
stake(uint256 amount): Stake FriendKey tokens to earn rewardsunstake(uint256 amount): Unstake tokens after lock periodclaimReward(): Claim earned rewards from stakingsetReward(uint256 amount): Set reward amount for distribution (owner only)
pull(uint256 tokenId, uint256 amount): Pull funds from bonding curve reservesdispatch(uint256 tokenId, uint256 amount): Dispatch funds cross-chain using DLNallowDispatch(uint256 tokenId, address recipient): Authorize fund dispatching
setMaxRoomsPerTier(RoomType, RoomTier, uint256 maxRooms): Set maximum rooms allowed per tierenableRoomType(RoomType, RoomTier): Enable room creation for a specific type/tierdisableRoomType(RoomType, RoomTier): Disable room creation for a specific type/tiersetTradingFees(uint16 devFee, uint16 creatorFee, uint16 poolFee): Configure trading room feessetSocialFees(uint16 devFee, uint16 creatorFee): Configure social room feescanRegisterRoom(address creator, RoomType, RoomTier): Check if creator can register a room
MockPool: A mock contract for testing the pool functionalityMockBridge: A mock contract for testing cross-chain operations
Note
Mock bridge should mimic the interface of the real bridge contract to ensure compatibility during testing. It should send fund to the owner address.
- Foundry
- Node.js & npm/yarn
See Foundry installation guide.
forge buildTip
If you get random errors try forge clean and forge build again.
forge test --forceImportant
Make sure to set up your environment variables (.env) before deploying.
# Required
PRIVATE_KEY=0x<PRIVATE_KEY>
# Network RPC URLs
TEST_RPC_URL=https://sepolia.base.org
RPC_URL=https://mainnet.base.org
# Optional - Protocol Configuration
USDC_ADDRESS=0x... # If not set or address(0), FriendUSD will be deployed
DLN_SOURCE_ADDRESS=0x... # Required for FriendPool deployment
AUTHORITY_ADDRESS=0x... # If not set, deployer address will be used
ETHERSCAN_API_KEY=your_api_key # For contract verification (only add if you want to verify, else skip)The Deploy.s.sol script provides a comprehensive deployment that:
- Deploys FriendUSD (if USDC address not provided)
- Deploys FriendStake beacon
- Deploys FriendKey proxy
- Deploys FriendPool proxy
- Configures FriendKey to use FriendPool
Using Makefile (Easiest):
# Deploy everything to testnet
make deploy-testnet
# Deploy everything to mainnet (with 5s safety delay)
make deploy-mainnet
# Simulate deployment without broadcasting
make simulateUsing Forge Script Directly:
# Simulate deployment
forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_URL -vvvv
# Deploy to testnet
forge script script/Deploy.s.sol:Deploy --rpc-url $TEST_RPC_URL --broadcast --verify -vvvv
# Deploy to mainnet
forge script script/Deploy.s.sol:Deploy --rpc-url $RPC_URL --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY -vvvvTip
If you get an error Failed to get EIP-1559 fees
you have to use --legacy flag source
FriendKey is an UUPS upgradable smart contract.
In order to run some checks about upgradability, previous version of contract is required and
@custom:oz-upgrades-from <reference> annotation in new version.
After deployment of one version, the file has to remain unchanged to verify upgradability to the next version.
It is recommended to create a new file for the next version.
To check upgradability run
npx @openzeppelin/upgrades-core validateThe bonding curve follows a polynomial formula that ensures price increases with supply. The curve is defined by:
price = (summation) * bondingTokenPriceUnit / 16000
Where summation is calculated based on supply and amount.
Three types of fees are applied to transactions:
- Developer fee: Sent to a designated address
- Creator fee: Accumulated for the creator to claim
- Trading pool fee: Sent to a designated address for ecosystem incentives
This project is licensed under the MIT License - see the LICENSE file for details.
To ensure proper file import in VSCode, add the following to your .vscode:
{
"solidity.packageDefaultDependenciesContractsDirectory": "src",
"solidity.packageDefaultDependenciesDirectory": "lib",
"solidity.compileUsingRemoteVersion": "v0.8.27",
"solidity.remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/"
]
}