Skip to content

Commit

Permalink
feat: Tx Register on Fly canister
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Nov 17, 2023
1 parent c245985 commit 83a4f97
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/declarations/dilazionato/dilazionato.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface DilazionatoInitData {
}
export type FlyError = { 'Configuration' : ConfigurationError } |
{ 'Pool' : PoolError } |
{ 'Register' : RegisterError } |
{ 'StorageError' : null } |
{ 'Balance' : BalanceError };
export type GenericValue = { 'Nat64Content' : bigint } |
Expand Down Expand Up @@ -84,6 +85,7 @@ export type NftError = { 'UnauthorizedOperator' : null } |
{ 'Other' : string };
export type PoolError = { 'PoolNotFound' : bigint } |
{ 'NotEnoughTokens' : null };
export type RegisterError = { 'TransactionNotFound' : null };
export type Result = { 'Ok' : null } |
{ 'Err' : DilazionatoError };
export type Result_1 = { 'Ok' : bigint } |
Expand Down
2 changes: 2 additions & 0 deletions src/declarations/dilazionato/dilazionato.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ export const idlFactory = ({ IDL }) => {
'PoolNotFound' : IDL.Nat,
'NotEnoughTokens' : IDL.Null,
});
const RegisterError = IDL.Variant({ 'TransactionNotFound' : IDL.Null });
const BalanceError = IDL.Variant({
'AccountNotFound' : IDL.Null,
'InsufficientBalance' : IDL.Null,
});
const FlyError = IDL.Variant({
'Configuration' : ConfigurationError,
'Pool' : PoolError,
'Register' : RegisterError,
'StorageError' : IDL.Null,
'Balance' : BalanceError,
});
Expand Down
3 changes: 2 additions & 1 deletion src/declarations/fly/fly.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ export type ConfigurationError = { 'AdminsCantBeEmpty' : null } |
{ 'AnonymousAdmin' : null };
export type FlyError = { 'Configuration' : ConfigurationError } |
{ 'Pool' : PoolError } |
{ 'Register' : RegisterError } |
{ 'StorageError' : null } |
{ 'Balance' : BalanceError };
export interface FlyInitData {
'minting_account' : Principal,
'initial_balances' : Array<[Account, bigint]>,
'dilazionato_canister' : Principal,
'admins' : Array<Principal>,
'total_supply' : bigint,
}
export type PoolError = { 'PoolNotFound' : bigint } |
{ 'NotEnoughTokens' : null };
export type RegisterError = { 'TransactionNotFound' : null };
export type Result = { 'Ok' : null } |
{ 'Err' : FlyError };
export type Result_1 = { 'Ok' : bigint } |
Expand Down
4 changes: 2 additions & 2 deletions src/declarations/fly/fly.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const idlFactory = ({ IDL }) => {
'subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)),
});
const FlyInitData = IDL.Record({
'minting_account' : IDL.Principal,
'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)),
'dilazionato_canister' : IDL.Principal,
'admins' : IDL.Vec(IDL.Principal),
Expand All @@ -22,13 +21,15 @@ export const idlFactory = ({ IDL }) => {
'PoolNotFound' : IDL.Nat,
'NotEnoughTokens' : IDL.Null,
});
const RegisterError = IDL.Variant({ 'TransactionNotFound' : IDL.Null });
const BalanceError = IDL.Variant({
'AccountNotFound' : IDL.Null,
'InsufficientBalance' : IDL.Null,
});
const FlyError = IDL.Variant({
'Configuration' : ConfigurationError,
'Pool' : PoolError,
'Register' : RegisterError,
'StorageError' : IDL.Null,
'Balance' : BalanceError,
});
Expand All @@ -47,7 +48,6 @@ export const init = ({ IDL }) => {
'subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)),
});
const FlyInitData = IDL.Record({
'minting_account' : IDL.Principal,
'initial_balances' : IDL.Vec(IDL.Tuple(Account, IDL.Nat64)),
'dilazionato_canister' : IDL.Principal,
'admins' : IDL.Vec(IDL.Principal),
Expand Down
2 changes: 1 addition & 1 deletion src/did/src/common/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
use candid::Nat;

/// An identifier in the dilazionato environment. It has the same syntax as an Ethereum address
/// An identifier in the dilazionato environment.
pub type ID = Nat;
58 changes: 57 additions & 1 deletion src/did/src/fly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use candid::{CandidType, Decode, Deserialize, Encode, Principal};
use ic_stable_structures::storable::Bound;
use ic_stable_structures::Storable;
use icrc::icrc1::account::Account;
use icrc::icrc1::transfer::Memo;
use thiserror::Error;

use crate::ID;
Expand All @@ -18,6 +19,8 @@ pub enum FlyError {
Configuration(ConfigurationError),
#[error("pool error {0}")]
Pool(PoolError),
#[error("register error {0}")]
Register(RegisterError),
#[error("storage error")]
StorageError,
}
Expand Down Expand Up @@ -46,14 +49,19 @@ pub enum PoolError {
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 = u64;

/// These are the arguments which are taken by the fly canister on init
#[derive(Debug, Clone, CandidType, Deserialize)]
pub struct FlyInitData {
pub admins: Vec<Principal>,
pub minting_account: Principal,
/// Total supply of $fly tokens
pub total_supply: u64,
/// Initial balances (wallet subaccount -> picofly)
Expand Down Expand Up @@ -108,9 +116,35 @@ impl Storable for Roles {
}
}

#[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<Memo>,
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 icrc::icrc1::account::Account;
use pretty_assertions::assert_eq;

use super::*;
Expand All @@ -123,4 +157,26 @@ mod test {
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,
fee: 1,
memo: None,
created_at: 0,
};

let data = tx.to_bytes();
let decoded_tx = Transaction::from_bytes(data);
assert_eq!(tx, decoded_tx);
}
}
2 changes: 2 additions & 0 deletions src/dilazionato/dilazionato.did
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type DilazionatoInitData = record {
type FlyError = variant {
Configuration : ConfigurationError;
Pool : PoolError;
Register : RegisterError;
StorageError;
Balance : BalanceError;
};
Expand Down Expand Up @@ -87,6 +88,7 @@ type NftError = variant {
Other : text;
};
type PoolError = variant { PoolNotFound : nat; NotEnoughTokens };
type RegisterError = variant { TransactionNotFound };
type Result = variant { Ok; Err : DilazionatoError };
type Result_1 = variant { Ok : nat; Err : NftError };
type Result_2 = variant { Ok : bool; Err : NftError };
Expand Down
79 changes: 79 additions & 0 deletions src/fly/src/app/register.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::cell::RefCell;

use candid::Nat;
use did::fly::{FlyError, FlyResult, RegisterError, Transaction};
use ic_stable_structures::memory_manager::VirtualMemory;
use ic_stable_structures::{DefaultMemoryImpl, StableVec};

use crate::app::memory::{MEMORY_MANAGER, REGISTER_MEMORY_ID};

thread_local! {
static REGISTER: RefCell<StableVec<Transaction, VirtualMemory<DefaultMemoryImpl>>> =
RefCell::new(StableVec::new(MEMORY_MANAGER.with(|mm| mm.get(REGISTER_MEMORY_ID))).unwrap());
}

/// The register contains the transactions history
pub struct Register;

impl Register {
/// Insert a transaction in the register.
/// Returns the transaction ID
pub fn insert_tx(tx: Transaction) -> FlyResult<Nat> {
REGISTER.with_borrow_mut(|register| {
let id = register.len();
register.push(&tx).map_err(|_| FlyError::StorageError)?;

Ok(id.into())
})
}

/// Get a transaction from the register by its ID
pub fn get_tx(id: u64) -> FlyResult<Transaction> {
REGISTER.with_borrow(|register| {
register
.get(id)
.ok_or(FlyError::Register(RegisterError::TransactionNotFound))
})
}
}

#[cfg(test)]
mod test {

use icrc::icrc1::transfer::Memo;
use pretty_assertions::assert_eq;

use super::*;
use crate::app::test_utils::{alice_account, bob_account};
use crate::constants::ICRC1_FEE;
use crate::utils::fly_to_picofly;

#[test]
fn test_should_insert_tx() {
let tx = Transaction {
from: alice_account(),
to: bob_account(),
amount: fly_to_picofly(50),
fee: ICRC1_FEE,
memo: None,
created_at: crate::utils::time(),
};
assert_eq!(Register::insert_tx(tx).unwrap(), Nat::from(0));
assert!(Register::get_tx(0).is_ok());

let tx = Transaction {
from: alice_account(),
to: bob_account(),
amount: fly_to_picofly(50),
fee: ICRC1_FEE,
memo: Some(Memo::from(
"12341235412523524353451234123541".as_bytes().to_vec(),
)),
created_at: crate::utils::time(),
};
assert_eq!(Register::insert_tx(tx).unwrap(), Nat::from(1));
assert!(Register::get_tx(1).is_ok());

assert!(Register::get_tx(2).is_err());
}
}

0 comments on commit 83a4f97

Please sign in to comment.