Skip to content

code-423n4/2022-12-escher

Repository files navigation

Escher.xyz contest details

  • Total Prize Pool: $36,500 USDC
    • HM awards: $25,500 USDC
    • QA report awards: $3,000 USDC
    • Gas report awards: $1,500 USDC
    • Judge + presort awards: $6,000 USDC
    • Scout awards: $500 USDC
  • Join C4 Discord to register
  • Submit findings using the C4 form
  • Read our guidelines for more details
  • Starts December 06, 2022 20:00 UTC
  • Ends December 09, 2022 20:00 UTC

C4udit / Publicly Known Issues

The C4audit output for the contest can be found here.

Note for C4 wardens: Anything included in the C4udit output is considered a publicly known issue and is ineligible for awards.

Overview

Escher Escher is a decentralized curated marketplace for editioned artwork

Contracts For Escher721, Sale contracts, and URI delegates, we inherit from the openzeppelen-contracts-upgradeable version of OpenZeppelin's libraries to keep deploy costs low, and the contracts aren't actually upgradeable

Escher.sol

This is a minimal curated registry of onchain addresses which are the creators onboarded to Escher. Users can be added as a Curator or a Creator. Curators are able to onboard creators to the smart contract system. All assigned roles are ERC1155 soulbound tokens.

Escher Editions

URIs

Each contract must declare a URI delegate which handles all token metadata.

Escher721.sol

This is the core creator contract implementation for Escher. It is built on top the OpenZepplin ERC721 contracts. Each contract is fully owned by the creators. Escher must be careful to not lose their keys or to add backup admins.

Escher721Factory.sol

This is the factory contract where all onboarded artists are able to mint their own Escher Edition contract. Each contract is a minimal proxy to keep is cheap and easy to make your own contract. While the Factory uses proxies, nothing in it is upgradeable.

Fixed Price Sales

FixedPriceSaleFactory.sol

This is the factory contract where fixed price sale proxies are created. The protocol controls a feeReceiver variable which is the address that receives 5% of all sales. The other function is creating the proxy. Variables and how the creation flow works will be covered below in Sales Patterns

FixedPriceSale.sol

This is the core contract for fixed price and fixed supply sales. Inside this contract there are two public functions. One to cancel the sale which is controlled by the creator and the other to purchase an edition.

Open Edition Sale

OpenEditionFactory.sol

This is the factory contract where open edition sale proxies are created. The protocol controls a feeReceiver variable which is the address that receives 5% of all sales. The other function is creating the proxy. Variables and how the creation flow works will be covered below in Sales Patterns

OpenEdition.sol

This is the core contract for fixed price and uncapped supply sales. Inside this contract there are two public functions.

Last Price Dutch Auction Sale (LPDA)

LPDAFactory.sol

This is the factory contract where LPDA sale proxies are created. The protocol controls a feeReceiver variable which is the address that receives 5% of all sales. The other function is creating the proxy. Variables and how the creation flow works will be covered below in Sales Patterns

LPDA.sol

This is the core contract for LPDA sales. Each contract is a proxy to the implementation of LPDAs.

Scope

Files in scope

File SLOC Description and Coverage Libraries
Contracts (12)
src/uris/Unique.sol 9 Metadata Contract,   0.00% @openzeppelin/*
src/uris/Base.sol 15 Metadata Contract,   0.00% @openzeppelin-upgradeable/*
src/uris/Generative.sol 🧮 20 Metadata Contract,   0.00%
src/Escher721Factory.sol 🌀 28 Token Factory,   0.00% @openzeppelin/*
src/minters/FixedPriceFactory.sol 🌀 31 Sale Factory,   100.00% @openzeppelin/*
src/minters/OpenEditionFactory.sol 🌀 31 Sale Factory,   100.00% @openzeppelin/*
src/minters/LPDAFactory.sol 🌀 35 Sale Factory,   100.00% @openzeppelin/*
src/Escher.sol 🧮 48 Manages roles,   80.00% @openzeppelin/*
src/Escher721.sol 🧮 64 ERC721 Token,   15.38% @openzeppelin-upgradeable/*
src/minters/FixedPrice.sol 💰 💣 📤 66 Sale Contract,   82.61% @openzeppelin-upgradeable/*
src/minters/OpenEdition.sol 💰 💣 📤 81 Sale Contract,   81.48% @openzeppelin-upgradeable/*
src/minters/LPDA.sol 💰 📤 106 Sale Contract,   91.11% @openzeppelin-upgradeable/*
Total (over 12 files): 534 72.67%

All other source contracts (not in scope)

File SLOC Description and Coverage Libraries
Interfaces (4)
src/interfaces/ISaleFactory.sol 4 -
src/interfaces/ITokenUriDelegate.sol 6 -
src/interfaces/ISale.sol 💰 8 -
src/interfaces/IEscher721.sol 10 - @openzeppelin/*
Total (over 4 files): 28 -

External Imports

Additional Context

Sales Patterns

Here we will cover the flow from start to finish for both fixed price and open edition sales. Sales require that the default royalties for a contract has been set at minimum. This is how we decide where payments go.

Fixed Price

  1. Set up the token URI by calling setTokenURI with the token ID and the URI (arweave recommended)
  2. If the artist would like sales and royalties to go somewhere other than the default royalty receiver, they must call setTokenRoyalty with the following variables:
  • id: the token ID of the sale
  • receiver: the address to receive paymnts
  • feeNumerator: the desired royalty %
  1. Call createFixedSale in the FixedPriceSaleFactory contract. This will create the proxy sale contract. The artist must provide the following variables:
  • edition: the contract address of their Escher Edition proxy
  • id: the id of the token to sell
  • price: the price in ether of each edition
  • saleTime: the starting time in unix of the sale
  • amount: the amount of editions to sell
  1. Call grantRole in the creators Escher Edition contract. This will allow the proxy contract to mint the tokens. The following variables must be provided:
  • role: the bytes32 MINTER_ROLE which can be found in the artist contract
  • account: the address of the sale proxy contract

Open Edition

  1. Set up the token URI by calling setTokenURI with the token ID and the URI (arweave recommended)
  2. If the artist would like sales and royalties to go somewhere other than the default royalty receiver, they must call setTokenRoyalty with the following variables:
  • id: the token ID of the sale
  • receiver: the address to receive paymnts
  • feeNumerator: the desired royalty %
  1. Call createOpenEdition in the OpenEditionSaleFactory contract. This will create the proxy sale contract. The artist must provide the following variables:
  • edition: the contract address of their Escher Edition proxy
  • id: the id of the token to sell
  • price: the price in ether of each edition
  • saleTime: the starting time in unix of the sale
  • endTime: the ending time in unix of the sale
  1. Call grantRole in the creators Escher Edition contract. This will allow the proxy contract to mint the tokens. The following variables must be provided:
  • role: the bytes32 MINTER_ROLE which can be found in the artist contract
  • account: the address of the sale proxy contract
  1. Once the sale has ended, the artist must call finalize to get their Ethereum.

Last Price Dutch Auction (LPDA)

  1. Set up the token URI by calling setTokenURI with the token ID and the URI (arweave recommended)
  2. If the artist would like sales and royalties to go somewhere other than the default royalty receiver, they must call setTokenRoyalty with the following variables:
  • id: the token ID of the sale
  • receiver: the address to receive paymnts
  • feeNumerator: the desired royalty %
  1. Call createLPDASale in the LPDAFactory contract. This will create the proxy sale contract. The artist must provide the following variables:
  • currentId: the starting id of the token to sell
  • finalId: the ending id of the token to sell
  • edition: the contract address of their Escher Edition proxy
  • startPrice: the starting price of the LDPA
  • finalPrice: the ending price of the LDPA
  • dropPerSecond: price decrease per second
  • endTime: the ending time in unix of the sale
  • saleReceiver: the account to send proceeds from the sale to
  • startTime: the starting time in unix of the sale
  1. Call grantRole in the creators Escher Edition contract. This will allow the proxy contract to mint the tokens. The following variables must be provided:
  • role: the bytes32 MINTER_ROLE which can be found in the artist contract
  • account: the address of the sale proxy contract
  1. Once the sale has ended, the users must call refund to get their Ether refunds based on their purchase price and lowest sale price.

Describe any novel or unique curve logic or mathematical models implemented in the contracts

Sponsor, please confirm/edit the information below.

Scoping Details

- If you have a public code repo, please share it here:  
- How many contracts are in scope?:   13
- Total SLoC for these contracts?:  525
- How many external imports are there?: 10 
- How many separate interfaces and struct definitions are there for the contracts within scope?:  5
- Does most of your code generally use composition or inheritance?:   composition
- How many external calls?:   0
- What is the overall line coverage percentage provided by your tests?:  100%
- Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol?:   false
- Please describe required context:   
- Does it use an oracle?:  false
- Does the token conform to the ERC20 standard?:  No token
- Are there any novel or unique curve logic or mathematical models?: No
- Does it use a timelock function?:  No
- Is it an NFT?: ERC721 are part of it
- Does it have an AMM?:   No
- Is it a fork of a popular project?:   false
- Does it use rollups?:   false
- Is it multi-chain?:  false
- Does it use a side-chain?: false

Quickstart

In one command

rm -Rf 2022-12-escher || true && git clone https://github.com/code-423n4/2022-12-escher.git && cd 2022-12-escher && npm ci && git submodule update --init --recursive && foundryup && forge test --gas-report

Tests

Install linting and formatting

npm ci

Install git submodules

git submodule update --init --recursive

Run tests

forge test

Run Slither

Make sure you're running the latest version of slither v0.9.1

slither .