From ed9364a5b4a9ea62a3985eee4276bf61cd312d9b Mon Sep 17 00:00:00 2001 From: veeso Date: Fri, 26 Jan 2024 12:08:03 +0100 Subject: [PATCH] fix: refactored fly did --- src/did/src/fly.rs | 253 +++--------------------------- src/did/src/fly/error.rs | 103 ++++++++++++ src/did/src/fly/eth_network.rs | 44 ++++++ src/did/src/fly/liquidity_pool.rs | 20 +++ src/did/src/fly/role.rs | 68 ++++++++ src/did/src/fly/transaction.rs | 64 ++++++++ 6 files changed, 317 insertions(+), 235 deletions(-) create mode 100644 src/did/src/fly/error.rs create mode 100644 src/did/src/fly/eth_network.rs create mode 100644 src/did/src/fly/liquidity_pool.rs create mode 100644 src/did/src/fly/role.rs create mode 100644 src/did/src/fly/transaction.rs diff --git a/src/did/src/fly.rs b/src/did/src/fly.rs index 9056508..5a9d357 100644 --- a/src/did/src/fly.rs +++ b/src/did/src/fly.rs @@ -1,114 +1,26 @@ //! Types associated to the "Fly" canister -use candid::{CandidType, Decode, Deserialize, Encode, Nat, Principal}; -use ic_cdk::api::call::RejectionCode; -use ic_stable_structures::storable::Bound; -use ic_stable_structures::Storable; +mod error; +mod eth_network; +mod liquidity_pool; +mod role; +mod transaction; + +use candid::{CandidType, Deserialize, Nat, Principal}; use icrc::icrc1::account::Account; -use icrc::icrc1::transfer::Memo; -use icrc::{icrc1, icrc2}; -use thiserror::Error; -use crate::{H160, ID}; +pub use self::error::{ + AllowanceError, BalanceError, ConfigurationError, EcdsaError, FlyError, PoolError, + RegisterError, +}; +pub use self::eth_network::EthNetwork; +pub use self::liquidity_pool::{LiquidityPoolAccounts, LiquidityPoolBalance}; +pub use self::role::{Role, Roles}; +pub use self::transaction::Transaction; +use crate::H160; pub type FlyResult = Result; -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum FlyError { - #[error("allowance error {0}")] - Allowance(AllowanceError), - #[error("balance error {0}")] - Balance(BalanceError), - #[error("configuration error {0}")] - Configuration(ConfigurationError), - #[error("ecdsa error {0}")] - Ecdsa(EcdsaError), - #[error("pool error {0}")] - Pool(PoolError), - #[error("register error {0}")] - Register(RegisterError), - #[error("storage error")] - StorageError, - #[error("inter-canister call error: ({0:?}): {1}")] - CanisterCall(RejectionCode, String), - #[error("icrc2 transfer error {0:?}")] - Icrc2Transfer(icrc2::transfer_from::TransferFromError), - #[error("icrc1 transfer error {0:?}")] - Icrc1Transfer(icrc1::transfer::TransferError), - #[error("xrc error")] - XrcError, -} - -impl From for FlyError { - fn from(value: icrc2::transfer_from::TransferFromError) -> Self { - Self::Icrc2Transfer(value) - } -} - -impl From for FlyError { - fn from(value: icrc1::transfer::TransferError) -> Self { - Self::Icrc1Transfer(value) - } -} - -impl From for FlyError { - fn from(_: xrc::ExchangeRateError) -> Self { - Self::XrcError - } -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum AllowanceError { - #[error("allowance not found")] - AllowanceNotFound, - #[error("allowance changed")] - AllowanceChanged, - #[error("allowance expired")] - AllowanceExpired, - #[error("the spender cannot be the caller")] - BadSpender, - #[error("the expiration date is in the past")] - BadExpiration, - #[error("insufficient funds")] - InsufficientFunds, -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum BalanceError { - #[error("account not found")] - AccountNotFound, - #[error("insufficient balance")] - InsufficientBalance, -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum ConfigurationError { - #[error("there must be at least one admin")] - AdminsCantBeEmpty, - #[error("the canister admin cannot be anonymous")] - AnonymousAdmin, -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum EcdsaError { - #[error("invalid public key")] - InvalidPublicKey, -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum PoolError { - #[error("pool not found for contract {0}")] - PoolNotFound(ID), - #[error("not enough tokens in pool")] - NotEnoughTokens, -} - -#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] -pub enum RegisterError { - #[error("transaction not found in the register")] - TransactionNotFound, -} - /// 0.000000000001 $fly pub type PicoFly = Nat; @@ -126,6 +38,8 @@ pub struct FlyInitData { pub erc20_bridge_address: H160, /// Initial ERC20 swap fee pub erc20_swap_fee: u64, + /// The Ethereum network + pub erc20_network: EthNetwork, /// Total supply of $picofly tokens pub total_supply: PicoFly, /// Initial balances (wallet subaccount -> picofly) @@ -143,134 +57,3 @@ pub struct FlyInitData { /// XRC canister pub xrc_canister: Principal, } - -/// Fly user roles. Defines permissions -#[derive(Clone, Copy, Debug, PartialEq, Eq, CandidType, Deserialize)] -pub enum Role { - /// Administrator - Admin, - /// Call reserved to Deferred Canister - DeferredCanister, - /// Call reserved to the marketplace - MarketplaceCanister, -} - -impl Storable for Role { - const BOUND: Bound = Bound::Bounded { - max_size: 8, - is_fixed_size: true, - }; - - fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - Encode!(&self).unwrap().into() - } - - fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - Decode!(&bytes, Role).unwrap() - } -} - -/// List of roles -#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] -pub struct Roles(pub Vec); - -impl From> for Roles { - fn from(roles: Vec) -> Self { - Self(roles) - } -} - -impl Storable for Roles { - const BOUND: Bound = Bound::Unbounded; - - fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - Encode!(&self).unwrap().into() - } - - fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - Decode!(&bytes, Vec).unwrap().into() - } -} - -#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] -pub struct Transaction { - pub from: Account, - pub to: Account, - pub amount: PicoFly, - pub fee: PicoFly, - pub memo: Option, - pub created_at: u64, -} - -impl Storable for Transaction { - const BOUND: Bound = Bound::Bounded { - max_size: 256, - is_fixed_size: false, - }; - - fn to_bytes(&self) -> std::borrow::Cow<[u8]> { - Encode!(&self).unwrap().into() - } - - fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { - Decode!(&bytes, Transaction).unwrap() - } -} - -/// The accounts that hold the liquidity pools for the CKBTC and ICP tokens. -#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] -pub struct LiquidityPoolAccounts { - /// The account that holds the pool for the CKBTC token. - pub ckbtc: Account, - /// The account that holds the pool for the ICP tokens. - pub icp: Account, -} - -/// The balance of the liquidity pool -#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] -pub struct LiquidityPoolBalance { - /// CKBTC tokens hold in the liquidity pool - pub ckbtc: Nat, - /// ICP tokens hold in the liquidity pool - pub icp: Nat, -} - -#[cfg(test)] -mod test { - - use icrc::icrc1::account::Account; - use pretty_assertions::assert_eq; - - use super::*; - - #[test] - fn test_should_encode_role() { - let role: Roles = vec![Role::Admin].into(); - - let data = role.to_bytes(); - let decoded_role = Roles::from_bytes(data); - assert_eq!(role, decoded_role); - } - - #[test] - fn test_should_encode_transaction() { - let tx = Transaction { - from: Account { - owner: Principal::management_canister(), - subaccount: Some([1u8; 32]), - }, - to: Account { - owner: Principal::management_canister(), - subaccount: None, - }, - amount: 100_u64.into(), - fee: 1_u64.into(), - memo: None, - created_at: 0, - }; - - let data = tx.to_bytes(); - let decoded_tx = Transaction::from_bytes(data); - assert_eq!(tx, decoded_tx); - } -} diff --git a/src/did/src/fly/error.rs b/src/did/src/fly/error.rs new file mode 100644 index 0000000..8d98cd9 --- /dev/null +++ b/src/did/src/fly/error.rs @@ -0,0 +1,103 @@ +use candid::CandidType; +use ic_cdk::api::call::RejectionCode; +use icrc::{icrc1, icrc2}; +use serde::Deserialize; +use thiserror::Error; + +use crate::ID; + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum FlyError { + #[error("allowance error {0}")] + Allowance(AllowanceError), + #[error("balance error {0}")] + Balance(BalanceError), + #[error("configuration error {0}")] + Configuration(ConfigurationError), + #[error("ecdsa error {0}")] + Ecdsa(EcdsaError), + #[error("pool error {0}")] + Pool(PoolError), + #[error("register error {0}")] + Register(RegisterError), + #[error("storage error")] + StorageError, + #[error("inter-canister call error: ({0:?}): {1}")] + CanisterCall(RejectionCode, String), + #[error("icrc2 transfer error {0:?}")] + Icrc2Transfer(icrc2::transfer_from::TransferFromError), + #[error("icrc1 transfer error {0:?}")] + Icrc1Transfer(icrc1::transfer::TransferError), + #[error("xrc error")] + XrcError, +} + +impl From for FlyError { + fn from(value: icrc2::transfer_from::TransferFromError) -> Self { + Self::Icrc2Transfer(value) + } +} + +impl From for FlyError { + fn from(value: icrc1::transfer::TransferError) -> Self { + Self::Icrc1Transfer(value) + } +} + +impl From for FlyError { + fn from(_: xrc::ExchangeRateError) -> Self { + Self::XrcError + } +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum AllowanceError { + #[error("allowance not found")] + AllowanceNotFound, + #[error("allowance changed")] + AllowanceChanged, + #[error("allowance expired")] + AllowanceExpired, + #[error("the spender cannot be the caller")] + BadSpender, + #[error("the expiration date is in the past")] + BadExpiration, + #[error("insufficient funds")] + InsufficientFunds, +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum BalanceError { + #[error("account not found")] + AccountNotFound, + #[error("insufficient balance")] + InsufficientBalance, +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum ConfigurationError { + #[error("there must be at least one admin")] + AdminsCantBeEmpty, + #[error("the canister admin cannot be anonymous")] + AnonymousAdmin, +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum EcdsaError { + #[error("invalid public key")] + InvalidPublicKey, +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum PoolError { + #[error("pool not found for contract {0}")] + PoolNotFound(ID), + #[error("not enough tokens in pool")] + NotEnoughTokens, +} + +#[derive(Clone, Debug, Error, CandidType, PartialEq, Eq, Deserialize)] +pub enum RegisterError { + #[error("transaction not found in the register")] + TransactionNotFound, +} diff --git a/src/did/src/fly/eth_network.rs b/src/did/src/fly/eth_network.rs new file mode 100644 index 0000000..7e91e19 --- /dev/null +++ b/src/did/src/fly/eth_network.rs @@ -0,0 +1,44 @@ +use candid::{CandidType, Decode, Deserialize, Encode}; +use ic_stable_structures::storable::Bound; +use ic_stable_structures::Storable; + +/// Ethereum network +#[derive(Clone, Copy, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub enum EthNetwork { + /// Main net + Ethereum, + /// Goerli testnet + Goerli, +} + +impl Storable for EthNetwork { + const BOUND: Bound = Bound::Bounded { + max_size: 8, + is_fixed_size: true, + }; + + fn to_bytes(&self) -> std::borrow::Cow<[u8]> { + Encode!(&self).unwrap().into() + } + + fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { + Decode!(&bytes, EthNetwork).unwrap() + } +} + +#[cfg(test)] +mod test { + + use pretty_assertions::assert_eq; + + use super::*; + + #[test] + fn test_should_encode_network() { + let network = EthNetwork::Ethereum; + + let data = network.to_bytes(); + let decoded = EthNetwork::from_bytes(data); + assert_eq!(network, decoded); + } +} diff --git a/src/did/src/fly/liquidity_pool.rs b/src/did/src/fly/liquidity_pool.rs new file mode 100644 index 0000000..3356985 --- /dev/null +++ b/src/did/src/fly/liquidity_pool.rs @@ -0,0 +1,20 @@ +use candid::{CandidType, Deserialize, Nat}; +use icrc::icrc1::account::Account; + +/// The accounts that hold the liquidity pools for the CKBTC and ICP tokens. +#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub struct LiquidityPoolAccounts { + /// The account that holds the pool for the CKBTC token. + pub ckbtc: Account, + /// The account that holds the pool for the ICP tokens. + pub icp: Account, +} + +/// The balance of the liquidity pool +#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub struct LiquidityPoolBalance { + /// CKBTC tokens hold in the liquidity pool + pub ckbtc: Nat, + /// ICP tokens hold in the liquidity pool + pub icp: Nat, +} diff --git a/src/did/src/fly/role.rs b/src/did/src/fly/role.rs new file mode 100644 index 0000000..46257db --- /dev/null +++ b/src/did/src/fly/role.rs @@ -0,0 +1,68 @@ +use candid::{CandidType, Decode, Deserialize, Encode}; +use ic_stable_structures::storable::Bound; +use ic_stable_structures::Storable; + +/// Fly user roles. Defines permissions +#[derive(Clone, Copy, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub enum Role { + /// Administrator + Admin, + /// Call reserved to Deferred Canister + DeferredCanister, + /// Call reserved to the marketplace + MarketplaceCanister, +} + +impl Storable for Role { + const BOUND: Bound = Bound::Bounded { + max_size: 8, + is_fixed_size: true, + }; + + fn to_bytes(&self) -> std::borrow::Cow<[u8]> { + Encode!(&self).unwrap().into() + } + + fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { + Decode!(&bytes, Role).unwrap() + } +} + +/// List of roles +#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub struct Roles(pub Vec); + +impl From> for Roles { + fn from(roles: Vec) -> Self { + Self(roles) + } +} + +impl Storable for Roles { + const BOUND: Bound = Bound::Unbounded; + + fn to_bytes(&self) -> std::borrow::Cow<[u8]> { + Encode!(&self).unwrap().into() + } + + fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { + Decode!(&bytes, Vec).unwrap().into() + } +} + +#[cfg(test)] +mod test { + + use pretty_assertions::assert_eq; + + use super::*; + + #[test] + fn test_should_encode_role() { + let role: Roles = vec![Role::Admin].into(); + + let data = role.to_bytes(); + let decoded_role = Roles::from_bytes(data); + assert_eq!(role, decoded_role); + } +} diff --git a/src/did/src/fly/transaction.rs b/src/did/src/fly/transaction.rs new file mode 100644 index 0000000..5b6d399 --- /dev/null +++ b/src/did/src/fly/transaction.rs @@ -0,0 +1,64 @@ +use candid::{CandidType, Decode, Deserialize, Encode}; +use ic_stable_structures::storable::Bound; +use ic_stable_structures::Storable; +use icrc::icrc1::account::Account; +use icrc::icrc1::transfer::Memo; + +use super::PicoFly; + +#[derive(Clone, Debug, PartialEq, Eq, CandidType, Deserialize)] +pub struct Transaction { + pub from: Account, + pub to: Account, + pub amount: PicoFly, + pub fee: PicoFly, + pub memo: Option, + pub created_at: u64, +} + +impl Storable for Transaction { + const BOUND: Bound = Bound::Bounded { + max_size: 256, + is_fixed_size: false, + }; + + fn to_bytes(&self) -> std::borrow::Cow<[u8]> { + Encode!(&self).unwrap().into() + } + + fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self { + Decode!(&bytes, Transaction).unwrap() + } +} + +#[cfg(test)] +mod test { + + use candid::Principal; + use icrc::icrc1::account::Account; + use pretty_assertions::assert_eq; + + use super::*; + + #[test] + fn test_should_encode_transaction() { + let tx = Transaction { + from: Account { + owner: Principal::management_canister(), + subaccount: Some([1u8; 32]), + }, + to: Account { + owner: Principal::management_canister(), + subaccount: None, + }, + amount: 100_u64.into(), + fee: 1_u64.into(), + memo: None, + created_at: 0, + }; + + let data = tx.to_bytes(); + let decoded_tx = Transaction::from_bytes(data); + assert_eq!(tx, decoded_tx); + } +}