Awesome
Carmine Protocol
This repository contains the smart contracts for the Carmine Protocol, developed by Carmine Finance s.r.o.
The Protocol lets any user to buy and sell options at a fair price.
Current State
We are live on ✨mainnet✨!
Documentation
High level docs here. Code docs will be published soon, but the code is pretty well commented.
Other Links
Oracles
Currently using only Pragma (formerly Empiric) oracle. When the AMM detects that Pragma is not publishing up-to-date data for any reason, trading is automatically halted.
We're working on our own solution to make arbitrary aggregated timestamped data available on the blockchain, the Chronos oracle. This will be used in the future in addition to traditional oracles to mitigate spot price manipulation around expiry and ensure the latest possible price is used when settling options.
Development
Setup on GitHub Codespaces
The easiest way to get up and running is to use Github Codespaces or VSCode with Docker and Devcontainers.
Local setup
- Clone this repo with
git clone --recurse-submodules
(Protostar uses submodules) - Cairo quickstart
- On a M1 Macbook, fastecdsa build might fail; this might help.
- note that Cairo requires Python 3.9
- Install Protostar
- setup a virtualenv, install requirements.txt...
make build
;make test
- nice to have:
echo $'\a'
plays a beep so you know to get back to work
Deployment
See scripts/deploy_governance.py
, this scripts declares all contracts and then deploys the governance contract, which, during its initialization deploys everything else through Starknet deploy syscalls.
Proxy Contracts
For proxy pattern, we decided to utilize OpenZeppelin library. Detailed explanation can be found here.
NOTE: To prevent any errors, deploy a new wallet with starknet deploy_account --account new_account
, otherwise you might not be able to declare/deploy and use the proxy pattern.
Components:
-
Implementation contract
- Contains the logic and functions that allow the use of proxy pattern
-
Proxy contract
- Contains function that delegates the calls to the Implementation contract
How to build:
- Run
protostar build
command
How to deploy:
-
Implementation contract
- This contract needs to be declared only, so protostar won't be of use since it can only deploy at the moment.
- Navigate to build folder and run
starknet declare --contract amm.json
- Save 'Contract class hash', ie.
export AMM_HASH=hash
-
Proxy contract
- This contract need to be deployed along with the Implementation contract's class hash as an input.
- Run
starknet deploy --contract proxy.json --input $AMM_HASH
- Save 'Contract address', ie.
export PROXY_ADDR=address
Use:
First thing that needs to be done is initializing the implementation contract by sending a call to the proxy contract. This will act as a Implementation contract's constructor.
You can initialize the contract by calling the initializer
function and passing the admin address as an input.
starknet invoke \
--address $PROXY_ADDR \
--abi amm_abi.json \
--function initializer \
--input $ADMIN_ADDRESS \
--max_fee 500000000000000
Important note: When calling any function from the implementation contract through the proxy contract, you must use the Proxy contract's adress, but the Implementations contract's abi.
Now you can verify that the admin stored inside the contract is the same as you specified.
starknet call \
--address $PROXY_ADD \
--abi amm_abi.json \
--function getAdmin \
Also you can try to invoke some function that can only be invoked by an admin to see that you won't be able to(provided you use different account).
starknet invoke \
--address $PROXY_ADD \
--abi amm_abi.json \
--function setAdmin --input 0x0000000000000000 \
--account not_admin
Error message: Proxy: caller is not admin
But you can call/invoke any function that is not restricted, for example init_pool
.
starknet invoke \
--address $PROXY_ADDR \
--abi amm_abi.json \
--function init_pool \
--max_fee 500000000000000
Or get_pool_balance
.
starknet call \
--address $PROXY_ADDR \
--abi amm_abi.json \
--function get_pool_balance \
--input 0
0x6072000000000000000
etc.
Upgrading:
Upgrading is done by invoking the upgrade
function stored in the Implementation contract and passing the new Implementation contract's class hash as an input. While the Proxy contract doesn't store any logic, it will preserve the state when upgrading, meaning that if you invoke the init_pool
function and then upgrade the contract, it will still return 12345 when calling the get_pool_balance
function(provided you didn't interact with the pool).
starknet invoke \
--address $PROXY_ADDR \
--abi amm_abi.json \
--function upgrade \
--input $NEW_AMM_HASH
After upgrading, interact with the contract using the upgraded abi.
Interacting via another contract:
This works exactly the same as with regular contracts, just use the Proxy contract's address.
%lang starknet
const PROXY_ADDR = $PROXY_ADDR
@contract_interface
namespace IAmm:
func get_pool_balance(option_type : felt) -> (pool_balance : felt):
end
end
@external
func pool_balance{syscall_ptr : felt*, range_check_ptr}(option_type : felt) -> (balance : felt):
let (res) = IAmm.get_pool_balance(PROXY_ADDR, option_type)
return (res)
end
License
Everything in this repository is licensed under the MIT license, see LICENSE
.
(c) Carmine Finance s.r.o. 2023