Skip to content

Commit

Permalink
Dex operations (#1)
Browse files Browse the repository at this point in the history
* Add dex operations and enhanced counter canister

* Enhanced canisters scaffolding

* Dex canister and adapt evm-proxy for dex operations

* fix currency pair

* Update docs

* evm-proxy: Add timeout to icp operations

* tests

* fix go mod
  • Loading branch information
lucaslopezf authored Dec 3, 2024
1 parent f19fac8 commit 9822db4
Show file tree
Hide file tree
Showing 61 changed files with 3,228 additions and 914 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ crate-type = ["cdylib"]
candid = "0.10"
ic-cdk = "0.13"
ic-cdk-macros = "0.13"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
num-traits = "0.2"
sha2 = "0.10"
leb128 = "0.2"
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
type CurrencyPair = record {
base_currency: text;
quote_currency: text;
};

type MintOperation = record {
currency: text;
amount: nat;
recipient: principal;
};

type BurnOperation = record {
currency: text;
amount: nat;
owner: principal;
};

type Value = variant {
Blob : blob;
Text : text;
Expand Down Expand Up @@ -40,4 +57,4 @@ service : {
icrc3_get_tip_certificate : () -> (opt DataCertificate) query;
icrc3_get_blocks : (GetBlocksArgs) -> (GetBlocksResult) query;
icrc3_supported_block_types : () -> (vec record { block_type : text; url : text }) query;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const INCREMENT_OPERATION: &str = "increment";
pub const GET_OPERATION: &str = "get";
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::state::STATE;
use crate::logging::log_operation;
use crate::constants::{INCREMENT_OPERATION, GET_OPERATION};
use ic_cdk_macros::*;

/// Increments the counter and logs the operation.
///
/// # Returns
///
/// * `u64` - The new value of the counter after incrementing.
#[update]
pub async fn increment() -> u64 {
let new_value = STATE.with(|state| {
let mut state = state.borrow_mut();
state.counter += 1;
state.counter
});

let _ = log_operation(INCREMENT_OPERATION, new_value).await;
new_value
}

/// Gets the current value of the counter and logs the operation.
///
/// # Returns
///
/// * `u64` - The current value of the counter.
#[update]
pub async fn get() -> u64 {
let value = STATE.with(|state| state.borrow().counter);
let _ = log_operation(GET_OPERATION, value).await;
value
}
168 changes: 168 additions & 0 deletions canisters/counter_canister/src/counter_canister_backend/src/icrc3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use candid::{ Principal, Nat};
use serde::Serialize;
use ic_cdk_macros::*;
use num_traits::cast::ToPrimitive;
use sha2::{Sha256, Digest};

use crate::types::{
GetArchivesArgs, GetArchivesResult, ArchiveInfo,
DataCertificate, GetBlocksArgs, GetBlocksResult,
BlockInfo, ArchivedBlocksRange, Value, BlockTypeInfo
};
use crate::state::BLOCKS;

// Note: These are stub implementations for Proof of Concept (PoC) purposes only.

impl Serialize for ArchivedBlocksRange {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("ArchivedBlocksRange", 2)?;
state.serialize_field("args", &self.args)?;
state.serialize_field("callback", &self.callback)?;
state.end()
}
}

// ICRC-3 Interface Implementations

/// Retrieves information about archives.
///
/// # Arguments
///
/// * `args` - A `GetArchivesArgs` struct containing optional starting principal.
///
/// # Returns
///
/// * `ArchiveInfo` - Information about the archive, including canister ID and block range.
#[query]
pub fn icrc3_get_archives(args: GetArchivesArgs) -> GetArchivesResult {
// Mock implementation
let archives = vec![
ArchiveInfo {
canister_id: Principal::from_text("aaaaa-aa").unwrap_or_else(|_| Principal::anonymous()),
start: Nat::from(0u64),
end: Nat::from(999u64),
},
ArchiveInfo {
canister_id: Principal::from_text("bbbbb-bb").unwrap_or_else(|_| Principal::anonymous()),
start: Nat::from(1000u64),
end: Nat::from(1999u64),
},
];

// If 'from' is provided, return archives starting from that canister
let filtered_archives = match args.from {
Some(from) => archives.into_iter().skip_while(|info| info.canister_id != from).collect(),
None => archives,
};

GetArchivesResult(filtered_archives)
}

/// Retrieves the tip certificate for data integrity verification.
///
/// # Returns
///
/// * `Option<DataCertificate>` - The tip certificate if available, or None.
#[query]
pub fn icrc3_get_tip_certificate() -> Option<DataCertificate> {
BLOCKS.with(|blocks| {
let blocks = blocks.borrow();
if blocks.is_empty() {
return None;
}

let last_block = blocks.last().unwrap();
let last_block_index = Nat::from(blocks.len() - 1);

let mut hasher = Sha256::new();
let mut certificate_data = Vec::new();

let mut leb_encoded = Vec::new();
let block_index = last_block_index.0.to_u64().unwrap_or(0);
leb128::write::unsigned(&mut leb_encoded, block_index).unwrap();

certificate_data.extend_from_slice(&leb_encoded);
certificate_data.extend_from_slice(&last_block.hash);

hasher.update(&certificate_data);
let hash_tree = hasher.finalize().to_vec();

Some(DataCertificate {
certificate: certificate_data,
hash_tree,
})
})
}

/// Retrieves blocks within a specified range.
///
/// # Arguments
///
/// * `args` - A `GetBlocksArgs` struct specifying the start and length of the block range.
///
/// # Returns
///
/// * `GetBlocksResult` - The result containing log length, blocks, and archived block ranges.
#[query]
pub fn icrc3_get_blocks(args: GetBlocksArgs) -> GetBlocksResult {
let start = args.start.0.to_u64().unwrap_or(0);
let length = args.length.0.to_u64().unwrap_or(0);

BLOCKS.with(|blocks| {
let blocks = blocks.borrow();
let log_length = blocks.len() as u64;
let blocks = blocks.iter()
.skip(start as usize)
.take(length as usize)
.enumerate()
.map(|(index, block)| BlockInfo {
id: Nat::from(start + index as u64),
block: Value::Map(vec![
("id".to_string(), Value::Nat(block.id.clone())),
("hash".to_string(), Value::Blob(block.hash.clone())),
("phash".to_string(), Value::Blob(block.phash.clone())),
("btype".to_string(), Value::Text(block.btype.clone())),
("ts".to_string(), Value::Nat(Nat::from(block.ts))),
("finalized".to_string(), Value::Text(block.finalized.to_string())),
("entries".to_string(), Value::Array(block.entries.iter().map(|entry| {
Value::Map(vec![
("timestamp".to_string(), Value::Nat(Nat::from(entry.timestamp))),
("operation".to_string(), Value::Text(entry.operation.clone())),
("details".to_string(), entry.details.clone()),
("caller".to_string(), Value::Text(entry.caller.clone())),
])
}).collect())),
])
})
.collect();

// NOTE: For PoC purposes, we're not actually implementing archived blocks.
// In a real implementation, this would check for and return actual archived blocks.
let archived_blocks = vec![];

GetBlocksResult {
log_length: Nat::from(log_length),
blocks,
archived_blocks,
}
})
}

/// Returns supported block types.
///
/// # Returns
///
/// * `Vec<BlockTypeInfo>` - A vector of supported block types and their corresponding URLs.
#[query]
pub fn icrc3_supported_block_types() -> Vec<BlockTypeInfo> {
vec![
BlockTypeInfo {
block_type: "log_entry".to_string(),
url: "https://github.com/dfinity/ICRC-1/blob/main/standards/ICRC-3/README.md".to_string(),
}
]
}
Loading

0 comments on commit 9822db4

Please sign in to comment.