Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions motoko/exchange_rate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ For example, the following aspect is particularly relevant for this app:
* [Certify query responses if they are relevant for security](https://internetcomputer.org/docs/current/references/security/general-security-best-practices#certify-query-responses-if-they-are-relevant-for-security), since this is essential when e.g. displaying important financial data (in this case exchange rates) in the frontend that may be used by users to decide on future transactions (based on the rate information).

## Dependencies
- [ic-cdk v0.5.7](https://crates.io/crates/ic-cdk/0.5.7) or above
- [dfx v0.12.0-beta.3](https://github.com/dfinity/sdk/releases/tag/0.12.0-beta.3) or above.
- [ic-cdk v0.6.5](https://crates.io/crates/ic-cdk/0.6.5) or above
- [dfx v0.12.0-beta.6](https://github.com/dfinity/sdk/releases/tag/0.12.0-beta.6) or above.
Use below command to install:
```DFX_VERSION=0.12.0-beta.3 sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"```
```DFX_VERSION=0.12.0-beta.6 sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"```
2 changes: 1 addition & 1 deletion motoko/exchange_rate/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ if [[ $ENV == "local" ]]; then
# Check DFX version
version=$(dfx -V | sed 's/dfx\ //g' | sed 's/-.*$//g')
if [[ "$version" < "0.12.0" ]]; then
echo "dfx 0.12.0 or above required. Please do: dfx upgrade"
echo "dfx 0.12.0-beta.6 or above required. Please do: DFX_VERSION=0.12.0-beta.6 sh -ci \"$(curl -fsSL https://internetcomputer.org/install.sh)\""
exit 1
fi

Expand Down
19 changes: 14 additions & 5 deletions motoko/exchange_rate/src/Main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ shared actor class ExchangeRate() = this {
);
};

public query func transform(raw : Types.CanisterHttpResponsePayload) : async Types.CanisterHttpResponsePayload {
public query func transform(raw : Types.TransformArgs) : async Types.CanisterHttpResponsePayload {
let transformed : Types.CanisterHttpResponsePayload = {
status = raw.status;
body = raw.body;
status = raw.response.status;
body = raw.response.body;
headers = [
{
name = "Content-Security-Policy";
Expand Down Expand Up @@ -260,13 +260,18 @@ shared actor class ExchangeRate() = this {
let url = "https://" # host # "/products/ICP-USD/candles?granularity=" # Nat64.toText(REMOTE_FETCH_GRANULARITY) # "&start=" # Nat64.toText(start_timestamp) # "&end=" # Nat64.toText(end_timestamp);
Debug.print(url);

let transform_context : Types.TransformContext = {
function = transform;
context = Blob.fromArray([]);
};

let request : Types.CanisterHttpRequestArgs = {
url = url;
max_response_bytes = ?MAX_RESPONSE_BYTES;
headers = request_headers;
body = null;
method = #get;
transform = ?(#function(transform));
transform = ?transform_context;
};
try {
Cycles.add(2_000_000_000);
Expand Down Expand Up @@ -325,13 +330,17 @@ shared actor class ExchangeRate() = this {
#Ok : Text;
#Err : Text;
} {
let transform_context : Types.TransformContext = {
function = transform;
context = Blob.fromArray([]);
};
let request : Types.CanisterHttpRequestArgs = {
url = url;
max_response_bytes = ?MAX_RESPONSE_BYTES;
headers = [];
body = null;
method = #get;
transform = ?(#function(transform));
transform = ?transform_context;
};
try {
Cycles.add(2_000_000_000);
Expand Down
14 changes: 9 additions & 5 deletions motoko/exchange_rate/src/Types.mo
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ module Types {
#head;
};

public type TransformType = {
#function : shared CanisterHttpResponsePayload -> async CanisterHttpResponsePayload;
public type TransformContext = {
function : shared query TransformArgs -> async CanisterHttpResponsePayload;
context : Blob;
};

public type CanisterHttpRequestArgs = {
Expand All @@ -36,9 +37,7 @@ module Types {
headers : [HttpHeader];
body : ?[Nat8];
method : HttpMethod;
transform : ?{
#function : shared query CanisterHttpResponsePayload -> async CanisterHttpResponsePayload;
};
transform : ?TransformContext;
};

public type CanisterHttpResponsePayload = {
Expand All @@ -47,6 +46,11 @@ module Types {
body : [Nat8];
};

public type TransformArgs = {
response : CanisterHttpResponsePayload;
context : Blob;
};

public type IC = actor {
http_request : Types.CanisterHttpRequestArgs -> async Types.CanisterHttpResponsePayload;
};
Expand Down
73 changes: 67 additions & 6 deletions rust/exchange_rate/Cargo.lock

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

6 changes: 4 additions & 2 deletions rust/exchange_rate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ name = "exchange_rate"
path = "src/main.rs"

[dependencies]
ic-cdk = "0.5.7"
# ic-cdk = "0.5.7"
ic-cdk = { path = "../../../cdk-rs/src/ic-cdk" }
ic-cdk-macros = "0.5.7"
serde = "1.0.126"
candid = "0.7.14"
candid = "0.8.0"
serde_json = "1.0.81"
serde_bytes = "0.11.7"
6 changes: 3 additions & 3 deletions rust/exchange_rate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ On top of that, we cache data that's already fetched, to save from future user r
triggering remote HTTP calls again.

## Dependencies
- [ic-cdk v0.5.7](https://crates.io/crates/ic-cdk/0.5.7) or above
- [dfx v0.12.0-beta.3](https://github.com/dfinity/sdk/releases/tag/0.12.0-beta.3) or above.
- [ic-cdk v0.6.5](https://crates.io/crates/ic-cdk/0.6.5) or above
- [dfx v0.12.0-beta.6](https://github.com/dfinity/sdk/releases/tag/0.12.0-beta.6) or above.
Use below command to install:
```DFX_VERSION=0.12.0-beta.3 sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"```
```DFX_VERSION=0.12.0-beta.6 sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"```

## Building the canister into wasm
`cd rust/exchange_rate`
Expand Down
2 changes: 1 addition & 1 deletion rust/exchange_rate/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ if [[ $ENV == "local" ]]; then
# Check DFX version
version=$(dfx -V | sed 's/dfx\ //g' | sed 's/-.*$//g')
if [[ "$version" < "0.12.0" ]]; then
echo "dfx 0.12.0 or above required. Please do: dfx upgrade"
echo "dfx 0.12.0-beta.6 or above required. Please do: DFX_VERSION=0.12.0-beta.6 sh -ci \"$(curl -fsSL https://internetcomputer.org/install.sh)\""
exit 1
fi

Expand Down
2 changes: 1 addition & 1 deletion rust/exchange_rate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub struct TimeRange {
pub end: Timestamp,
}

#[derive(Clone, Debug, PartialEq, CandidType, Serialize, Deserialize)]
#[derive(CandidType, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RatesWithInterval {
pub interval: usize,
pub rates: HashMap<Timestamp, Rate>,
Expand Down
34 changes: 9 additions & 25 deletions rust/exchange_rate/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use candid::Principal;
use exchange_rate::{Rate, RatesWithInterval, TimeRange, Timestamp};
use ic_cdk::api::management_canister::http_request::{
CanisterHttpRequestArgument, HttpHeader, HttpMethod, HttpResponse, TransformFunc, TransformType,
http_request, CanisterHttpRequestArgument, HttpHeader, HttpMethod, HttpResponse, TransformArgs,
TransformContext,
};
use ic_cdk::storage;
use ic_cdk_macros::{self, heartbeat, post_upgrade, pre_upgrade, query, update};
Expand Down Expand Up @@ -205,37 +205,21 @@ async fn get_rate(job: Timestamp) {
let url = format!("https://{host}/products/ICP-USD/candles?granularity={REMOTE_FETCH_GRANULARITY}&start={start_timestamp}&end={end_timestamp}");
ic_cdk::api::print(url.clone());

ic_cdk::api::print(format!("Making IC http_request call {} now.", job));
let request = CanisterHttpRequestArgument {
url: url,
method: HttpMethod::GET,
body: None,
max_response_bytes: Some(MAX_RESPONSE_BYTES),
transform: Some(TransformType::Function(TransformFunc(candid::Func {
principal: ic_cdk::api::id(),
method: "transform".to_string(),
}))),
transform: Some(TransformContext::new(transform, vec![])),
headers: request_headers,
};

let body = candid::utils::encode_one(&request).unwrap();
ic_cdk::api::print(format!("Making IC http_request call {} now.", job));

match ic_cdk::api::call::call_raw(
Principal::management_canister(),
"http_request",
&body[..],
2_000_000_000,
)
.await
{
Ok(result) => {
// decode the result
let decoded_result: HttpResponse =
candid::utils::decode_one(&result).expect("IC http_request failed!");
match http_request(request).await {
Ok((response,)) => {
// put the result to hashmap
FETCHED.with(|fetched| {
let mut fetched = fetched.borrow_mut();
let decoded_body = String::from_utf8(decoded_result.body)
let decoded_body = String::from_utf8(response.body)
.expect("Remote service response is not UTF-8 encoded.");
decode_body_to_rates(&decoded_body, &mut fetched);
});
Expand All @@ -262,8 +246,8 @@ fn decode_body_to_rates(body: &str, fetched: &mut RefMut<HashMap<u64, f32>>) {
}

#[query]
async fn transform(raw: HttpResponse) -> HttpResponse {
let mut sanitized = raw.clone();
fn transform(raw: TransformArgs) -> HttpResponse {
let mut sanitized = raw.response.clone();
sanitized.headers = vec![
HttpHeader {
name: "Content-Security-Policy".to_string(),
Expand Down