Skip to content

Commit

Permalink
feat: pragma oracle (#1169)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

<!-- Give an estimate of the time you spent on this PR in terms of work
days.
Did you spend 0.5 days on this PR or rather 2 days?  -->

Time spent on this PR:

## Pull request type

<!-- Please try to limit your pull request to one type,
submit multiple pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying,
or link to a relevant issue. -->

Resolves #<Issue number>

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by
this PR. -->

-
-
-

<!-- Reviewable:start -->
- - -
This change is [<img src="https://reviewable.io/review_button.svg"
height="34" align="absmiddle"
alt="Reviewable"/>](https://reviewable.io/reviews/kkrt-labs/kakarot/1169)
<!-- Reviewable:end -->
  • Loading branch information
enitrat committed Jun 3, 2024
1 parent af2ecf4 commit fe9acd9
Show file tree
Hide file tree
Showing 17 changed files with 450 additions and 17 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ jobs:
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Install Scarb
uses: software-mansion/setup-scarb@v1
with:
tool-versions: ./cairo1_contracts/mock_pragma/.tool-versions #TODO: support multiple tool-versions files
- name: Load cached katana
id: cached-katana
uses: actions/cache@v4
Expand Down
1 change: 1 addition & 0 deletions .trunk/trunk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ lint:
paths: [poetry.lock]
- linters: [ALL]
paths:
- cairo1_contracts
- logs*
- lib*
- resources*
Expand Down
40 changes: 39 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ KKRT_SSJ_RELEASE_ID = 154615699
# Kakarot SSJ artifacts for precompiles.
KKRT_SSJ_BUILD_ARTIFACT_URL = $(shell curl -L https://api.github.com/repos/kkrt-labs/kakarot-ssj/releases/${KKRT_SSJ_RELEASE_ID} | jq -r '.assets[0].browser_download_url')
KATANA_VERSION = v0.7.0-alpha.0
ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))


build: check
$(MAKE) clean
poetry run python ./kakarot_scripts/compile_kakarot.py
Expand Down Expand Up @@ -42,7 +45,7 @@ test: build-sol deploy
test-unit: build-sol
poetry run pytest tests/src -m "not NoCI" -n logical

test-end-to-end: build-sol deploy
test-end-to-end: build-sol build-cairo1 deploy
poetry run pytest tests/end_to_end

deploy: build
Expand Down Expand Up @@ -76,6 +79,41 @@ build-sol-experimental:
--foundry-command build


# Builds Cairo 1.0 contracts by iterating over subdirectories,
# compiling contracts, and copying the resulting .sierra.json (old versions) or .contract_class.json
# files to the ROOT_DIR/build/fixtures directory with appropriate file extensions.
build-cairo1:
@mkdir -p build/ssj
@for d in cairo1_contracts/*/ ; do \
if [ "$$d" != "cairo1_contracts/build/" ]; then \
echo "Building $$d"; \
cd $$d; \
scarb build; \
for f in target/dev/*.sierra.json target/dev/*.contract_class.json target/dev/*.casm.json target/dev/*.compiled_contract_class.json; do \
if [ -e "$$f" ]; then \
case "$$f" in \
*.sierra.json) \
CONTRACT_NAME="$$(basename $$f | sed -E 's/^.*_([^_.]*)\.sierra\.json$$/\1/')"; \
cp "$$f" "$(ROOT_DIR)/build/ssj/contracts_$$CONTRACT_NAME.contract_class.json"; \
;; \
*.contract_class.json) \
CONTRACT_NAME="$$(basename $$f | sed -E 's/^.*_([^_.]*)\.contract_class\.json$$/\1/')"; \
cp "$$f" "$(ROOT_DIR)/build/ssj/contracts_$$CONTRACT_NAME.contract_class.json"; \
;; \
*.casm.json) \
CONTRACT_NAME="$$(basename $$f | sed -E 's/^.*_([^_.]*)\.casm\.json$$/\1/')"; \
cp "$$f" "$(ROOT_DIR)/build/ssj/contracts_$$CONTRACT_NAME.compiled_contract_class.json"; \
;; \
*.compiled_contract_class.json) \
CONTRACT_NAME="$$(basename $$f | sed -E 's/^.*_([^_.]*)\.compiled_contract_class\.json$$/\1/')"; \
cp "$$f" "$(ROOT_DIR)/build/ssj/contracts_$$CONTRACT_NAME.compiled_contract_class.json"; \
;; \
esac; \
fi; \
done; \
cd -; \
fi; \
done

install-katana:
cargo install --git https://github.com/dojoengine/dojo --locked --tag "${KATANA_VERSION}" katana
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ ln -s <YOUR_PATH_TO_YOUR_PYTHON_VENV_BINARIES>/starknet-compile-deprecated <YOUR

## Build

To build the Cairo files:
To build the CairoZero files:

```bash
make build
Expand All @@ -124,6 +124,14 @@ To build the test Solidity smart contracts:
make build-sol
```

To build the Cairo1 files:

```bash
# install scarb via ASDF if you haven't done it already
# https://docs.swmansion.com/scarb/download.html#install-via-asdf
make build-cairo1
```

## Code style

The project uses [trunk.io](https://trunk.io/) to run a comprehensive list of
Expand Down
1 change: 1 addition & 0 deletions cairo1_contracts/mock_pragma/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
1 change: 1 addition & 0 deletions cairo1_contracts/mock_pragma/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 0.7.0
55 changes: 55 additions & 0 deletions cairo1_contracts/mock_pragma/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "alexandria_data_structures"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4#46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4"

[[package]]
name = "alexandria_math"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4#46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "alexandria_sorting"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4#46c8d8ab9e3bfb68b70a29b3246f809cd8bf70e4"

[[package]]
name = "alexandria_storage"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git?rev=92c3c1b4ac35a4a56c14abe992814581aee875a8#92c3c1b4ac35a4a56c14abe992814581aee875a8"

[[package]]
name = "cubit"
version = "1.2.0"
source = "git+https://github.com/influenceth/cubit?rev=2ccb2536dffa3f15ebd38b755c1be65fde1eab0c#2ccb2536dffa3f15ebd38b755c1be65fde1eab0c"

[[package]]
name = "mock_pragma"
version = "0.1.0"
dependencies = [
"pragma",
]

[[package]]
name = "openzeppelin"
version = "0.7.0"
source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.7.0#bb8c56817577b66cea9f18a241fe59726db42dd5"

[[package]]
name = "pragma"
version = "1.0.0"
source = "git+https://github.com/astraly-labs/pragma-oracle?tag=v1.0.5#71e762dcb725b95a4a4966190219bca8380bc823"
dependencies = [
"alexandria_data_structures",
"alexandria_math",
"alexandria_sorting",
"alexandria_storage",
"cubit",
"openzeppelin",
]
14 changes: 14 additions & 0 deletions cairo1_contracts/mock_pragma/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "mock_pragma"
version = "0.1.0"
edition = "2023_10"

# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html

[dependencies]
starknet = "2.2.0"
pragma = { git = "https://github.com/astraly-labs/pragma-oracle", tag = "v1.0.5" }

[[target.starknet-contract]]
casm = true
casm-add-pythonic-hints = true
1 change: 1 addition & 0 deletions cairo1_contracts/mock_pragma/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod mock_pragma_oracle;
91 changes: 91 additions & 0 deletions cairo1_contracts/mock_pragma/src/mock_pragma_oracle.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use pragma::entry::structs::{DataType, PragmaPricesResponse};

#[starknet::interface]
trait IOracle<TContractState> {
fn get_data_median(self: @TContractState, data_type: DataType) -> PragmaPricesResponse;
}

#[starknet::interface]
trait IMockPragmaOracle<TContractState> {
fn set_price(
ref self: TContractState,
pair_id: felt252,
price: u128,
decimals: u32,
last_updated_timestamp: u64,
num_sources_aggregated: u32,
);
}

#[starknet::contract]
mod MockPragmaOracle {
use starknet::ContractAddress;
use pragma::entry::structs::{DataType, PragmaPricesResponse};

use super::{IOracle, IMockPragmaOracle};

#[storage]
struct Storage {
pair_id: felt252,
price: u128,
decimals: u32,
last_updated_timestamp: u64,
num_sources_aggregated: u32
}

//! Must be compatible with Cairo 2.2.0
#[external(v0)]
impl IPragmaOracleImpl of IOracle<ContractState> {
fn get_data_median(self: @ContractState, data_type: DataType) -> PragmaPricesResponse {
match data_type {
DataType::SpotEntry => {
PragmaPricesResponse {
price: self.price.read(),
decimals: self.decimals.read(),
last_updated_timestamp: self.last_updated_timestamp.read(),
num_sources_aggregated: self.num_sources_aggregated.read(),
expiration_timestamp: Option::None,
}
},
DataType::FutureEntry => {
PragmaPricesResponse {
price: self.price.read(),
decimals: self.decimals.read(),
last_updated_timestamp: self.last_updated_timestamp.read(),
num_sources_aggregated: self.num_sources_aggregated.read(),
expiration_timestamp: Option::Some(
self.last_updated_timestamp.read() + 1000
),
}
},
DataType::GenericEntry => {
PragmaPricesResponse {
price: self.price.read(),
decimals: self.decimals.read(),
last_updated_timestamp: self.last_updated_timestamp.read(),
num_sources_aggregated: self.num_sources_aggregated.read(),
expiration_timestamp: Option::None,
}
}
}
}
}

#[external(v0)]
impl IMockPragmaOracleImpl of IMockPragmaOracle<ContractState> {
fn set_price(
ref self: ContractState,
pair_id: felt252,
price: u128,
decimals: u32,
last_updated_timestamp: u64,
num_sources_aggregated: u32
) {
self.pair_id.write(pair_id);
self.price.write(price);
self.decimals.write(decimals);
self.last_updated_timestamp.write(last_updated_timestamp);
self.num_sources_aggregated.write(num_sources_aggregated);
}
}
}
1 change: 1 addition & 0 deletions kakarot_scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ class ArtifactType(Enum):
{"contract_name": "Cairo1HelpersFixture", "cairo_version": ArtifactType.cairo1},
{"contract_name": "replace_class", "cairo_version": ArtifactType.cairo0},
{"contract_name": "Counter", "cairo_version": ArtifactType.cairo0},
{"contract_name": "MockPragmaOracle", "cairo_version": ArtifactType.cairo1},
]

EVM_PRIVATE_KEY = os.getenv("EVM_PRIVATE_KEY")
Expand Down
1 change: 1 addition & 0 deletions kakarot_scripts/deploy_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ async def main():
BLOCK_GAS_LIMIT,
)
deployments["Counter"] = await deploy("Counter")
deployments["MockPragmaOracle"] = await deploy("MockPragmaOracle")

dump_deployments(deployments)

Expand Down
4 changes: 2 additions & 2 deletions kakarot_scripts/utils/kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from starknet_py.net.signer.stark_curve_signer import KeyPair
from starkware.starknet.public.abi import starknet_keccak
from web3 import Web3
from web3._utils.abi import map_abi_data
from web3._utils.abi import get_abi_output_types, map_abi_data
from web3._utils.events import get_event_data
from web3._utils.normalizers import BASE_RETURN_NORMALIZERS
from web3.contract import Contract as Web3Contract
Expand Down Expand Up @@ -288,7 +288,7 @@ async def _wrapper(self, *args, **kwargs):
if result.success == 0:
raise EvmTransactionError(bytes(result.return_data))
result = result.return_data
types = [o["type"] for o in abi["outputs"]]
types = get_abi_output_types(abi)
decoded = decode(types, bytes(result))
normalized = map_abi_data(BASE_RETURN_NORMALIZERS, types, decoded)
return normalized[0] if len(normalized) == 1 else normalized
Expand Down
24 changes: 12 additions & 12 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ case-converter = "^1.1.0"
requests = "^2.28.2"
codespell = "^2.2.2"
eth-account = "^0.8.0"
eth-abi = "^4.0.0"
eth-abi = "^5.1.0"
eth-utils = "^2.1.0"
eth-keys = "^0.4.0"
ipykernel = "^6.23.1"
Expand Down Expand Up @@ -153,6 +153,7 @@ markers = [
"UniswapV2Factory",
"EIP3074",
"RIP7212",
"CairoPrecompiles",
"UniswapV2Router",
"AccountContract",
"Utils",
Expand Down
Loading

0 comments on commit fe9acd9

Please sign in to comment.