Skip to content

Commit

Permalink
feat: admin_withdraw_icp method
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Aug 10, 2024
1 parent 1e63b0e commit 3988a3b
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 51 deletions.
16 changes: 14 additions & 2 deletions integration-tests/src/client/ekoke_liquidity_pool.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use candid::{Encode, Principal};
use candid::{Encode, Nat, Principal};
use did::ekoke_liquidity_pool::WithdrawError;
use icrc::icrc1::account::Subaccount;
use icrc::icrc1::account::{Account, Subaccount};

use crate::actor::admin;
use crate::TestEnv;

pub struct EkokeLiquidityPoolClient<'a> {
Expand Down Expand Up @@ -33,4 +34,15 @@ impl<'a> EkokeLiquidityPoolClient<'a> {
)
.unwrap()
}

pub fn admin_withdraw_icp(&self, to: Account, amount: Nat) -> Result<(), WithdrawError> {
self.env
.update(
self.env.ekoke_liquidity_pool_id,
admin(),
"admin_withdraw_icp",
Encode!(&to, &amount).unwrap(),
)
.unwrap()
}
}
33 changes: 33 additions & 0 deletions integration-tests/tests/inspect/ekoke_liquidity_pool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use candid::{Encode, Nat};
use integration_tests::actor::{admin, bob};
use integration_tests::TestEnv;

#[test]
#[serial_test::serial]
fn test_should_inspect_is_admin() {
let env = TestEnv::init();

assert!(env
.update::<Nat>(
env.ekoke_liquidity_pool_id,
admin(),
"admin_cycles",
Encode!().unwrap(),
)
.is_ok());
}

#[test]
#[serial_test::serial]
fn test_should_fail_inspect_admin() {
let env = TestEnv::init();
// not an admin
assert!(env
.update::<Nat>(
env.ekoke_liquidity_pool_id,
bob(),
"admin_cycles",
Encode!().unwrap(),
)
.is_err());
}
1 change: 1 addition & 0 deletions integration-tests/tests/inspect/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod deferred;
mod ekoke_erc20_swap;
mod ekoke_liquidity_pool;
mod ekoke_reward_pool;
mod marketplace;
1 change: 1 addition & 0 deletions integration-tests/tests/use_case/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ mod register_sell_contract;
mod reserve_reward_pool;
mod update_contract_property;
mod withdraw_contract_deposit;
mod withdraw_liquidity_pool_icp;
34 changes: 34 additions & 0 deletions integration-tests/tests/use_case/withdraw_liquidity_pool_icp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use candid::Nat;
use integration_tests::actor::bob_account;
use integration_tests::client::{EkokeLiquidityPoolClient, IcrcLedgerClient};
use integration_tests::TestEnv;

#[test]
#[serial_test::serial]
fn test_as_seller_i_should_withdraw_contract_deposit_after_being_paid() {
let env = TestEnv::init();
let icp_ledger_client = IcrcLedgerClient::new(env.icp_ledger_id, &env);
let ekoke_liquidity_pool_client = EkokeLiquidityPoolClient::from(&env);

// withdraw icp to bob
let current_bob_balance = icp_ledger_client.icrc1_balance_of(bob_account());
let current_pool_balance =
icp_ledger_client.icrc1_balance_of(env.ekoke_liquidity_pool_id.into());

let icp_fee = icp_ledger_client.icrc1_fee();

let withdraw_amount = Nat::from(1_000_000_000u64);

let expected_bob_balance = current_bob_balance + withdraw_amount.clone();
let expected_pool_balance = current_pool_balance - withdraw_amount.clone() - icp_fee;

assert!(ekoke_liquidity_pool_client
.admin_withdraw_icp(bob_account(), withdraw_amount)
.is_ok());

let new_bob_balance = icp_ledger_client.icrc1_balance_of(bob_account());
let new_pool_balance = icp_ledger_client.icrc1_balance_of(env.ekoke_liquidity_pool_id.into());

assert_eq!(new_bob_balance, expected_bob_balance);
assert_eq!(new_pool_balance, expected_pool_balance);
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ type RejectionCode = variant {
SysFatal;
CanisterReject;
};
type Result = variant { Ok : LiquidityPoolBalance; Err : EkokeError };
type Result_1 = variant { Ok; Err : WithdrawError };
type Result = variant { Ok; Err : WithdrawError };
type Result_1 = variant { Ok : LiquidityPoolBalance; Err : EkokeError };
type TransferError = variant {
GenericError : record { message : text; error_code : nat };
TemporarilyUnavailable;
Expand Down Expand Up @@ -103,9 +103,10 @@ service : (EkokeLiquidityPoolInitData) -> {
admin_set_admins : (vec principal) -> ();
admin_set_deferred_canister : (principal) -> ();
admin_set_icp_ledger_canister : (principal) -> ();
admin_withdraw_icp : (Account, nat) -> (Result);
create_refunds : (vec record { principal; nat }) -> ();
http_request : (HttpRequest) -> (HttpResponse) query;
liquidity_pool_accounts : () -> (LiquidityPoolAccounts) query;
liquidity_pool_balance : () -> (Result) query;
withdraw_refund : (opt blob) -> (Result_1);
liquidity_pool_balance : () -> (Result_1) query;
withdraw_refund : (opt blob) -> (Result);
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ export type RejectionCode = { 'NoError' : null } |
{ 'Unknown' : null } |
{ 'SysFatal' : null } |
{ 'CanisterReject' : null };
export type Result = { 'Ok' : LiquidityPoolBalance } |
{ 'Err' : EkokeError };
export type Result_1 = { 'Ok' : null } |
export type Result = { 'Ok' : null } |
{ 'Err' : WithdrawError };
export type Result_1 = { 'Ok' : LiquidityPoolBalance } |
{ 'Err' : EkokeError };
export type TransferError = {
'GenericError' : { 'message' : string, 'error_code' : bigint }
} |
Expand Down Expand Up @@ -105,11 +105,12 @@ export interface _SERVICE {
'admin_set_admins' : ActorMethod<[Array<Principal>], undefined>,
'admin_set_deferred_canister' : ActorMethod<[Principal], undefined>,
'admin_set_icp_ledger_canister' : ActorMethod<[Principal], undefined>,
'admin_withdraw_icp' : ActorMethod<[Account, bigint], Result>,
'create_refunds' : ActorMethod<[Array<[Principal, bigint]>], undefined>,
'http_request' : ActorMethod<[HttpRequest], HttpResponse>,
'liquidity_pool_accounts' : ActorMethod<[], LiquidityPoolAccounts>,
'liquidity_pool_balance' : ActorMethod<[], Result>,
'withdraw_refund' : ActorMethod<[[] | [Uint8Array | number[]]], Result_1>,
'liquidity_pool_balance' : ActorMethod<[], Result_1>,
'withdraw_refund' : ActorMethod<[[] | [Uint8Array | number[]]], Result>,
}
export declare const idlFactory: IDL.InterfaceFactory;
export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];
71 changes: 36 additions & 35 deletions src/declarations/ekoke-liquidity-pool/ekoke-liquidity-pool.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,38 @@ export const idlFactory = ({ IDL }) => {
'icp_ledger_canister' : IDL.Principal,
'admins' : IDL.Vec(IDL.Principal),
});
const Account = IDL.Record({
'owner' : IDL.Principal,
'subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)),
});
const TransferError = IDL.Variant({
'GenericError' : IDL.Record({
'message' : IDL.Text,
'error_code' : IDL.Nat,
}),
'TemporarilyUnavailable' : IDL.Null,
'BadBurn' : IDL.Record({ 'min_burn_amount' : IDL.Nat }),
'Duplicate' : IDL.Record({ 'duplicate_of' : IDL.Nat }),
'BadFee' : IDL.Record({ 'expected_fee' : IDL.Nat }),
'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }),
'TooOld' : IDL.Null,
'InsufficientFunds' : IDL.Record({ 'balance' : IDL.Nat }),
});
const RejectionCode = IDL.Variant({
'NoError' : IDL.Null,
'CanisterError' : IDL.Null,
'SysTransient' : IDL.Null,
'DestinationInvalid' : IDL.Null,
'Unknown' : IDL.Null,
'SysFatal' : IDL.Null,
'CanisterReject' : IDL.Null,
});
const WithdrawError = IDL.Variant({
'NothingToWithdraw' : IDL.Principal,
'Transfer' : TransferError,
'CanisterCall' : IDL.Tuple(RejectionCode, IDL.Text),
});
const Result = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : WithdrawError });
const HttpRequest = IDL.Record({
'url' : IDL.Text,
'method' : IDL.Text,
Expand All @@ -16,10 +48,6 @@ export const idlFactory = ({ IDL }) => {
'upgrade' : IDL.Opt(IDL.Bool),
'status_code' : IDL.Nat16,
});
const Account = IDL.Record({
'owner' : IDL.Principal,
'subaccount' : IDL.Opt(IDL.Vec(IDL.Nat8)),
});
const LiquidityPoolAccounts = IDL.Record({ 'icp' : Account });
const LiquidityPoolBalance = IDL.Record({ 'icp' : IDL.Nat });
const ConfigurationError = IDL.Variant({
Expand All @@ -40,19 +68,6 @@ export const idlFactory = ({ IDL }) => {
'Expired' : IDL.Record({ 'ledger_time' : IDL.Nat64 }),
'InsufficientFunds' : IDL.Record({ 'balance' : IDL.Nat }),
});
const TransferError = IDL.Variant({
'GenericError' : IDL.Record({
'message' : IDL.Text,
'error_code' : IDL.Nat,
}),
'TemporarilyUnavailable' : IDL.Null,
'BadBurn' : IDL.Record({ 'min_burn_amount' : IDL.Nat }),
'Duplicate' : IDL.Record({ 'duplicate_of' : IDL.Nat }),
'BadFee' : IDL.Record({ 'expected_fee' : IDL.Nat }),
'CreatedInFuture' : IDL.Record({ 'ledger_time' : IDL.Nat64 }),
'TooOld' : IDL.Null,
'InsufficientFunds' : IDL.Record({ 'balance' : IDL.Nat }),
});
const PoolError = IDL.Variant({
'PoolNotFound' : IDL.Nat,
'NotEnoughTokens' : IDL.Null,
Expand All @@ -66,15 +81,6 @@ export const idlFactory = ({ IDL }) => {
'InsufficientFunds' : IDL.Null,
});
const RegisterError = IDL.Variant({ 'TransactionNotFound' : IDL.Null });
const RejectionCode = IDL.Variant({
'NoError' : IDL.Null,
'CanisterError' : IDL.Null,
'SysTransient' : IDL.Null,
'DestinationInvalid' : IDL.Null,
'Unknown' : IDL.Null,
'SysFatal' : IDL.Null,
'CanisterReject' : IDL.Null,
});
const BalanceError = IDL.Variant({
'AccountNotFound' : IDL.Null,
'InsufficientBalance' : IDL.Null,
Expand Down Expand Up @@ -113,21 +119,16 @@ export const idlFactory = ({ IDL }) => {
'Icrc2Transfer' : TransferFromError,
'Ecdsa' : EcdsaError,
});
const Result = IDL.Variant({
const Result_1 = IDL.Variant({
'Ok' : LiquidityPoolBalance,
'Err' : EkokeError,
});
const WithdrawError = IDL.Variant({
'NothingToWithdraw' : IDL.Principal,
'Transfer' : TransferError,
'CanisterCall' : IDL.Tuple(RejectionCode, IDL.Text),
});
const Result_1 = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : WithdrawError });
return IDL.Service({
'admin_cycles' : IDL.Func([], [IDL.Nat], ['query']),
'admin_set_admins' : IDL.Func([IDL.Vec(IDL.Principal)], [], []),
'admin_set_deferred_canister' : IDL.Func([IDL.Principal], [], []),
'admin_set_icp_ledger_canister' : IDL.Func([IDL.Principal], [], []),
'admin_withdraw_icp' : IDL.Func([Account, IDL.Nat], [Result], []),
'create_refunds' : IDL.Func(
[IDL.Vec(IDL.Tuple(IDL.Principal, IDL.Nat))],
[],
Expand All @@ -139,8 +140,8 @@ export const idlFactory = ({ IDL }) => {
[LiquidityPoolAccounts],
['query'],
),
'liquidity_pool_balance' : IDL.Func([], [Result], ['query']),
'withdraw_refund' : IDL.Func([IDL.Opt(IDL.Vec(IDL.Nat8))], [Result_1], []),
'liquidity_pool_balance' : IDL.Func([], [Result_1], ['query']),
'withdraw_refund' : IDL.Func([IDL.Opt(IDL.Vec(IDL.Nat8))], [Result], []),
});
};
export const init = ({ IDL }) => {
Expand Down
9 changes: 5 additions & 4 deletions src/ekoke_liquidity_pool/ekoke-liquidity-pool.did
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ type RejectionCode = variant {
SysFatal;
CanisterReject;
};
type Result = variant { Ok : LiquidityPoolBalance; Err : EkokeError };
type Result_1 = variant { Ok; Err : WithdrawError };
type Result = variant { Ok; Err : WithdrawError };
type Result_1 = variant { Ok : LiquidityPoolBalance; Err : EkokeError };
type TransferError = variant {
GenericError : record { message : text; error_code : nat };
TemporarilyUnavailable;
Expand Down Expand Up @@ -103,9 +103,10 @@ service : (EkokeLiquidityPoolInitData) -> {
admin_set_admins : (vec principal) -> ();
admin_set_deferred_canister : (principal) -> ();
admin_set_icp_ledger_canister : (principal) -> ();
admin_withdraw_icp : (Account, nat) -> (Result);
create_refunds : (vec record { principal; nat }) -> ();
http_request : (HttpRequest) -> (HttpResponse) query;
liquidity_pool_accounts : () -> (LiquidityPoolAccounts) query;
liquidity_pool_balance : () -> (Result) query;
withdraw_refund : (opt blob) -> (Result_1);
liquidity_pool_balance : () -> (Result_1) query;
withdraw_refund : (opt blob) -> (Result);
}
9 changes: 9 additions & 0 deletions src/ekoke_liquidity_pool/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ impl EkokeLiquidityPoolCanister {
Ok(())
}

/// Withdraw icp to an account
pub async fn admin_withdraw_icp(to: Account, amount: Nat) -> Result<(), WithdrawError> {
if !Inspect::inspect_is_admin(utils::caller()) {
ic_cdk::trap("Unauthorized");
}

LiquidityPool::withdraw_icp(to, amount).await
}

/// Returns cycles
pub fn admin_cycles() -> Nat {
if !Inspect::inspect_is_admin(utils::caller()) {
Expand Down
8 changes: 7 additions & 1 deletion src/ekoke_liquidity_pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use did::ekoke_liquidity_pool::{
EkokeLiquidityPoolInitData, LiquidityPoolAccounts, LiquidityPoolBalance, WithdrawError,
};
use ic_cdk_macros::{init, query, update};
use icrc::icrc1::account::Subaccount;
use icrc::icrc1::account::{Account, Subaccount};

use self::app::EkokeLiquidityPoolCanister;

Expand Down Expand Up @@ -49,6 +49,12 @@ pub async fn withdraw_refund(subaccount: Option<Subaccount>) -> Result<(), Withd
EkokeLiquidityPoolCanister::withdraw_refund(subaccount).await
}

#[update]
#[candid_method(update)]
pub async fn admin_withdraw_icp(to: Account, amount: Nat) -> Result<(), WithdrawError> {
EkokeLiquidityPoolCanister::admin_withdraw_icp(to, amount).await
}

#[query]
#[candid_method(query)]
pub fn admin_cycles() -> Nat {
Expand Down

0 comments on commit 3988a3b

Please sign in to comment.