Skip to content

Commit

Permalink
resolving sncast script read access permission issue for testnet
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-de-leon-cll authored and archseer committed Apr 8, 2024
1 parent 47e6061 commit b51637a
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 81 deletions.
74 changes: 20 additions & 54 deletions examples/contracts/aggregator_consumer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,84 +172,50 @@ Once the container is restarted, let's deploy the MockAggregator contract and th
make devnet-deploy
```

The AggregatorConsumer takes the address of an aggregator contract as input (in this case it is the MockAggregator). It comes with two methods:
The AggregatorConsumer is a simple contract that can be used to store the latest answer of an Aggregator contract. It takes the address of an aggregator contract as input (in this case it is the MockAggregator), and it comes with the following methods:

- `set_answer`: this function reads the latest round data from the aggregator and stores the round answer in storage.
- `set_answer`: this function sets the answer to a new value.
- `read_answer`: this function reads the answer from storage. The answer is initially set to 0 on deployment.
- `read_ocr_address`: this function returns the address of the aggregator contract.

At this point, the latest round data has not been set, so calling `set_answer` on the AggregatorConsumer contract will trivially set the answer to 0. You can run the following commands to verify this:
At this point, the AggregatorConsumer's answer has not been set, so calling `read_answer` on the AggregatorConsumer contract will return 0. You can run the following commands to verify this:

- Let's check that the answer is initially set to 0:
Command:

Command:

```sh
make ac-read-answer NETWORK=devnet
```

Output:


```text
Result::Ok(CallResult { data: [0] })
command: script run
status: success
```

- Let's set the answer:

Command:

```sh
make ac-set-answer NETWORK=devnet
```

Output:

```text
Transaction hash = 0x7897b80451a4e4d6df1dc575fffe9b6ebc774bd675eb24c8cf83e0a3818071
Result::Ok(InvokeResult { transaction_hash: 213068772556793858646692905972104002796353690311811440814152767066255491185 })
command: script run
status: success
```

- The answer should still be 0:

Command:

```sh
make ac-read-answer NETWORK=devnet
```

Output:
```sh
make ac-read-answer NETWORK=devnet
```

Output:

```text
Result::Ok(CallResult { data: [0] })
command: script run
status: success
```

```text
Result::Ok(CallResult { data: [0] })
command: script run
status: success
```

To change this, let's set the latest round data to some dummy values:
To change this, let's use the set the MockAggregator's latest round data to some dummy values:

```sh
make ma-set-latest-round NETWORK=devnet
```

Then let's refresh the AggregatorConsumer's answer:
Now let's query the latest round data from the MockAggregator and store the latest round answer in the AggregatorConsumer:

```sh
make ac-set-answer NETWORK=devnet
```

Finally, let's read the AggregatorConsumer's answer:
Now, reading the AggregatorConsumer's answer returns a non-zero value:

Command:

```sh
make ac-read-answer NETWORK=devnet
```

This should result in the following:
Output:

```text
Result::Ok(CallResult { data: [1] })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn main() {
.try_into()
.unwrap();

let result = call(aggregator_address, selector!("read_decimals"), array![]);
let result = call(aggregator_address, selector!("decimals"), array![]);
if result.is_err() {
println!("{:?}", result.unwrap_err());
panic_with_felt252('call failed');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn declare_and_deploy(
contract_name: ByteArray, constructor_calldata: Array<felt252>
) -> DeployResult {
let mut class_hash: ClassHash =
0x100157fc2d1c88776e1c9b75dd6ee62e83212f45f6c4b511e02e1d5d3ae08f6
0x6d1dd0e5fa4e0284dcf341997f1d781bc2fb7d76ada684da7a2a33c38031df5
.try_into()
.unwrap();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use starknet::ContractAddress;
fn main() {
// If you are using testnet, this address may need to be changed
// If you are using the local starknet-devnet-rs container, this can be left alone
let consumer_address = 0x5b12015734ce4bc3c72f9ae4d87ed80e2a28497b21e220a702d1e20e854b0cd
let consumer_address = 0x56e078ee90929f13f2ca83545c71b98136c99b22822ada66ad2aff9595439fc
.try_into()
.unwrap();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
use sncast_std::{invoke, InvokeResult, get_nonce};
use sncast_std::{invoke, InvokeResult, call, CallResult, get_nonce};

use starknet::ContractAddress;

fn main() {
// If you are using testnet, this address may need to be changed
// If you are using the local starknet-devnet-rs container, this can be left alone
let consumer_address = 0x5b12015734ce4bc3c72f9ae4d87ed80e2a28497b21e220a702d1e20e854b0cd
let consumer_address = 0x56e078ee90929f13f2ca83545c71b98136c99b22822ada66ad2aff9595439fc
.try_into()
.unwrap();

// Reads the aggregator address from the AggregatorConsumer
let read_ocr_address = call(consumer_address, selector!("read_ocr_address"), array![]);
if read_ocr_address.is_err() {
println!("{:?}", read_ocr_address.unwrap_err());
panic_with_felt252('call failed');
} else {
println!("{:?}", read_ocr_address);
}

// Queries the aggregator for the latest round data
let mut read_ocr_address_data = read_ocr_address.unwrap().data.span();
let aggregator_address = Serde::<
starknet::ContractAddress
>::deserialize(ref read_ocr_address_data)
.unwrap();
let latest_round = call(aggregator_address, selector!("latest_round_data"), array![]);
if latest_round.is_err() {
println!("{:?}", latest_round.unwrap_err());
panic_with_felt252('call failed');
} else {
println!("{:?}", latest_round);
}

// Uses the latest round data to set a new answer on the AggregatorConsumer
let mut latest_round_data = latest_round.unwrap().data.span();
let round = Serde::<chainlink::ocr2::aggregator::Round>::deserialize(ref latest_round_data)
.unwrap();
let result = invoke(
consumer_address,
selector!("set_answer"),
array![],
array![round.answer.into()],
Option::None,
Option::Some(get_nonce('pending'))
);

if result.is_err() {
println!("{:?}", result.unwrap_err());
panic_with_felt252('invoke failed');
Expand Down
16 changes: 10 additions & 6 deletions examples/contracts/aggregator_consumer/scripts/src/example.cairo
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use sncast_std::{call, CallResult};

fn main() {
let eth = 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7;
let call_result = call(eth.try_into().unwrap(), selector!("decimals"), array![])
.expect('call failed');
let call_result = *call_result.data[0];
assert(call_result == 18, call_result);
println!("{:?}", call_result);
let address = 0x775ee7f2f0b3e15953f4688f7a2ce5a0d1e7c8e18e5f929d461c037f14b690e
.try_into()
.unwrap();
let result = call(address, selector!("description"), array![]);
if result.is_err() {
println!("{:?}", result.unwrap_err());
panic_with_felt252('call failed');
} else {
println!("{:?}", result);
}
}

18 changes: 14 additions & 4 deletions examples/contracts/aggregator_consumer/src/ocr2/consumer.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#[starknet::interface]
pub trait IAggregatorConsumer<TContractState> {
fn read_latest_round(self: @TContractState) -> chainlink::ocr2::aggregator::Round;
fn read_ocr_address(self: @TContractState) -> starknet::ContractAddress;
fn read_answer(self: @TContractState) -> u128;
fn set_answer(ref self: TContractState);
fn set_answer(ref self: TContractState, answer: u128);
}

#[starknet::contract]
Expand Down Expand Up @@ -29,14 +31,22 @@ mod AggregatorConsumer {

#[abi(embed_v0)]
impl AggregatorConsumerImpl of super::IAggregatorConsumer<ContractState> {
fn set_answer(ref self: ContractState) {
let round = IAggregatorDispatcher { contract_address: self._ocr_address.read() }
fn read_latest_round(self: @ContractState) -> Round {
return IAggregatorDispatcher { contract_address: self._ocr_address.read() }
.latest_round_data();
self._answer.write(round.answer);
}


fn set_answer(ref self: ContractState, answer: u128) {
self._answer.write(answer);
}

fn read_answer(self: @ContractState) -> u128 {
return self._answer.read();
}

fn read_ocr_address(self: @ContractState) -> ContractAddress {
return self._ocr_address.read();
}
}
}
29 changes: 19 additions & 10 deletions examples/contracts/aggregator_consumer/tests/test_consumer.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,17 @@ fn test_set_and_read_answer() {
let consumer_address = deploy_consumer(mock_aggregator_address);
let consumer_dispatcher = IAggregatorConsumerDispatcher { contract_address: consumer_address };

// The answer has not been set, so it should default to 0
assert(0 == consumer_dispatcher.read_answer(), 'Invalid answer');
// Let's make sure the AggregatorConsumer was initialized correctly
assert(consumer_dispatcher.read_ocr_address() == mock_aggregator_address, 'Invalid OCR address');
assert(consumer_dispatcher.read_answer() == 0, 'Invalid initial answer');

// No mock data has been set for the MockAggregator, so this should write 0 into answer
consumer_dispatcher.set_answer();

// Let's double check that the answer is still 0
assert(0 == consumer_dispatcher.read_answer(), 'Invalid answer');
// 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;
Expand All @@ -95,10 +98,16 @@ fn test_set_and_read_answer() {
mock_aggregator_dispatcher
.set_latest_round_data(answer, block_num, observation_timestamp, transmission_timestamp);

// This should update the consumer's answer with the mocked value
consumer_dispatcher.set_answer();
// The consumer should be able to query the aggregator for the new latest round 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');

// Let's double check that this is the case
// Now let's test that we can set the answer
consumer_dispatcher.set_answer(latest_round.answer);
assert(answer == consumer_dispatcher.read_answer(), 'Invalid answer');
}

0 comments on commit b51637a

Please sign in to comment.