diff --git a/.github/actions/install-starknet-foundry/action.yml b/.github/actions/install-starknet-foundry/action.yml new file mode 100644 index 000000000..060ea6298 --- /dev/null +++ b/.github/actions/install-starknet-foundry/action.yml @@ -0,0 +1,19 @@ +name: Install Starknet Foundry (snforge and sncast) +description: A composite action that installs the snforge and sncast binaries + +inputs: + starknet_foundry_version: + description: Starknet Foundry release version + default: "0.18.0" + required: false + +runs: + using: composite + steps: + - name: Setup Starknet Foundry for Linux + id: install-starknet-foundry + shell: bash + run: | + curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh + snfoundryup -v ${{ inputs.starknet_foundry_version }} + diff --git a/.github/workflows/integration_contracts.yml b/.github/workflows/examples.yml similarity index 73% rename from .github/workflows/integration_contracts.yml rename to .github/workflows/examples.yml index fa421a134..28572831c 100644 --- a/.github/workflows/integration_contracts.yml +++ b/.github/workflows/examples.yml @@ -1,4 +1,4 @@ -name: Integration Contracts (Vendor, Examples) +name: Example Contracts on: push: @@ -8,7 +8,7 @@ on: pull_request: jobs: - integration_contracts_run_tests: + run_examples_tests: name: Run Tests runs-on: ubuntu-latest steps: @@ -23,5 +23,8 @@ jobs: - name: Install Cairo uses: ./.github/actions/install-cairo + - name: Install Starknet Foundry + uses: ./.github/actions/install-starknet-foundry + - name: Test - run: nix develop -c make test-integration-contracts + run: nix develop -c make test-examples diff --git a/Makefile b/Makefile index 22937f60b..81eafba7d 100644 --- a/Makefile +++ b/Makefile @@ -213,16 +213,10 @@ test-integration-soak-ci: cd integration-tests/ && \ go test --timeout=1h -v -count=1 -json ./soak -.PHONY: test-integration-contracts -# TODO: better network lifecycle setup - requires external network (L1 + L2) -# TODO: readd test examples -# cd examples/contracts/aggregator-consumer/ && \ -# yarn test -test-integration-contracts: build-ts env-devnet-hardhat - echo "Tests currently broken because of starknet-hardhat-plugin" - exit 1 - cd packages-ts/starknet/ && \ - yarn test +.PHONY: test-examples +test-examples: + cd ./examples/contracts/aggregator_consumer && \ + snforge test .PHONY: test-integration-gauntlet # TODO: fix example diff --git a/contracts/package.json b/contracts/package.json index 131fc7d74..8bad5cc62 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -24,7 +24,6 @@ }, "dependencies": { "@chainlink/contracts": "^0.4.2", - "@chainlink/starknet": "^1.0.0", "@openzeppelin/contracts": "^4.7.3", "axios": "^0.24.0" } diff --git a/packages-ts/starknet/src/account.ts b/contracts/test/account.ts similarity index 100% rename from packages-ts/starknet/src/account.ts rename to contracts/test/account.ts diff --git a/contracts/test/emergency/StarknetValidator.test.ts b/contracts/test/emergency/StarknetValidator.test.ts index 54385109f..5eedc51fa 100644 --- a/contracts/test/emergency/StarknetValidator.test.ts +++ b/contracts/test/emergency/StarknetValidator.test.ts @@ -1,6 +1,6 @@ import { ethers, starknet, network } from 'hardhat' import { BigNumber, Contract, ContractFactory } from 'ethers' -import { hash, number } from 'starknet' +import { hash } from 'starknet' import { Account, StarknetContractFactory, @@ -13,7 +13,8 @@ import { abi as aggregatorAbi } from '../../artifacts/@chainlink/contracts/src/v import { abi as accessControllerAbi } from '../../artifacts/@chainlink/contracts/src/v0.8/interfaces/AccessControllerInterface.sol/AccessControllerInterface.json' import { abi as starknetMessagingAbi } from '../../artifacts/vendor/starkware-libs/cairo-lang/src/starkware/starknet/solidity/IStarknetMessaging.sol/IStarknetMessaging.json' import { deployMockContract, MockContract } from '@ethereum-waffle/mock-contract' -import { account, addCompilationToNetwork, expectSuccessOrDeclared } from '@chainlink/starknet' +import * as account from '../account' +import { addCompilationToNetwork, expectSuccessOrDeclared } from '../utils' describe('StarknetValidator', () => { /** Fake L2 target */ diff --git a/contracts/test/ocr2/aggregator.test.ts b/contracts/test/ocr2/aggregator.test.ts index 3d53e7ccd..2b07e55d0 100644 --- a/contracts/test/ocr2/aggregator.test.ts +++ b/contracts/test/ocr2/aggregator.test.ts @@ -3,7 +3,8 @@ import { starknet } from 'hardhat' import { ec, hash, num } from 'starknet' import { Account, StarknetContract, StarknetContractFactory } from 'hardhat/types/runtime' import { TIMEOUT } from '../constants' -import { account, expectInvokeError, expectSuccessOrDeclared } from '@chainlink/starknet' +import { expectInvokeError, expectSuccessOrDeclared } from '../utils' +import * as account from '../account' import { bytesToFelts } from '@chainlink/starknet-gauntlet' interface Oracle { diff --git a/packages-ts/starknet/src/utils.ts b/contracts/test/utils.ts similarity index 100% rename from packages-ts/starknet/src/utils.ts rename to contracts/test/utils.ts diff --git a/examples/contracts/aggregator-consumer/.gitignore b/examples/contracts/aggregator-consumer/.gitignore deleted file mode 100644 index 96611b42c..000000000 --- a/examples/contracts/aggregator-consumer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target -cache/ -starknet-artifacts/ diff --git a/examples/contracts/aggregator-consumer/README.md b/examples/contracts/aggregator-consumer/README.md deleted file mode 100644 index e390b44ef..000000000 --- a/examples/contracts/aggregator-consumer/README.md +++ /dev/null @@ -1,70 +0,0 @@ -This example demonstrates how consumer contracts can read from the aggregator, with and without consideration of the uptime feed address. Recall that the purpose of the uptime feed is to let consumer contracts know if the L2 layer is up and healthy. - -We demonstrate these examples by mocking an aggregator to return stubbed values. - -You can run this example on your local devnet or on goerli (up to you). NOTE: at the time of writing, the examples only work against devnet due to Starknet version incompatability. - -Note: `.env` will contain account details, such as DEPLOYER_ACCOUNT_ADDRESS and DEPLOYER_PRIVATE_KEY - -## Deploy on Devnet - -### 1. Install Local Dev Environment - -Follow [steps 1 and 2 here](../proxy_consumer/README.md) - -At this point, you should have starknet-devnet, cairo, and scarb installed. Your virtualenv will also have the starknet cli tool but we won't be using that to test against devnet - -All instructions begin at the root of this folder -### 2. Compile cairo 1 contracts - -``` - yarn compile:cairo -``` - - -### 3. Setup Starknet Devnet - -In a seperate terminal, run: -``` - starknet-devnet --cairo-compiler-manifest ../../vendor/cairo/Cargo.toml --seed 0 --lite-mode ../../../vendor/cairo/Cargo.toml -``` -This will start up the devnet and enable you to deploy and run cairo 1 contracts against it - -### 4. Deploy Devnet Account - -This command will deploy a devnet account onto devnet and write the DEPLOYER_ACCOUNT_ADDRESS and DEPLOYER_PRIVATE_KEY into a .env file in the current directory (it will create .env if it doesn't exist). This account will be utilized for deploying and interacting with the rest of the contracts in this section. - -``` - yarn deployAccount -``` - -## 5. Deploy Contracts - -This will deploy the following contracts: -* SequencerUptimeFeed: Displays the status of the L2 layer. If it is up, then it is safe to read from the aggregator contract. For more information please see this [document](../../../docs/emergency-protocol/README.md). -* MockAggregator: A mocked version of the aggregator with limited functionality. It gives the reader the ability to set and view the latest round data in order so the reader can familiarize themselves for testing purposes. -* AggregatorConsumer: Simply reads the mocked aggregator's latest values -* AggregatorPriceConsumerWithSequencer: Reads the mocked aggregator's values but also queries the SequencerUptimeFeed to determine if the value should be used or not. If the SequencerUptimeFeed is too stale, then that means the L2 layer is down. We've arbitrarily chosen the threshold of 60 seconds. - - -``` - yarn deployContracts -``` - -## 6. Interact with Contracts - -``` -# deployer calls AggregatorConsumer to read the decimals method of the MockAggregator -yarn readDecimals - -# deployer calls AggregatorConsumer to read the latest round of the MockAggregator -yarn readLatestRound - -# deployer calls Aggregator Consumer to poll decimals AND latest round of MockAggregator -yarn readContinuously - -# deployer calls MockAggregator to manually set the new round's data -yarn updateLatestRound - -# deployer calls AggregatorPriceConsumerWithSequencer to read latest round or revert if uptime feed is stale -yarn getLatestPriceSeqCheck diff --git a/examples/contracts/aggregator-consumer/Scarb.toml b/examples/contracts/aggregator-consumer/Scarb.toml deleted file mode 100644 index 18ac52c35..000000000 --- a/examples/contracts/aggregator-consumer/Scarb.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "chainlink_examples" -version = "0.1.0" - -# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest - -[dependencies] - chainlink = { path = "../../../contracts" } - -[[target.starknet-contract]] -# note these two options only work on scarb 0.2.0 and forward -casm = true -# Emit Python-powered hints in order to run compiled CASM class with legacy Cairo VM. -casm-add-pythonic-hints = true diff --git a/examples/contracts/aggregator-consumer/cairo_project.toml b/examples/contracts/aggregator-consumer/cairo_project.toml deleted file mode 100644 index e2ab1041e..000000000 --- a/examples/contracts/aggregator-consumer/cairo_project.toml +++ /dev/null @@ -1,2 +0,0 @@ -[crate_roots] -chainlink_examples = "src" diff --git a/examples/contracts/aggregator-consumer/example.env b/examples/contracts/aggregator-consumer/example.env deleted file mode 100644 index 7d29ecc93..000000000 --- a/examples/contracts/aggregator-consumer/example.env +++ /dev/null @@ -1,11 +0,0 @@ -# This is a an example .env file that contains pre-deployed and pre-funded account that come with starknet-devnet -# To see a complete list of pre-deployed starknet-devnet accounts just run `starknet-devnet` - -# hypothetical devnet account -DEPLOYER_ACCOUNT_ADDRESS= -DEPLOYER_PRIVATE_KEY= - -# Below is what you'd set to run these examples on GOERLI -# DEPLOYER_ACCOUNT_ADDRESS= -# DEPLOYER_PRIVATE_KEY= -# NETWORK=GOERLI diff --git a/examples/contracts/aggregator-consumer/hardhat.config.ts b/examples/contracts/aggregator-consumer/hardhat.config.ts deleted file mode 100644 index 965b87a03..000000000 --- a/examples/contracts/aggregator-consumer/hardhat.config.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { HardhatUserConfig } from 'hardhat/types' -import '@shardlabs/starknet-hardhat-plugin' -import '@nomiclabs/hardhat-ethers' - -const config: HardhatUserConfig = { - solidity: '0.8.14', - starknet: { - venv: 'active', - network: 'devnet', - wallets: { - OpenZeppelin: { - accountName: 'OpenZeppelin', - modulePath: 'starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount', - accountPath: '~/.starknet_accounts', - }, - }, - }, - networks: { - devnet: { - url: 'http://127.0.0.1:5050', - args: ['--cairo-compiler-manifest', '../../../vendor/cairo/Cargo.toml'], - }, - integratedDevnet: { - url: 'http://127.0.0.1:5050', - venv: 'active', - args: ['--cairo-compiler-manifest', '../../../vendor/cairo/Cargo.toml', '--lite-mode'], - // dockerizedVersion: "0.2.0" - }, - }, - paths: { - cairoPaths: ['../../contracts/src'], - }, -} - -export default config diff --git a/examples/contracts/aggregator-consumer/package.json b/examples/contracts/aggregator-consumer/package.json deleted file mode 100644 index 2aff8abbe..000000000 --- a/examples/contracts/aggregator-consumer/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@chainlink/latest-starknet-ocr2-consumer", - "version": "0.1.0", - "description": "", - "main": "index.js", - "scripts": { - "compile:cairo": "scarb --profile release build", - "compile": "yarn compile:cairo", - "test": "yarn hardhat --network localhost test", - "deployAccount": "yarn ts-node ./scripts/deploy_accounts.ts", - "deployContracts": "yarn ts-node ./scripts/deploy_contracts.ts", - "readDecimals": "yarn ts-node ./scripts/readDecimals.ts", - "readLatestRound": "yarn ts-node ./scripts/readLatestRound.ts", - "readContinuously": "yarn ts-node ./scripts/readContinuously.ts", - "updateLatestRound": "yarn ts-node ./scripts/updateLatestRound.ts", - "getLatestPriceSeqCheck": "yarn ts-node ./scripts/getLatestPriceSeqCheck.ts" - }, - "keywords": [], - "author": "", - "license": "MIT", - "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.1.0", - "@shardlabs/starknet-hardhat-plugin": "^0.8.0-alpha.0", - "@types/chai": "^4.3.3", - "@types/mocha": "^9.1.1", - "chai": "^4.3.6", - "hardhat": "^*" - }, - "dependencies": { - "@chainlink/starknet": "^1.0.0", - "dotenv": "^16.0.1" - } -} diff --git a/examples/contracts/aggregator-consumer/scripts/consumerValidator.ts b/examples/contracts/aggregator-consumer/scripts/consumerValidator.ts deleted file mode 100644 index 6441c3ff2..000000000 --- a/examples/contracts/aggregator-consumer/scripts/consumerValidator.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { ethers, starknet, network } from 'hardhat' -import { Contract, ContractFactory } from 'ethers' -import { HttpNetworkConfig } from 'hardhat/types' - -import dotenv from 'dotenv' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { deployMockContract, MockContract } from '@ethereum-waffle/mock-contract' -import { Account, CompiledContract, Contract as StarknetContract } from 'starknet' -import { - createDeployerAccount, - loadContractPath, - loadContract_Solidity, - loadContract_Solidity_V8, - makeProvider, -} from './utils' - -dotenv.config({ path: __dirname + '/../.env' }) -const UPTIME_FEED_PATH = '../../../../contracts/target/release/chainlink_SequencerUptimeFeed' - -// TODO: need to modify when the cairo-1.0 branch is rebased to include StarknetValidator changes - -let validator: Contract -let mockStarknetMessengerFactory: ContractFactory -let mockStarknetMessenger: Contract -let deployer: SignerWithAddress -let eoaValidator: SignerWithAddress -let networkUrl: string -let account: Account - -let mockGasPriceFeed: MockContract -let mockAccessController: MockContract -let mockAggregator: MockContract - -export async function consumerValidator() { - const provider = makeProvider() - - account = createDeployerAccount(provider) - - networkUrl = (network.config as HttpNetworkConfig).url - const accounts = await ethers.getSigners() - deployer = accounts[0] - eoaValidator = accounts[1] - - const aggregatorAbi = loadContract_Solidity_V8('AggregatorV3Interface') - const accessControllerAbi = loadContract_Solidity_V8('AccessControllerInterface') - - // Deploy the mock feed - mockGasPriceFeed = await deployMockContract(deployer, aggregatorAbi.abi) - await mockGasPriceFeed.mock.latestRoundData.returns( - '73786976294838220258' /** roundId */, - '96800000000' /** answer */, - '163826896' /** startedAt */, - '1638268960' /** updatedAt */, - '73786976294838220258' /** answeredInRound */, - ) - - // Deploy the mock access controller - mockAccessController = await deployMockContract(deployer, accessControllerAbi.abi) - - // Deploy the mock aggregator - mockAggregator = await deployMockContract(deployer, aggregatorAbi.abi) - await mockAggregator.mock.latestRoundData.returns( - '73786976294838220258' /** roundId */, - 1 /** answer */, - '163826896' /** startedAt */, - '1638268960' /** updatedAt */, - '73786976294838220258' /** answeredInRound */, - ) - - const validatorArtifact = await loadContract_Solidity('emergency', 'StarknetValidator') - const validatorFactory = await ethers.getContractFactoryFromArtifact(validatorArtifact, deployer) - - const mockStarknetMessagingArtifact = await loadContract_Solidity( - 'mocks', - 'MockStarknetMessaging', - ) - mockStarknetMessengerFactory = await ethers.getContractFactoryFromArtifact( - mockStarknetMessagingArtifact, - deployer, - ) - - const messageCancellationDelay = 5 * 60 // seconds - mockStarknetMessenger = await mockStarknetMessengerFactory.deploy(messageCancellationDelay) - await mockStarknetMessenger.deployed() - - const UptimeFeedArtifact = loadContractPath(UPTIME_FEED_PATH) as CompiledContract - - const mockUptimeFeedDeploy = new StarknetContract( - UptimeFeedArtifact.abi, - process.env.UPTIME_FEED as string, - provider, - ) - - mockUptimeFeedDeploy.connect(account) - - validator = await validatorFactory.deploy( - mockStarknetMessenger.address, - mockAccessController.address, - mockGasPriceFeed.address, - mockAggregator.address, - mockUptimeFeedDeploy.address, - 0, - ) - - console.log('Validator address: ', validator.address) - - const tx = await mockUptimeFeedDeploy.invoke('set_l1_sender', [validator.address]) - - await provider.waitForTransaction(tx.transaction_hash) - - await validator.addAccess(eoaValidator.address) - setInterval(callFunction, 60_000) -} - -async function callFunction() { - await starknet.devnet.loadL1MessagingContract(networkUrl, mockStarknetMessenger.address) - - await validator.connect(eoaValidator).validate(0, 0, 1, 1) - - const flushL1Response = await starknet.devnet.flush() - flushL1Response.consumed_messages.from_l1 -} -consumerValidator() diff --git a/examples/contracts/aggregator-consumer/scripts/deploy_accounts.ts b/examples/contracts/aggregator-consumer/scripts/deploy_accounts.ts deleted file mode 100644 index e031c63fa..000000000 --- a/examples/contracts/aggregator-consumer/scripts/deploy_accounts.ts +++ /dev/null @@ -1,80 +0,0 @@ -import * as fs from 'fs' -import * as dotenv from 'dotenv' -import { starknet } from 'hardhat' -import { exit } from 'node:process' -import { loadContract_Account, makeProvider } from './utils' -import { Account, CallData, ec, hash } from 'starknet' - -const ENV_PATH = __dirname + '/../.env' - -dotenv.config({ path: ENV_PATH }) - -interface UserAccount { - address: string - privateKey: string -} - -export async function deployAccount() { - const account = await createAccount() - - try { - fs.appendFileSync(ENV_PATH, '\n# Autogenerated Dev Account from ./scripts/deploy_account.ts') - fs.appendFileSync(ENV_PATH, '\nDEPLOYER_ACCOUNT_ADDRESS=' + account.address) - fs.appendFileSync(ENV_PATH, '\nDEPLOYER_PRIVATE_KEY=' + account.privateKey) - } catch (err) { - throw err - } - - exit(0) // must manually exit due to ts-node weirdness -} - -async function createAccount(): Promise { - // use pre-deployed accounts to deploy new OZ account - const accounts = await starknet.devnet.getPredeployedAccounts() - const account = accounts[0] - const provider = makeProvider() - const fundedAccount = new Account(provider, account.address, account.private_key) - - // define account parameters - const OZaccountClassHash = '0x4d07e40e93398ed3c76981e72dd1fd22557a78ce36c0515f679e27f0bb5bc5f' - const compiledAccount = loadContract_Account('Account') - const privateKey = ec.starkCurve.utils.randomPrivateKey() - const starkKeyPub = ec.starkCurve.getStarkKey(privateKey) - const OZaccountConstructorCallData = CallData.compile({ publicKey: starkKeyPub }) - - // declare OZ account - const declareTx = await fundedAccount.declare({ - classHash: OZaccountClassHash, - contract: compiledAccount, - }) - console.log('Declare new Account...') - await provider.waitForTransaction(declareTx.transaction_hash) - - // fund OZ account - const OZcontractAddress = hash.calculateContractAddressFromHash( - starkKeyPub, - OZaccountClassHash, - OZaccountConstructorCallData, - 0, - ) - console.log('hash calculation contract address', OZcontractAddress) - await starknet.devnet.mint(OZcontractAddress, 1e22, true) - - // // deploy - const OZaccount = new Account(provider, OZcontractAddress, privateKey) - - const { transaction_hash, contract_address } = await OZaccount.deployAccount({ - classHash: OZaccountClassHash, - constructorCalldata: OZaccountConstructorCallData, - addressSalt: starkKeyPub, - }) - - console.log('Waiting for Tx to be Accepted on Starknet - OZ Account Deployment...') - await provider.waitForTransaction(transaction_hash) - - console.log('actual contract address', contract_address) - - return { address: contract_address, privateKey: '0x' + Buffer.from(privateKey).toString('hex') } -} - -deployAccount() diff --git a/examples/contracts/aggregator-consumer/scripts/deploy_contracts.ts b/examples/contracts/aggregator-consumer/scripts/deploy_contracts.ts deleted file mode 100644 index ece2fed60..000000000 --- a/examples/contracts/aggregator-consumer/scripts/deploy_contracts.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { CairoAssembly, CallData, CompiledContract, Contract } from 'starknet' -import { - loadContract, - createDeployerAccount, - loadContractPath, - makeProvider, - loadCasmContract, -} from './utils' -import * as fs from 'fs' -import * as dotenv from 'dotenv' - -const AGGREGATOR = 'MockAggregator' - -const AGGREGATOR_PATH = '../../../../contracts/target/release/chainlink_MockAggregator' - -const CONSUMER = 'AggregatorConsumer' - -const UPTIME_FEED_PATH = '../../../../contracts/target/release/chainlink_SequencerUptimeFeed' - -const PRICE_CONSUMER = 'AggregatorPriceConsumerWithSequencer' - -const DECIMALS = '18' - -dotenv.config({ path: __dirname + '/../.env' }) - -export async function deployContract() { - const provider = makeProvider() - const AggregatorArtifact = loadContractPath(`${AGGREGATOR_PATH}.sierra`) as CompiledContract - const ConsumerArtifact = loadContract(CONSUMER) - const PriceConsumerArtifact = loadContract(PRICE_CONSUMER) - const UptimeFeedArtifact = loadContractPath(`${UPTIME_FEED_PATH}.sierra`) as CompiledContract - - const account = createDeployerAccount(provider) - - console.log('Deploying Contracts...(this may take 3-5 minutes)') - - const declareDeployAggregator = await account.declareAndDeploy({ - casm: loadContractPath(`${AGGREGATOR_PATH}.casm`) as CairoAssembly, - contract: AggregatorArtifact, - constructorCalldata: [DECIMALS], - }) - - const aggregatorDeploy = new Contract( - AggregatorArtifact.abi, - declareDeployAggregator.deploy.contract_address, - provider, - ) - - const declareDeployConsumer = await account.declareAndDeploy({ - casm: loadCasmContract(CONSUMER), - contract: ConsumerArtifact, - constructorCalldata: [aggregatorDeploy.address as string], - }) - - const consumerDeploy = new Contract( - ConsumerArtifact.abi, - declareDeployConsumer.deploy.contract_address, - provider, - ) - - const declareDeployUptimeFeed = await account.declareAndDeploy({ - casm: loadContractPath(`${UPTIME_FEED_PATH}.casm`) as CairoAssembly, - contract: UptimeFeedArtifact, - constructorCalldata: ['0', account.address], - }) - - const uptimeFeedDeploy = new Contract( - UptimeFeedArtifact.abi, - declareDeployUptimeFeed.deploy.contract_address, - provider, - ) - - const declareDeployPriceConsumer = await account.declareAndDeploy({ - casm: loadCasmContract(PRICE_CONSUMER), - contract: PriceConsumerArtifact, - constructorCalldata: [uptimeFeedDeploy.address as string, aggregatorDeploy.address as string], - }) - - const priceConsumerDeploy = new Contract( - PriceConsumerArtifact.abi, - declareDeployPriceConsumer.deploy.contract_address, - provider, - ) - - fs.appendFile(__dirname + '/../.env', '\nCONSUMER=' + consumerDeploy.address, function (err) { - if (err) throw err - }) - fs.appendFile(__dirname + '/../.env', '\nMOCK=' + aggregatorDeploy.address, function (err) { - if (err) throw err - }) - fs.appendFile( - __dirname + '/../.env', - '\nPRICE_CONSUMER=' + priceConsumerDeploy.address, - function (err) { - if (err) throw err - }, - ) - fs.appendFile(__dirname + '/../.env', '\nUPTIME_FEED=' + uptimeFeedDeploy.address, function ( - err, - ) { - if (err) throw err - }) -} - -deployContract() diff --git a/examples/contracts/aggregator-consumer/scripts/getLatestPriceSeqCheck.ts b/examples/contracts/aggregator-consumer/scripts/getLatestPriceSeqCheck.ts deleted file mode 100644 index d58cb9c27..000000000 --- a/examples/contracts/aggregator-consumer/scripts/getLatestPriceSeqCheck.ts +++ /dev/null @@ -1,79 +0,0 @@ -import dotenv from 'dotenv' -import { createDeployerAccount, loadContract, loadContractPath, makeProvider } from './utils' -import { CompiledContract, Contract, GatewayError } from 'starknet' - -const PRICE_CONSUMER_NAME = 'AggregatorPriceConsumerWithSequencer' -const UPTIME_FEED_PATH = '../../../../contracts/target/release/chainlink_SequencerUptimeFeed.sierra' - -dotenv.config({ path: __dirname + '/../.env' }) - -const SEQ_UP_REPORT_STALE = 'L2 seq up & report stale' -const SEQ_DOWN_REPORT_STALE = 'L2 seq down & report stale' -const SEQ_DOWN_REPORT_OK = 'L2 seq down & report ok' - -const HEX_SEQ_UP_REPORT_STALE = revertMessageHex(SEQ_UP_REPORT_STALE) -const HEX_SEQ_DOWN_REPORT_STALE = revertMessageHex(SEQ_DOWN_REPORT_STALE) -const HEX_SEQ_DOWN_REPORT_OK = revertMessageHex(SEQ_DOWN_REPORT_OK) - -function revertMessageHex(msg: string): string { - return Buffer.from(msg, 'utf8').toString('hex') -} - -export async function getLatestPrice() { - const provider = makeProvider() - - const account = createDeployerAccount(provider) - - const priceConsumerArtifact = loadContract(PRICE_CONSUMER_NAME) - const UptimeFeedArtifact = loadContractPath(UPTIME_FEED_PATH) as CompiledContract - - const priceConsumer = new Contract( - priceConsumerArtifact.abi, - process.env.PRICE_CONSUMER as string, - provider, - ) - const uptimeFeed = new Contract( - UptimeFeedArtifact.abi, - process.env.UPTIME_FEED as string, - provider, - ) - - priceConsumer.connect(account) - uptimeFeed.connect(account) - - let res = await uptimeFeed.invoke('add_access', [priceConsumer.address]) - console.log('Waiting for add_access Tx to be Accepted on Starknet...') - await provider.waitForTransaction(res.transaction_hash) - - try { - console.log('Waiting to get latest price') - const latestPrice = await priceConsumer.call('get_latest_price', []) - console.log('answer= ', latestPrice) - } catch (e) { - // transaction reverted because sequencer is down or report is stale - console.log('Getting latest price not possible (reason below)') - if (e instanceof GatewayError) { - switch (true) { - case e.message.includes(HEX_SEQ_UP_REPORT_STALE): { - console.log(SEQ_UP_REPORT_STALE) - break - } - case e.message.includes(HEX_SEQ_DOWN_REPORT_STALE): { - console.log(SEQ_DOWN_REPORT_STALE) - break - } - case e.message.includes(HEX_SEQ_DOWN_REPORT_OK): { - console.log(SEQ_DOWN_REPORT_OK) - break - } - default: - console.log(e) - break - } - } else { - console.log(e) - } - } -} - -getLatestPrice() diff --git a/examples/contracts/aggregator-consumer/scripts/readContinuously.ts b/examples/contracts/aggregator-consumer/scripts/readContinuously.ts deleted file mode 100644 index 442c732f9..000000000 --- a/examples/contracts/aggregator-consumer/scripts/readContinuously.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Contract, Account, CallContractResponse, Result } from 'starknet' - -import { createDeployerAccount, loadContract, makeProvider } from './utils' -import dotenv from 'dotenv' - -const CONTRACT_NAME = 'AggregatorConsumer' -let account: Account -let consumer: Contract - -dotenv.config({ path: __dirname + '/../.env' }) - -async function readContinuously() { - const provider = makeProvider() - - account = createDeployerAccount(provider) - - const AggregatorArtifact = loadContract(CONTRACT_NAME) - - consumer = new Contract(AggregatorArtifact.abi, process.env.CONSUMER as string) - consumer.connect(account) - setInterval(callFunction, 3000) -} - -async function callFunction() { - const latestRound = await consumer.call('read_latest_round') - const decimals = await consumer.call('read_decimals') - printResult(latestRound, decimals) -} - -function printResult(latestRound: Result, decimals: Result) { - console.log('---------------') - console.log('round_id= ', latestRound['round_id']) - console.log('answer= ', latestRound['answer']) - console.log('block_num= ', latestRound['block_num']) - console.log('started_at= ', latestRound['started_at']) - console.log('updated_at= ', latestRound['updated_at']) - console.log('decimals= ', decimals.toString()) -} - -readContinuously() diff --git a/examples/contracts/aggregator-consumer/scripts/readDecimals.ts b/examples/contracts/aggregator-consumer/scripts/readDecimals.ts deleted file mode 100644 index 7d76b80bb..000000000 --- a/examples/contracts/aggregator-consumer/scripts/readDecimals.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Account, Contract } from 'starknet' -import { createDeployerAccount, loadContract, makeProvider } from './utils' -import dotenv from 'dotenv' - -const CONSUMER_NAME = 'AggregatorConsumer' -let account: Account -let consumer: Contract - -dotenv.config({ path: __dirname + '/../.env' }) - -export async function readDecimals() { - const provider = makeProvider() - account = createDeployerAccount(provider) - - const AggregatorArtifact = loadContract(CONSUMER_NAME) - consumer = new Contract(AggregatorArtifact.abi, process.env.CONSUMER as string) - - consumer.connect(account) - - const decimals = await consumer.call('read_decimals') - - console.log('decimals= ', decimals.toString()) - return decimals -} - -readDecimals() diff --git a/examples/contracts/aggregator-consumer/scripts/readLatestRound.ts b/examples/contracts/aggregator-consumer/scripts/readLatestRound.ts deleted file mode 100644 index 762144a1b..000000000 --- a/examples/contracts/aggregator-consumer/scripts/readLatestRound.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Account, Contract, Result } from 'starknet' - -import { createDeployerAccount, loadContract, makeProvider } from './utils' -import dotenv from 'dotenv' - -const CONSUMER_NAME = 'AggregatorConsumer' -let account: Account -let consumer: Contract - -dotenv.config({ path: __dirname + '/../.env' }) - -export async function readLatestRound() { - const provider = makeProvider() - account = createDeployerAccount(provider) - - const AggregatorArtifact = loadContract(CONSUMER_NAME) - consumer = new Contract(AggregatorArtifact.abi, process.env.CONSUMER as string) - - consumer.connect(account) - - const latestRound = await consumer.call('read_latest_round') - - printResult(latestRound) - return latestRound -} - -function printResult(latestRound: Result) { - console.log('round_id= ', latestRound['round_id']) - console.log('answer= ', latestRound['answer']) - console.log('block_num= ', latestRound['block_num']) - console.log('started_at= ', latestRound['started_at']) - console.log('updated_at= ', latestRound['updated_at']) -} - -readLatestRound() diff --git a/examples/contracts/aggregator-consumer/scripts/updateLatestRound.ts b/examples/contracts/aggregator-consumer/scripts/updateLatestRound.ts deleted file mode 100644 index 5d0b1bcd4..000000000 --- a/examples/contracts/aggregator-consumer/scripts/updateLatestRound.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Account, CompiledContract, Contract, Provider, ec, number } from 'starknet' -import { loadContract, loadContractPath, makeProvider } from './utils' -import dotenv from 'dotenv' - -interface Transmission { - answer: number - block_num: number - observation_timestamp: number - transmission_timestamp: number -} - -const CONTRACT_NAME = 'MockAggregator' -const CONTRACT_PATH = '../../../../contracts/target/release/chainlink_MockAggregator' -let account: Account -let mock: Contract -let transmission: Transmission -let provider: Provider - -dotenv.config({ path: __dirname + '/../.env' }) - -const rl = require('readline').createInterface({ - input: process.stdin, - output: process.stdout, -}) - -async function updateLatestRound() { - provider = makeProvider() - - transmission = { - answer: 0, - block_num: 0, - observation_timestamp: 0, - transmission_timestamp: 0, - } - - const privateKey = process.env.DEPLOYER_PRIVATE_KEY as string - account = new Account(provider, process.env.DEPLOYER_ACCOUNT_ADDRESS as string, privateKey) - - const MockArtifact = loadContractPath(`${CONTRACT_PATH}.sierra`) as CompiledContract - - mock = new Contract(MockArtifact.abi, process.env.MOCK as string) - mock.connect(account) - - transmission.answer = Number(await input('Enter a number for new answer: ')) - transmission.block_num = Number(await input('Enter a number for new block_num: ')) - transmission.observation_timestamp = Number( - await input('Enter a number for new observation_timestamp: '), - ) - transmission.transmission_timestamp = Number( - await input('Enter a number for new transmission_timestamp: '), - ) - rl.close() - - await callFunction(transmission) -} - -async function callFunction(transmission: Transmission) { - const tx = await mock.invoke('set_latest_round_data', [ - transmission.answer, - transmission.block_num, - transmission.observation_timestamp, - transmission.transmission_timestamp, - ]) - - console.log('Waiting for Tx to be Accepted on Starknet: Updating Latest Round') - await provider.waitForTransaction(tx.transaction_hash) -} - -function input(prompt: string) { - return new Promise((callbackFn, errorFn) => { - rl.question(prompt, (uinput: string) => { - switch (isNaN(Number(uinput))) { - case true: - console.log('input is not a number we will use the default value of 1') - uinput = '1' - break - default: - break - } - callbackFn(uinput) - }) - }) -} - -updateLatestRound() diff --git a/examples/contracts/aggregator-consumer/scripts/utils.ts b/examples/contracts/aggregator-consumer/scripts/utils.ts deleted file mode 100644 index ef4e8cf54..000000000 --- a/examples/contracts/aggregator-consumer/scripts/utils.ts +++ /dev/null @@ -1,85 +0,0 @@ -import fs from 'fs' -import dotenv from 'dotenv' -import { CompiledContract, json, ec, Account, Provider, constants, CairoAssembly } from 'starknet' - -const DEVNET_NAME = 'devnet' - -export const loadContract_Account = (name: string): CompiledContract => { - return json.parse( - fs - .readFileSync( - `${__dirname}/../../../../node_modules/@shardlabs/starknet-hardhat-plugin/dist/contract-artifacts/OpenZeppelinAccount/0.5.1/${name}.cairo/${name}.json`, - ) - .toString('ascii'), - ) -} - -export const loadContract = (name: string): CompiledContract => { - return json.parse( - fs - .readFileSync(`${__dirname}/../target/release/chainlink_examples_${name}.sierra.json`) - .toString('ascii'), - ) -} - -export const loadCasmContract = (name: string): CairoAssembly => { - return json.parse( - fs - .readFileSync(`${__dirname}/../target/release/chainlink_examples_${name}.casm.json`) - .toString('ascii'), - ) -} - -export const loadContractPath = (path: string): CompiledContract | CairoAssembly => { - return json.parse(fs.readFileSync(`${__dirname}/${path}.json`).toString('ascii')) -} - -export const loadContract_Solidity = (path: string, name: string): any => { - return json.parse( - fs - .readFileSync( - `${__dirname}/../../../../contracts/artifacts/src/chainlink/solidity/${path}/${name}.sol/${name}.json`, - ) - .toString('ascii'), - ) -} -export const loadContract_Solidity_V8 = (name: string): any => { - return json.parse( - fs - .readFileSync( - `${__dirname}/../../../../contracts/artifacts/@chainlink/contracts/src/v0.8/interfaces/${name}.sol/${name}.json`, - ) - .toString('ascii'), - ) -} - -export function createDeployerAccount(provider: Provider): Account { - dotenv.config({ path: __dirname + '/../.env' }) - - const privateKey: string = process.env.DEPLOYER_PRIVATE_KEY as string - const accountAddress: string = process.env.DEPLOYER_ACCOUNT_ADDRESS as string - if (!privateKey || !accountAddress) { - throw new Error('Deployer account address or private key is undefined!') - } - - return new Account(provider, accountAddress, privateKey) -} - -export const makeProvider = () => { - const network = process.env.NETWORK || DEVNET_NAME - if (network === DEVNET_NAME) { - return new Provider({ - sequencer: { - baseUrl: 'http://127.0.0.1:5050/', - feederGatewayUrl: 'feeder_gateway', - gatewayUrl: 'gateway', - }, - }) - } else { - return new Provider({ - sequencer: { - network: constants.NetworkName.SN_GOERLI, - }, - }) - } -} diff --git a/examples/contracts/aggregator-consumer/src/lib.cairo b/examples/contracts/aggregator-consumer/src/lib.cairo deleted file mode 100644 index f14ff3b83..000000000 --- a/examples/contracts/aggregator-consumer/src/lib.cairo +++ /dev/null @@ -1 +0,0 @@ -mod ocr2; diff --git a/examples/contracts/aggregator-consumer/src/ocr2.cairo b/examples/contracts/aggregator-consumer/src/ocr2.cairo deleted file mode 100644 index 6c9dcdfe1..000000000 --- a/examples/contracts/aggregator-consumer/src/ocr2.cairo +++ /dev/null @@ -1,2 +0,0 @@ -mod consumer; -mod price_consumer_with_sequencer; diff --git a/examples/contracts/aggregator-consumer/src/ocr2/consumer.cairo b/examples/contracts/aggregator-consumer/src/ocr2/consumer.cairo deleted file mode 100644 index 3b3916316..000000000 --- a/examples/contracts/aggregator-consumer/src/ocr2/consumer.cairo +++ /dev/null @@ -1,30 +0,0 @@ -#[starknet::contract] -mod AggregatorConsumer { - use starknet::ContractAddress; - - use chainlink::ocr2::aggregator::Round; - - use chainlink::ocr2::aggregator_proxy::IAggregator; - use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcher; - use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcherTrait; - - #[storage] - struct Storage { - _ocr_address: ContractAddress, - } - - #[constructor] - fn constructor(ref self: ContractState, ocr_address: ContractAddress) { - self._ocr_address.write(ocr_address); - } - - #[external(v0)] - fn read_latest_round(self: @ContractState) -> Round { - IAggregatorDispatcher { contract_address: self._ocr_address.read() }.latest_round_data() - } - - #[external(v0)] - fn read_decimals(self: @ContractState) -> u8 { - IAggregatorDispatcher { contract_address: self._ocr_address.read() }.decimals() - } -} diff --git a/examples/contracts/aggregator-consumer/tsconfig.json b/examples/contracts/aggregator-consumer/tsconfig.json deleted file mode 100644 index 445119cd5..000000000 --- a/examples/contracts/aggregator-consumer/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist", - }, - "include": [ - "scripts/**/*" - ], - "exclude": [ - "dist", - "**/*.spec.ts", - "**/*.test.ts" - ], - "files": [ - "./hardhat.config.ts" - ] -} diff --git a/examples/contracts/aggregator_consumer/.gitignore b/examples/contracts/aggregator_consumer/.gitignore new file mode 100644 index 000000000..73aa31e60 --- /dev/null +++ b/examples/contracts/aggregator_consumer/.gitignore @@ -0,0 +1,2 @@ +target +.snfoundry_cache/ diff --git a/examples/contracts/aggregator_consumer/Makefile b/examples/contracts/aggregator_consumer/Makefile new file mode 100644 index 000000000..dccb98269 --- /dev/null +++ b/examples/contracts/aggregator_consumer/Makefile @@ -0,0 +1,12 @@ +# TODO: these helper commands may be useful for the deploy script(s) +# +# ACCOUNT_NAME="test-acct" +# +# create-account: +# @sncast account create --name $(ACCOUNT_NAME) +# +# deploy-account: +# @sncast account deploy --name $(ACCOUNT_NAME) + +run-script: + @cd ./scripts && sncast script run $(NAME) diff --git a/examples/contracts/aggregator_consumer/README.md b/examples/contracts/aggregator_consumer/README.md new file mode 100644 index 000000000..bea900f50 --- /dev/null +++ b/examples/contracts/aggregator_consumer/README.md @@ -0,0 +1,27 @@ +# Examples + +## Overview + +## Prerequisites + + +To get started, ensure that you have the following tools installed on your machine: + +- [starknet-foundry (v0.18.0)](https://github.com/foundry-rs/starknet-foundry/releases/tag/v0.18.0) +- [scarb (v2.5.4)](https://github.com/software-mansion/scarb/releases/tag/v2.5.4) + +## Running Test Cases + +To run all test cases, you can use the following command: + +```sh +snforge test +``` + +## Running Scripts + +To run a script, you can use the following command replacing the `NAME` argument with the name of the script to run: + +```sh +make run-script NAME=example +``` diff --git a/examples/contracts/aggregator_consumer/Scarb.lock b/examples/contracts/aggregator_consumer/Scarb.lock new file mode 100644 index 000000000..cd4104701 --- /dev/null +++ b/examples/contracts/aggregator_consumer/Scarb.lock @@ -0,0 +1,27 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "aggregator_consumer" +version = "0.1.0" +dependencies = [ + "chainlink", + "snforge_std", +] + +[[package]] +name = "chainlink" +version = "0.1.0" +dependencies = [ + "openzeppelin", +] + +[[package]] +name = "openzeppelin" +version = "0.9.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.9.0#861fc416f87addbe23a3b47f9d19ab27c10d5dc8" + +[[package]] +name = "snforge_std" +version = "0.18.0" +source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.18.0#48f909a56b08cbdc5ca6a21a836b0fbc6c36d55b" diff --git a/examples/contracts/aggregator_consumer/Scarb.toml b/examples/contracts/aggregator_consumer/Scarb.toml new file mode 100644 index 000000000..3e4b516d4 --- /dev/null +++ b/examples/contracts/aggregator_consumer/Scarb.toml @@ -0,0 +1,21 @@ +# This project was generated using snforge init +# +# https://foundry-rs.github.io/starknet-foundry/appendix/snforge/init.html +# + +[package] +name = "aggregator_consumer" +version = "0.1.0" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.18.0" } +chainlink = { path = "../../../contracts" } + +[[target.starknet-contract]] +casm = true +build-external-contracts = [ + "chainlink::emergency::sequencer_uptime_feed::SequencerUptimeFeed", + "chainlink::ocr2::mocks::mock_aggregator::MockAggregator", +] diff --git a/examples/contracts/aggregator_consumer/scripts/Scarb.lock b/examples/contracts/aggregator_consumer/scripts/Scarb.lock new file mode 100644 index 000000000..4dca6a9b5 --- /dev/null +++ b/examples/contracts/aggregator_consumer/scripts/Scarb.lock @@ -0,0 +1,14 @@ +# Code generated by scarb DO NOT EDIT. +version = 1 + +[[package]] +name = "sncast_std" +version = "0.18.0" +source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.18.0#48f909a56b08cbdc5ca6a21a836b0fbc6c36d55b" + +[[package]] +name = "src" +version = "0.1.0" +dependencies = [ + "sncast_std", +] diff --git a/examples/contracts/aggregator_consumer/scripts/Scarb.toml b/examples/contracts/aggregator_consumer/scripts/Scarb.toml new file mode 100644 index 000000000..d912a6217 --- /dev/null +++ b/examples/contracts/aggregator_consumer/scripts/Scarb.toml @@ -0,0 +1,10 @@ +[package] +name = "src" +version = "0.1.0" +edition = "2023_11" + +# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html + +[dependencies] +sncast_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.18.0" } +starknet = ">=2.5.4" diff --git a/examples/contracts/aggregator_consumer/scripts/src/deploy.cairo b/examples/contracts/aggregator_consumer/scripts/src/deploy.cairo new file mode 100644 index 000000000..13218da2f --- /dev/null +++ b/examples/contracts/aggregator_consumer/scripts/src/deploy.cairo @@ -0,0 +1,5 @@ +use sncast_std::{call, CallResult}; + +fn main() { + println!("replace this with a deploy script!"); +} diff --git a/examples/contracts/aggregator_consumer/scripts/src/example.cairo b/examples/contracts/aggregator_consumer/scripts/src/example.cairo new file mode 100644 index 000000000..b995a7004 --- /dev/null +++ b/examples/contracts/aggregator_consumer/scripts/src/example.cairo @@ -0,0 +1,9 @@ +use sncast_std::{call, CallResult}; + +// The example below uses a contract deployed to the Goerli testnet +fn main() { + let contract_address = 0x7ad10abd2cc24c2e066a2fee1e435cd5fa60a37f9268bfbaf2e98ce5ca3c436; + let call_result = call(contract_address.try_into().unwrap(), 'get_greeting', array![]); + assert(*call_result.data[0] == 'Hello, Starknet!', *call_result.data[0]); + println!("{:?}", call_result); +} diff --git a/examples/contracts/aggregator_consumer/scripts/src/lib.cairo b/examples/contracts/aggregator_consumer/scripts/src/lib.cairo new file mode 100644 index 000000000..a58158aab --- /dev/null +++ b/examples/contracts/aggregator_consumer/scripts/src/lib.cairo @@ -0,0 +1,2 @@ +mod example; +mod deploy; diff --git a/examples/contracts/aggregator_consumer/snfoundry.toml b/examples/contracts/aggregator_consumer/snfoundry.toml new file mode 100644 index 000000000..253c2f3c5 --- /dev/null +++ b/examples/contracts/aggregator_consumer/snfoundry.toml @@ -0,0 +1,8 @@ +# A full list of RPC endpoints can be found here: +# +# https://blastapi.io/public-api/starknet +# + +[sncast.default] +url = "https://starknet-testnet.public.blastapi.io/rpc/v0_6" + diff --git a/examples/contracts/aggregator_consumer/src/lib.cairo b/examples/contracts/aggregator_consumer/src/lib.cairo new file mode 100644 index 000000000..ea174388f --- /dev/null +++ b/examples/contracts/aggregator_consumer/src/lib.cairo @@ -0,0 +1,2 @@ +pub mod ocr2; + diff --git a/examples/contracts/aggregator_consumer/src/ocr2.cairo b/examples/contracts/aggregator_consumer/src/ocr2.cairo new file mode 100644 index 000000000..8947aa72c --- /dev/null +++ b/examples/contracts/aggregator_consumer/src/ocr2.cairo @@ -0,0 +1,2 @@ +pub mod price_consumer; +pub mod consumer; diff --git a/examples/contracts/aggregator_consumer/src/ocr2/consumer.cairo b/examples/contracts/aggregator_consumer/src/ocr2/consumer.cairo new file mode 100644 index 000000000..bf67f2a3c --- /dev/null +++ b/examples/contracts/aggregator_consumer/src/ocr2/consumer.cairo @@ -0,0 +1,37 @@ +#[starknet::interface] +pub trait IAggregatorConsumer { + fn read_latest_round(self: @TContractState) -> chainlink::ocr2::aggregator::Round; + fn read_decimals(self: @TContractState) -> u8; +} + +#[starknet::contract] +mod AggregatorConsumer { + use starknet::ContractAddress; + + use chainlink::ocr2::aggregator::Round; + + use chainlink::ocr2::aggregator_proxy::IAggregator; + use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcher; + use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcherTrait; + + #[storage] + struct Storage { + _ocr_address: ContractAddress, + } + + #[constructor] + fn constructor(ref self: ContractState, ocr_address: ContractAddress) { + self._ocr_address.write(ocr_address); + } + + #[abi(embed_v0)] + impl AggregatorConsumerImpl of super::IAggregatorConsumer { + fn read_latest_round(self: @ContractState) -> Round { + IAggregatorDispatcher { contract_address: self._ocr_address.read() }.latest_round_data() + } + + fn read_decimals(self: @ContractState) -> u8 { + IAggregatorDispatcher { contract_address: self._ocr_address.read() }.decimals() + } + } +} diff --git a/examples/contracts/aggregator-consumer/src/ocr2/price_consumer_with_sequencer.cairo b/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo similarity index 70% rename from examples/contracts/aggregator-consumer/src/ocr2/price_consumer_with_sequencer.cairo rename to examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo index 06ddd331a..9f2d2e381 100644 --- a/examples/contracts/aggregator-consumer/src/ocr2/price_consumer_with_sequencer.cairo +++ b/examples/contracts/aggregator_consumer/src/ocr2/price_consumer.cairo @@ -1,5 +1,10 @@ +#[starknet::interface] +pub trait IAggregatorPriceConsumer { + fn get_latest_price(self: @TContractState) -> u128; +} + #[starknet::contract] -mod AggregatorPriceConsumerWithSequencer { +mod AggregatorPriceConsumer { use box::BoxTrait; use starknet::ContractAddress; use zeroable::Zeroable; @@ -32,28 +37,28 @@ mod AggregatorPriceConsumerWithSequencer { } - #[extrnal(v0)] - fn get_latest_price(self: @ContractState) -> u128 { - assert_sequencer_healthy(self); - let round = IAggregatorDispatcher { contract_address: self._aggregator_address.read() } - .latest_round_data(); - round.answer + #[abi(embed_v0)] + impl AggregatorPriceConsumerImpl of super::IAggregatorPriceConsumer { + fn get_latest_price(self: @ContractState) -> u128 { + assert_sequencer_healthy(self); + let round = IAggregatorDispatcher { contract_address: self._aggregator_address.read() } + .latest_round_data(); + round.answer + } } fn assert_sequencer_healthy(self: @ContractState) { let round = IAggregatorDispatcher { contract_address: self._uptime_feed_address.read() } .latest_round_data(); - let timestamp = starknet::info::get_block_info().unbox().block_timestamp; + let timestamp = starknet::get_block_info().unbox().block_timestamp; // After 60 sec the report is considered stale let report_stale = timestamp - round.updated_at > 60_u64; // 0 if the sequencer is up and 1 if it is down. No other options besides 1 and 0 match round.answer.into() { - 0 => { - assert(!report_stale, 'L2 seq up & report stale'); - }, - 1 | _ => { + 0 => { assert(!report_stale, 'L2 seq up & report stale'); }, + _ => { assert(!report_stale, 'L2 seq down & report stale'); assert(false, 'L2 seq down & report ok'); } diff --git a/examples/contracts/aggregator_consumer/tests/test_consumer.cairo b/examples/contracts/aggregator_consumer/tests/test_consumer.cairo new file mode 100644 index 000000000..1bb3b4fe4 --- /dev/null +++ b/examples/contracts/aggregator_consumer/tests/test_consumer.cairo @@ -0,0 +1,67 @@ +use snforge_std::{declare, ContractClassTrait}; + +use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcherTrait; +use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcher; + +use aggregator_consumer::ocr2::consumer::IAggregatorConsumerDispatcherTrait; +use aggregator_consumer::ocr2::consumer::IAggregatorConsumerDispatcher; + +use starknet::ContractAddress; + +fn deploy_mock_aggregator(decimals: u8) -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(decimals.into()); + return declare('MockAggregator').deploy(@calldata).unwrap(); +} + +fn deploy_consumer(aggregator_address: ContractAddress) -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(aggregator_address.into()); + return declare('AggregatorConsumer').deploy(@calldata).unwrap(); +} + +#[test] +fn test_read_decimals() { + let decimals = 16; + let mock_aggregator_address = deploy_mock_aggregator(decimals); + let consumer_address = deploy_consumer(mock_aggregator_address); + let consumer_dispatcher = IAggregatorConsumerDispatcher { contract_address: consumer_address }; + assert(decimals == consumer_dispatcher.read_decimals(), 'Invalid decimals'); +} + +#[test] +fn test_read_latest_round() { + // Deploys the mock aggregator + let mock_aggregator_address = deploy_mock_aggregator(16); + let mock_aggregator_dispatcher = IMockAggregatorDispatcher { + contract_address: mock_aggregator_address + }; + + // Deploys the consumer + let consumer_address = deploy_consumer(mock_aggregator_address); + let consumer_dispatcher = IAggregatorConsumerDispatcher { contract_address: consumer_address }; + + // No round data has been initialized, so reading the latest round should return no data + let empty_latest_round = consumer_dispatcher.read_latest_round(); + assert(empty_latest_round.round_id == 0, 'round_id != 0'); + assert(empty_latest_round.answer == 0, 'answer != 0'); + assert(empty_latest_round.block_num == 0, 'block_num != 0'); + assert(empty_latest_round.started_at == 0, 'started_at != 0'); + assert(empty_latest_round.updated_at == 0, 'updated_at != 0'); + + // Now let's set the latest round data to some random values + let answer = 1; + let block_num = 12345; + let observation_timestamp = 100000; + let transmission_timestamp = 200000; + mock_aggregator_dispatcher + .set_latest_round_data(answer, block_num, observation_timestamp, transmission_timestamp); + + // The latest round should now have some data + let latest_round = consumer_dispatcher.read_latest_round(); + assert(latest_round.round_id == 1, 'round_id != 1'); + assert(latest_round.answer == answer, 'bad answer'); + assert(latest_round.block_num == block_num, 'bad block_num'); + assert(latest_round.started_at == observation_timestamp, 'bad started_at'); + assert(latest_round.updated_at == transmission_timestamp, 'bad updated_at'); +} diff --git a/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo b/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo new file mode 100644 index 000000000..cd61f77fc --- /dev/null +++ b/examples/contracts/aggregator_consumer/tests/test_price_consumer_with_sequencer.cairo @@ -0,0 +1,89 @@ +use snforge_std::{declare, ContractClassTrait, start_prank, stop_prank, CheatTarget}; + +use chainlink::emergency::sequencer_uptime_feed::ISequencerUptimeFeedDispatcherTrait; +use chainlink::emergency::sequencer_uptime_feed::ISequencerUptimeFeedDispatcher; +use chainlink::libraries::access_control::IAccessControllerDispatcherTrait; +use chainlink::libraries::access_control::IAccessControllerDispatcher; +use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcherTrait; +use chainlink::ocr2::mocks::mock_aggregator::IMockAggregatorDispatcher; + +use aggregator_consumer::ocr2::price_consumer::IAggregatorPriceConsumerDispatcherTrait; +use aggregator_consumer::ocr2::price_consumer::IAggregatorPriceConsumerDispatcher; + +use starknet::contract_address_const; +use starknet::get_caller_address; +use starknet::ContractAddress; + +fn deploy_mock_aggregator(decimals: u8) -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(decimals.into()); + return declare('MockAggregator').deploy(@calldata).unwrap(); +} + +fn deploy_uptime_feed(initial_status: u128, owner_address: ContractAddress) -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(initial_status.into()); + calldata.append(owner_address.into()); + return declare('SequencerUptimeFeed').deploy(@calldata).unwrap(); +} + +fn deploy_price_consumer( + uptime_feed_address: ContractAddress, aggregator_address: ContractAddress +) -> ContractAddress { + let mut calldata = ArrayTrait::new(); + calldata.append(uptime_feed_address.into()); + calldata.append(aggregator_address.into()); + return declare('AggregatorPriceConsumer').deploy(@calldata).unwrap(); +} + +#[test] +fn test_get_latest_price() { + // Defines helper variables + let owner = contract_address_const::<1>(); + let init_status = 0; + let decimals = 18; + + // Deploys contracts + let mock_aggregator_address = deploy_mock_aggregator(decimals); + let uptime_feed_address = deploy_uptime_feed(init_status, owner); + let price_consumer_address = deploy_price_consumer( + uptime_feed_address, mock_aggregator_address + ); + + // Adds the price consumer contract to the sequencer uptime feed access control list + // which allows the price consumer to call the get_latest_price function + start_prank(CheatTarget::All, owner); + IAccessControllerDispatcher { contract_address: uptime_feed_address } + .add_access(price_consumer_address); + + // The get_latest_price function returns the mock aggregator's latest round answer. At + // this point in the test, there is only one round that is initialized and that is the + // one that the sequencer uptime feed creates when it is deployed. In its constructor, + // a new round is initialized using its initial status as the round's answer, so the + // latest price should be the initial status that was passed into the sequencer uptime + // feed's constructor. + start_prank(CheatTarget::All, price_consumer_address); + let latest_price = IAggregatorPriceConsumerDispatcher { + contract_address: price_consumer_address + } + .get_latest_price(); + assert(latest_price == init_status, 'latest price is incorrect'); + + // Now let's update the round + stop_prank(CheatTarget::All); + let answer = 1; + let block_num = 12345; + let observation_timestamp = 100000; + let transmission_timestamp = 200000; + IMockAggregatorDispatcher { contract_address: mock_aggregator_address } + .set_latest_round_data(answer, block_num, observation_timestamp, transmission_timestamp); + + // This should now return the updated answer + start_prank(CheatTarget::All, price_consumer_address); + let updated_latest_price = IAggregatorPriceConsumerDispatcher { + contract_address: price_consumer_address + } + .get_latest_price(); + assert(updated_latest_price == answer, 'updlatest price is incorrect'); +} + diff --git a/examples/contracts/proxy-consumer/.gitignore b/examples/contracts/proxy-consumer/.gitignore deleted file mode 100644 index eb5a316cb..000000000 --- a/examples/contracts/proxy-consumer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -target diff --git a/examples/contracts/proxy-consumer/README.md b/examples/contracts/proxy-consumer/README.md deleted file mode 100644 index 4cce785e9..000000000 --- a/examples/contracts/proxy-consumer/README.md +++ /dev/null @@ -1,65 +0,0 @@ -This is a simple example for how to read Chainlink data feeds on Starknet. - -### Requirements - -Set up your environment to run the examples. Make sure to clone this repo before you start these instructions - -1. Clone the [smartcontractkit/chainlink-starknet](https://github.com/smartcontractkit/chainlink-starknet) repository, which includes the example contracts for this guide: - ``` - git clone https://github.com/smartcontractkit chainlink-starknet.git - git submodule update --init --recursive - ``` - We use git submodules to pin specific versions of cairo and scarb (you'll see this come into play later). - -1. Setup your local Starknet environment. We will install starknet cli tools, the rust cairo compiler, and scarb which is a framework and dependency manager for cairo. If you already have them installed, feel free to skip this step (if you later find that your versions are not working, follow the steps below because they are pinned to specific versions). - ``` - # Part 1: Install starknet cli tools via virtualenv - - cd chainlink-starknet - # tested on python 3.9 and onwards - python -m venv venv - source ./venv/bin/activate - pip install -r contracts/requirements.txt - ``` - Next we'll install cairo. If you've already installed cairo, make sure to disable that path first. - ``` - # Part 2: Install cairo - cd vendor/cairo && cargo build --all --release - # Add cairo executable to your path - export PATH="$HOME/path/to/chainlink-starknet/vendor/cairo/target/release:$PATH" - ``` - Lastly, we'll install scarb. You should be able to install the scarb 0.2.0-alpha.2 binary from [here](https://github.com/software-mansion/scarb/releases/tag/v0.2.0-alpha.2) for your operating system. Install it in your `$HOME` directory and add it to your path. - ``` - # assuming you've downloaded the scarb artifact already to your $HOME directory - export PATH="$HOME/scarb/bin:$PATH" - ``` - Awesome, that was a lot of work, but now we're ready to start! - -1. Set up a Starknet account. Follow instructions to [set up environment variables](https://docs.starknet.io/documentation/getting_started/deploying_contracts/#setting_up_environment_variables) and [deploy an account](https://docs.starknet.io/documentation/getting_started/deploying_contracts/#setting_up_an_account). This deploys an account on Starknet's `alpha-goerli` network and funds it with [testnet ETH](https://faucet.goerli.starknet.io/). These examples expect the OpenZeppelin wallet, which stores your addresses and private keys at `~/.starknet_accounts/` by default. - -1. [Install NodeJS](https://nodejs.org/en/download/) in the version in the `>=14 <=18` version range. -1. [Install Yarn](https://classic.yarnpkg.com/lang/en/docs/install/). -1. Change directories to the proxy consumer example: `cd ./chainlink-starknet/examples/new_contracts/proxy_consumer/` -1. Run `yarn install` to install the required packages including [Starknet.js](https://www.starknetjs.com/) - -### Running the on-chain example - -1. Find the your account address and private key for your funded Starknet testnet account. By default, the OpenZeppelin wallet contains these values at `~/.starknet_accounts/starknet_open_zeppelin_accounts.json`. -1. Export your address to the `DEPLOYER_ACCOUNT_ADDRESS` environment variable and your private key to the `DEPLOYER_PRIVATE_KEY` environment variable. - - ```shell - export DEPLOYER_ACCOUNT_ADDRESS= - ``` - - ```shell - export DEPLOYER_PRIVATE_KEY= - ``` -1. Run `yarn build` to build the cairo artifacts via scarb. These will be put in the target/ directory -1. Run `yarn deploy` to deploy the example consumer contract to the Starknet Goerli testnet. The console prints the contract address and transaction hash. -1. Run `yarn readLatestRound ` to send an invoke transaction to the deployed contract. Specify the contract address printed by the deploy step. The deployed contract reads the latest round data from the proxy, stores the values, and prints the resulting values. - -### Running the off-chain example - -This example simply reads the proxy contract to get the latest values with no account or contract compiling steps required. - -1. Run `yarn readLatestRoundOffChain`. diff --git a/examples/contracts/proxy-consumer/Scarb.toml b/examples/contracts/proxy-consumer/Scarb.toml deleted file mode 100644 index b497f3adb..000000000 --- a/examples/contracts/proxy-consumer/Scarb.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "proxy_consumer" -version = "0.1.0" - -# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest - -[dependencies] - chainlink = { path = "../../../contracts" } - - -[[target.starknet-contract]] -# note these two options only work on scarb 0.2.0 and forward -casm = true -# Emit Python-powered hints in order to run compiled CASM class with legacy Cairo VM. -casm-add-pythonic-hints = true - diff --git a/examples/contracts/proxy-consumer/package.json b/examples/contracts/proxy-consumer/package.json deleted file mode 100644 index 676bed3ef..000000000 --- a/examples/contracts/proxy-consumer/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "proxy-consumer", - "version": "1.0.0", - "description": "Simple contracts to read Chainlink data feeds on-chain and off-chain", - "scripts": { - "build": "scarb --profile release build", - "deploy": "yarn ts-node ./scripts/deployConsumer.ts", - "readLatestRound": "yarn ts-node ./scripts/readLatestRound.ts", - "readLatestRoundOffChain": "yarn ts-node ./scripts/readLatestRoundOffChain.ts" - }, - "license": "MIT", - "dependencies": { - "starknet": "^6.0.0-beta.11" - } -} diff --git a/examples/contracts/proxy-consumer/scripts/deployConsumer.ts b/examples/contracts/proxy-consumer/scripts/deployConsumer.ts deleted file mode 100644 index 4326c4a98..000000000 --- a/examples/contracts/proxy-consumer/scripts/deployConsumer.ts +++ /dev/null @@ -1,64 +0,0 @@ -import fs from 'fs' -import { Account, Provider, Contract, json, ec, constants } from 'starknet' - -// The Cairo contract that is compiled and ready to declare and deploy -const consumerContractName = 'ProxyConsumer' - -/** - * Network: Starknet Goerli testnet - * Aggregator: LINK/USD - * Address: 0x2579940ca3c41e7119283ceb82cd851c906cbb1510908a913d434861fdcb245 - * Find more feed address at: - * https://docs.chain.link/data-feeds/price-feeds/addresses?network=starknet - */ -const dataFeedAddress = '0x2579940ca3c41e7119283ceb82cd851c906cbb1510908a913d434861fdcb245' - -/** Environment variables for a deployed and funded account to use for deploying contracts - * Find your OpenZeppelin account address and private key at: - * ~/.starknet_accounts/starknet_open_zeppelin_accounts.json - */ -const accountAddress = process.env.DEPLOYER_ACCOUNT_ADDRESS as string -const accountPrivateKey = process.env.DEPLOYER_PRIVATE_KEY as string -const starkKeyPub = ec.starkCurve.getStarkKey(accountPrivateKey) - -export async function deployContract() { - const provider = new Provider({ - sequencer: { - // Starknet network: Either goerli-alpha or mainnet-alpha - network: constants.NetworkName.SN_GOERLI, - }, - }) - - const account = new Account(provider, accountAddress, accountPrivateKey) - - const consumerContract = json.parse( - fs - .readFileSync( - `${__dirname}/../target/release/proxy_consumer_${consumerContractName}.sierra.json`, - ) - .toString('ascii'), - ) - - const declareDeployConsumer = await account.declareAndDeploy({ - contract: consumerContract, - casm: json.parse( - fs - .readFileSync( - `${__dirname}/../target/release/proxy_consumer_${consumerContractName}.casm.json`, - ) - .toString('ascii'), - ), - constructorCalldata: [dataFeedAddress as string], - }) - - const consumerDeploy = new Contract( - consumerContract.abi, - declareDeployConsumer.deploy.contract_address, - provider, - ) - - console.log('Contract address: ' + consumerDeploy.address) - console.log('Transaction hash: ' + declareDeployConsumer.deploy.transaction_hash) -} - -deployContract() diff --git a/examples/contracts/proxy-consumer/scripts/readLatestRound.ts b/examples/contracts/proxy-consumer/scripts/readLatestRound.ts deleted file mode 100644 index 4516e86fd..000000000 --- a/examples/contracts/proxy-consumer/scripts/readLatestRound.ts +++ /dev/null @@ -1,80 +0,0 @@ -import fs from 'fs' -import { Account, Provider, Contract, CallContractResponse, json, ec, constants } from 'starknet' - -/** Environment variables for a deployed and funded account to use for deploying contracts - * Find your OpenZeppelin account address and private key at: - * ~/.starknet_accounts/starknet_open_zeppelin_accounts.json - */ -const accountAddress = process.env.DEPLOYER_ACCOUNT_ADDRESS as string -const privateKey = process.env.DEPLOYER_PRIVATE_KEY as string -const starkKeyPub = ec.starkCurve.getStarkKey(privateKey) - -const consumerContractName = 'ProxyConsumer' - -const contractAddress = process.argv.at(2) as string - -const provider = new Provider({ - sequencer: { - // Starknet network: Either goerli-alpha or mainnet-alpha - network: constants.NetworkName.SN_GOERLI, - }, -}) - -const account = new Account(provider, accountAddress, privateKey) - -export async function updateStoredRound(account: Account, contractAddress: string) { - const consumerContract = json.parse( - fs - .readFileSync( - `${__dirname}/../target/release/proxy_consumer_${consumerContractName}.sierra.json`, - ) - .toString('ascii'), - ) - - const targetContract = new Contract(consumerContract.abi, contractAddress, account) - - const response = await targetContract.invoke('get_latest_round_data') - - console.log('\nInvoking the get_latest_round_data function.') - console.log('Transaction hash: ' + response.transaction_hash) - - console.log('Waiting for transaction...') - let transactionStatus = (await provider.getTransactionReceipt(response.transaction_hash)).status - while (transactionStatus !== 'REJECTED' && transactionStatus !== 'ACCEPTED_ON_L2') { - console.log('Transaction status is: ' + transactionStatus) - await new Promise((f) => setTimeout(f, 10000)) - transactionStatus = (await provider.getTransactionReceipt(response.transaction_hash)).status - } - console.log('Transaction is: ' + transactionStatus) - readStoredRound(account, contractAddress) -} - -export async function readStoredRound(account: Account, contractAddress: string) { - const round = await account.callContract({ - contractAddress: contractAddress, - entrypoint: 'get_stored_round', - }) - - console.log('\nStored values are:') - printResult(round) - return round -} - -export async function readStoredProxy(account: Account, contractAddress: string) { - const feed = await account.callContract({ - contractAddress: contractAddress, - entrypoint: 'get_stored_feed_address', - }) - - return feed -} - -function printResult(latestRound: CallContractResponse) { - console.log('round_id =', parseInt(latestRound.result[0], 16)) - console.log('answer =', parseInt(latestRound.result[1], 16)) - console.log('block_num =', parseInt(latestRound.result[2], 16)) - console.log('observation_timestamp =', parseInt(latestRound.result[3], 16)) - console.log('transmission_timestamp =', parseInt(latestRound.result[4], 16)) -} - -updateStoredRound(account, contractAddress) diff --git a/examples/contracts/proxy-consumer/scripts/readLatestRoundOffChain.ts b/examples/contracts/proxy-consumer/scripts/readLatestRoundOffChain.ts deleted file mode 100644 index 4fd27ebc7..000000000 --- a/examples/contracts/proxy-consumer/scripts/readLatestRoundOffChain.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Provider, CallContractResponse, constants } from 'starknet' - -// Starknet network: Either goerli-alpha or mainnet-alpha -const network = 'goerli-alpha' - -/** - * Network: Starknet Goerli testnet - * Aggregator: LINK/USD - * Address: 0x2579940ca3c41e7119283ceb82cd851c906cbb1510908a913d434861fdcb245 - * Find more proxy address at: - * https://docs.chain.link/data-feeds/price-feeds/addresses?network=starknet - */ -const dataFeedAddress = '0x2579940ca3c41e7119283ceb82cd851c906cbb1510908a913d434861fdcb245' - -export async function readLatestRoundOffChain() { - const provider = new Provider({ - sequencer: { - network: constants.NetworkName.SN_GOERLI, - }, - }) - - const latestRound = await provider.callContract({ - contractAddress: dataFeedAddress, - entrypoint: 'latest_round_data', - }) - - printResult(latestRound) - return latestRound -} - -function printResult(latestRound: CallContractResponse) { - console.log('round_id =', parseInt(latestRound.result[0], 16)) - console.log('answer =', parseInt(latestRound.result[1], 16)) - console.log('block_num =', parseInt(latestRound.result[2], 16)) - console.log('observation_timestamp =', parseInt(latestRound.result[3], 16)) - console.log('transmission_timestamp =', parseInt(latestRound.result[4], 16)) -} - -readLatestRoundOffChain() diff --git a/examples/contracts/proxy-consumer/src/lib.cairo b/examples/contracts/proxy-consumer/src/lib.cairo deleted file mode 100644 index bf37de90f..000000000 --- a/examples/contracts/proxy-consumer/src/lib.cairo +++ /dev/null @@ -1 +0,0 @@ -mod proxy_consumer; diff --git a/examples/contracts/proxy-consumer/src/proxy_consumer.cairo b/examples/contracts/proxy-consumer/src/proxy_consumer.cairo deleted file mode 100644 index 5be522cd5..000000000 --- a/examples/contracts/proxy-consumer/src/proxy_consumer.cairo +++ /dev/null @@ -1,52 +0,0 @@ -#[starknet::contract] -mod ProxyConsumer { - use zeroable::Zeroable; - use traits::Into; - use traits::TryInto; - use option::OptionTrait; - - use starknet::ContractAddress; - use starknet::StorageBaseAddress; - use starknet::SyscallResult; - use starknet::storage_read_syscall; - use starknet::storage_write_syscall; - use starknet::storage_address_from_base_and_offset; - - use chainlink::ocr2::aggregator::Round; - - use chainlink::ocr2::aggregator_proxy::IAggregator; - use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcher; - use chainlink::ocr2::aggregator_proxy::IAggregatorDispatcherTrait; - - - #[storage] - struct Storage { - _proxy_address: ContractAddress, - _feed_data: Round, - } - - #[constructor] - fn constructor(ref self: ContractState, proxy_address: ContractAddress) { - assert(!proxy_address.is_zero(), 'proxy address 0'); - self._proxy_address.write(proxy_address); - get_latest_round_data(ref self); - } - - #[external(v0)] - fn get_latest_round_data(ref self: ContractState) -> Round { - let round = IAggregatorDispatcher { contract_address: self._proxy_address.read() } - .latest_round_data(); - self._feed_data.write(round); - round - } - - #[external(v0)] - fn get_stored_round(self: @ContractState) -> Round { - self._feed_data.read() - } - - #[external(v0)] - fn get_stored_feed_address(self: @ContractState) -> ContractAddress { - self._proxy_address.read() - } -} diff --git a/examples/contracts/proxy-consumer/yarn.lock b/examples/contracts/proxy-consumer/yarn.lock deleted file mode 100644 index 818935665..000000000 --- a/examples/contracts/proxy-consumer/yarn.lock +++ /dev/null @@ -1,95 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@noble/curves@^0.8.2": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-0.8.3.tgz#ad6d48baf2599cf1d58dcb734c14d5225c8996e0" - integrity sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ== - dependencies: - "@noble/hashes" "1.3.0" - -"@noble/curves@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" - integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== - dependencies: - "@noble/hashes" "1.3.0" - -"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== - -isomorphic-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" - integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== - dependencies: - node-fetch "^2.6.1" - whatwg-fetch "^3.4.1" - -lossless-json@^2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/lossless-json/-/lossless-json-2.0.9.tgz#2e9a71a3dcbc6c59dee565e537b9084107b7fe37" - integrity sha512-PUfJ5foxULG1x/dXpSckmt0woBDqyq/WFoI885vEqjGwuP41K2EBYh2IT3zYx9dWqcTLIfXiCE5AjhF1jk9Sbg== - -micro-starknet@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/micro-starknet/-/micro-starknet-0.2.3.tgz#ff4e7caf599255d2110e9c57bb483dfaf493ccb3" - integrity sha512-6XBcC+GerlwJSR4iA0VaeXtS2wrayWFcA4PEzrJPMuFmWCaUtuGIq5K/DB5F/XgnL54/zl2Bxo690Lj7mYVA8A== - dependencies: - "@noble/curves" "~1.0.0" - "@noble/hashes" "~1.3.0" - -node-fetch@^2.6.1: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== - dependencies: - whatwg-url "^5.0.0" - -pako@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" - integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== - -starknet@^5.2.0: - version "5.9.2" - resolved "https://registry.yarnpkg.com/starknet/-/starknet-5.9.2.tgz#2f82f2eb5e24912468e00df64cb349fd0f6fdc9c" - integrity sha512-zCoMQOlmaNeYlNjvVjYevaYcZv+6Uvivtn1n9IuF8cGtZKHVYf/7wqha8rjpFgGGCgDthJjqj+B7Zxu9oh0GFg== - dependencies: - "@noble/curves" "^0.8.2" - isomorphic-fetch "^3.0.0" - lossless-json "^2.0.8" - micro-starknet "^0.2.1" - pako "^2.0.4" - url-join "^4.0.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -url-join@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" - integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" diff --git a/packages-ts/starknet/.gitignore b/packages-ts/starknet/.gitignore deleted file mode 100644 index 6cb6d202b..000000000 --- a/packages-ts/starknet/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -cache/ -node_modules/ -starknet-artifacts/ diff --git a/packages-ts/starknet/hardhat.config.ts b/packages-ts/starknet/hardhat.config.ts deleted file mode 100644 index 7e851a738..000000000 --- a/packages-ts/starknet/hardhat.config.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { HardhatUserConfig } from 'hardhat/types' -import '@shardlabs/starknet-hardhat-plugin' - -/** - * @type import('hardhat/config').HardhatUserConfig - */ -const config: HardhatUserConfig = { - starknet: { - venv: 'active', - network: 'devnet', - wallets: { - OpenZeppelin: { - accountName: 'OpenZeppelin', - modulePath: 'starkware.starknet.wallets.open_zeppelin.OpenZeppelinAccount', - accountPath: '~/.starknet_accounts', - }, - }, - }, - networks: { - devnet: { - url: 'http://127.0.0.1:5050/', - }, - }, -} - -export default config diff --git a/packages-ts/starknet/package.json b/packages-ts/starknet/package.json deleted file mode 100644 index f298931e7..000000000 --- a/packages-ts/starknet/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@chainlink/starknet", - "version": "1.0.0", - "description": "Starknet test utilities", - "keywords": [ - "typescript", - "cli" - ], - "main": "./dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist/**/*", - "!dist/**/*.test.js" - ], - "scripts": { - "lint": "tsc", - "test": "npx hardhat --network localhost test", - "gauntlet": "ts-node ./src/index.ts", - "format": "yarn prettier --write ./src", - "format:check": "yarn prettier --check ./src", - "clean": "rm -rf ./dist/ ./bin/", - "build": "yarn clean && tsc -b", - "bundle": "yarn build && pkg ." - }, - "devDependencies": { - "@shardlabs/starknet-hardhat-plugin": "^0.8.0-alpha.2", - "hardhat": "^2.16.1" - } -} diff --git a/packages-ts/starknet/src/index.ts b/packages-ts/starknet/src/index.ts deleted file mode 100644 index c90169921..000000000 --- a/packages-ts/starknet/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './utils' -export * as account from './account' diff --git a/packages-ts/starknet/test/fundAccount.test.ts b/packages-ts/starknet/test/fundAccount.test.ts deleted file mode 100644 index b9aff5eb0..000000000 --- a/packages-ts/starknet/test/fundAccount.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { assert } from 'chai' -import { account } from '@chainlink/starknet' -import { Account, ec, RpcProvider, stark } from 'starknet' -import { DEVNET_URL, ERC20_ADDRESS } from '../src/account' - -describe('fundAccount', function () { - this.timeout(900_000) - let alice: Account - let bob: Account - let provider: RpcProvider - - const opts = account.makeFunderOptsFromEnv() - const funder = new account.Funder(opts) - - before(async function () { - const gateway_url = process.env.NODE_URL || DEVNET_URL - provider = new RpcProvider({ nodeUrl: gateway_url }) - - const aliceStarkKeyPair = ec.starkCurve.utils.randomPrivateKey() - const bobStarkKeyPair = ec.starkCurve.utils.randomPrivateKey() - - const default_alice_address = stark.randomAddress() - const default_bob_address = stark.randomAddress() - - alice = new Account(provider, default_alice_address, aliceStarkKeyPair) - bob = new Account(provider, default_bob_address, bobStarkKeyPair) - - await funder.fund([ - { account: alice.address, amount: 5000 }, - { account: bob.address, amount: 8000 }, - ]) - }) - - it('should have fund alice', async () => { - const balance = await alice.callContract({ - contractAddress: ERC20_ADDRESS, - entrypoint: 'balanceOf', - calldata: [BigInt(alice.address).toString(10)], - }) - assert.deepEqual(balance, ['0x1388', '0x0']) - }) - - it('should have fund bob', async () => { - const balance = await bob.callContract({ - contractAddress: ERC20_ADDRESS, - entrypoint: 'balanceOf', - calldata: [BigInt(bob.address).toString(10)], - }) - assert.deepEqual(balance, ['0x1f40', '0x0']) - }) - - it("should increament alice's fees", async () => { - await funder.fund([{ account: alice.address, amount: 100 }]) - - const balance = await alice.callContract({ - contractAddress: ERC20_ADDRESS, - entrypoint: 'balanceOf', - calldata: [BigInt(alice.address).toString(10)], - }) - assert.deepEqual(balance, ['0x13ec', '0x0']) - }) - - it("should increament bob's fees", async () => { - await funder.fund([{ account: bob.address, amount: 1000 }]) - - const balance = await bob.callContract({ - contractAddress: ERC20_ADDRESS, - entrypoint: 'balanceOf', - calldata: [BigInt(bob.address).toString(10)], - }) - assert.deepEqual(balance, ['0x2328', '0x0']) - }) -}) diff --git a/packages-ts/starknet/tsconfig.json b/packages-ts/starknet/tsconfig.json deleted file mode 100644 index 2b6853d59..000000000 --- a/packages-ts/starknet/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src" - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "dist", - "**/*.spec.ts", - "**/*.test.ts" - ], -} diff --git a/tsconfig.json b/tsconfig.json index cb4dd770b..0c5132c51 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,9 +3,6 @@ "files": [], "include": [], "references": [ - { - "path": "./packages-ts/starknet" - }, { "path": "./packages-ts/starknet-gauntlet" },