diff --git a/.github/workflows/ci-starknet-contract.yml b/.github/workflows/ci-starknet-contract.yml index 3debc96ed8..1070997281 100644 --- a/.github/workflows/ci-starknet-contract.yml +++ b/.github/workflows/ci-starknet-contract.yml @@ -25,8 +25,8 @@ jobs: tool-versions: target_chains/starknet/contracts/.tool-versions - name: Install Starkli run: curl https://get.starkli.sh | sh && . ~/.config/.starkli/env && starkliup -v $(awk '/starkli/{print $2}' .tool-versions) - - name: Install Katana - run: curl -L https://install.dojoengine.org | bash && PATH="$PATH:$HOME/.config/.dojo/bin" dojoup -v $(awk '/dojo/{print $2}' .tool-versions) + - name: Install Devnet + run: cargo install starknet-devnet --version $(awk '/starknet-devnet/{print $2}' .tool-versions) - name: Check formatting run: scarb fmt --check - name: Run tests @@ -36,7 +36,7 @@ jobs: - name: Check ABI files run: | cat target/dev/pyth_pyth.contract_class.json | jq .abi > /tmp/pyth.json - cat target/dev/pyth_ERC20.contract_class.json | jq .abi > /tmp/erc20.json + cat target/dev/pyth_ERC20Upgradeable.contract_class.json | jq .abi > /tmp/erc20.json if ! cmp --silent /tmp/pyth.json ../sdk/js/src/abi/pyth.json ; then >&2 echo ABI file mismatch for sdk/js/src/abi/pyth.json exit 1 @@ -46,7 +46,7 @@ jobs: exit 1 fi - name: Test local deployment script - run: bash -c 'PATH="$PATH:$HOME/.config/.dojo/bin" katana & . ~/.config/.starkli/env && deploy/deploy.sh' + run: bash -c 'starknet-devnet --seed 0 --account-class cairo1 & . ~/.config/.starkli/env && deploy/deploy.sh' - name: Verify version run: | version1=$(grep version Scarb.toml | cut -d '"' -f 2) diff --git a/.github/workflows/ci-starknet-tools.yml b/.github/workflows/ci-starknet-tools.yml index f1d9f95976..be0b0566fe 100644 --- a/.github/workflows/ci-starknet-tools.yml +++ b/.github/workflows/ci-starknet-tools.yml @@ -14,7 +14,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.78.0 + toolchain: 1.86.0 components: rustfmt, clippy override: true - uses: actions/checkout@v3 @@ -35,8 +35,10 @@ jobs: run: cargo run --manifest-path ./target_chains/starknet/tools/test_vaas/Cargo.toml --bin generate_keypair - name: Check test data run: | - . ~/.config/.starkli/env && cargo run --manifest-path ./target_chains/starknet/tools/test_vaas/Cargo.toml --bin generate_test_data > /tmp/data.cairo - if ! diff ./target_chains/starknet/contracts/tests/data.cairo /tmp/data.cairo; then + . ~/.config/.starkli/env && cargo run --manifest-path ./target_chains/starknet/tools/test_vaas/Cargo.toml --bin generate_test_data > ./target_chains/starknet/contracts/data.cairo + cd target_chains/starknet/contracts && scarb fmt data.cairo + if ! diff ./tests/data.cairo data.cairo; then >&2 echo "Re-run generate_test_data to update data.cairo" exit 1 fi + rm data.cairo diff --git a/target_chains/starknet/contracts/.tool-versions b/target_chains/starknet/contracts/.tool-versions index a311602a5c..c57b6d8470 100644 --- a/target_chains/starknet/contracts/.tool-versions +++ b/target_chains/starknet/contracts/.tool-versions @@ -1,4 +1,5 @@ -scarb 2.5.4 -starknet-foundry 0.21.0 -dojo 0.6.0 -starkli 0.2.8 +dojo 1.4.0 +starkli 0.4.0 +scarb 2.11.1 +starknet-foundry 0.38.3 +starknet-devnet 0.4.0 diff --git a/target_chains/starknet/contracts/README.md b/target_chains/starknet/contracts/README.md index cb42fc3d48..4fa6da34ea 100644 --- a/target_chains/starknet/contracts/README.md +++ b/target_chains/starknet/contracts/README.md @@ -28,7 +28,7 @@ Run `scarb fmt` to automatically format the source code. ## Local deployment 1. Install Starkli (a cli tool for Starknet) by following [the installation instructions](https://github.com/xJonathanLEI/starkli). -2. Install Katana (a local Starknet node) by following [the installation instructions](https://book.starknet.io/ch02-04-katana.html). +2. Install Devnet (a local Starknet node) by following [the installation instructions](https://0xspaceshard.github.io/starknet-devnet/). 3. Run the deployment script: ```bash diff --git a/target_chains/starknet/contracts/Scarb.lock b/target_chains/starknet/contracts/Scarb.lock index 0ba4cea8b5..4ff39691d5 100644 --- a/target_chains/starknet/contracts/Scarb.lock +++ b/target_chains/starknet/contracts/Scarb.lock @@ -3,8 +3,122 @@ version = 1 [[package]] name = "openzeppelin" -version = "0.10.0" -source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.10.0#d77082732daab2690ba50742ea41080eb23299d3" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:320185f3e17cf9fafda88b1ce490f5eaed0bfcc273036b56cd22ce4fb8de628f" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a39a4ea1582916c637bf7e3aee0832c3fe1ea3a3e39191955e8dc39d08327f9b" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:7e943a2de32ddca4d48e467e52790e380ab1f49c4daddbbbc4634dd930d0243f" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:9fa9e91d39b6ccdfa31eef32fdc087cd06c0269cc9c6b86e32d57f5a6997d98b" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:c05add2974b3193c3a5c022b9586a84cf98c5970cdb884dcf201c77dbe359f55" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_introspection" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:34e088ecf19e0b3012481a29f1fbb20e600540cb9a5db1c3002a97ebb7f5a32a" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a5341705514a3d9beeeb39cf11464111f7355be621639740d2c5006786aa63dc" + +[[package]] +name = "openzeppelin_presets" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:4eb098e2ee3ac0e67b6828115a7de62f781418beab767d4e80b54e176808369d" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_security" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:1deb811a239c4f9cc28fc302039e2ffcb19911698a8c612487207448d70d2e6e" + +[[package]] +name = "openzeppelin_token" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:33fcb84a1a76d2d3fff9302094ff564f78d45b743548fd7568c130b272473f66" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:36f7a03e7e7111577916aacf31f88ad0053de20f33ee10b0ab3804849c3aa373" + +[[package]] +name = "openzeppelin_utils" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:fd348b31c4a4407add33adc3c2b8f26dca71dbd7431faaf726168f37a91db0c1" [[package]] name = "pyth" @@ -14,7 +128,17 @@ dependencies = [ "snforge_std", ] +[[package]] +name = "snforge_scarb_plugin" +version = "0.38.3" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:0cd914b547acd96b4cad99a78e95c0eda001d0c280da4969b2161e286079cf46" + [[package]] name = "snforge_std" -version = "0.21.0" -source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.21.0#2996b8c1dd66b2715fc67e69578089f278a46790" +version = "0.38.3" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:d376526fbbe22535ad89ed630b11d6e209f22c50168de6c6430c0591c81c3174" +dependencies = [ + "snforge_scarb_plugin", +] diff --git a/target_chains/starknet/contracts/Scarb.toml b/target_chains/starknet/contracts/Scarb.toml index 3e8becfae5..4ae8d83b8b 100644 --- a/target_chains/starknet/contracts/Scarb.toml +++ b/target_chains/starknet/contracts/Scarb.toml @@ -1,14 +1,16 @@ [package] name = "pyth" version = "0.1.0" -edition = "2023_11" +edition = "2024_07" [dependencies] -starknet = ">=2.5.4" -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.10.0" } -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.21.0" } +starknet = "2.11.1" +openzeppelin = "1.0.0" + +[dev-dependencies] +snforge_std = "0.38.3" [lib] [[target.starknet-contract]] -build-external-contracts = ["openzeppelin::presets::erc20::ERC20"] +build-external-contracts = ["openzeppelin::presets::erc20::ERC20Upgradeable"] diff --git a/target_chains/starknet/contracts/deploy/deploy.sh b/target_chains/starknet/contracts/deploy/deploy.sh index c79f6547ca..f698ea607c 100755 --- a/target_chains/starknet/contracts/deploy/deploy.sh +++ b/target_chains/starknet/contracts/deploy/deploy.sh @@ -3,7 +3,7 @@ # Deploys Wormhole and Pyth contracts. # # Variables: -# PYTH_DEPLOY_MODE - "katana" or "sepolia" (default: "katana") +# PYTH_DEPLOY_MODE - "devnet" or "sepolia" (default: "devnet") # PYTH_WORMHOLE_ADDRESS - if set, the script will use it instead of deploying a new Wormhole contract. # When using sepolia, set STARKNET_ACCOUNT and STARKNET_KEYSTORE as well. @@ -13,19 +13,20 @@ set -o pipefail set -x if [ -z ${PYTH_DEPLOY_MODE+x} ]; then - PYTH_DEPLOY_MODE=katana + PYTH_DEPLOY_MODE=devnet fi -if [ "${PYTH_DEPLOY_MODE}" == "katana" ]; then - export STARKNET_ACCOUNT=katana-0 +if [ "${PYTH_DEPLOY_MODE}" == "devnet" ]; then export STARKNET_RPC=http://0.0.0.0:5050 + export STARKNET_ACCOUNT=0x064b48806902a367c8598f4f95c305e8c1a1acba5f082d294a43793113115691 + export STARKNET_ACCOUNT_PK=0x0000000000000000000000000000000071d7bb07b9a64f6f78ac4c816aff4da9 chain_id=50075 # starknet_sepolia - # predeployed fee token contract in katana + # predeployed fee token contract in devnet fee_token_address1=0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 - # there is no second fee token pre-deployed in katana + # there is no second fee token pre-deployed in devnet fee_token_address2=0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 elif [ "${PYTH_DEPLOY_MODE}" == "sepolia" ]; then export STARKNET_RPC=https://starknet-sepolia.public.blastapi.io/rpc/v0_6 @@ -59,12 +60,13 @@ cd "$(dirname "$0")/.." scarb build 1>&2 if [ -z ${PYTH_WORMHOLE_ADDRESS+x} ]; then - wormhole_hash=$(starkli declare --watch target/dev/pyth_wormhole.contract_class.json) + starkli account fetch ${STARKNET_ACCOUNT} --output=account.json --rpc=${STARKNET_RPC} + wormhole_hash=$(starkli declare --watch target/dev/pyth_wormhole.contract_class.json --account ./account.json --private-key ${STARKNET_ACCOUNT_PK}) # deploying wormhole with mainnet guardians # guardian set #4 - PYTH_WORMHOLE_ADDRESS=$(starkli deploy --not-unique --salt 0 --watch "${wormhole_hash}" \ + PYTH_WORMHOLE_ADDRESS=$(starkli deploy --not-unique --salt 0 --watch "${wormhole_hash}" --account ./account.json --private-key ${STARKNET_ACCOUNT_PK} \ 4 `# guardian_set_index` \ 19 `# num_guardians` \ 0x5893B5A76c3f739645648885bDCcC06cd70a3Cd3 \ @@ -97,7 +99,7 @@ if [ -z ${PYTH_WORMHOLE_ADDRESS+x} ]; then fi echo "PYTH_WORMHOLE_ADDRESS=${PYTH_WORMHOLE_ADDRESS}" -pyth_hash=$(starkli declare --watch target/dev/pyth_pyth.contract_class.json) +pyth_hash=$(starkli declare --watch target/dev/pyth_pyth.contract_class.json --account ./account.json --private-key ${STARKNET_ACCOUNT_PK}) emitter_chain_id1=26 emitter_address1=0xf8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0 @@ -108,7 +110,7 @@ emitter_address2=0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4a governance_emitter_chain_id=1 governance_emitter_address=0x5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e -pyth_address=$(starkli deploy --not-unique --salt 0 --watch "${pyth_hash}" \ +pyth_address=$(starkli deploy --not-unique --salt 0 --watch "${pyth_hash}" --account ./account.json --private-key ${STARKNET_ACCOUNT_PK} \ "${PYTH_WORMHOLE_ADDRESS}" \ "${fee_token_address1}" \ 1 0 `# fee_amount1` \ @@ -122,9 +124,9 @@ pyth_address=$(starkli deploy --not-unique --salt 0 --watch "${pyth_hash}" \ 0 `# governance_initial_sequence` \ ) -starkli invoke --watch "${fee_token_address1}" approve "${pyth_address}" 1000 0 +starkli invoke --watch "${fee_token_address1}" approve "${pyth_address}" 1000 0 --account ./account.json --private-key ${STARKNET_ACCOUNT_PK} -starkli invoke --watch "${pyth_address}" update_price_feeds \ +starkli invoke --watch "${pyth_address}" --account ./account.json --private-key ${STARKNET_ACCOUNT_PK} update_price_feeds \ 11 41 141887862745809943100717722154781668656425228150258363002663887732857548075 399793171101922163607717906910020156439802651815166374105600343045575931912 205983572864866548810075966151139050810706099666354694408986588005072300221 151451952208610765038741735376830560508647207417250420083288609153397964481 86500771940909656434129966404881206990783089169853273096126376095161148476 226128071698991949569342896653857259217290864736270016974365368327197190188 377698250859392108521341636250067678937984182266455992791761951028534274645 359481881021010868573625646624159016709204941239347558851817240293252854322 269752247307988210724584131415546296182395279893478036136383326770680756016 1795390197095010264738527441013169771569683827600670029637766897428840143 234116006474879126519208934707397575502368608154160721412947025189419787194 189800847222104556167598630039931285094024694085247523307780296439180585091 286206863474379560841614954761399331434812535348350225390274576176798886031 380778504466325787198909189418135115031120011427014465236265515817642556890 128785010970678423864351132498736713626005612397319240493515416417380099413 395419216432871057204438489759682910781574046646010114747283889104834443397 184981610545658962928833309057692452941395349433458372962283948260273947156 110573687320157468197346423602708230855113764048934963897254568602798981891 359831064918860887692030922920274851680298668214543004760245859301314852951 430048018037020109398934292236837834709370591653776097569938580165539734124 265079002668517523371293797450754079826401787503533883360533359118093613709 118956066417175616647948432812222980193178970842860785889932661265944570805 289275771653255859826082430219295399339085718287922176066620100061685069367 236281080443323007784750945204995275799519083197981738780888611083509567478 251042542087561162756580709366349731114715604419679060279244203132266921212 98235342442817522140724982668491795525073680697047819016960109902179866805 88342865348230190810084665689239940103607621061956069700600977485132311440 362045407519743532711403801060857872682086780812134177115599591240431143367 16066483776176414842828409371714210177224680637354816226962534075790344474 247660009137502548346315865368477795392972486141407802997108167405894850048 3530678887550352072827758533436734366288448089041832078266099519 272101358674132653417860014541384836605087997364704101839695292681479883518 394112423559676785086059691419737479771752814065338155011845462169193807974 151755140354137577506498286435010205890750984061355535849635897370673003944 210196797635098012510106281616028853575843684847951745405842531072933610917 65848881303037889845233189630325925691014776183743685310081069912626992101 110542381473451658848567609117180625628841970992142907142739541724557861958 157546342890129829983246193527213822449181723932011157069167729323808635205 165998047372192053828934221954381957675361960853286466716634704795532379661 28583007876111384456149499846085318299326698960792831530075402396150538907 126290914008245563820443505 >&2 echo Pyth contract has been successfully deployed at "${pyth_address}" diff --git a/target_chains/starknet/contracts/src/byte_buffer.cairo b/target_chains/starknet/contracts/src/byte_buffer.cairo index e395dce3cb..319337b3f6 100644 --- a/target_chains/starknet/contracts/src/byte_buffer.cairo +++ b/target_chains/starknet/contracts/src/byte_buffer.cairo @@ -22,7 +22,7 @@ impl DebugByteBuffer of Debug { Option::Some(v) => { write!(f, "{:?}, ", v).unwrap(); }, Option::None => { break; }, } - }; + } write!(f, "]}}") } } @@ -45,7 +45,7 @@ pub impl ByteBufferImpl of ByteBufferTrait { let last: u256 = (*data.at(data.len() - 1)).into(); assert!( last / one_shift_left_bytes_u256(num_last_bytes) == 0, - "ByteBufferImpl::new: last value is too large" + "ByteBufferImpl::new: last value is too large", ); } ByteBuffer { num_last_bytes, data } @@ -75,8 +75,7 @@ pub impl ByteBufferImpl of ByteBufferTrait { #[cfg(test)] mod tests { - use super::{ByteBuffer, ByteBufferImpl}; - use pyth::util::array_try_into; + use super::ByteBufferImpl; #[test] fn empty_byte_array() { @@ -132,7 +131,7 @@ mod tests { let value2_31_bytes = 0x2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f; let value3_5_bytes = 0x4142434445; let mut array = ByteBufferImpl::new( - array![value_31_bytes, value2_31_bytes, value3_5_bytes], 5 + array![value_31_bytes, value2_31_bytes, value3_5_bytes], 5, ); assert!(array.len() == 67); assert!(array.pop_front() == Option::Some((value_31_bytes, 31))); diff --git a/target_chains/starknet/contracts/src/hash.cairo b/target_chains/starknet/contracts/src/hash.cairo index 8178786c38..11ef457bb1 100644 --- a/target_chains/starknet/contracts/src/hash.cairo +++ b/target_chains/starknet/contracts/src/hash.cairo @@ -1,10 +1,10 @@ -use super::reader::{Reader, ReaderImpl}; use core::cmp::min; use core::integer::u128_byte_reverse; use pyth::util::{ - ONE_SHIFT_160, UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, ONE_SHIFT_64, one_shift_left_bytes_u64, + ONE_SHIFT_160, ONE_SHIFT_64, UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, one_shift_left_bytes_u64, u64_byte_reverse, }; +use super::reader::{Reader, ReaderImpl}; /// Allows to push data as big endian to a buffer and apply /// the keccak256 hash. @@ -77,9 +77,9 @@ pub impl HasherImpl of HasherTrait { u64_byte_reverse(self.last_be) / one_shift_left_bytes_u64(8 - self.num_last_bytes) }; let hash_le = core::keccak::cairo_keccak( - ref self.inputs_le, last_le, self.num_last_bytes.into() + ref self.inputs_le, last_le, self.num_last_bytes.into(), ); - u256 { low: u128_byte_reverse(hash_le.high), high: u128_byte_reverse(hash_le.low), } + u256 { low: u128_byte_reverse(hash_le.high), high: u128_byte_reverse(hash_le.low) } } } @@ -91,7 +91,7 @@ impl HasherPrivateImpl of HasherPrivateTrait { if num_bytes != 8 { assert!( value / one_shift_left_bytes_u64(num_bytes) == 0, - "Hasher::push_num_bytes: value is too large" + "Hasher::push_num_bytes: value is too large", ); } let num_high_bytes = min(num_bytes, 8 - self.num_last_bytes); @@ -107,14 +107,14 @@ impl HasherPrivateImpl of HasherPrivateTrait { if num_bytes != 8 { assert!( value / one_shift_left_bytes_u64(num_bytes) == 0, - "Hasher::push_to_last: value is too large" + "Hasher::push_to_last: value is too large", ); } if num_bytes == 8 { self.last_be = value; } else { self.last_be = self.last_be * one_shift_left_bytes_u64(num_bytes) + value; - }; + } self.num_last_bytes += num_bytes; if self.num_last_bytes == 8 { self.inputs_le.append(u64_byte_reverse(self.last_be)); diff --git a/target_chains/starknet/contracts/src/lib.cairo b/target_chains/starknet/contracts/src/lib.cairo index 15d45987a2..4591dcfc4d 100644 --- a/target_chains/starknet/contracts/src/lib.cairo +++ b/target_chains/starknet/contracts/src/lib.cairo @@ -1,16 +1,16 @@ -pub mod pyth; -pub mod wormhole; pub mod byte_buffer; -pub mod reader; pub mod hash; -pub mod util; pub mod merkle_tree; +pub mod pyth; +pub mod reader; +pub mod util; +pub mod wormhole; pub use byte_buffer::{ByteBuffer, ByteBufferTrait}; pub use pyth::{ - Event, PriceFeedUpdated, WormholeAddressSet, GovernanceDataSourceSet, ContractUpgraded, - DataSourcesSet, FeeSet, GetPriceUnsafeError, GovernanceActionError, UpdatePriceFeedsError, - GetPriceNoOlderThanError, UpdatePriceFeedsIfNecessaryError, ParsePriceFeedsError, IPyth, - IPythDispatcher, IPythDispatcherTrait, DataSource, Price, PriceFeedPublishTime, PriceFeed, + ContractUpgraded, DataSource, DataSourcesSet, Event, FeeSet, GetPriceNoOlderThanError, + GetPriceUnsafeError, GovernanceActionError, GovernanceDataSourceSet, IPyth, IPythDispatcher, + IPythDispatcherTrait, ParsePriceFeedsError, Price, PriceFeed, PriceFeedPublishTime, + PriceFeedUpdated, UpdatePriceFeedsError, UpdatePriceFeedsIfNecessaryError, WormholeAddressSet, }; -pub use util::{exp10, UnwrapWithFelt252, ResultMapErrInto}; +pub use util::{ResultMapErrInto, UnwrapWithFelt252, exp10}; diff --git a/target_chains/starknet/contracts/src/merkle_tree.cairo b/target_chains/starknet/contracts/src/merkle_tree.cairo index a20b308b54..d802d81e9a 100644 --- a/target_chains/starknet/contracts/src/merkle_tree.cairo +++ b/target_chains/starknet/contracts/src/merkle_tree.cairo @@ -1,9 +1,9 @@ -use super::hash::{Hasher, HasherImpl}; +use core::cmp::{max, min}; +use core::panic_with_felt252; +use super::hash::HasherImpl; use super::reader::{Reader, ReaderImpl}; use super::byte_buffer::ByteBuffer; use super::util::ONE_SHIFT_96; -use core::cmp::{min, max}; -use core::panic_with_felt252; const MERKLE_LEAF_PREFIX: u8 = 0; const MERKLE_NODE_PREFIX: u8 = 1; @@ -60,7 +60,7 @@ pub fn read_and_verify_proof(root_digest: u256, message: @ByteBuffer, ref reader let sibling_digest = reader.read_u160(); current_hash = node_hash(current_hash, sibling_digest); i += 1; - }; + } if root_digest != current_hash { panic_with_felt252(MerkleVerificationError::DigestMismatch.into()); diff --git a/target_chains/starknet/contracts/src/pyth.cairo b/target_chains/starknet/contracts/src/pyth.cairo index 4282e3e8a0..014587424d 100644 --- a/target_chains/starknet/contracts/src/pyth.cairo +++ b/target_chains/starknet/contracts/src/pyth.cairo @@ -1,51 +1,54 @@ mod errors; -mod interface; -mod price_update; -pub mod governance; mod fake_upgrades; - -pub use pyth::{ - Event, PriceFeedUpdated, WormholeAddressSet, GovernanceDataSourceSet, ContractUpgraded, - DataSourcesSet, FeeSet, -}; +pub mod governance; +mod interface; +mod price_update; pub use errors::{ - GetPriceUnsafeError, GovernanceActionError, UpdatePriceFeedsError, GetPriceNoOlderThanError, - UpdatePriceFeedsIfNecessaryError, ParsePriceFeedsError, GetSingleUpdateFeeError, + GetPriceNoOlderThanError, GetPriceUnsafeError, GetSingleUpdateFeeError, GovernanceActionError, + ParsePriceFeedsError, UpdatePriceFeedsError, UpdatePriceFeedsIfNecessaryError, }; pub use interface::{ - IPyth, IPythDispatcher, IPythDispatcherTrait, DataSource, GetDataSource, Price, - PriceFeedPublishTime, PriceFeed + DataSource, GetDataSource, IPyth, IPythDispatcher, IPythDispatcherTrait, Price, PriceFeed, + PriceFeedPublishTime, +}; + +pub use pyth::{ + ContractUpgraded, DataSourcesSet, Event, FeeSet, GovernanceDataSourceSet, PriceFeedUpdated, + WormholeAddressSet, }; #[starknet::contract] mod pyth { + use core::dict::Felt252Dict; + use core::fmt::{Debug, Formatter}; + use core::nullable::{FromNullableResult, NullableTrait, match_nullable}; + use core::panic_with_felt252; + use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; + use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl}; use pyth::pyth::interface::IPyth; - use super::price_update::{ - PriceInfo, PriceFeedMessage, read_and_verify_message, read_and_verify_header, - parse_wormhole_proof + use pyth::reader::ReaderImpl; + use pyth::util::{ResultMapErrInto, write_i64}; + use pyth::wormhole::{IWormholeDispatcher, IWormholeDispatcherTrait, VerifiedVM}; + use starknet::storage::{ + Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, }; - use pyth::reader::{Reader, ReaderImpl}; - use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl}; - use core::panic_with_felt252; - use core::starknet::{ - ContractAddress, get_caller_address, get_execution_info, ClassHash, SyscallResultTrait, - get_contract_address, get_block_timestamp, + use starknet::syscalls::replace_class_syscall; + use starknet::{ + ClassHash, ContractAddress, SyscallResultTrait, get_block_timestamp, get_contract_address, + get_execution_info, }; - use core::starknet::syscalls::replace_class_syscall; - use pyth::wormhole::{IWormholeDispatcher, IWormholeDispatcherTrait, VerifiedVM}; - use super::{ - DataSource, GetDataSource, UpdatePriceFeedsError, GovernanceActionError, Price, - GetPriceUnsafeError, IPythDispatcher, IPythDispatcherTrait, PriceFeedPublishTime, - GetPriceNoOlderThanError, UpdatePriceFeedsIfNecessaryError, PriceFeed, ParsePriceFeedsError, - GetSingleUpdateFeeError, + use super::price_update::{ + PriceFeedMessage, PriceInfo, parse_wormhole_proof, read_and_verify_header, + read_and_verify_message, }; - use super::governance; use super::governance::GovernancePayload; - use openzeppelin::token::erc20::interface::{IERC20CamelDispatcherTrait, IERC20CamelDispatcher}; - use pyth::util::{ResultMapErrInto, write_i64}; - use core::nullable::{NullableTrait, match_nullable, FromNullableResult}; - use core::fmt::{Debug, Formatter}; + use super::{ + DataSource, GetDataSource, GetPriceNoOlderThanError, GetPriceUnsafeError, + GetSingleUpdateFeeError, GovernanceActionError, IPythDispatcher, IPythDispatcherTrait, + ParsePriceFeedsError, Price, PriceFeed, PriceFeedPublishTime, UpdatePriceFeedsError, + UpdatePriceFeedsIfNecessaryError, governance, + }; #[event] #[derive(Drop, Clone, Debug, PartialEq, Serde, starknet::Event)] @@ -79,7 +82,7 @@ mod pyth { #[cfg(test)] #[test] fn test_debug_price_feed_updated() { - let value = PriceFeedUpdated { price_id: 1, price: 2, conf: 3, publish_time: 5, }; + let value = PriceFeedUpdated { price_id: 1, price: 2, conf: 3, publish_time: 5 }; let expected = "PriceFeedUpdated { price_id: 1, price: 2, conf: 3, publish_time: 5 }"; let actual = format!("{:?}", value); assert!(actual == expected); @@ -124,11 +127,11 @@ mod pyth { fee_token_address2: ContractAddress, single_update_fee1: u256, single_update_fee2: u256, - data_sources: LegacyMap, + data_sources: Map, num_data_sources: usize, // For fast validation. - is_valid_data_source: LegacyMap, - latest_price_info: LegacyMap, + is_valid_data_source: Map, + latest_price_info: Map, governance_data_source: DataSource, last_executed_governance_sequence: u64, // Governance data source index is used to prevent replay attacks, @@ -138,18 +141,20 @@ mod pyth { /// Initializes the Pyth contract. /// - /// `wormhole_address` is the address of the deployed Wormhole contract implemented in the `wormhole` module. + /// `wormhole_address` is the address of the deployed Wormhole contract implemented in the + /// `wormhole` module. /// /// `fee_token_address1` is the address of the ERC20 token used to pay fees to Pyth /// for price updates. There is no native token on Starknet so an ERC20 contract has to be used. - /// On Katana, an ETH fee contract is pre-deployed. On Starknet testnet, ETH and STRK fee tokens are - /// available. Any other ERC20-compatible token can also be used. + /// On Devnet, an ETH fee contract is pre-deployed. On Starknet testnet, ETH and STRK fee tokens + /// are available. Any other ERC20-compatible token can also be used. /// In a Starknet Forge testing environment, a fee contract must be deployed manually. /// - /// `single_update_fee1` is the number of tokens of `fee_token_address1` charged for a single price update. + /// `single_update_fee1` is the number of tokens of `fee_token_address1` charged for a single + /// price update. /// - /// `fee_token_address2` and `single_update_fee2` specify the secondary fee contract and fee rate - /// that can be used instead of the main fee token. + /// `fee_token_address2` and `single_update_fee2` specify the secondary fee contract and fee + /// rate that can be used instead of the main fee token. /// /// `data_sources` is the list of Wormhole data sources accepted by this contract. #[constructor] @@ -177,7 +182,7 @@ mod pyth { DataSource { emitter_chain_id: governance_emitter_chain_id, emitter_address: governance_emitter_address, - } + }, ); self.last_executed_governance_sequence.write(governance_initial_sequence); } @@ -185,7 +190,7 @@ mod pyth { #[abi(embed_v0)] impl PythImpl of super::IPyth { fn get_price_no_older_than( - self: @ContractState, price_id: u256, age: u64 + self: @ContractState, price_id: u256, age: u64, ) -> Result { let info = self.get_price_unsafe(price_id).map_err_into()?; if !is_no_older_than(info.publish_time, age) { @@ -195,9 +200,9 @@ mod pyth { } fn get_price_unsafe( - self: @ContractState, price_id: u256 + self: @ContractState, price_id: u256, ) -> Result { - let info = self.latest_price_info.read(price_id); + let info = self.latest_price_info.entry(price_id).read(); if info.publish_time == 0 { return Result::Err(GetPriceUnsafeError::PriceFeedNotFound); } @@ -211,7 +216,7 @@ mod pyth { } fn get_ema_price_no_older_than( - self: @ContractState, price_id: u256, age: u64 + self: @ContractState, price_id: u256, age: u64, ) -> Result { let info = self.get_ema_price_unsafe(price_id).map_err_into()?; if !is_no_older_than(info.publish_time, age) { @@ -221,9 +226,9 @@ mod pyth { } fn get_ema_price_unsafe( - self: @ContractState, price_id: u256 + self: @ContractState, price_id: u256, ) -> Result { - let info = self.latest_price_info.read(price_id); + let info = self.latest_price_info.entry(price_id).read(); if info.publish_time == 0 { return Result::Err(GetPriceUnsafeError::PriceFeedNotFound); } @@ -237,7 +242,7 @@ mod pyth { } fn query_price_feed_no_older_than( - self: @ContractState, price_id: u256, age: u64 + self: @ContractState, price_id: u256, age: u64, ) -> Result { let feed = self.query_price_feed_unsafe(price_id).map_err_into()?; if !is_no_older_than(feed.price.publish_time, age) { @@ -247,9 +252,9 @@ mod pyth { } fn query_price_feed_unsafe( - self: @ContractState, price_id: u256 + self: @ContractState, price_id: u256, ) -> Result { - let info = self.latest_price_info.read(price_id); + let info = self.latest_price_info.entry(price_id).read(); if info.publish_time == 0 { return Result::Err(GetPriceUnsafeError::PriceFeedNotFound); } @@ -272,12 +277,12 @@ mod pyth { } fn price_feed_exists(self: @ContractState, price_id: u256) -> bool { - let info = self.latest_price_info.read(price_id); + let info = self.latest_price_info.entry(price_id).read(); info.publish_time != 0 } fn latest_price_info_publish_time(self: @ContractState, price_id: u256) -> u64 { - let info = self.latest_price_info.read(price_id); + let info = self.latest_price_info.entry(price_id).read(); info.publish_time } @@ -305,20 +310,20 @@ mod pyth { fn update_price_feeds_if_necessary( ref self: ContractState, update: ByteBuffer, - required_publish_times: Array + required_publish_times: Array, ) { let mut i = 0; let mut found = false; while i < required_publish_times.len() { let item = required_publish_times.at(i); - let latest_time = self.latest_price_info.read(*item.price_id).publish_time; + let latest_time = self.latest_price_info.entry(*item.price_id).read().publish_time; if latest_time < *item.publish_time { self.update_price_feeds(update); found = true; break; } i += 1; - }; + } if !found { panic_with_felt252(UpdatePriceFeedsIfNecessaryError::NoFreshUpdate.into()); } @@ -329,11 +334,11 @@ mod pyth { data: ByteBuffer, price_ids: Array, min_publish_time: u64, - max_publish_time: u64 + max_publish_time: u64, ) -> Array { self .update_price_feeds_internal( - data, price_ids, min_publish_time, max_publish_time, false + data, price_ids, min_publish_time, max_publish_time, false, ) } @@ -346,7 +351,7 @@ mod pyth { ) -> Array { self .update_price_feeds_internal( - data, price_ids, publish_time, publish_time + max_staleness, true + data, price_ids, publish_time, publish_time + max_staleness, true, ) } @@ -373,14 +378,14 @@ mod pyth { let mut i = 0; let mut output = array![]; while i < count { - output.append(self.data_sources.read(i)); + output.append(self.data_sources.entry(i).read()); i += 1; - }; + } output } fn is_valid_data_source(self: @ContractState, source: DataSource) -> bool { - self.is_valid_data_source.read(source) + self.is_valid_data_source.entry(source).read() } fn governance_data_source(self: @ContractState) -> DataSource { @@ -450,7 +455,7 @@ mod pyth { panic_with_felt252(GovernanceActionError::InvalidGovernanceTarget.into()); } self.upgrade_contract(payload.new_implementation); - } + }, } } @@ -466,32 +471,36 @@ mod pyth { #[generate_trait] impl PrivateImpl of PrivateTrait { fn write_data_sources( - ref self: ContractState, data_sources: @Array + ref self: ContractState, data_sources: @Array, ) -> Array { let num_old = self.num_data_sources.read(); let mut i = 0; let mut old_data_sources = array![]; while i < num_old { - let old_source = self.data_sources.read(i); + let old_source = self.data_sources.entry(i).read(); old_data_sources.append(old_source); - self.is_valid_data_source.write(old_source, false); - self.data_sources.write(i, Default::default()); + self.is_valid_data_source.entry(old_source).write(false); + self.data_sources.entry(i).write(Default::default()); i += 1; - }; + } self.num_data_sources.write(data_sources.len()); i = 0; while i < data_sources.len() { let source = data_sources.at(i); - self.is_valid_data_source.write(*source, true); - self.data_sources.write(i, *source); + self.is_valid_data_source.entry(*source).write(true); + self.data_sources.entry(i).write(*source); i += 1; - }; + } old_data_sources } fn update_latest_price_if_necessary(ref self: ContractState, message: @PriceFeedMessage) { - let latest_publish_time = self.latest_price_info.read(*message.price_id).publish_time; + let latest_publish_time = self + .latest_price_info + .entry(*message.price_id) + .read() + .publish_time; if *message.publish_time > latest_publish_time { let info = PriceInfo { price: *message.price, @@ -501,7 +510,7 @@ mod pyth { ema_price: *message.ema_price, ema_conf: *message.ema_conf, }; - self.latest_price_info.write(*message.price_id, info); + self.latest_price_info.entry(*message.price_id).write(info); let event = PriceFeedUpdated { price_id: *message.price_id, @@ -530,7 +539,7 @@ mod pyth { } fn check_new_wormhole( - ref self: ContractState, wormhole_address: ContractAddress, vm: ByteBuffer + ref self: ContractState, wormhole_address: ContractAddress, vm: ByteBuffer, ) { let wormhole = IWormholeDispatcher { contract_address: wormhole_address }; let vm = wormhole.parse_and_verify_vm(vm); @@ -546,22 +555,22 @@ mod pyth { if vm.sequence != self.last_executed_governance_sequence.read() { panic_with_felt252(GovernanceActionError::InvalidWormholeAddressToSet.into()); } - // Purposefully, we don't check whether the chainId is the same as the current chainId because - // we might want to change the chain id of the wormhole contract. + // Purposefully, we don't check whether the chainId is the same as the current chainId + // because we might want to change the chain id of the wormhole contract. let data = governance::parse_instruction(vm.payload); match data.payload { GovernancePayload::SetWormholeAddress(payload) => { - // The following check is not necessary for security, but is a sanity check that the new wormhole - // contract parses the payload correctly. + // The following check is not necessary for security, but is a sanity check that + // the new wormhole contract parses the payload correctly. if payload.address != wormhole_address { panic_with_felt252( - GovernanceActionError::InvalidWormholeAddressToSet.into() + GovernanceActionError::InvalidWormholeAddressToSet.into(), ); } }, _ => { panic_with_felt252(GovernanceActionError::InvalidWormholeAddressToSet.into()); - } + }, } } @@ -576,7 +585,7 @@ mod pyth { } let request_payload = match instruction.payload { GovernancePayload::RequestGovernanceDataSourceTransfer(payload) => payload, - _ => { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()) } + _ => { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()) }, }; // Governance data source index is used to prevent replay attacks, // so a claimVaa cannot be used twice. @@ -639,7 +648,7 @@ mod pyth { let wormhole = IWormholeDispatcher { contract_address: self.wormhole_address.read() }; let vm = wormhole.parse_and_verify_vm(wormhole_proof); - if !self.is_valid_data_source.read(vm.data_source()) { + if !self.is_valid_data_source.entry(vm.data_source()).read() { panic_with_felt252(UpdatePriceFeedsError::InvalidUpdateDataSource.into()); } @@ -685,16 +694,16 @@ mod pyth { if should_output { output .insert( - output_index.into(), NullableTrait::new(message.into()) + output_index.into(), NullableTrait::new(message.into()), ); } } }, - Option::None => {} + Option::None => {}, } i += 1; - }; + } if reader.len() != 0 { panic_with_felt252(UpdatePriceFeedsError::InvalidUpdateData.into()); @@ -707,13 +716,13 @@ mod pyth { match match_nullable(value) { FromNullableResult::Null => { panic_with_felt252( - ParsePriceFeedsError::PriceFeedNotFoundWithinRange.into() + ParsePriceFeedsError::PriceFeedNotFoundWithinRange.into(), ) }, - FromNullableResult::NotNull(value) => { output_array.append(value.unbox()); } + FromNullableResult::NotNull(value) => { output_array.append(value.unbox()); }, } i += 1; - }; + } output_array } @@ -741,7 +750,7 @@ mod pyth { while i < expo { output *= 10; i += 1; - }; + } output } @@ -762,7 +771,7 @@ mod pyth { break; } i += 1; - }; + } if i == ids.len() { Option::None } else { diff --git a/target_chains/starknet/contracts/src/pyth/errors.cairo b/target_chains/starknet/contracts/src/pyth/errors.cairo index d3035f6b5f..bde7546806 100644 --- a/target_chains/starknet/contracts/src/pyth/errors.cairo +++ b/target_chains/starknet/contracts/src/pyth/errors.cairo @@ -27,7 +27,7 @@ impl GetPriceNoOlderThanErrorIntoFelt252 of Into { fn into(self: GetPriceUnsafeError) -> GetPriceNoOlderThanError { match self { @@ -87,7 +87,7 @@ pub enum UpdatePriceFeedsIfNecessaryError { } impl UpdatePriceFeedsIfNecessaryErrorIntoFelt252 of Into< - UpdatePriceFeedsIfNecessaryError, felt252 + UpdatePriceFeedsIfNecessaryError, felt252, > { fn into(self: UpdatePriceFeedsIfNecessaryError) -> felt252 { match self { diff --git a/target_chains/starknet/contracts/src/pyth/fake_upgrades.cairo b/target_chains/starknet/contracts/src/pyth/fake_upgrades.cairo index e81a646384..7ba1c7f50f 100644 --- a/target_chains/starknet/contracts/src/pyth/fake_upgrades.cairo +++ b/target_chains/starknet/contracts/src/pyth/fake_upgrades.cairo @@ -1,4 +1,4 @@ -use pyth::pyth::{GetPriceUnsafeError, DataSource, Price}; +use pyth::pyth::{GetPriceUnsafeError, Price}; // Only used for tests. @@ -10,8 +10,7 @@ pub trait IFakePyth { #[starknet::contract] mod pyth_fake_upgrade1 { - use pyth::pyth::{GetPriceUnsafeError, DataSource, Price}; - use pyth::byte_buffer::ByteBuffer; + use pyth::pyth::{GetPriceUnsafeError, Price}; #[storage] struct Storage {} @@ -22,9 +21,9 @@ mod pyth_fake_upgrade1 { #[abi(embed_v0)] impl PythImpl of super::IFakePyth { fn get_price_unsafe( - self: @ContractState, price_id: u256 + self: @ContractState, price_id: u256, ) -> Result { - let price = Price { price: 42, conf: 2, expo: -5, publish_time: 101, }; + let price = Price { price: 42, conf: 2, expo: -5, publish_time: 101 }; Result::Ok(price) } fn pyth_upgradable_magic(self: @ContractState) -> u32 { @@ -35,8 +34,7 @@ mod pyth_fake_upgrade1 { #[starknet::contract] mod pyth_fake_upgrade_wrong_magic { - use pyth::pyth::{GetPriceUnsafeError, DataSource, Price}; - use pyth::byte_buffer::ByteBuffer; + use pyth::pyth::{GetPriceUnsafeError, Price}; #[storage] struct Storage {} @@ -47,7 +45,7 @@ mod pyth_fake_upgrade_wrong_magic { #[abi(embed_v0)] impl PythImpl of super::IFakePyth { fn get_price_unsafe( - self: @ContractState, price_id: u256 + self: @ContractState, price_id: u256, ) -> Result { panic!("unsupported") } diff --git a/target_chains/starknet/contracts/src/pyth/governance.cairo b/target_chains/starknet/contracts/src/pyth/governance.cairo index 6d89775d1d..6effc50b74 100644 --- a/target_chains/starknet/contracts/src/pyth/governance.cairo +++ b/target_chains/starknet/contracts/src/pyth/governance.cairo @@ -1,10 +1,9 @@ -use pyth::reader::ReaderTrait; use core::array::ArrayTrait; -use pyth::reader::{Reader, ReaderImpl}; +use core::panic_with_felt252; use pyth::byte_buffer::ByteBuffer; use pyth::pyth::errors::GovernanceActionError; -use core::panic_with_felt252; -use core::starknet::{ContractAddress, ClassHash}; +use pyth::reader::{ReaderImpl, ReaderTrait}; +use starknet::{ClassHash, ContractAddress}; use super::DataSource; const MAGIC: u32 = 0x5054474d; @@ -33,7 +32,7 @@ impl U8TryIntoGovernanceAction of TryInto { 5 => GovernanceAction::RequestGovernanceDataSourceTransfer, 6 => GovernanceAction::SetWormholeAddress, 7 => GovernanceAction::SetFeeInToken, - _ => { return Option::None; } + _ => { return Option::None; }, }; Option::Some(v) } @@ -91,14 +90,15 @@ pub struct RequestGovernanceDataSourceTransfer { pub struct AuthorizeGovernanceDataSourceTransfer { // Transfer governance control over this contract to another data source. // The claim_vaa field is a VAA created by the new data source; using a VAA prevents mistakes - // in the handoff by ensuring that the new data source can send VAAs (i.e., is not an invalid address). + // in the handoff by ensuring that the new data source can send VAAs (i.e., is not an invalid + // address). pub claim_vaa: ByteBuffer, } #[derive(Drop, Clone, Debug, PartialEq, Serde)] pub struct UpgradeContract { - // Class hash of the new contract class. The contract class must already be deployed on the network - // (e.g. with `starkli declare`). Class hash is a Poseidon hash of all properties + // Class hash of the new contract class. The contract class must already be deployed on the + // network (e.g. with `starkli declare`). Class hash is a Poseidon hash of all properties // of the contract code, including entry points, ABI, and bytecode, // so specifying a hash securely identifies the new implementation. pub new_implementation: ClassHash, @@ -139,13 +139,13 @@ pub fn parse_instruction(payload: ByteBuffer) -> GovernanceInstruction { let len = reader.len(); let claim_vaa = reader.read_byte_array(len); GovernancePayload::AuthorizeGovernanceDataSourceTransfer( - AuthorizeGovernanceDataSourceTransfer { claim_vaa } + AuthorizeGovernanceDataSourceTransfer { claim_vaa }, ) }, GovernanceAction::RequestGovernanceDataSourceTransfer => { let governance_data_source_index = reader.read_u32(); GovernancePayload::RequestGovernanceDataSourceTransfer( - RequestGovernanceDataSourceTransfer { governance_data_source_index } + RequestGovernanceDataSourceTransfer { governance_data_source_index }, ) }, GovernanceAction::SetDataSources => { @@ -157,7 +157,7 @@ pub fn parse_instruction(payload: ByteBuffer) -> GovernanceInstruction { let emitter_address = reader.read_u256(); sources.append(DataSource { emitter_chain_id, emitter_address }); i += 1; - }; + } GovernancePayload::SetDataSources(SetDataSources { sources }) }, GovernanceAction::SetFee => { @@ -197,5 +197,5 @@ pub fn parse_instruction(payload: ByteBuffer) -> GovernanceInstruction { if reader.len() != 0 { panic_with_felt252(GovernanceActionError::InvalidGovernanceMessage.into()); } - GovernanceInstruction { target_chain_id, payload, } + GovernanceInstruction { target_chain_id, payload } } diff --git a/target_chains/starknet/contracts/src/pyth/interface.cairo b/target_chains/starknet/contracts/src/pyth/interface.cairo index 56b37f4fa1..f8cfbfd703 100644 --- a/target_chains/starknet/contracts/src/pyth/interface.cairo +++ b/target_chains/starknet/contracts/src/pyth/interface.cairo @@ -1,22 +1,22 @@ -use super::{GetPriceUnsafeError, GetPriceNoOlderThanError}; -use pyth::byte_buffer::ByteBuffer; -use pyth::wormhole::VerifiedVM; -use core::starknet::ContractAddress; use core::fmt::{Debug, Formatter}; +use pyth::byte_buffer::ByteBuffer; use pyth::util::write_i64; +use pyth::wormhole::VerifiedVM; +use starknet::ContractAddress; +use super::{GetPriceNoOlderThanError, GetPriceUnsafeError}; #[starknet::interface] pub trait IPyth { fn get_price_no_older_than( - self: @T, price_id: u256, age: u64 + self: @T, price_id: u256, age: u64, ) -> Result; fn get_price_unsafe(self: @T, price_id: u256) -> Result; fn get_ema_price_no_older_than( - self: @T, price_id: u256, age: u64 + self: @T, price_id: u256, age: u64, ) -> Result; fn get_ema_price_unsafe(self: @T, price_id: u256) -> Result; fn query_price_feed_no_older_than( - self: @T, price_id: u256, age: u64 + self: @T, price_id: u256, age: u64, ) -> Result; fn query_price_feed_unsafe(self: @T, price_id: u256) -> Result; fn price_feed_exists(self: @T, price_id: u256) -> bool; @@ -24,14 +24,14 @@ pub trait IPyth { fn update_price_feeds(ref self: T, data: ByteBuffer); fn update_price_feeds_if_necessary( - ref self: T, update: ByteBuffer, required_publish_times: Array + ref self: T, update: ByteBuffer, required_publish_times: Array, ); fn parse_price_feed_updates( ref self: T, data: ByteBuffer, price_ids: Array, min_publish_time: u64, - max_publish_time: u64 + max_publish_time: u64, ) -> Array; fn parse_unique_price_feed_updates( ref self: T, @@ -70,7 +70,7 @@ pub trait GetDataSource { impl GetDataSourceFromVerifiedVM of GetDataSource { fn data_source(self: @VerifiedVM) -> DataSource { DataSource { - emitter_chain_id: *self.emitter_chain_id, emitter_address: *self.emitter_address + emitter_chain_id: *self.emitter_chain_id, emitter_address: *self.emitter_address, } } } @@ -97,7 +97,7 @@ impl DebugPrice of Debug { #[cfg(test)] #[test] fn test_debug_price() { - let value = Price { price: 2, conf: 3, expo: -4, publish_time: 5, }; + let value = Price { price: 2, conf: 3, expo: -4, publish_time: 5 }; let expected = "Price { price: 2, conf: 3, expo: -4, publish_time: 5 }"; let actual = format!("{:?}", value); assert!(actual == expected); diff --git a/target_chains/starknet/contracts/src/pyth/price_update.cairo b/target_chains/starknet/contracts/src/pyth/price_update.cairo index 8b4dc1ce19..feac7216ee 100644 --- a/target_chains/starknet/contracts/src/pyth/price_update.cairo +++ b/target_chains/starknet/contracts/src/pyth/price_update.cairo @@ -1,10 +1,10 @@ -use pyth::reader::{Reader, ReaderImpl}; -use pyth::pyth::{UpdatePriceFeedsError, PriceFeed, Price}; +use core::fmt::{Debug, Formatter}; use core::panic_with_felt252; use pyth::byte_buffer::ByteBuffer; use pyth::merkle_tree::read_and_verify_proof; +use pyth::pyth::{Price, PriceFeed, UpdatePriceFeedsError}; +use pyth::reader::{Reader, ReaderImpl}; use pyth::util::{u32_as_i32, u64_as_i64, write_i64}; -use core::fmt::{Debug, Formatter}; // Stands for PNAU (Pyth Network Accumulator Update) const ACCUMULATOR_MAGIC: u32 = 0x504e4155; @@ -40,7 +40,7 @@ impl DebugPriceInfo of Debug { #[test] fn test_debug_price_info() { let value = PriceInfo { - price: 2, conf: 3, expo: -4, publish_time: 5, ema_price: 7, ema_conf: 8 + price: 2, conf: 3, expo: -4, publish_time: 5, ema_price: 7, ema_conf: 8, }; let expected = "PriceInfo { price: 2, conf: 3, expo: -4, publish_time: 5, ema_price: 7, ema_conf: 8 }"; @@ -56,7 +56,7 @@ impl DefaultPriceInfo of Default { #[derive(Drop, Copy, Debug, PartialEq, Serde, Hash)] enum UpdateType { - WormholeMerkle + WormholeMerkle, } impl U8TryIntoUpdateType of TryInto { @@ -71,7 +71,7 @@ impl U8TryIntoUpdateType of TryInto { #[derive(Drop, Copy, Debug, PartialEq, Serde, Hash)] enum MessageType { - PriceFeed + PriceFeed, } impl U8TryIntoMessageType of TryInto { @@ -107,7 +107,7 @@ impl DebugPriceFeedMessage of Debug { f, ", publish_time: {}, prev_publish_time: {}, ema_price: ", self.publish_time, - self.prev_publish_time + self.prev_publish_time, )?; write_i64(ref f, *self.ema_price)?; write!(f, ", ema_conf: {} }}", self.ema_conf) @@ -125,7 +125,7 @@ fn test_debug_price_feed_message() { publish_time: 5, prev_publish_time: 6, ema_price: 7, - ema_conf: 8 + ema_conf: 8, }; let expected = "PriceFeedMessage { price_id: 1, price: 2, conf: 3, expo: -4, publish_time: 5, prev_publish_time: 6, ema_price: 7, ema_conf: 8 }"; @@ -143,7 +143,7 @@ impl DefaultPriceFeedMessage of Default { publish_time: 0, prev_publish_time: 0, ema_price: 0, - ema_conf: 0 + ema_conf: 0, } } } @@ -168,7 +168,7 @@ pub fn read_and_verify_header(ref reader: Reader) { .expect(UpdatePriceFeedsError::InvalidUpdateData.into()); match update_type { - UpdateType::WormholeMerkle => {} + UpdateType::WormholeMerkle => {}, } } @@ -184,7 +184,7 @@ pub fn parse_wormhole_proof(payload: ByteBuffer) -> u256 { .expect(UpdatePriceFeedsError::InvalidUpdateData.into()); match update_type { - UpdateType::WormholeMerkle => {} + UpdateType::WormholeMerkle => {}, } let _slot = reader.read_u64(); @@ -204,7 +204,7 @@ pub fn read_and_verify_message(ref reader: Reader, root_digest: u256) -> PriceFe .expect(UpdatePriceFeedsError::InvalidUpdateData.into()); match message_type { - MessageType::PriceFeed => {} + MessageType::PriceFeed => {}, } let price_id = message_reader.read_u256(); diff --git a/target_chains/starknet/contracts/src/reader.cairo b/target_chains/starknet/contracts/src/reader.cairo index 59c244be77..f71ced8944 100644 --- a/target_chains/starknet/contracts/src/reader.cairo +++ b/target_chains/starknet/contracts/src/reader.cairo @@ -1,8 +1,5 @@ -use core::option::OptionTrait; use core::array::ArrayTrait; -use core::keccak::cairo_keccak; -use core::integer::u128_byte_reverse; -use core::fmt::{Debug, Formatter}; +use core::option::OptionTrait; use pyth::util::{UNEXPECTED_OVERFLOW, UNEXPECTED_ZERO, one_shift_left_bytes_u128}; use super::byte_buffer::{ByteBuffer, ByteBufferImpl}; @@ -117,7 +114,7 @@ pub impl ReaderImpl of ReaderTrait { num_last_bytes = num_read; break; } - }; + } // num_last_bytes < 31 let num_last_bytes = num_last_bytes.try_into().expect(UNEXPECTED_OVERFLOW); ByteBufferImpl::new(array, num_last_bytes) @@ -179,7 +176,7 @@ impl ReaderPrivateImpl of ReaderPrivateTrait { // Moved out from `read_bytes` because we cannot use `return` or `?` within a loop. fn read_bytes_iteration( - ref self: Reader, num_bytes: usize, ref array: Array + ref self: Reader, num_bytes: usize, ref array: Array, ) -> (usize, bool) { if num_bytes >= 31 { let high = self.read_num_bytes(15); diff --git a/target_chains/starknet/contracts/src/util.cairo b/target_chains/starknet/contracts/src/util.cairo index 7eb4f2284c..b26b2ff49b 100644 --- a/target_chains/starknet/contracts/src/util.cairo +++ b/target_chains/starknet/contracts/src/util.cairo @@ -1,10 +1,9 @@ mod exp10_; +use core::fmt::Formatter; +use core::integer::u128_byte_reverse; pub use exp10_::exp10; -use core::integer::u128_byte_reverse; -use core::fmt::Formatter; - pub const ONE_SHIFT_160: u256 = 0x10000000000000000000000000000000000000000; pub const ONE_SHIFT_96: u256 = 0x1000000000000000000000000; @@ -156,7 +155,7 @@ pub fn array_try_into, +Drop, +Drop>(mut input: Array Option::Some(v) => { output.append(v.try_into().unwrap()); }, Option::None => { break; }, } - }; + } output } @@ -175,7 +174,7 @@ impl ResultMapErrIntoImpl> of ResultMapErrInto, + consumed_governance_actions: Map, /// All known guardian sets. - guardian_sets: LegacyMap, + guardian_sets: Map, /// Public keys of guardians in all known guardian sets. /// Key = (guardian_set_index, guardian_index). - guardian_keys: LegacyMap<(u32, u8), EthAddress>, + guardian_keys: Map<(u32, u8), EthAddress>, } /// Initializes the contract. @@ -100,7 +100,7 @@ mod wormhole { impl WormholeImpl of IWormhole { fn parse_and_verify_vm(self: @ContractState, encoded_vm: ByteBuffer) -> VerifiedVM { let vm = parse_vm(encoded_vm); - let guardian_set = self.guardian_sets.read(vm.guardian_set_index); + let guardian_set = self.guardian_sets.entry(vm.guardian_set_index).read(); if guardian_set.num_guardians == 0 { panic_with_felt252(ParseAndVerifyVmError::InvalidGuardianSetIndex.into()); } @@ -120,14 +120,12 @@ mod wormhole { Option::None => { break; }, }; - match last_index { - Option::Some(last_index) => { - if *(@signature).guardian_index <= last_index { - panic_with_felt252(ParseAndVerifyVmError::InvalidSignatureOrder.into()); - } - }, - Option::None => {}, - }; + if let Some(last_index) = last_index { + let guardian_index = *(@signature).guardian_index; + if guardian_index <= last_index { + panic_with_felt252(ParseAndVerifyVmError::InvalidSignatureOrder.into()); + } + } last_index = Option::Some(*(@signature).guardian_index); if signature.guardian_index.into() >= guardian_set.num_guardians { @@ -136,41 +134,42 @@ mod wormhole { let guardian_key = self .guardian_keys - .read((vm.guardian_set_index, signature.guardian_index)) + .entry((vm.guardian_set_index, signature.guardian_index)) + .read() .try_into() .expect(UNEXPECTED_OVERFLOW); is_eth_signature_valid(vm.hash, signature.signature, guardian_key) .unwrap_with_felt252(); - }; + } vm } fn get_guardian_set(self: @ContractState, index: u32) -> super::GuardianSet { let mut keys = array![]; - let set = self.guardian_sets.read(index); + let set = self.guardian_sets.entry(index).read(); if set.num_guardians == 0 { panic_with_felt252(GetGuardianSetError::InvalidIndex.into()); } let mut i: u8 = 0; while i.into() < set.num_guardians { - keys.append(self.guardian_keys.read((index, i))); + keys.append(self.guardian_keys.entry((index, i)).read()); i += 1; - }; + } super::GuardianSet { keys, expiration_time: if set.expiration_time == 0 { Option::None } else { Option::Some(set.expiration_time) - } + }, } } fn get_current_guardian_set_index(self: @ContractState) -> u32 { self.current_guardian_set_index.read() } fn governance_action_is_consumed(self: @ContractState, hash: u256) -> bool { - self.consumed_governance_actions.read(hash) + self.consumed_governance_actions.entry(hash).read() } fn chain_id(self: @ContractState) -> u16 { self.chain_id.read() @@ -201,7 +200,7 @@ mod wormhole { self.store_guardian_set(new_set.set_index, @new_set.keys); self.expire_guardian_set(current_set_index, get_block_timestamp()); - self.consumed_governance_actions.write(vm.hash, true); + self.consumed_governance_actions.entry(vm.hash).write(true); let event = GuardianSetAdded { index: new_set.set_index }; self.emit(event); @@ -213,7 +212,7 @@ mod wormhole { /// Validates the new guardian set and writes it to the storage. /// `SubmitNewGuardianSetError` enumerates possible panic payloads. fn store_guardian_set( - ref self: ContractState, set_index: u32, guardians: @Array + ref self: ContractState, set_index: u32, guardians: @Array, ) { if guardians.len() == 0 { panic_with_felt252(SubmitNewGuardianSetError::NoGuardiansSpecified.into()); @@ -228,27 +227,28 @@ mod wormhole { panic_with_felt252(SubmitNewGuardianSetError::InvalidGuardianKey.into()); } i += 1; - }; + } let set = GuardianSet { num_guardians: guardians.len(), expiration_time: 0 }; - self.guardian_sets.write(set_index, set); + self.guardian_sets.entry(set_index).write(set); i = 0; while i < guardians.len() { let key = *guardians.at(i); // i < 256 self .guardian_keys - .write((set_index, i.try_into().expect(UNEXPECTED_OVERFLOW)), key.into()); + .entry((set_index, i.try_into().expect(UNEXPECTED_OVERFLOW))) + .write(key.into()); i += 1; - }; + } self.current_guardian_set_index.write(set_index); } /// Marks the specified guardian set to expire in 24 hours. fn expire_guardian_set(ref self: ContractState, set_index: u32, now: u64) { - let mut set = self.guardian_sets.read(set_index); + let mut set = self.guardian_sets.entry(set_index).read(); set.expiration_time = now + 86400; - self.guardian_sets.write(set_index, set); + self.guardian_sets.entry(set_index).write(set); } /// Checks required properties of the governance instruction. @@ -263,7 +263,7 @@ mod wormhole { if self.governance_contract.read() != *vm.emitter_address { panic_with_felt252(GovernanceError::WrongContract.into()); } - if self.consumed_governance_actions.read(*vm.hash) { + if self.consumed_governance_actions.entry(*vm.hash).read() { panic_with_felt252(GovernanceError::ActionAlreadyConsumed.into()); } } diff --git a/target_chains/starknet/contracts/src/wormhole/governance.cairo b/target_chains/starknet/contracts/src/wormhole/governance.cairo index bc6ec62809..32a502e9f0 100644 --- a/target_chains/starknet/contracts/src/wormhole/governance.cairo +++ b/target_chains/starknet/contracts/src/wormhole/governance.cairo @@ -1,8 +1,8 @@ +use core::panic_with_felt252; use pyth::reader::{Reader, ReaderImpl}; use pyth::util::UNEXPECTED_OVERFLOW; +use starknet::EthAddress; use super::GovernanceError; -use core::panic_with_felt252; -use core::starknet::EthAddress; // "Core" (left padded) const MODULE: u256 = 0x00000000000000000000000000000000000000000000000000000000436f7265; @@ -76,7 +76,7 @@ pub fn parse_new_guardian_set(ref reader: Reader) -> NewGuardianSet { let key = reader.read_u160(); keys.append(key.try_into().expect(UNEXPECTED_OVERFLOW)); i += 1; - }; + } assert(reader.len() == 0, GovernanceError::TrailingData.into()); NewGuardianSet { set_index, keys } } diff --git a/target_chains/starknet/contracts/src/wormhole/interface.cairo b/target_chains/starknet/contracts/src/wormhole/interface.cairo index f7dbb07ec8..75fb91534d 100644 --- a/target_chains/starknet/contracts/src/wormhole/interface.cairo +++ b/target_chains/starknet/contracts/src/wormhole/interface.cairo @@ -1,7 +1,7 @@ -use super::errors::SubmitNewGuardianSetError; use pyth::byte_buffer::ByteBuffer; -use core::starknet::secp256_trait::Signature; -use core::starknet::EthAddress; +use starknet::EthAddress; +use starknet::secp256_trait::Signature; +use super::errors::SubmitNewGuardianSetError; /// Wormhole provides a secure means for communication between multiple chains. /// This contract allows users to parse and verify a Wormhole message that informs @@ -42,7 +42,8 @@ pub trait IWormhole { // in the Pyth contract. /// Executes a governance instruction to add a new guardian set. The new set becomes - /// active immediately. The previous guardian set will be available for 24 hours and then expire. + /// active immediately. The previous guardian set will be available for 24 hours and then + /// expire. /// `SubmitNewGuardianSetError` enumerates possible panic payloads. fn submit_new_guardian_set(ref self: T, encoded_vm: ByteBuffer); } diff --git a/target_chains/starknet/contracts/src/wormhole/parse_vm.cairo b/target_chains/starknet/contracts/src/wormhole/parse_vm.cairo index 129d41bc74..88204aac15 100644 --- a/target_chains/starknet/contracts/src/wormhole/parse_vm.cairo +++ b/target_chains/starknet/contracts/src/wormhole/parse_vm.cairo @@ -1,9 +1,9 @@ -use pyth::reader::{Reader, ReaderImpl}; -use pyth::hash::{Hasher, HasherImpl}; -use super::{VerifiedVM, GuardianSignature, ParseAndVerifyVmError}; -use core::starknet::secp256_trait::Signature; -use pyth::byte_buffer::ByteBuffer; use core::panic_with_felt252; +use pyth::byte_buffer::ByteBuffer; +use pyth::hash::HasherImpl; +use pyth::reader::{Reader, ReaderImpl}; +use starknet::secp256_trait::Signature; +use super::{GuardianSignature, ParseAndVerifyVmError, VerifiedVM}; /// Parses information about a guardian signature within a Wormhole message. /// `pyth::reader::Error` enumerates possible panic payloads. @@ -33,7 +33,7 @@ pub fn parse_vm(encoded_vm: ByteBuffer) -> VerifiedVM { while i < sig_count { signatures.append(parse_signature(ref reader)); i += 1; - }; + } let mut reader_for_hash = reader.clone(); let mut hasher = HasherImpl::new(); @@ -51,7 +51,6 @@ pub fn parse_vm(encoded_vm: ByteBuffer) -> VerifiedVM { let consistency_level = reader.read_u8(); let payload_len = reader.len(); let payload = reader.read_byte_array(payload_len); - VerifiedVM { version, guardian_set_index, diff --git a/target_chains/starknet/contracts/tests/data.cairo b/target_chains/starknet/contracts/tests/data.cairo index 0e8a735e85..b1e686d521 100644 --- a/target_chains/starknet/contracts/tests/data.cairo +++ b/target_chains/starknet/contracts/tests/data.cairo @@ -185,7 +185,8 @@ pub fn unique_update1() -> ByteBuffer { ByteBufferImpl::new(bytes, 11) } -// An actual mainnet wormhole governance VAA from https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 +// An actual mainnet wormhole governance VAA from +// https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 pub fn mainnet_guardian_set_upgrade1() -> ByteBuffer { let bytes = array![ 1766847064779994277746302277072294871108550301449637470263976489521154979, @@ -210,7 +211,8 @@ pub fn mainnet_guardian_set_upgrade1() -> ByteBuffer { ByteBufferImpl::new(bytes, 16) } -// An actual mainnet wormhole governance VAA from https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 +// An actual mainnet wormhole governance VAA from +// https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 pub fn mainnet_guardian_set_upgrade2() -> ByteBuffer { let bytes = array![ 1766847065210651126944505525521222069599854288126726949998063840465138797, @@ -261,7 +263,8 @@ pub fn mainnet_guardian_set_upgrade2() -> ByteBuffer { ByteBufferImpl::new(bytes, 2) } -// An actual mainnet wormhole governance VAA from https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 +// An actual mainnet wormhole governance VAA from +// https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 pub fn mainnet_guardian_set_upgrade3() -> ByteBuffer { let bytes = array![ 1766847065622031860560134801367788401015571316785630090767859240961980367, @@ -312,7 +315,8 @@ pub fn mainnet_guardian_set_upgrade3() -> ByteBuffer { ByteBufferImpl::new(bytes, 2) } -// An actual mainnet wormhole governance VAA from https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 +// An actual mainnet wormhole governance VAA from +// https://github.com/pyth-network/pyth-crosschain/blob/main/contract_manager/src/contracts/wormhole.ts#L32-L37 pub fn mainnet_guardian_set_upgrade4() -> ByteBuffer { let bytes = array![ 1766847066033426987337757245669159273063358729535478806850006662056807068, @@ -405,7 +409,8 @@ pub fn wrong_index_upgrade() -> ByteBuffer { ByteBufferImpl::new(bytes, 28) } -// A wormhole governance guardian set upgrade instruction signed by test guardian #1 containing test guardian #2 as the new guardian set. +// A wormhole governance guardian set upgrade instruction signed by test guardian #1 containing test +// guardian #2 as the new guardian set. pub fn upgrade_to_test2() -> ByteBuffer { let bytes = array![ 1766847064779995287375101177319600239435018729139341591012343354326614060, @@ -470,7 +475,8 @@ pub fn pyth_set_wormhole() -> ByteBuffer { ByteBufferImpl::new(bytes, 8) } -// A Pyth governance instruction to request governance data source transfer signed by the test guardian #1. +// A Pyth governance instruction to request governance data source transfer signed by the test +// guardian #1. pub fn pyth_request_transfer() -> ByteBuffer { let bytes = array![ 1766847064779995673162446580588349917525470038054832932592992288867429640, @@ -482,7 +488,8 @@ pub fn pyth_request_transfer() -> ByteBuffer { ByteBufferImpl::new(bytes, 11) } -// A Pyth governance instruction to authorize governance data source transfer signed by the test guardian #1. +// A Pyth governance instruction to authorize governance data source transfer signed by the test +// guardian #1. pub fn pyth_auth_transfer() -> ByteBuffer { let bytes = array![ 1766847064779996877169354131457289870145133774197236214231189828595607612, @@ -513,12 +520,12 @@ pub fn pyth_set_fee_alt_emitter() -> ByteBuffer { // A Pyth governance instruction to upgrade the contract signed by the test guardian #1. pub fn pyth_upgrade_fake1() -> ByteBuffer { let bytes = array![ - 1766847064779994791169214817472264547450542145364282319310439743685771618, - 175385590228001769706203572954671062839210335359545531991708252078677402742, - 338282801975945534678621806212670914146735662234331326855531973960850735104, + 1766847064779993492421078898621749108301966584597749110380074216576492888, + 60258065057775975798387405360763783639924599499836853429424581305786207127, + 294385177406295901350483669059442590907311847252050490331308693098780950528, 49565958604199796163020368, - 148907253453589022320407306335457538262203456299261498528172020674942501293, - 9624434269354675143, + 148907253453589022392359363374850830855966874133251507057423871596698519420, + 10995973506805932843, ]; ByteBufferImpl::new(bytes, 8) } @@ -526,12 +533,12 @@ pub fn pyth_upgrade_fake1() -> ByteBuffer { // A Pyth governance instruction to upgrade the contract signed by the test guardian #1. pub fn pyth_upgrade_not_pyth() -> ByteBuffer { let bytes = array![ - 1766847064779994185568390976518139178339359117743780499979078006447412818, - 312550937452923367391560946919832045570249370029901542796468563830775031789, - 297548922588419398887374641748895591794744646787122275140580663536136486912, + 1766847064779995437839944646329090026725755072259133687464299849962947211, + 364933187739398371603726688032548912702512452515951176241438841103710487524, + 168967372750473928993542488698926019424107017631122163941566426102978576384, 49565958604199796163020368, - 148907253453589022305803196061110108233921773465491227564264876752079119569, - 6736708290019375278, + 148907253453589022290209221717003441680286754164662723445001054939097582646, + 12012828459144055253, ]; ByteBufferImpl::new(bytes, 8) } @@ -539,12 +546,12 @@ pub fn pyth_upgrade_not_pyth() -> ByteBuffer { // A Pyth governance instruction to upgrade the contract signed by the test guardian #1. pub fn pyth_upgrade_wrong_magic() -> ByteBuffer { let bytes = array![ - 1766847064779993581380818181711092803131812037068363180730038764700119064, - 43179698701133869693008541869474965453366967663087320291846878688486859828, - 257191826617037171240065659464096594985467828231875472974396182656981139456, + 1766847064779994990936574265957823314838802547799335095459100055033612531, + 337609657713579979105798034723839884757527387288729213036462364653817969892, + 344707358034822539834365850761803570529219340665056667519361118443881365504, 49565958604199796163020368, - 148907253453589022340563264373887392414227070562033595690783947835630084766, - 5698494087895763928, + 148907253453589022385922519334474515578648255544414696069419539273709501349, + 9034297375272282805, ]; ByteBufferImpl::new(bytes, 8) } diff --git a/target_chains/starknet/contracts/tests/pyth.cairo b/target_chains/starknet/contracts/tests/pyth.cairo index a488deed71..bac1bd3112 100644 --- a/target_chains/starknet/contracts/tests/pyth.cairo +++ b/target_chains/starknet/contracts/tests/pyth.cairo @@ -1,17 +1,19 @@ -use snforge_std::{ - declare, ContractClassTrait, start_prank, stop_prank, CheatTarget, spy_events, SpyOn, EventSpy, - EventFetcher, event_name_hash, Event, start_warp, stop_warp, ContractClass, -}; +use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use pyth::byte_buffer::ByteBufferImpl; use pyth::pyth::{ - IPythDispatcher, IPythDispatcherTrait, DataSource, Event as PythEvent, PriceFeedUpdated, - WormholeAddressSet, GovernanceDataSourceSet, ContractUpgraded, DataSourcesSet, FeeSet, - PriceFeedPublishTime, GetPriceNoOlderThanError, Price, PriceFeed, GetPriceUnsafeError, + ContractUpgraded, DataSource, DataSourcesSet, Event as PythEvent, FeeSet, + GetPriceNoOlderThanError, GetPriceUnsafeError, GovernanceDataSourceSet, IPythDispatcher, + IPythDispatcherTrait, Price, PriceFeed, PriceFeedPublishTime, PriceFeedUpdated, + WormholeAddressSet, }; -use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl}; -use pyth::util::{array_try_into, UnwrapWithFelt252}; +use pyth::util::{UnwrapWithFelt252, array_try_into}; use pyth::wormhole::{IWormholeDispatcher, IWormholeDispatcherTrait}; -use core::starknet::ContractAddress; -use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait}; +use snforge_std::{ + ContractClass, ContractClassTrait, DeclareResultTrait, Event, EventSpyTrait, EventsFilterTrait, + declare, spy_events, start_cheat_block_timestamp, start_cheat_caller_address, + stop_cheat_block_timestamp, stop_cheat_caller_address, +}; +use starknet::ContractAddress; use super::wormhole::corrupted_vm; use super::data; @@ -22,11 +24,11 @@ impl DecodeEventHelpers of DecodeEventHelpersTrait { } fn pop_u256(ref self: Array) -> u256 { - u256 { low: self.pop(), high: self.pop(), } + u256 { low: self.pop(), high: self.pop() } } fn pop_data_source(ref self: Array) -> DataSource { - DataSource { emitter_chain_id: self.pop(), emitter_address: self.pop_u256(), } + DataSource { emitter_chain_id: self.pop(), emitter_address: self.pop_u256() } } fn pop_data_sources(ref self: Array) -> Array { @@ -36,14 +38,14 @@ impl DecodeEventHelpers of DecodeEventHelpersTrait { while i < count { output.append(self.pop_data_source()); i += 1; - }; + } output } } fn decode_event(mut event: Event) -> PythEvent { let key0: felt252 = event.keys.pop(); - let output = if key0 == event_name_hash('PriceFeedUpdated') { + let output = if key0 == selector!("PriceFeedUpdated") { let event = PriceFeedUpdated { price_id: event.keys.pop_u256(), price: event.data.pop(), @@ -51,30 +53,30 @@ fn decode_event(mut event: Event) -> PythEvent { publish_time: event.data.pop(), }; PythEvent::PriceFeedUpdated(event) - } else if key0 == event_name_hash('FeeSet') { + } else if key0 == selector!("FeeSet") { let event = FeeSet { old_fee: event.data.pop_u256(), new_fee: event.data.pop_u256(), token: event.data.pop(), }; PythEvent::FeeSet(event) - } else if key0 == event_name_hash('DataSourcesSet') { + } else if key0 == selector!("DataSourcesSet") { let event = DataSourcesSet { old_data_sources: event.data.pop_data_sources(), new_data_sources: event.data.pop_data_sources(), }; PythEvent::DataSourcesSet(event) - } else if key0 == event_name_hash('WormholeAddressSet') { + } else if key0 == selector!("WormholeAddressSet") { let event = WormholeAddressSet { old_address: event.data.pop(), new_address: event.data.pop(), }; PythEvent::WormholeAddressSet(event) - } else if key0 == event_name_hash('GovernanceDataSourceSet') { + } else if key0 == selector!("GovernanceDataSourceSet") { let event = GovernanceDataSourceSet { old_data_source: event.data.pop_data_source(), new_data_source: event.data.pop_data_source(), last_executed_governance_sequence: event.data.pop(), }; PythEvent::GovernanceDataSourceSet(event) - } else if key0 == event_name_hash('ContractUpgraded') { + } else if key0 == selector!("ContractUpgraded") { let event = ContractUpgraded { new_class_hash: event.data.pop() }; PythEvent::ContractUpgraded(event) } else { @@ -95,7 +97,7 @@ fn test_getters_work() { pyth .fee_token_addresses() == array![ ctx.fee_contract.contract_address, ctx.fee_contract2.contract_address, - ] + ], ); assert!(pyth.get_single_update_fee(ctx.fee_contract.contract_address) == 1000); assert!(pyth.get_single_update_fee(ctx.fee_contract2.contract_address) == 2000); @@ -105,8 +107,8 @@ fn test_getters_work() { DataSource { emitter_chain_id: 26, emitter_address: 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, - } - ] + }, + ], ); assert!( pyth @@ -114,11 +116,11 @@ fn test_getters_work() { DataSource { emitter_chain_id: 26, emitter_address: 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, - } - ) + }, + ), ); assert!( - !pyth.is_valid_data_source(DataSource { emitter_chain_id: 26, emitter_address: 0xbad, }) + !pyth.is_valid_data_source(DataSource { emitter_chain_id: 26, emitter_address: 0xbad }), ); assert!( !pyth @@ -126,23 +128,23 @@ fn test_getters_work() { DataSource { emitter_chain_id: 27, emitter_address: 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, - } - ) + }, + ), ); assert!( - pyth.governance_data_source() == DataSource { emitter_chain_id: 1, emitter_address: 41, } + pyth.governance_data_source() == DataSource { emitter_chain_id: 1, emitter_address: 41 }, ); assert!( pyth .is_valid_governance_data_source( - DataSource { emitter_chain_id: 1, emitter_address: 41, } - ) + DataSource { emitter_chain_id: 1, emitter_address: 41 }, + ), ); assert!( !pyth .is_valid_governance_data_source( - DataSource { emitter_chain_id: 1, emitter_address: 42, } - ) + DataSource { emitter_chain_id: 1, emitter_address: 42 }, + ), ); assert!(pyth.last_executed_governance_sequence() == 0); assert!(pyth.governance_data_source_index() == 0); @@ -155,28 +157,28 @@ fn update_price_feeds_works() { let pyth = ctx.pyth; assert!( - !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 0 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 0, ); let fee = pyth.get_update_fee(data::good_update1(), ctx.fee_contract.contract_address); assert!(fee == 1000); ctx.approve_fee(fee); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().emitted_by(pyth.contract_address).events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = PriceFeedUpdated { @@ -217,13 +219,13 @@ fn update_price_feeds_works() { assert!(feed.ema_price.publish_time == 1712589206); assert!( - pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 1712589206 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 1712589206, ); } @@ -234,9 +236,9 @@ fn update_price_feeds_works2() { let fee = pyth.get_update_fee(data::good_update2(), ctx.fee_contract.contract_address); assert!(fee == 1000); ctx.approve_fee(fee); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update2()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -245,13 +247,13 @@ fn test_accepts_secondary_fee() { let pyth = ctx.pyth; assert!( - !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 0 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 0, ); let fee2 = pyth.get_update_fee(data::good_update1(), ctx.fee_contract2.contract_address); @@ -260,9 +262,9 @@ fn test_accepts_secondary_fee() { let balance1 = ctx.fee_contract.balanceOf(ctx.user); let balance2 = ctx.fee_contract2.balanceOf(ctx.user); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); assert!(balance1 - ctx.fee_contract.balanceOf(ctx.user) == 0); assert!(balance2 - ctx.fee_contract2.balanceOf(ctx.user) == 2000); } @@ -273,13 +275,13 @@ fn test_accepts_secondary_fee_if_first_allowance_insufficient() { let pyth = ctx.pyth; assert!( - !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 0 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 0, ); let fee = pyth.get_update_fee(data::good_update1(), ctx.fee_contract.contract_address); @@ -291,9 +293,9 @@ fn test_accepts_secondary_fee_if_first_allowance_insufficient() { let balance1 = ctx.fee_contract.balanceOf(ctx.user); let balance2 = ctx.fee_contract2.balanceOf(ctx.user); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); assert!(balance1 - ctx.fee_contract.balanceOf(ctx.user) == 0); assert!(balance2 - ctx.fee_contract2.balanceOf(ctx.user) == 2000); } @@ -304,41 +306,41 @@ fn test_accepts_secondary_fee_if_first_balance_insufficient() { let pyth = ctx.pyth; let user2 = 'user2'.try_into().unwrap(); - start_prank(CheatTarget::One(ctx.fee_contract.contract_address), ctx.user); + start_cheat_caller_address(ctx.fee_contract.contract_address, ctx.user); ctx.fee_contract.transfer(user2, 500); - stop_prank(CheatTarget::One(ctx.fee_contract.contract_address)); + stop_cheat_caller_address(ctx.fee_contract.contract_address); - start_prank(CheatTarget::One(ctx.fee_contract2.contract_address), ctx.user); + start_cheat_caller_address(ctx.fee_contract2.contract_address, ctx.user); ctx.fee_contract2.transfer(user2, 2000); - stop_prank(CheatTarget::One(ctx.fee_contract2.contract_address)); + stop_cheat_caller_address(ctx.fee_contract2.contract_address); assert!( - !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 0 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 0, ); let fee = pyth.get_update_fee(data::good_update1(), ctx.fee_contract.contract_address); assert!(fee == 1000); let fee2 = pyth.get_update_fee(data::good_update1(), ctx.fee_contract2.contract_address); assert!(fee2 == 2000); - start_prank(CheatTarget::One(ctx.fee_contract.contract_address), user2); + start_cheat_caller_address(ctx.fee_contract.contract_address, user2); ctx.fee_contract.approve(ctx.pyth.contract_address, fee); - stop_prank(CheatTarget::One(ctx.fee_contract.contract_address)); + stop_cheat_caller_address(ctx.fee_contract.contract_address); - start_prank(CheatTarget::One(ctx.fee_contract2.contract_address), user2); + start_cheat_caller_address(ctx.fee_contract2.contract_address, user2); ctx.fee_contract2.approve(ctx.pyth.contract_address, fee2); - stop_prank(CheatTarget::One(ctx.fee_contract2.contract_address)); + stop_cheat_caller_address(ctx.fee_contract2.contract_address); let balance1 = ctx.fee_contract.balanceOf(user2); let balance2 = ctx.fee_contract2.balanceOf(user2); - start_prank(CheatTarget::One(pyth.contract_address), user2.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, user2.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); assert!(balance1 - ctx.fee_contract.balanceOf(user2) == 0); assert!(balance2 - ctx.fee_contract2.balanceOf(user2) == 2000); } @@ -350,13 +352,13 @@ fn test_rejects_if_both_fees_insufficient() { let pyth = ctx.pyth; assert!( - !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) + !pyth.price_feed_exists(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43), ); assert!( pyth .latest_price_info_publish_time( - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 - ) == 0 + 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, + ) == 0, ); let fee = pyth.get_update_fee(data::good_update1(), ctx.fee_contract.contract_address); @@ -366,9 +368,9 @@ fn test_rejects_if_both_fees_insufficient() { ctx.approve_fee(500); ctx.approve_fee2(1500); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -395,12 +397,12 @@ fn test_update_if_necessary_works() { let pyth = ctx.pyth; ctx.approve_fee(10000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); let price_id = 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43; assert!(pyth.get_price_unsafe(price_id).is_err()); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); let times = array![PriceFeedPublishTime { price_id, publish_time: 1715769470 }]; pyth.update_price_feeds_if_necessary(data::test_price_update1(), times); @@ -408,8 +410,8 @@ fn test_update_if_necessary_works() { assert!(last_price.price == 6281060000000); assert!(last_price.publish_time == 1715769470); - spy.fetch_events(); - assert!(spy.events.len() == 1); + let mut events = spy.get_events().emitted_by(pyth.contract_address).events; + assert!(events.len() == 1); let times = array![PriceFeedPublishTime { price_id, publish_time: 1715769475 }]; pyth.update_price_feeds_if_necessary(data::test_price_update2(), times); @@ -418,10 +420,10 @@ fn test_update_if_necessary_works() { assert!(last_price.price == 6281522520745); assert!(last_price.publish_time == 1715769475); - spy.fetch_events(); - assert!(spy.events.len() == 2); + events = spy.get_events().emitted_by(pyth.contract_address).events; + assert!(events.len() == 2); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -433,33 +435,31 @@ fn test_parse_price_feed_updates_works() { assert!(fee == 1000); ctx.approve_fee(1000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); let output = pyth .parse_price_feed_updates( data::good_update1(), array![0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43], 0, - 1712589208 + 1712589208, ); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); assert!(output.len() == 1); let output = output.at(0).clone(); let expected = PriceFeed { id: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, - price: Price { - price: 7192002930010, conf: 3596501465, expo: -8, publish_time: 1712589206, - }, + price: Price { price: 7192002930010, conf: 3596501465, expo: -8, publish_time: 1712589206 }, ema_price: Price { price: 7181868900000, conf: 4096812700, expo: -8, publish_time: 1712589206, }, }; assert!(output == expected); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().emitted_by(pyth.contract_address).events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = PriceFeedUpdated { @@ -497,7 +497,7 @@ fn test_parse_price_feed_updates_rejects_bad_price_id() { assert!(fee == 1000); ctx.approve_fee(fee); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.parse_price_feed_updates(data::good_update1(), array![0x14], 0, 1712589208); } @@ -511,13 +511,13 @@ fn test_parse_price_feed_updates_rejects_out_of_range() { assert!(fee == 1000); ctx.approve_fee(fee); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth .parse_price_feed_updates( data::good_update1(), array![0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43], 0, - 1712589000 + 1712589000, ); } @@ -530,7 +530,7 @@ fn test_parse_price_feed_updates_unique_works() { assert!(fee1 == 1000); ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); let output = pyth .parse_unique_price_feed_updates( data::unique_update1(), @@ -538,14 +538,12 @@ fn test_parse_price_feed_updates_unique_works() { 1716904943, 2, ); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); assert!(output.len() == 1); let output = output.at(0).clone(); let expected = PriceFeed { id: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43, - price: Price { - price: 6751021151231, conf: 7471389383, expo: -8, publish_time: 1716904943, - }, + price: Price { price: 6751021151231, conf: 7471389383, expo: -8, publish_time: 1716904943 }, ema_price: Price { price: 6815630100000, conf: 6236878200, expo: -8, publish_time: 1716904943, }, @@ -563,7 +561,7 @@ fn test_parse_price_feed_updates_unique_rejects_non_unique() { assert!(fee1 == 1000); ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth .parse_unique_price_feed_updates( data::good_update1(), @@ -580,9 +578,9 @@ fn test_update_if_necessary_rejects_empty() { let pyth = ctx.pyth; ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds_if_necessary(data::test_price_update1(), array![]); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -592,15 +590,15 @@ fn test_update_if_necessary_rejects_no_fresh() { let pyth = ctx.pyth; ctx.approve_fee(10000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds_if_necessary(data::test_price_update1(), array![]); let price_id = 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43; assert!(pyth.get_price_unsafe(price_id).is_err()); - spy.fetch_events(); - assert!(spy.events.len() == 0); + let mut events = spy.get_events().events; + assert!(events.len() == 0); let times = array![PriceFeedPublishTime { price_id, publish_time: 1715769470 }]; pyth.update_price_feeds_if_necessary(data::test_price_update1(), times); @@ -609,8 +607,8 @@ fn test_update_if_necessary_rejects_no_fresh() { assert!(last_price.price == 6281060000000); assert!(last_price.publish_time == 1715769470); - spy.fetch_events(); - assert!(spy.events.len() == 1); + events = spy.get_events().events; + assert!(events.len() == 1); let times = array![PriceFeedPublishTime { price_id, publish_time: 1715769470 }]; pyth.update_price_feeds_if_necessary(data::test_price_update2(), times); @@ -637,11 +635,11 @@ fn test_get_no_older_works() { ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); - start_warp(CheatTarget::One(pyth.contract_address), 1712589210); + start_cheat_block_timestamp(pyth.contract_address, 1712589210); let err = pyth.get_price_no_older_than(price_id, 3).unwrap_err(); assert!(err == GetPriceNoOlderThanError::StalePrice); let err = pyth.get_ema_price_no_older_than(price_id, 3).unwrap_err(); @@ -649,7 +647,7 @@ fn test_get_no_older_works() { let err = pyth.query_price_feed_no_older_than(price_id, 3).unwrap_err(); assert!(err == GetPriceNoOlderThanError::StalePrice); - start_warp(CheatTarget::One(pyth.contract_address), 1712589208); + start_cheat_block_timestamp(pyth.contract_address, 1712589208); let val = pyth.get_price_no_older_than(price_id, 3).unwrap_with_felt252(); assert!(val.publish_time == 1712589206); assert!(val.price == 7192002930010); @@ -660,7 +658,7 @@ fn test_get_no_older_works() { assert!(val.price.publish_time == 1712589206); assert!(val.price.price == 7192002930010); - start_warp(CheatTarget::One(pyth.contract_address), 1712589204); + start_cheat_block_timestamp(pyth.contract_address, 1712589204); let val = pyth.get_price_no_older_than(price_id, 3).unwrap_with_felt252(); assert!(val.publish_time == 1712589206); assert!(val.price == 7192002930010); @@ -671,7 +669,7 @@ fn test_get_no_older_works() { assert!(val.price.publish_time == 1712589206); assert!(val.price.price == 7192002930010); - stop_warp(CheatTarget::One(pyth.contract_address)); + stop_cheat_block_timestamp(pyth.contract_address); } #[test] @@ -686,9 +684,9 @@ fn test_governance_set_fee_works() { ctx.approve_fee(10000); let mut balance = fee_contract.balanceOf(user); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let new_balance = fee_contract.balanceOf(user); assert!(balance - new_balance == 1000); balance = new_balance; @@ -697,17 +695,17 @@ fn test_governance_set_fee_works() { .unwrap_with_felt252(); assert!(last_price.price == 6281060000000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_set_fee()); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = FeeSet { - old_fee: 1000, new_fee: 4200, token: ctx.fee_contract.contract_address + old_fee: 1000, new_fee: 4200, token: ctx.fee_contract.contract_address, }; assert!(event == PythEvent::FeeSet(expected)); @@ -717,9 +715,9 @@ fn test_governance_set_fee_works() { .get_update_fee(data::test_price_update2(), ctx.fee_contract2.contract_address); assert!(fee2_alt == 2000); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_price_update2()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let new_balance = fee_contract.balanceOf(user); assert!(balance - new_balance == 4200); let last_price = pyth @@ -740,9 +738,9 @@ fn test_governance_set_fee_in_token_works() { ctx.approve_fee(1000); let mut balance = fee_contract.balanceOf(user); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let new_balance = fee_contract.balanceOf(user); assert!(balance - new_balance == 1000); balance = new_balance; @@ -751,17 +749,17 @@ fn test_governance_set_fee_in_token_works() { .unwrap_with_felt252(); assert!(last_price.price == 6281060000000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_set_fee_in_token()); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = FeeSet { - old_fee: 2000, new_fee: 4200, token: ctx.fee_contract2.contract_address + old_fee: 2000, new_fee: 4200, token: ctx.fee_contract2.contract_address, }; assert!(event == PythEvent::FeeSet(expected)); @@ -773,9 +771,9 @@ fn test_governance_set_fee_in_token_works() { ctx.approve_fee2(4200); let balance2 = ctx.fee_contract2.balanceOf(user); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_price_update2()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let new_balance2 = ctx.fee_contract2.balanceOf(user); assert!(balance2 - new_balance2 == 4200); let last_price = pyth @@ -801,21 +799,21 @@ fn test_governance_set_data_sources_works() { let pyth = ctx.pyth; ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); assert!(last_price.price == 6281060000000); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_set_data_sources()); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = DataSourcesSet { @@ -823,21 +821,21 @@ fn test_governance_set_data_sources_works() { DataSource { emitter_chain_id: 26, emitter_address: 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, - } + }, ], new_data_sources: array![ DataSource { emitter_chain_id: 1, emitter_address: 0x6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25, }, - DataSource { emitter_chain_id: 3, emitter_address: 0x12d, }, + DataSource { emitter_chain_id: 3, emitter_address: 0x12d }, ], }; assert!(event == PythEvent::DataSourcesSet(expected)); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_update2_alt_emitter()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); @@ -851,9 +849,9 @@ fn test_rejects_update_after_data_source_changed() { let pyth = ctx.pyth; ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); @@ -861,9 +859,9 @@ fn test_rejects_update_after_data_source_changed() { pyth.execute_governance_instruction(data::pyth_set_data_sources()); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_price_update2()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); @@ -872,28 +870,28 @@ fn test_rejects_update_after_data_source_changed() { #[test] fn test_governance_set_wormhole_works() { - let wormhole_class = declare("wormhole"); + let wormhole_class = declare("wormhole").unwrap().contract_class(); // Arbitrary let wormhole_address = 0x42.try_into().unwrap(); let wormhole = super::wormhole::deploy_declared_with_test_guardian_at( - @wormhole_class, wormhole_address + wormhole_class, wormhole_address, ); let user = 'user'.try_into().unwrap(); - let fee_class = declare("ERC20"); + let fee_class = declare("ERC20Upgradeable").unwrap().contract_class().deref(); let fee_contract = deploy_fee_contract(fee_class, fee_address1(), user); let fee_contract2 = deploy_fee_contract(fee_class, fee_address2(), user); let pyth = deploy_pyth_default( - wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address + wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address, ); - start_prank(CheatTarget::One(fee_contract.contract_address), user); + start_cheat_caller_address(fee_contract.contract_address, user); fee_contract.approve(pyth.contract_address, 10000); - stop_prank(CheatTarget::One(fee_contract.contract_address)); + stop_cheat_caller_address(fee_contract.contract_address); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); @@ -904,17 +902,17 @@ fn test_governance_set_wormhole_works() { .try_into() .unwrap(); let wormhole2 = super::wormhole::deploy_declared_with_test_guardian_at( - @wormhole_class, wormhole2_address + wormhole_class, wormhole2_address, ); wormhole2.submit_new_guardian_set(data::upgrade_to_test2()); - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_set_wormhole()); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = WormholeAddressSet { @@ -922,9 +920,9 @@ fn test_governance_set_wormhole_works() { }; assert!(event == PythEvent::WormholeAddressSet(expected)); - start_prank(CheatTarget::One(pyth.contract_address), user); + start_cheat_caller_address(pyth.contract_address, user); pyth.update_price_feeds(data::test_update2_set2()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); @@ -939,15 +937,15 @@ fn test_rejects_price_update_without_setting_wormhole() { ctx.approve_fee(10000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_price_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); let last_price = pyth .get_price_unsafe(0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) .unwrap_with_felt252(); assert!(last_price.price == 6281060000000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user); + start_cheat_caller_address(pyth.contract_address, ctx.user); pyth.update_price_feeds(data::test_update2_set2()); } @@ -958,19 +956,19 @@ fn test_rejects_price_update_without_setting_wormhole() { #[should_panic] #[ignore] fn test_rejects_set_wormhole_without_deploying() { - let wormhole_class = declare("wormhole"); + let wormhole_class = declare("wormhole").unwrap().contract_class(); // Arbitrary let wormhole_address = 0x42.try_into().unwrap(); let wormhole = super::wormhole::deploy_declared_with_test_guardian_at( - @wormhole_class, wormhole_address + wormhole_class, wormhole_address, ); let user = 'user'.try_into().unwrap(); - let fee_class = declare("ERC20"); + let fee_class = declare("ERC20Upgradeable").unwrap().contract_class().deref(); let fee_contract = deploy_fee_contract(fee_class, fee_address1(), user); let fee_contract2 = deploy_fee_contract(fee_class, fee_address2(), user); let pyth = deploy_pyth_default( - wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address + wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address, ); pyth.execute_governance_instruction(data::pyth_set_wormhole()); } @@ -978,19 +976,19 @@ fn test_rejects_set_wormhole_without_deploying() { #[test] #[should_panic(expected: ('Invalid signature',))] fn test_rejects_set_wormhole_with_incompatible_guardians() { - let wormhole_class = declare("wormhole"); + let wormhole_class = declare("wormhole").unwrap().contract_class(); // Arbitrary let wormhole_address = 0x42.try_into().unwrap(); let wormhole = super::wormhole::deploy_declared_with_test_guardian_at( - @wormhole_class, wormhole_address + wormhole_class, wormhole_address, ); let user = 'user'.try_into().unwrap(); - let fee_class = declare("ERC20"); + let fee_class = declare("ERC20Upgradeable").unwrap().contract_class().deref(); let fee_contract = deploy_fee_contract(fee_class, fee_address1(), user); let fee_contract2 = deploy_fee_contract(fee_class, fee_address2(), user); let pyth = deploy_pyth_default( - wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address + wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address, ); // Address used in the governance instruction @@ -998,7 +996,7 @@ fn test_rejects_set_wormhole_with_incompatible_guardians() { .try_into() .unwrap(); super::wormhole::deploy_declared_at( - @wormhole_class, + wormhole_class, 0, array_try_into(array![0x301]), super::wormhole::CHAIN_ID, @@ -1014,18 +1012,16 @@ fn test_governance_transfer_works() { let ctx = deploy_test(); let pyth = ctx.pyth; - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); - + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_auth_transfer()); - - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); let expected = GovernanceDataSourceSet { - old_data_source: DataSource { emitter_chain_id: 1, emitter_address: 41, }, - new_data_source: DataSource { emitter_chain_id: 2, emitter_address: 43, }, + old_data_source: DataSource { emitter_chain_id: 1, emitter_address: 41 }, + new_data_source: DataSource { emitter_chain_id: 2, emitter_address: 43 }, last_executed_governance_sequence: 1, }; assert!(event == PythEvent::GovernanceDataSourceSet(expected)); @@ -1057,18 +1053,15 @@ fn test_upgrade_works() { let ctx = deploy_test(); let pyth = ctx.pyth; - let class = declare("pyth_fake_upgrade1"); - - let mut spy = spy_events(SpyOn::One(pyth.contract_address)); - + let class = declare("pyth_fake_upgrade1").unwrap().contract_class(); + let mut spy = spy_events(); pyth.execute_governance_instruction(data::pyth_upgrade_fake1()); - - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == pyth.contract_address); let event = decode_event(event); - let expected = ContractUpgraded { new_class_hash: class.class_hash }; + let expected = ContractUpgraded { new_class_hash: class.class_hash.deref() }; assert!(event == PythEvent::ContractUpgraded(expected)); let last_price = pyth.get_price_unsafe(1234).unwrap_with_felt252(); @@ -1092,7 +1085,7 @@ fn test_upgrade_rejects_not_pyth() { let ctx = deploy_test(); let pyth = ctx.pyth; - declare("pyth_fake_upgrade_not_pyth"); + declare("pyth_fake_upgrade_not_pyth").unwrap().contract_class(); pyth.execute_governance_instruction(data::pyth_upgrade_not_pyth()); } @@ -1102,7 +1095,7 @@ fn test_upgrade_rejects_wrong_magic() { let ctx = deploy_test(); let pyth = ctx.pyth; - declare("pyth_fake_upgrade_wrong_magic"); + declare("pyth_fake_upgrade_wrong_magic").unwrap().contract_class(); pyth.execute_governance_instruction(data::pyth_upgrade_wrong_magic()); } @@ -1113,9 +1106,9 @@ fn update_price_feeds_with_set3_rejects_on_guardian_set4() { let ctx = deploy_with_wormhole(wormhole); let pyth = ctx.pyth; ctx.approve_fee(1000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -1124,9 +1117,9 @@ fn update_price_feeds_works_with_guardian_set4() { let ctx = deploy_with_wormhole(wormhole); let pyth = ctx.pyth; ctx.approve_fee(1000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::unique_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[test] @@ -1135,10 +1128,10 @@ fn update_price_feeds_works_with_guardian_sets_3_4() { let ctx = deploy_with_wormhole(wormhole); let pyth = ctx.pyth; ctx.approve_fee(2000); - start_prank(CheatTarget::One(pyth.contract_address), ctx.user.try_into().unwrap()); + start_cheat_caller_address(pyth.contract_address, ctx.user.try_into().unwrap()); pyth.update_price_feeds(data::good_update1()); pyth.update_price_feeds(data::unique_update1()); - stop_prank(CheatTarget::One(pyth.contract_address)); + stop_cheat_caller_address(pyth.contract_address); } #[derive(Drop, Copy)] @@ -1153,15 +1146,15 @@ struct Context { #[generate_trait] impl ContextImpl of ContextTrait { fn approve_fee(self: Context, amount: u256) { - start_prank(CheatTarget::One(self.fee_contract.contract_address), self.user); + start_cheat_caller_address(self.fee_contract.contract_address, self.user); self.fee_contract.approve(self.pyth.contract_address, amount); - stop_prank(CheatTarget::One(self.fee_contract.contract_address)); + stop_cheat_caller_address(self.fee_contract.contract_address); } fn approve_fee2(self: Context, amount: u256) { - start_prank(CheatTarget::One(self.fee_contract2.contract_address), self.user); + start_cheat_caller_address(self.fee_contract2.contract_address, self.user); self.fee_contract2.approve(self.pyth.contract_address, amount); - stop_prank(CheatTarget::One(self.fee_contract2.contract_address)); + stop_cheat_caller_address(self.fee_contract2.contract_address); } } @@ -1175,11 +1168,11 @@ fn deploy_mainnet() -> Context { fn deploy_with_wormhole(wormhole: IWormholeDispatcher) -> Context { let user = 'user'.try_into().unwrap(); - let fee_class = declare("ERC20"); + let fee_class = declare("ERC20Upgradeable").unwrap().contract_class().deref(); let fee_contract = deploy_fee_contract(fee_class, fee_address1(), user); let fee_contract2 = deploy_fee_contract(fee_class, fee_address2(), user); let pyth = deploy_pyth_default( - wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address + wormhole.contract_address, fee_contract.contract_address, fee_contract2.contract_address, ); Context { user, wormhole, fee_contract, fee_contract2, pyth } } @@ -1187,7 +1180,7 @@ fn deploy_with_wormhole(wormhole: IWormholeDispatcher) -> Context { fn deploy_pyth_default( wormhole_address: ContractAddress, fee_token_address1: ContractAddress, - fee_token_address2: ContractAddress + fee_token_address2: ContractAddress, ) -> IPythDispatcher { deploy_pyth( wormhole_address, @@ -1199,7 +1192,7 @@ fn deploy_pyth_default( DataSource { emitter_chain_id: 26, emitter_address: 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, - } + }, ], 1, 41, @@ -1223,10 +1216,10 @@ fn deploy_pyth( (fee_token_address2, single_update_fee2).serialize(ref args); (data_sources, governance_emitter_chain_id).serialize(ref args); (governance_emitter_address, governance_initial_sequence).serialize(ref args); - let contract = declare("pyth"); - let contract_address = match contract.deploy(@args) { + let contract = declare("pyth").unwrap().contract_class(); + let (contract_address, _) = match contract.deploy(@args) { Result::Ok(v) => { v }, - Result::Err(err) => { panic(err.panic_data) }, + Result::Err(err) => { panic(err) }, }; IPythDispatcher { contract_address } } @@ -1239,15 +1232,16 @@ fn fee_address2() -> ContractAddress { } fn deploy_fee_contract( - class: ContractClass, at: ContractAddress, recipient: ContractAddress + class: ContractClass, at: ContractAddress, recipient: ContractAddress, ) -> IERC20CamelDispatcher { let mut args = array![]; let name: ByteArray = "eth"; let symbol: ByteArray = "eth"; - (name, symbol, 100000_u256, recipient).serialize(ref args); - let contract_address = match class.deploy_at(@args, at) { + let owner: ContractAddress = 1.try_into().unwrap(); + (name, symbol, 100000_u256, recipient, owner).serialize(ref args); + let (contract_address, _) = match class.deploy_at(@args, at) { Result::Ok(v) => { v }, - Result::Err(err) => { panic(err.panic_data) }, + Result::Err(err) => { panic(err) }, }; IERC20CamelDispatcher { contract_address } } diff --git a/target_chains/starknet/contracts/tests/wormhole.cairo b/target_chains/starknet/contracts/tests/wormhole.cairo index 25abac4726..cd03f40450 100644 --- a/target_chains/starknet/contracts/tests/wormhole.cairo +++ b/target_chains/starknet/contracts/tests/wormhole.cairo @@ -1,20 +1,18 @@ -use snforge_std::{ - declare, ContractClass, ContractClassTrait, start_prank, stop_prank, CheatTarget, Event, - event_name_hash, spy_events, SpyOn, EventFetcher, -}; +use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl}; +use pyth::reader::ReaderImpl; +use pyth::util::{array_try_into, one_shift_left_bytes_u256}; use pyth::wormhole::{ - IWormholeDispatcher, IWormholeDispatcherTrait, ParseAndVerifyVmError, Event as WormholeEvent, - GuardianSetAdded + Event as WormholeEvent, GuardianSetAdded, IWormholeDispatcher, IWormholeDispatcherTrait, }; -use pyth::reader::ReaderImpl; -use pyth::byte_buffer::{ByteBuffer, ByteBufferImpl}; -use pyth::util::{UnwrapWithFelt252, array_try_into, one_shift_left_bytes_u256}; -use core::starknet::{ContractAddress, EthAddress}; -use core::panic_with_felt252; -use super::data; +use snforge_std::{ + ContractClass, ContractClassTrait, DeclareResultTrait, Event, EventSpyTrait, EventsFilterTrait, + declare, spy_events, +}; +use starknet::{ContractAddress, EthAddress}; use super::wormhole_guardians::{ - guardian_set0, guardian_set1, guardian_set2, guardian_set3, guardian_set4 + guardian_set0, guardian_set1, guardian_set2, guardian_set3, guardian_set4, }; +use super::data; #[generate_trait] impl DecodeEventHelpers of DecodeEventHelpersTrait { @@ -25,8 +23,8 @@ impl DecodeEventHelpers of DecodeEventHelpersTrait { fn decode_event(mut event: Event) -> WormholeEvent { let key0: felt252 = event.keys.pop(); - let output = if key0 == event_name_hash('GuardianSetAdded') { - let event = GuardianSetAdded { index: event.data.pop(), }; + let output = if key0 == selector!("GuardianSetAdded") { + let event = GuardianSetAdded { index: event.data.pop() }; WormholeEvent::GuardianSetAdded(event) } else { panic!("unrecognized event") @@ -65,7 +63,7 @@ fn test_parse_and_verify_vm_works() { assert!(vm.nonce == 0); assert!(vm.emitter_chain_id == 26); assert!( - vm.emitter_address == 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71 + vm.emitter_address == 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71, ); assert!(vm.sequence == 0x2f03161); assert!(vm.consistency_level == 1); @@ -109,7 +107,7 @@ fn test_submit_guardian_set_rejects_wrong_index_in_signer() { fn test_submit_guardian_set_emits_events() { let dispatcher = deploy_with_mainnet_guardian_set0(); - let mut spy = spy_events(SpyOn::One(dispatcher.contract_address)); + let mut spy = spy_events(); assert!(dispatcher.get_current_guardian_set_index() == 0); let hash1 = 107301215816534416941414788869570552056251358022232518071775510605007996627157; @@ -120,10 +118,9 @@ fn test_submit_guardian_set_emits_events() { assert!(!dispatcher.governance_action_is_consumed(hash3)); dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade1()); - - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().emitted_by(dispatcher.contract_address).events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == dispatcher.contract_address); let event = decode_event(event); let expected = GuardianSetAdded { index: 1 }; @@ -134,11 +131,12 @@ fn test_submit_guardian_set_emits_events() { assert!(!dispatcher.governance_action_is_consumed(hash2)); assert!(!dispatcher.governance_action_is_consumed(hash3)); + let mut spy = spy_events(); dispatcher.submit_new_guardian_set(data::mainnet_guardian_set_upgrade2()); - spy.fetch_events(); - assert!(spy.events.len() == 1); - let (from, event) = spy.events.pop_front().unwrap(); + let mut events = spy.get_events().emitted_by(dispatcher.contract_address).events; + assert!(events.len() == 1); + let (from, event) = events.pop_front().unwrap(); assert!(from == dispatcher.contract_address); let event = decode_event(event); let expected = GuardianSetAdded { index: 2 }; @@ -232,6 +230,7 @@ fn test_submit_guardian_set_rejects_corrupted(pos: usize, random1: usize, random } #[test] +#[fuzzer(runs: 100, seed: 0)] #[should_panic(expected: ('wrong governance chain',))] fn test_submit_guardian_set_rejects_non_governance(pos: usize, random1: usize, random2: usize) { let dispatcher = deploy_with_mainnet_guardian_set0(); @@ -261,9 +260,9 @@ pub fn deploy_declared_at( Option::Some(address) => class.deploy_at(@args, address), Option::None => class.deploy(@args), }; - let contract_address = match result { + let (contract_address, _) = match result { Result::Ok(v) => { v }, - Result::Err(err) => { panic(err.panic_data) }, + Result::Err(err) => { panic(err) }, }; IWormholeDispatcher { contract_address } } @@ -276,7 +275,7 @@ fn deploy( governance_chain_id: u16, governance_contract: u256, ) -> IWormholeDispatcher { - let class = declare("wormhole"); + let class = declare("wormhole").unwrap().contract_class().deref(); deploy_declared_at( @class, guardian_set_index, @@ -284,7 +283,7 @@ fn deploy( chain_id, governance_chain_id, governance_contract, - Option::None + Option::None, ) } @@ -314,7 +313,8 @@ pub fn deploy_with_mainnet_guardian_set0() -> IWormholeDispatcher { deploy(0, guardian_set0(), CHAIN_ID, GOVERNANCE_CHAIN_ID, GOVERNANCE_CONTRACT) } -// Declares and deploys the contract with the test guardian address that's used to sign VAAs generated in `test_vaas`. +// Declares and deploys the contract with the test guardian address that's used to sign VAAs +// generated in `test_vaas`. pub fn deploy_with_test_guardian() -> IWormholeDispatcher { deploy( 0, @@ -328,7 +328,7 @@ pub fn deploy_with_test_guardian() -> IWormholeDispatcher { // Deploys a previously declared wormhole contract class // with the test guardian address that's used to sign VAAs generated in `test_vaas`. pub fn deploy_declared_with_test_guardian_at( - class: @ContractClass, address: ContractAddress + class: @ContractClass, address: ContractAddress, ) -> IWormholeDispatcher { deploy_declared_at( class, @@ -342,7 +342,7 @@ pub fn deploy_declared_with_test_guardian_at( } pub fn corrupted_vm( - mut real_data: ByteBuffer, pos: usize, random1: usize, random2: usize + mut real_data: ByteBuffer, pos: usize, random1: usize, random2: usize, ) -> ByteBuffer { let mut new_data = array![]; @@ -356,7 +356,7 @@ pub fn corrupted_vm( loop { let (real_bytes, num_bytes) = match real_data.pop_front() { Option::Some(v) => v, - Option::None => { break; } + Option::None => { break; }, }; if num_bytes < 31 { new_data.append(real_bytes); @@ -370,7 +370,7 @@ pub fn corrupted_vm( new_data.append(real_bytes); } i += 1; - }; + } ByteBufferImpl::new(new_data, num_last_bytes) } @@ -395,7 +395,7 @@ fn corrupted_bytes(input: felt252, index: usize, random1: usize, random2: usize) }; value = value * 256 + new_byte.into(); i += 1; - }; + } let value: felt252 = value.try_into().expect('corrupted bytes overflow'); value.try_into().expect('corrupted bytes overflow') } diff --git a/target_chains/starknet/contracts/tests/wormhole_guardians.cairo b/target_chains/starknet/contracts/tests/wormhole_guardians.cairo index 7ffa6ab516..58476b2481 100644 --- a/target_chains/starknet/contracts/tests/wormhole_guardians.cairo +++ b/target_chains/starknet/contracts/tests/wormhole_guardians.cairo @@ -1,5 +1,5 @@ use pyth::util::array_try_into; -use core::starknet::EthAddress; +use starknet::EthAddress; // Initial mainnet guardian set. pub fn guardian_set0() -> Array { diff --git a/target_chains/starknet/sdk/js/src/abi/erc20.json b/target_chains/starknet/sdk/js/src/abi/erc20.json index 9502988386..ae4a8040da 100644 --- a/target_chains/starknet/sdk/js/src/abi/erc20.json +++ b/target_chains/starknet/sdk/js/src/abi/erc20.json @@ -1,8 +1,91 @@ [ + { + "type": "impl", + "name": "UpgradeableImpl", + "interface_name": "openzeppelin_upgrades::interface::IUpgradeable" + }, + { + "type": "interface", + "name": "openzeppelin_upgrades::interface::IUpgradeable", + "items": [ + { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "new_class_hash", + "type": "core::starknet::class_hash::ClassHash" + } + ], + "outputs": [], + "state_mutability": "external" + } + ] + }, + { + "type": "impl", + "name": "OwnableMixinImpl", + "interface_name": "openzeppelin_access::ownable::interface::OwnableABI" + }, + { + "type": "interface", + "name": "openzeppelin_access::ownable::interface::OwnableABI", + "items": [ + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "transfer_ownership", + "inputs": [ + { + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "renounce_ownership", + "inputs": [], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "core::starknet::contract_address::ContractAddress" + } + ], + "outputs": [], + "state_mutability": "external" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "state_mutability": "external" + } + ] + }, { "type": "impl", "name": "ERC20MixinImpl", - "interface_name": "openzeppelin::token::erc20::interface::ERC20ABI" + "interface_name": "openzeppelin_token::erc20::interface::IERC20Mixin" }, { "type": "struct", @@ -52,7 +135,7 @@ }, { "type": "interface", - "name": "openzeppelin::token::erc20::interface::ERC20ABI", + "name": "openzeppelin_token::erc20::interface::IERC20Mixin", "items": [ { "type": "function", @@ -270,12 +353,67 @@ { "name": "recipient", "type": "core::starknet::contract_address::ContractAddress" + }, + { + "name": "owner", + "type": "core::starknet::contract_address::ContractAddress" } ] }, { "type": "event", - "name": "openzeppelin::token::erc20::erc20::ERC20Component::Transfer", + "name": "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", + "kind": "struct", + "members": [ + { + "name": "previous_owner", + "type": "core::starknet::contract_address::ContractAddress", + "kind": "key" + }, + { + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress", + "kind": "key" + } + ] + }, + { + "type": "event", + "name": "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + "kind": "struct", + "members": [ + { + "name": "previous_owner", + "type": "core::starknet::contract_address::ContractAddress", + "kind": "key" + }, + { + "name": "new_owner", + "type": "core::starknet::contract_address::ContractAddress", + "kind": "key" + } + ] + }, + { + "type": "event", + "name": "openzeppelin_access::ownable::ownable::OwnableComponent::Event", + "kind": "enum", + "variants": [ + { + "name": "OwnershipTransferred", + "type": "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferred", + "kind": "nested" + }, + { + "name": "OwnershipTransferStarted", + "type": "openzeppelin_access::ownable::ownable::OwnableComponent::OwnershipTransferStarted", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "openzeppelin_token::erc20::erc20::ERC20Component::Transfer", "kind": "struct", "members": [ { @@ -297,7 +435,7 @@ }, { "type": "event", - "name": "openzeppelin::token::erc20::erc20::ERC20Component::Approval", + "name": "openzeppelin_token::erc20::erc20::ERC20Component::Approval", "kind": "struct", "members": [ { @@ -319,29 +457,63 @@ }, { "type": "event", - "name": "openzeppelin::token::erc20::erc20::ERC20Component::Event", + "name": "openzeppelin_token::erc20::erc20::ERC20Component::Event", "kind": "enum", "variants": [ { "name": "Transfer", - "type": "openzeppelin::token::erc20::erc20::ERC20Component::Transfer", + "type": "openzeppelin_token::erc20::erc20::ERC20Component::Transfer", "kind": "nested" }, { "name": "Approval", - "type": "openzeppelin::token::erc20::erc20::ERC20Component::Approval", + "type": "openzeppelin_token::erc20::erc20::ERC20Component::Approval", + "kind": "nested" + } + ] + }, + { + "type": "event", + "name": "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Upgraded", + "kind": "struct", + "members": [ + { + "name": "class_hash", + "type": "core::starknet::class_hash::ClassHash", + "kind": "data" + } + ] + }, + { + "type": "event", + "name": "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Event", + "kind": "enum", + "variants": [ + { + "name": "Upgraded", + "type": "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Upgraded", "kind": "nested" } ] }, { "type": "event", - "name": "openzeppelin::presets::erc20::ERC20::Event", + "name": "openzeppelin_presets::erc20::ERC20Upgradeable::Event", "kind": "enum", "variants": [ + { + "name": "OwnableEvent", + "type": "openzeppelin_access::ownable::ownable::OwnableComponent::Event", + "kind": "flat" + }, { "name": "ERC20Event", - "type": "openzeppelin::token::erc20::erc20::ERC20Component::Event", + "type": "openzeppelin_token::erc20::erc20::ERC20Component::Event", + "kind": "flat" + }, + { + "name": "UpgradeableEvent", + "type": "openzeppelin_upgrades::upgradeable::UpgradeableComponent::Event", "kind": "flat" } ] diff --git a/target_chains/starknet/tools/test_vaas/rust-toolchain b/target_chains/starknet/tools/test_vaas/rust-toolchain index 54227249d1..b7844a6ffd 100644 --- a/target_chains/starknet/tools/test_vaas/rust-toolchain +++ b/target_chains/starknet/tools/test_vaas/rust-toolchain @@ -1 +1 @@ -1.78.0 +1.86.0