Platform: Code4rena
Start Date: 23/11/2022
End Date: 28/11/2022
Period: 5 days
Status: Completed
Pot Size: $24,500 CANTO
Participants: 37
Reporter: itsmetechjay
Judge: berndartmueller
Id: 185
League: ETH
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.
This contest covers:
Code for a new cosmos module (x/csr
).
Code for a smart contract (contracts/turnstile.sol
), which is the same as (CIP-001/src/Turnstile.sol
). The smart contract is a modification of ERC 721 while Canto/x/csr
is a standard cosmos module that was scaffolded by Ignite CLI.
Smart Contract | SLOC | Purpose | Libraries used |
---|---|---|---|
CIP-001/src/Turnstile.sol (same as Canto/contracts/turnstile.sol) | 54 | Contract that registers other contracts for CSR | @openzeppelin/token/ERC721/extensions/ERC721Enumerable.sol @openzeppelin/access/Ownable.sol @openzeppelin/utils/Counters.sol |
Cosmos Module File (all in x/csr) | SLOC | Purpose |
---|---|---|
keeper/csr.go | 34 | Contains set and get logic for csr store |
keeper/keeper.go | 20 | Contains keeper and key definitions |
keeper/evm.go | 47 | Contains all of the functionality that allows us to interact with the EVM and Turnstile contract. |
keeper/event_handler.go | 47 | This defines the events that the module will be looking out for in the EVM hook defined and implemented in evm_hooks.go. |
keeper/evm_hooks.go | 62 | This is where the core fee distribution logic exists. TLDR is every transaction will have a set of events that are emitted. We check if the tx had either a assign or register event emitted, internally store the contract to its associated NFT if necessary, and distribute the fees accordingly. If the smart contract was previously registered, we check if the smart belongs to some CSR NFT by looking through the keeper. If so, we distribute fees that have accumulated. |
all other contracts and Cosmos SDK modules are out of scope for this contest
We present a novel economic mechanism which modifies EIP-1559 to distribute a portion of the total base fee (an amount that would otherwise be burnt) to the deployers of the contracts that consume gas within a given block. Our goal is to implement the CSR protocol with as few changes as possible to the existing EIP-1559 specification while also providing a simple and flexible user experience.
The Canto CSR Store is a revenue-sharing-per-transaction model that allows smart contract developers to accumulate revenue to a tradable NFT. In this model, developers deploy smart contracts that generate revenue via transaction fees that go directly to an NFT. Developers register their dApps with a special CSR smart contract that mints an NFT or adds smart contracts to an existing NFT. The split between transactions fees that go to network operators and NFTs is implemented and configurable by the x/csr
module.
On the application layer, CSR functions as a series of smart contracts responsible for generating and maintaining a registry of eligible contract addresses. As a contract creator, participation is on an opt-in basis. Should a contract creator choose to deploy a CSR enabled contract, they must integrate support for the CSR Turnstile, described in the section below. Upon deployment of a CSR enabled contract, the contract creator is minted a CSR NFT. This NFT acts as a claim ticket for all future fees accrued. Smart contract developers can add smart contracts to existing NFTs. Smart contracts that are written using the factory pattern can be automatically CSR-enabled when the turnstile code is injected.
The CSR Turnstile contract is deployed by the CSR module account upon genesis.
The CSR NFT Smart contract is an extension of ERC721 and is deployed by the module account on genesis. Upon registration of a smart contract, the CSR module account will mint a new NFT from the CSR NFT smart contract. The register defaults to minting a new NFT as the beneficiary
and sending that NFT to fromAddr
, but the function can be called to assign an existing NFT as the beneficiary or send the newly minted NFT to another address. The beneficiary
must call the withdrawal method on the smart contract along with an NFT ID to retrieve transaction revenue.
Developers register their application in the CSR Store by
register
or assign
– into their smart contracts
Turnstile
can be called with two possible function signatures. One will allow the user to add the deployed smart contract to an existing NFT (assign
), the other will allow the user to mint a new NFT (register
).msg.sender
being the new smart contract that needs to be registered.PostTxProcessing
hook will listen for registration events coming from the turnstile address and will update the CSR store accordingly.// register the smart contract to an existing CSR nft function assign(uint64 _tokenId) public { .... emit UpdateCSREvent(msg.sender, id); } // register and mint a new CSR nft that will be transferred // to the to address entered function register(address to) public { .... emit RegisterCSREvent(msg.sender, to); }
When a transaction is executed, the entire gas fee amount is sent to the FeeCollector
module account during the Cosmos SDK AnteHandler execution. After the EVM transaction finishes executing, the user receives a refund of (gasLimit - gasUsed) * gasPrice
. In total, a user will pay a gas fee of txFee = gasUsed * gasPrice
to complete an arbitrary transaction on the EVM. This transaction fee is distributed between the NFTs minted by the CSR smart contract and network operators (validators). The distribution between the CSR smart contract and network operators is defined as follows,
// Calculate fees to be distributed = intFloor(GasUsed * GasPrice * csrShares) fee := sdk.NewIntFromUint64(receipt.GasUsed).Mul(sdk.NewIntFromBigInt(msg.GasPrice())) csrFee := sdk.NewDecFromInt(fee).Mul(params.CsrShares).TruncateInt() evmDenom := h.k.evmKeeper.GetParams(ctx).EvmDenom csrFees := sdk.Coins{{Denom: evmDenom, Amount: csrFee}}
csrFees
is then sent to Turnstile.sol
smart contract that was deployed by the module account. Users can then withdraw their revenue by calling withdraw(uint256 _tokenId, address payable _recipient, uint256 _amount)
on the Turnstile
.
Any set of registered contracts will be associated with a NFT. Each NFT will have a different set of smart contracts accumulating revenue. As such, distributing transaction fees requires the implementation of a beneficiary
account which will be accumulating rewards on behalf of the NFT. Every NFT will have a single beneficiary
account which is sent transaction revenue when smart contracts pertaining to the NFT the beneficiary
belongs to. When users withdrawal revenue from the NFT, they are sending funds the the beneficiary
account to their own account.
Users are lazily allocated fees. This means that each user withdraws all fees they have accrued since the last time they have withdrawn from a NFT.
Keeper / Client / Types (164 LOC)
Most of the keeper, client, and types code was scaffolded by ignite and filled in by the developers. It follows the standard Cosmos paradigm of writing module code.
x/csr
stores the following state the address of the turnstile smart contract and CSR objects which look like the following:
// The CSR struct is a wrapper to all of the metadata associated with a given CST NFT message CSR { // Contracts is the list of all EVM address that are registered to this NFT repeated string contracts = 1; // The NFT id which this CSR corresponds to uint64 id = 2; // The total number of transactions for this CSR NFT uint64 txs = 3; // The cumulative revenue for this CSR NFT -> represented as a sdk.Int string revenue = 4 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false ]; }
EVM Hook
assign
or register
event emitted, internally store the contract to its associated NFT if necessary, and distribute the fees accordingly.Turnstile
contract.If using Ubuntu:
Install all dependencies:
sudo snap install go --classic && sudo apt-get install git && sudo apt-get install gcc && sudo apt-get install make
Or install individually:
sudo snap install go --classic
sudo apt-get install git
sudo apt-get install gcc
sudo apt-get install make
If using Arch Linux:
pacman -S go
pacman -S git
pacman -S gcc
pacman -S make
cantod
In order to make install on some machines, you may need to rename contracts/compiled_contracts/turnstile.json to contracts/compiled_contracts/Turnstile.json (capitalize the t in turnstile.json)
cd Canto make install
# inside Canto directory ./init_testnet.sh
# inside x/csr/keeper and x/csr/types directories: go test -v -race ./
# under CIP-001 forge install && forge test --gas-report
- If you have a public code repo, please share it here: https://github.com/Canto-Network/Canto - How many contracts are in scope?: 1 cosmos SDK module, 1 smart contract - Total SLoC for these contracts?: 275 - How many external imports are there?: 4 - How many separate interfaces and struct definitions are there for the contracts within scope?: 0 - Does most of your code generally use composition or inheritance?: inheritance - 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?: n/a - Are there any novel or unique curve logic or mathematical models?: no - Does it use a timelock function?: no - Is it an NFT?: yes, uses ERC721 - 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
hihen | 1/37 | $4,480.36 | 3 | 1 | 0 | 2 | 1 | 0 | 0 | 0 |
Jeiwan | 2/37 | $4,285.93 | 2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
ronnyx2017 | 3/37 | $4,057.68 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
sorrynotsorry | 4/37 | $2,197.91 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 |
Ruhum | 5/37 | $1,978.12 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
0xSmartContract | 6/37 | $758.10 | 1 | 0 | 0 | 0 | 0 | Grade A | 0 | 0 |
rotcivegaf | 7/37 | $596.84 | 2 | 0 | 0 | 0 | 0 | Grade A | Grade B | 0 |
Tricko | 8/37 | $269.94 | 1 | 0 | 0 | 0 | 0 | 0 | Grade A | 0 |
nicobevi | 9/37 | $207.65 | 1 | 0 | 0 | 0 | 0 | 0 | Grade A | 0 |
Beepidibop | 10/37 | $207.65 | 1 | 0 | 0 | 0 | 0 | 0 | Grade A | 0 |
Auditor per page