Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/deployment and royalty #42

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export ETH_RPC_URL=http://127.0.0.1:8545
export ETH_FROM=[YOUR_ADDRESS_IN_KEYSTORE]
export ADMIN=[ADDRESS_FOR_ADMIN_ACCESS]
export PROTOCOL_FEE_RECIPIENT=[PROTOCOL_FEE_RECIPIENT_ADDRESS]
export PROTOCOL_FEE_MULTIPLIER=0
export LEGACY_TX=true
export ETH_GAS_PRICE=1000000000
export ETH_KEYSTORE=/home/[YOUR_USERNAME]/.ethereum/keystore
export ETH_PASSWORD=password.txt
export ETH_KEYSTORE_FILE=/home/[YOUR_USERNAME]/.ethereum/keystore/UTC--****--[YOUR_WALLET_ADDRESS]
export ROYALTY_RECIPIENT=[YOUR_ROYALTY_RECIPIENT]
1,760 changes: 880 additions & 880 deletions .gas-snapshot

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ out/
.env

# Forge
cache/
cache/

password.txt
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable.git
[submodule "lib/prb-math"]
path = lib/prb-math
url = https://github.com/hifi-finance/prb-math.git
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/rari-capital/solmate
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
install: update npm solc

# deps
update:; forge update
update:; forge update && \
cd lib/solmate && git checkout v6 && cd - # fix solmate version issue

# npm deps for linting etc.
npm:; yarn install
Expand All @@ -28,6 +29,7 @@ test-deploy :; ./scripts/test-deploy.sh

# Deployment helpers
deploy :; @./scripts/deploy.sh
deploy_extra :; @./scripts/deploy_extra.sh

# mainnet
deploy-mainnet: export ETH_RPC_URL = $(call network,mainnet)
Expand Down
74 changes: 74 additions & 0 deletions README_LOCAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Installation

## Requirements
`make`, `node`, `npm`, `yarn`, `jq`

### Install Forge
https://github.com/foundry-rs/foundry/blob/master/README.md

Requires: `curl` and `git`

### Install Nix
https://nixos.org/download.html

Requires: `xz-utils`

Choose single user installation for simplicity.


### Install Dapptools
https://github.com/dapphub/dapptools


### Preparation
### Local RPC Node

We recommend you to use `anvil` that comes with foundry during forge installation. Create a fork of mainnet and deploy a local node.

```bash
anvil \
--fork-url https://rpc.ankr.com/eth \
--chain-id 31337 \
--block-time 3 \
--block-base-fee-per-gas 1000000000
```
It will list default wallets that has balances. You can import one of them into your keystore and use it as a deployer to deploy contracts.

Please make sure your wallet is in you keystore. If you have to import a private key string, use the `ethsign` command.
```
ethsign import
```
Make sure that you set a passphrase and save the passphrase to a `password.txt` file in the root of this project directory.


Once these packages are installed, please copy `.env.local` into `.env`. And set your preferred wallet address and keystores.
```
export ETH_RPC_URL=http://127.0.0.1:8545
export ETH_FROM=[YOUR_ADDRESS_IN_KEYSTORE] # wallet address of $ETH_KEYSTORE_FILE specified below
export ADMIN=[ADDRESS_FOR_ADMIN_ACCESS] # wallet address to set as admin, set it to the same value as $ETH_FROM for convenience
export PROTOCOL_FEE_RECIPIENT=[PROTOCOL_FEE_RECIPIENT_ADDRESS] # wallet address to set as protocol fee recipient, set it to the same value as $ETH_FROM for convenience
export PROTOCOL_FEE_MULTIPLIER=0 # set protocol fee to 0
export LEGACY_TX=true
export ETH_GAS_PRICE=1000000000
export ETH_KEYSTORE=/home/[YOUR_USERNAME]/.ethereum/keystore # your keystore directory used by ethsign/seth commands, should contain your $ETH_KEYSTORE_FILE
export ETH_PASSWORD=password.txt # passphrase of your $ETH_KEYSTORE_FILE wallet
export ETH_KEYSTORE_FILE=/home/[YOUR_USERNAME]/.ethereum/keystore/UTC--****--[YOUR_WALLET_ADDRESS]
export ROYALTY_RECIPIENT=[YOUR_ROYALTY_RECIPIENT_ADDRESS] # Royalty recipient for Test NFTs
```

## Deployment

### Contracts
```
make
make build
make deploy
```

Once deployed, all the addresses are saved in `out/addresses.json` file.

### Fake Router and Test tokens (ERC20, ERC721, ERC721Enumerable)
```
make deploy_extra
```
Once deployed, all token and router addresses are saved in `out/addresses_extra.json` file. This should be executed after contracts deployment above.
1 change: 0 additions & 1 deletion lib/prb-math
Submodule prb-math deleted from 16b3e9
37 changes: 36 additions & 1 deletion scripts/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,44 @@ cat >"$ADDRESSES_FILE" <<EOF
}
EOF

deploy() {
NAME=$1
ARGS=(${@:2})

local flags=("--json")

flags+=("--rpc-url $ETH_RPC_URL")

if [ "$LEGACY_TX" != "" ]; then
flags+=("--legacy")
fi

if [ "$ETH_PRIVATE_KEY" != "" ]; then
flags+=("--private-key" "$ETH_PRIVATE_KEY")
elif [ "$ETH_KEYSTORE_FILE" != "" ]; then
flags+=("--keystore" "$ETH_KEYSTORE_FILE")
if [ "$ETH_PASSWORD" != "" ]; then
flags+=("--password" "$(cat $ETH_PASSWORD)")
fi
elif [ "$ETH_RPC_ACCOUNTS" != "" ]; then
flags+=("--unlocked")
fi

if [ "${#ARGS[@]}" != 0 ]; then
flags+=("--constructor-args")
flags+=(${ARGS[@]})
fi

ADDRESS=$(forge create ${flags[@]} -- "$NAME" | jq -r .deployedTo)

saveContract "$NAME" "$ADDRESS"

echo "$ADDRESS"
}

# Call as `ETH_FROM=0x... ETH_RPC_URL=<url> deploy ContractName arg1 arg2 arg3`
# (or omit the env vars if you have already set them)
deploy() {
_deploy() {
NAME=$1
ARGS=${@:2}

Expand Down
51 changes: 51 additions & 0 deletions scripts/deploy_extra.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash

set -eo pipefail

OUT_DIR=${OUT_DIR:-$PWD/out}
export CONTRACTS_FILE="$OUT_DIR/addresses.json"
export ADDRESSES_FILE="$OUT_DIR/addresses_extra.json"

# import the deployment helpers
. $(dirname $0)/common.sh

export LSSVMRouterAddr="${LSSVMRouterAddr:-$(jq -r .LSSVMRouter $CONTRACTS_FILE)}"
export LSSVMPairFactoryAddr="${LSSVMPairFactoryAddr:-$(jq -r .LSSVMPairFactory $CONTRACTS_FILE)}"

# Deploy tokens

# ERC20
Test20Addr=$(deploy Test20)
log "Test20 deployed at:" $Test20Addr

send $Test20Addr "mint(address,uint256)" $ETH_FROM 1000000000000000000000000 # 100M
log "100M T20 ($Test20Addr) minted for $ETH_FROM"

# Test721BatchMintWithRoyalty
Test721Addr=$(deploy Test721BatchMintWithRoyalty ${ROYALTY_RECIPIENT:-$ETH_fROM} 0 100000000000000) # flat, 0.0001 ETH
log "Test721 deployed at:" $Test721Addr

send $Test721Addr "batchMint(address,uint256[])" $ETH_FROM "[$(seq -s ',' 0 99)]"

# Test721EnumerableBatchMintWithRoyalty
Test721EnumerableAddr=$(deploy Test721EnumerableBatchMintWithRoyalty ${ROYALTY_RECIPIENT:-$ETH_fROM} 1 10000000000000000) # percent, 0.01 * salePrice ETH
log "Test721Enumerable deployed at:" $Test721EnumerableAddr

send $Test721EnumerableAddr "batchMint(address,uint256[])" $ETH_FROM "[$(seq -s ',' 0 99)]"

# Deploy routerWithRoyalties
LSSVMRouterWithRoyaltiesAddr=$(deploy LSSVMRouterWithRoyalties $LSSVMPairFactoryAddr)
log "LSSVMRouterWithRoyalties deployed at:" $LSSVMRouterWithRoyaltiesAddr

# Initialize royaltyRegistry address
send $LSSVMRouterWithRoyaltiesAddr "initializeRoyaltyRegistry(address)" ${ROYALTY_REGISTRY:-0x0000000000000000000000000000000000000000}
ROYALTY_REGISTRY="0x$(seth --rpc-url $ETH_RPC_URL call $LSSVMRouterWithRoyaltiesAddr "ROYALTY_REGISTRY()" | tail -c 41)"
log "LSSVMRoyaltyWithRoyalties initialized with address $ROYALTY_REGISTRY"

# save royalty registry
saveContract RoyaltyRegistry "$ROYALTY_REGISTRY"

# Whitelist routerWithRoyalties in factory
send $LSSVMPairFactoryAddr "setRouterAllowed(address,bool)" $LSSVMRouterWithRoyaltiesAddr true
log "Whitelisted routerWithRoyalties in factory"

2 changes: 2 additions & 0 deletions src/ILSSVMPairFactoryLike.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface ILSSVMPairFactoryLike {

function callAllowed(address target) external view returns (bool);

function swapAllowed(address caller) external view returns (bool);

function routerStatus(LSSVMRouter router)
external
view
Expand Down
28 changes: 25 additions & 3 deletions src/LSSVMPair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ abstract contract LSSVMPair is
spotPrice = _spotPrice;
}

modifier swapAllowed() {
require(
ILSSVMPairFactoryLike(factory()).swapAllowed(msg.sender),
"swap not allowed"
);
_;
}

/**
* External state-changing functions
*/
Expand All @@ -132,7 +140,14 @@ abstract contract LSSVMPair is
address nftRecipient,
bool isRouter,
address routerCaller
) external payable virtual nonReentrant returns (uint256 inputAmount) {
)
external
payable
virtual
nonReentrant
swapAllowed
returns (uint256 inputAmount)
{
// Store locally to remove extra calls
ILSSVMPairFactoryLike _factory = factory();
ICurve _bondingCurve = bondingCurve();
Expand Down Expand Up @@ -196,7 +211,14 @@ abstract contract LSSVMPair is
address nftRecipient,
bool isRouter,
address routerCaller
) external payable virtual nonReentrant returns (uint256 inputAmount) {
)
external
payable
virtual
nonReentrant
swapAllowed
returns (uint256 inputAmount)
{
// Store locally to remove extra calls
ILSSVMPairFactoryLike _factory = factory();
ICurve _bondingCurve = bondingCurve();
Expand Down Expand Up @@ -254,7 +276,7 @@ abstract contract LSSVMPair is
address payable tokenRecipient,
bool isRouter,
address routerCaller
) external virtual nonReentrant returns (uint256 outputAmount) {
) external virtual nonReentrant swapAllowed returns (uint256 outputAmount) {
// Store locally to remove extra calls
ILSSVMPairFactoryLike _factory = factory();
ICurve _bondingCurve = bondingCurve();
Expand Down
9 changes: 9 additions & 0 deletions src/LSSVMPairERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,13 @@ abstract contract LSSVMPairERC20 is LSSVMPair {
emit TokenWithdrawal(amount);
}
}

function depositERC20(ERC20 a, uint256 amount) external {
a.safeTransferFrom(msg.sender, address(this), amount);

if (a == token()) {
// emit event since it is the pair token
emit TokenDeposit(amount);
}
}
}
37 changes: 28 additions & 9 deletions src/LSSVMPairFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,19 @@ contract LSSVMPairFactory is Ownable, ILSSVMPairFactoryLike {
bondingCurveAllowed[_bondingCurve],
"Bonding curve not whitelisted"
);

// Check to see if the NFT supports Enumerable to determine which template to use
address template;
try IERC165(address(_nft)).supportsInterface(INTERFACE_ID_ERC721_ENUMERABLE) returns (bool isEnumerable) {
template = isEnumerable ? address(enumerableETHTemplate)
: address(missingEnumerableETHTemplate);
try
IERC165(address(_nft)).supportsInterface(
INTERFACE_ID_ERC721_ENUMERABLE
)
returns (bool isEnumerable) {
template = isEnumerable
? address(enumerableETHTemplate)
: address(missingEnumerableETHTemplate);
} catch {
template = address(missingEnumerableETHTemplate);
template = address(missingEnumerableETHTemplate);
}

pair = LSSVMPairETH(
Expand Down Expand Up @@ -185,11 +190,16 @@ contract LSSVMPairFactory is Ownable, ILSSVMPairFactoryLike {

// Check to see if the NFT supports Enumerable to determine which template to use
address template;
try IERC165(address(params.nft)).supportsInterface(INTERFACE_ID_ERC721_ENUMERABLE) returns (bool isEnumerable) {
template = isEnumerable ? address(enumerableERC20Template)
: address(missingEnumerableERC20Template);
try
IERC165(address(params.nft)).supportsInterface(
INTERFACE_ID_ERC721_ENUMERABLE
)
returns (bool isEnumerable) {
template = isEnumerable
? address(enumerableERC20Template)
: address(missingEnumerableERC20Template);
} catch {
template = address(missingEnumerableERC20Template);
template = address(missingEnumerableERC20Template);
}

pair = LSSVMPairERC20(
Expand Down Expand Up @@ -218,6 +228,15 @@ contract LSSVMPairFactory is Ownable, ILSSVMPairFactoryLike {
emit NewPair(address(pair));
}

/**
@notice Checks if swap is allows for given caller. Used by LLSVMPair to only serve allowed callers
@param caller The address of swap function caller
@return True if the address is allows to execute swaps in LSSVMPair contract, false otherwise
*/
function swapAllowed(address caller) external view override returns (bool) {
return routerStatus[LSSVMRouter(payable(caller))].allowed;
}

/**
@notice Checks if an address is a LSSVMPair. Uses the fact that the pairs are EIP-1167 minimal proxies.
@param potentialPair The address to check
Expand Down
17 changes: 15 additions & 2 deletions src/LSSVMRouterWithRoyalties.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ contract LSSVMRouterWithRoyalties is LSSVMRouter {
uint256 royaltyAmount
);

IRoyaltyRegistry public constant ROYALTY_REGISTRY =
IRoyaltyRegistry(0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D);
// IRoyaltyRegistry public constant ROYALTY_REGISTRY =
// IRoyaltyRegistry(0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D);
IRoyaltyRegistry public ROYALTY_REGISTRY = IRoyaltyRegistry(address(0));

uint256 public immutable FETCH_TOKEN_ID;

Expand All @@ -47,6 +48,18 @@ contract LSSVMRouterWithRoyalties is LSSVMRouter {
FETCH_TOKEN_ID = uint256(keccak256(abi.encode(address(this))));
}

function initializeRoyaltyRegistry(address _royaltyRegistry) external {
if (address(ROYALTY_REGISTRY) == address(0)) {
if (_royaltyRegistry == address(0)) {
ROYALTY_REGISTRY = IRoyaltyRegistry(
0xaD2184FB5DBcfC05d8f056542fB25b04fa32A95D // mainnet
);
} else {
ROYALTY_REGISTRY = IRoyaltyRegistry(_royaltyRegistry);
}
}
}

function supportsRoyalty(address collection) external view returns (bool) {
// get royalty lookup address from the shared royalty registry
address lookupAddress = ROYALTY_REGISTRY.getRoyaltyLookupAddress(
Expand Down
Loading