From 48cb8c58e382e176263190d54aa017330c784c3d Mon Sep 17 00:00:00 2001 From: Rahees Date: Mon, 22 Apr 2024 21:13:29 +0530 Subject: [PATCH] perf: mainnet deployad branch contains Blob [Nat8] issues --- .gitignore | 3 +- dfx.json | 7 +- mops.toml | 2 +- scripts/calculate-wasm-hash.sh | 34 +++ scripts/deploy-local.sh | 16 +- src/backend/Utils.mo | 6 +- src/backend/cycle-pool/main.mo | 53 ++--- src/backend/cycle-reserve/main.mo | 23 ++- src/backend/stablecoin-minter/main.mo | 287 +++++++++++++------------- 9 files changed, 244 insertions(+), 187 deletions(-) create mode 100755 scripts/calculate-wasm-hash.sh diff --git a/.gitignore b/.gitignore index 27354f7..93bf76d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ vite.config.js.timestamp-* vite.config.ts.timestamp-* .dfx deps -.mops \ No newline at end of file +.mops +canister_ids.json \ No newline at end of file diff --git a/dfx.json b/dfx.json index 3f93479..0617df7 100644 --- a/dfx.json +++ b/dfx.json @@ -29,7 +29,6 @@ "type": "motoko", "dependencies": [ "icp_ledger", - "cycles_ledger", "cycle_reserve", "cycle_minting_canister", "usdx_ledger", @@ -55,8 +54,9 @@ }, "icp_ledger": { "type": "custom", - "candid": "https://raw.githubusercontent.com/dfinity/ic/206a50f01306b398eb7e25988c7925fcd0e2caa4/rs/rosetta-api/icp_ledger/ledger.did", - "wasm": "https://download.dfinity.systems/ic/206a50f01306b398eb7e25988c7925fcd0e2caa4/canisters/ledger-canister.wasm.gz", + "candid": "https://raw.githubusercontent.com/dfinity/ic/33dd2ef2184a64c00e64ff0412e7378d46507005/rs/rosetta-api/icp_ledger/ledger.did", + "wasm": "https://download.dfinity.systems/ic/33dd2ef2184a64c00e64ff0412e7378d46507005/canisters/ledger-canister.wasm.gz", + "specified_id": "ryjl3-tyaaa-aaaaa-aaaba-cai", "remote": { "id": { "ic": "ryjl3-tyaaa-aaaaa-aaaba-cai" @@ -67,6 +67,7 @@ "type": "custom", "candid": "https://raw.githubusercontent.com/dfinity/ic/2e921c9adfc71f3edc96a9eb5d85fc742e7d8a9f/rs/nns/cmc/cmc.did", "wasm": "https://download.dfinity.systems/ic/2e921c9adfc71f3edc96a9eb5d85fc742e7d8a9f/canisters/cycles-minting-canister.wasm.gz", + "specified_id": "rkp4c-7iaaa-aaaaa-aaaca-cai", "remote": { "id": { "ic": "rkp4c-7iaaa-aaaaa-aaaca-cai" diff --git a/mops.toml b/mops.toml index b27b9aa..4a8e474 100644 --- a/mops.toml +++ b/mops.toml @@ -1,5 +1,5 @@ [dependencies] -base = "0.10.4" +base = "0.11.1" "encoding.mo" = "https://github.com/aviate-labs/encoding.mo#master@2711d18727e954b11afc0d37945608512b5fbce2" account-identifier = "1.0.2" map = "9.0.1" diff --git a/scripts/calculate-wasm-hash.sh b/scripts/calculate-wasm-hash.sh new file mode 100755 index 0000000..dfef56c --- /dev/null +++ b/scripts/calculate-wasm-hash.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +dfx build cycle_reserve --ic +dfx build cycle_pool --ic +dfx build stablecoin_minter --ic + +echo "######################## IC Mainnet WASM Hash ###########################" +# Define paths to each wasm file +cycle_reserve_path=".dfx/ic/canisters/cycle_reserve/cycle_reserve.wasm" +cycle_pool_path=".dfx/ic/canisters/cycle_pool/cycle_pool.wasm" +stablecoin_minter_path=".dfx/ic/canisters/stablecoin_minter/stablecoin_minter.wasm" + + +# Loop through each wasm file path +for wasm_file_path in "$cycle_reserve_path" "$cycle_pool_path" "$stablecoin_minter_path"; do + # Extract filename from path (optional) + filename=$(basename "$wasm_file_path") + + # Check if the file exists + if [ ! -f "$wasm_file_path" ]; then + echo "Error: File '$wasm_file_path' does not exist." + continue # Skip to the next iteration if file is missing + fi + + # Calculate the SHA256 hash using shasum + sha256_hash=$(shasum -a 256 "$wasm_file_path" | awk '{print $1}') + + # Print the hash with filename (if extracted) + if [ -n "$filename" ]; then + echo "SHA256 hash of '$filename' : $sha256_hash" + else + echo "SHA256 hash of '$wasm_file_path': $sha256_hash" + fi +done diff --git a/scripts/deploy-local.sh b/scripts/deploy-local.sh index 44ea6cf..f8438b1 100755 --- a/scripts/deploy-local.sh +++ b/scripts/deploy-local.sh @@ -17,7 +17,7 @@ fi export MINTER_ACCOUNT_ID=$(dfx ledger account-id --identity minter) export DEFAULT_ACCOUNT_ID=$(dfx ledger account-id --identity default) -dfx deploy --specified-id ryjl3-tyaaa-aaaaa-aaaba-cai icp_ledger --argument " +dfx deploy icp_ledger --specified-id ryjl3-tyaaa-aaaaa-aaaba-cai --argument " (variant { Init = record { minting_account = \"$MINTER_ACCOUNT_ID\"; @@ -58,22 +58,22 @@ dfx deploy cycles_ledger --specified-id um5iw-rqaaa-aaaaq-qaaba-cai --argument ' variant { Init= record { max_blocks_per_request = 1; index_id = null }} )' -#Deploy Backend Canister -dfx deploy cycle_reserve --specified-id br5f7-7uaaa-aaaaa-qaaca-cai --with-cycles 1_000_000 +#Deploy Reserve Canister +dfx deploy cycle_reserve --with-cycles 1_000_000 # Creating Local USDx Ledger before deploying stablecoin_minter (stablecoin_minter is a dependency of usdx_ledger) -dfx canister create usdx_ledger --specified-id bd3sg-teaaa-aaaaa-qaaba-cai +dfx canister create usdx_ledger --specified-id irorr-5aaaa-aaaak-qddsq-cai # Creating Local stablecoin_minter , root_canister before deploying usdx_ledger (These are minteraccount and archivecontroller for usdx_ledger) -dfx canister create stablecoin_minter --specified-id be2us-64aaa-aaaaa-qaabq-cai -dfx canister create root_canister --specified-id b77ix-eeaaa-aaaaa-qaada-cai +dfx canister create stablecoin_minter --specified-id iyn2n-liaaa-aaaak-qddta-cai +dfx canister create root_canister --specified-id iwpxf-qyaaa-aaaak-qddsa-cai # Deploy USDx Ledger Locally ./scripts/deploy-local-usdx.sh -dfx canister create cycle_pool --specified-id bw4dl-smaaa-aaaaa-qaacq-cai -dfx deploy cycle_pool --argument '(principal "bw4dl-smaaa-aaaaa-qaacq-cai")' +dfx canister create cycle_pool --specified-id i7m4z-gqaaa-aaaak-qddtq-cai +dfx deploy cycle_pool --argument '(principal "i7m4z-gqaaa-aaaak-qddtq-cai")' dfx deploy stablecoin_minter diff --git a/src/backend/Utils.mo b/src/backend/Utils.mo index cd403f7..334ea22 100644 --- a/src/backend/Utils.mo +++ b/src/backend/Utils.mo @@ -127,7 +127,7 @@ module { }; }; - let subValue : SubValue = format(value); + let _subValue : SubValue = format(value); return hmap; }; @@ -136,4 +136,8 @@ module { public func accountIdentifierDefault(id : Principal) : [Nat8] { Blob.toArray(Account.accountIdentifier(id, Account.defaultSubaccount())); }; + + public func accountIdentifierDefaultBlob(id : Principal) : Blob { + Account.accountIdentifier(id, Account.defaultSubaccount()); + }; }; diff --git a/src/backend/cycle-pool/main.mo b/src/backend/cycle-pool/main.mo index 8a0a10c..9917cdd 100644 --- a/src/backend/cycle-pool/main.mo +++ b/src/backend/cycle-pool/main.mo @@ -18,7 +18,7 @@ import Principal "mo:base/Principal"; import Blob "mo:base/Blob"; import Utils "../Utils"; -actor class CyclePool(cyclePoolCanisterId : Principal) { +actor CyclePool { type Operation = { #Add; #Subtract : { amount : Nat } }; type ReserveResult = Result.Result<(), Text>; type Time = Time.Time; @@ -41,6 +41,7 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { }; }; + let cyclePoolCanisterId = Principal.fromText("i7m4z-gqaaa-aaaak-qddtq-cai"); private stable var xdrUsd : XdrUsd = { rate = 0; timestamp = 0 }; let failedToUpdateXdrUsdRate = Buffer.Buffer(0); @@ -49,7 +50,7 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { stable let cyclesPoolTopUpFailed = StableBuffer.init(); public func cycle_pool_receive() : async Nat { - Cycles.accept(Cycles.available()); + Cycles.accept(Cycles.available()); // Emit event cycles received }; @@ -67,9 +68,9 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { }; system func postupgrade() : () { - restartTotalSupplyTimer(); - restartXrcFetchTimer(); - restartReserveAdjustTimer(); + restartTotalSupplyTimer(); + restartXrcFetchTimer(); + restartReserveAdjustTimer(); }; //////////// Fetch XDR/USD rate from XRC and update xdrUsd ////////////// @@ -88,7 +89,7 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { if (expectedCyclesInReserveWithNewRate > expectedCyclesInReserveWithPreviousRate) { let cycleInNeedToMaintainPeg : Nat = expectedCyclesInReserveWithNewRate - expectedCyclesInReserveWithPreviousRate; let cyclePoolBalance = Cycles.balance(); - let balanceMinusAdditionalforComputaion : Int = cyclePoolBalance - 10_000_000_000_000; // Ten trillion cycles as a buffer + let balanceMinusAdditionalforComputaion : Int = cyclePoolBalance - 2_000_000_000_000; // Ten trillion cycles as a buffer if (balanceMinusAdditionalforComputaion < cycleInNeedToMaintainPeg) { let cyclesToMint = cycleInNeedToMaintainPeg - balanceMinusAdditionalforComputaion; @@ -117,7 +118,7 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { amount = { e8s = icpAmountE8s }; fee = { e8s = 10_000 }; from_subaccount = null; - to = Blob.toArray(accountIdentifierCMC); + to = accountIdentifierCMC; created_at_time = ?{ timestamp_nanos = Nat64.fromIntWrap(Time.now()) }; }; @@ -136,7 +137,7 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { canister_id = cyclePoolCanisterId; }; let notifyTopUpResult = await CMC.notify_top_up(notifyTopUpArg); - let mintedCycles : Nat = switch (notifyTopUpResult) { + let _mintedCycles : Nat = switch (notifyTopUpResult) { case (#Ok(value)) { value }; case (#Err(error)) { StableBuffer.add(cyclesPoolTopUpFailed, { error_time = Time.now(); error = #CMC error }); @@ -150,8 +151,8 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { let quote_asset = { symbol = "USD"; class_ = #FiatCurrency }; let timestamp = null; - Cycles.add(10_000_000_000); - let getExchangeRateResult = await XRC.get_exchange_rate({ + Cycles.add(10_000_000_000); + let _getExchangeRateResult = await XRC.get_exchange_rate({ base_asset; quote_asset; timestamp; @@ -195,25 +196,25 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { let oneHourInNanoSec = 3600_000_000_000; let nextFetchTime = oneHourInNanoSec - (Time.now() % oneHourInNanoSec); - Timer.setTimer( + Timer.setTimer( #nanoseconds(Int.abs nextFetchTime), func() : async () { - xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); + xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); await timerUpdateXdrUsdRate(); } ); }; - private func restartXrcFetchTimer() : () { + private func restartXrcFetchTimer() : () { // let currentTime = Time.now(); let oneHourInNanoSec = 3600_000_000_000; let nextFetchTime = oneHourInNanoSec - (Time.now() % oneHourInNanoSec); - xrcFetchTimerId := Timer.setTimer( + xrcFetchTimerId := Timer.setTimer( #nanoseconds(Int.abs nextFetchTime), func() : async () { - xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); + xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); await timerUpdateXdrUsdRate(); } ); @@ -231,11 +232,11 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { stable var totalSupplyTimerId : TimerId = 0; totalSupplyTimerId := do { - Timer.recurringTimer(#seconds 1, fetchTotalSupply); + Timer.recurringTimer(#seconds 1, fetchTotalSupply); }; - private func restartTotalSupplyTimer() : () { - totalSupplyTimerId := Timer.recurringTimer(#seconds 1, fetchTotalSupply); + private func restartTotalSupplyTimer() : () { + totalSupplyTimerId := Timer.recurringTimer(#seconds 1, fetchTotalSupply); }; private func stopTotalSupplyTimer() : () { @@ -248,22 +249,26 @@ actor class CyclePool(cyclePoolCanisterId : Principal) { let expectedCyclesInReserve__ : Float = usdxTotalSupply * (1 / xdrUsd.rate) * 1_000_000_000_000; let expectedCyclesInReserve = Int.abs(Float.toInt(expectedCyclesInReserve__)); + // 8,511,097,318,335 = 11.33606873*(1/1.331916239)*1,000,000,000,000 + // 8,511,097,318,335 > 8_499_224_797_270 + if (expectedCyclesInReserve > reserveCurrentCycles) { - Cycles.add(expectedCyclesInReserve - reserveCurrentCycles); - let result = await CycleReserve.cycle_reserve_adjust(#Add); + // 11,872,521,065 + Cycles.add(expectedCyclesInReserve - reserveCurrentCycles); + let _result = await CycleReserve.cycle_reserve_adjust(#Add); } else if (expectedCyclesInReserve < reserveCurrentCycles) { let amount : Nat = reserveCurrentCycles - expectedCyclesInReserve; - let result = await CycleReserve.cycle_reserve_adjust(#Subtract { amount }); + let _result = await CycleReserve.cycle_reserve_adjust(#Subtract { amount }); }; }; stable var reserveAdjustTimerId : TimerId = 0; reserveAdjustTimerId := do { - Timer.recurringTimer(#seconds 1, adjustCycleReserve); + Timer.recurringTimer(#seconds 1, adjustCycleReserve); }; - private func restartReserveAdjustTimer() : () { - reserveAdjustTimerId := Timer.recurringTimer(#seconds 1, adjustCycleReserve); + private func restartReserveAdjustTimer() : () { + reserveAdjustTimerId := Timer.recurringTimer(#seconds 1, adjustCycleReserve); }; private func stopReserveAdjustTimer() : () { Timer.cancelTimer(reserveAdjustTimerId); diff --git a/src/backend/cycle-reserve/main.mo b/src/backend/cycle-reserve/main.mo index b6c8dc8..43e9221 100644 --- a/src/backend/cycle-reserve/main.mo +++ b/src/backend/cycle-reserve/main.mo @@ -1,6 +1,6 @@ import Principal "mo:base/Principal"; import Cycles "mo:base/ExperimentalCycles"; -import Debug "mo:base/Debug"; + import Result "mo:base/Result"; import Text "mo:base/Text"; @@ -15,16 +15,17 @@ actor { let cyclePool : actor { cycle_pool_receive : shared () -> async Nat; - } = actor ("bw4dl-smaaa-aaaaa-qaacq-cai"); + } = actor ("i7m4z-gqaaa-aaaak-qddtq-cai"); - let stablecoinMinter : actor {} = actor ("bd3sg-teaaa-aaaaa-qaaba-cai"); + let stablecoinMinter : Principal = Principal.fromText("iyn2n-liaaa-aaaak-qddta-cai"); // Ledger canister or Cycle minting canister will call this method to add cycles to the reserve public shared ({ caller }) func cycle_reserve_receive() : async Result { - if (caller != Principal.fromActor(stablecoinMinter)) { + if (caller != stablecoinMinter) { #err("Unauthorized caller"); } else { - let acceptCycles = Cycles.accept(Cycles.available()); + // accept Cycles + let _acceptCycles = Cycles.accept(Cycles.available()); #ok(); }; @@ -32,18 +33,18 @@ actor { // Pool canister will call this method to adjust the reserve public shared ({ caller }) func cycle_reserve_adjust(operaion : Operation) : async Result { - if (caller != Principal.fromActor(cyclePool)) { + if (caller != Principal.fromText("i7m4z-gqaaa-aaaak-qddtq-cai")) { return #err("Unauthorized caller"); }; switch (operaion) { case (#Add) { - let acceptCycles = Cycles.accept(Cycles.available()); + let _acceptCycles = Cycles.accept(Cycles.available()); #ok(); }; case (#Subtract { amount : Nat }) { - Cycles.add(amount); - let subtractedCycles = await cyclePool.cycle_pool_receive(); + Cycles.add(amount); + let _subtractedCycles = await cyclePool.cycle_pool_receive(); #ok(); }; }; @@ -51,8 +52,8 @@ actor { }; // Get current cycle reserve balance - public shared func cycle_reserve_balance() : async Nat { - let balance = Cycles.balance(); + public shared query func cycle_reserve_balance() : async Nat { + Cycles.balance(); }; }; diff --git a/src/backend/stablecoin-minter/main.mo b/src/backend/stablecoin-minter/main.mo index 6f9bded..a17e7ca 100644 --- a/src/backend/stablecoin-minter/main.mo +++ b/src/backend/stablecoin-minter/main.mo @@ -1,4 +1,4 @@ -import CycleLedger "canister:cycles_ledger"; +// import CyclesLedger "canister:cycles_ledger"; import IcpLedger "canister:icp_ledger"; import CycleReserve "canister:cycle_reserve"; import CMC "canister:cycle_minting_canister"; @@ -30,7 +30,7 @@ actor StablecoinMinter { type Time = Time.Time; type TimerId = Timer.TimerId; - type CLBlockIndex = CycleLedger.BlockIndex; + // type CLBlockIndex = CyclesLedger.BlockIndex; type IcpBlockIndex = IcpLedger.BlockIndex; type USDxBlockIndex = USDx.BlockIndex; @@ -96,7 +96,7 @@ actor StablecoinMinter { let { nhash; n64hash } = Map; // key = Notify cycles Ledger Tx BlockIndex , value = (Cycle Withdraw Tx BlockIndex, USDx mint Tx BlockIndex) - private stable let processedMintRequestFromCylesLedgerTx = Map.new(); + // private stable let processedMintRequestFromCylesLedgerTx = Map.new(); // key = Notify ICP Tx BlockIndex , value = (ICP to CMC Tx BlockIndex, USDx mint Tx BlockIndex) private stable let processedMintRequestFromIcpTx = Map.new(); @@ -107,7 +107,7 @@ actor StablecoinMinter { let failedToUpdateXdrUsdRate = Buffer.Buffer(0); - public shared ({ caller }) func notify_mint_with_icp(icpBlockIndex : Nat64, coin : MintCoin) : async NotifyMintWithICPResult { + public shared ({ caller }) func notify_mint_with_icp(icpBlockIndex : Nat64, _coin : MintCoin) : async NotifyMintWithICPResult { trapAnonymousUser(caller); await trapWhenXRateIsZero(); @@ -134,7 +134,7 @@ actor StablecoinMinter { amount = { e8s = txAmount - 10_000 }; fee = { e8s = 10_000 }; from_subaccount = null; - to = Blob.toArray(accountIdentifierCMC); + to = accountIdentifierCMC; created_at_time = ?{ timestamp_nanos = Nat64.fromIntWrap(Time.now()) }; }; @@ -163,8 +163,8 @@ actor StablecoinMinter { let mintFee : Nat = calculateMintFee(cyclesAmount); let reserveAmount : Nat = cyclesAmount - mintFee; - Cycles.add(reserveAmount); - let result = await CycleReserve.cycle_reserve_receive(); + Cycles.add(reserveAmount); + let _result = await CycleReserve.cycle_reserve_receive(); // calling ICRC to mint the stablecoin let usdxTransferResult : USDx.TransferResult = await USDx.icrc1_transfer({ @@ -189,69 +189,75 @@ actor StablecoinMinter { #ok(usdxBlockIndex); }; - public shared ({ caller }) func notify_mint_with_cycles_ledger_transfer(blockIndex_ : CLBlockIndex, coin : MintCoin) : async NotifyMintWithCyclesLedgerTransferResult { - trapAnonymousUser(caller); - await trapWhenXRateIsZero(); - - if (Map.get(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_) != null) { - - let (?(cyclesLedgerBlockIndex, usdxBlockIndex)) = Map.get(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_) else { - return #err(#Other({ error_message = "blockIndex not found"; error_code = 1 })); - }; - return #err(#AlreadyProcessed { blockIndex = usdxBlockIndex }); - }; - - let validatedBlockResult = await validateCyclesLedgerBlock(blockIndex_, caller); - let (txAmount, mintTo) : (Nat, Account) = switch (validatedBlockResult) { - case (#ok(value)) { value }; - case (#err(error)) { return #err(error) }; - }; - - // Calculate mint fee , cycles ledger transaction fee, and transfer the remaining cycles to the reserve - let mintFee : Nat = calculateMintFee(txAmount); - let ledgerTxFee : Nat = 100_000_000; // For withdrawing cycles - let reserveAmount : Nat = txAmount - mintFee - ledgerTxFee; - - ///// transfer the cycles to the reserve and mint the stablecoin - let withdrawArgs : CycleLedger.WithdrawArgs = { - amount = reserveAmount; - from_subaccount = null; - to = Principal.fromActor(CycleReserve); - created_at_time = ?Nat64.fromIntWrap(Time.now()); - }; - let withdrawCyclesResult = await CycleLedger.withdraw(withdrawArgs); - - let withdrawBlockIndex = switch (withdrawCyclesResult) { - case (#Ok(value)) { value }; - case (#Err(error)) { - return #err(#Other({ error_message = "Cycles Ledger Withdraw Error"; error_code = 2 })); - }; - }; - - // calling ICRC to mint the stablecoin - let transferResult : USDx.TransferResult = await USDx.icrc1_transfer({ - to = mintTo; - fee = null; - memo = null; - from_subaccount = null; - created_at_time = null; - amount = calculateMintAmount(reserveAmount); - }); - - let usdxBlockIndex = switch (transferResult) { - case (#Ok(value)) { value }; - case (#Err(error)) { - return #err(#Other({ error_message = "USDx Ledger Transfer Error"; error_code = 7 })); - }; - }; - - // Then add Block index to the processedMintRequestFromCylesLedger - Map.set(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_, (withdrawBlockIndex, usdxBlockIndex)); - - #ok(usdxBlockIndex); - }; - - public shared ({ caller }) func mint_through_call(coin : MintCoin, account : ?Account) : async MintThroughCallResult { + /* + public shared ({ caller }) func notify_mint_with_cycles_ledger_transfer(blockIndex_ : CLBlockIndex, coin : MintCoin) : async NotifyMintWithCyclesLedgerTransferResult { + trapAnonymousUser(caller); + await trapWhenXRateIsZero(); + + if (Map.get(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_) != null) { + + let (?(cyclesLedgerBlockIndex, usdxBlockIndex)) = Map.get(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_) else { + return #err(#Other({ error_message = "blockIndex not found"; error_code = 1 })); + }; + return #err(#AlreadyProcessed { blockIndex = usdxBlockIndex }); + }; + + let validatedBlockResult = await validateCyclesLedgerBlock(blockIndex_, caller); + let (txAmount, mintTo) : (Nat, Account) = switch (validatedBlockResult) { + case (#ok(value)) { value }; + case (#err(error)) { return #err(error) }; + }; + + // Calculate mint fee , withdraw amount, and transfer the remaining cycles to the reserve + let mintFee : Nat = calculateMintFee(txAmount); + let ledgerTxFee : Nat = 100_000_000; // For withdrawing cycles + let withdrawAmount : Nat = txAmount - ledgerTxFee; + let reserveAmount : Nat = withdrawAmount - mintFee; + + ///// withdraw cycles to the stablecoin minter and mint the stablecoin + let withdrawArgs : CyclesLedger.WithdrawArgs = { + amount = withdrawAmount; + from_subaccount = null; + to = Principal.fromActor(StablecoinMinter); + created_at_time = ?Nat64.fromIntWrap(Time.now()); + }; + let withdrawCyclesResult = await CyclesLedger.withdraw(withdrawArgs); + + let withdrawBlockIndex = switch (withdrawCyclesResult) { + case (#Ok(value)) { value }; + case (#Err(error)) { + return #err(#Other({ error_message = "Cycles Ledger Withdraw Error"; error_code = 2 })); + }; + }; + + Cycles.add(reserveAmount); + let result = await CycleReserve.cycle_reserve_receive(); + + // calling ICRC to mint the stablecoin + let transferResult : USDx.TransferResult = await USDx.icrc1_transfer({ + to = mintTo; + fee = null; + memo = null; + from_subaccount = null; + created_at_time = null; + amount = calculateMintAmount(reserveAmount); + }); + + let usdxBlockIndex = switch (transferResult) { + case (#Ok(value)) { value }; + case (#Err(error)) { + return #err(#Other({ error_message = "USDx Ledger Transfer Error"; error_code = 7 })); + }; + }; + + // Then add Block index to the processedMintRequestFromCylesLedger + Map.set(processedMintRequestFromCylesLedgerTx, nhash, blockIndex_, (withdrawBlockIndex, usdxBlockIndex)); + + #ok(usdxBlockIndex); + }; + */ + + public shared ({ caller }) func mint_through_call(_coin : MintCoin, account : ?Account) : async MintThroughCallResult { trapAnonymousUser(caller); await trapWhenXRateIsZero(); @@ -265,14 +271,14 @@ actor StablecoinMinter { }; // obtaining cycles received through function call - let obtainedCycles = Cycles.accept(cyclesAmount); + let obtainedCycles = Cycles.accept(cyclesAmount); // After collecting minting fee in cycles send remaining cycles to reserve let mintFee : Nat = calculateMintFee(obtainedCycles); let reserveAmount : Nat = obtainedCycles - mintFee; - Cycles.add(reserveAmount); - let result = await CycleReserve.cycle_reserve_receive(); + Cycles.add(reserveAmount); + let _result = await CycleReserve.cycle_reserve_receive(); // calling ICRC to mint the stablecoin let transferResult : USDx.TransferResult = await USDx.icrc1_transfer({ @@ -304,9 +310,9 @@ actor StablecoinMinter { public query func get_account_identitier_of_stablecoin_minter() : async (IcpLedger.AccountIdentifier, Text) { let accountIdentitifier = Utils.accountIdentifierDefault(Principal.fromActor(StablecoinMinter)); - (accountIdentitifier, Utils.toHex(accountIdentitifier)); + (Blob.fromArray(accountIdentitifier), Utils.toHex(accountIdentitifier)); }; - public shared query ({ caller }) func caller_account_identifier() : async Text { + public shared query ({ caller }) func get_caller_account_identifier_for_cmc() : async Text { let accountIdentitifier = Utils.accountIdentifier( Principal.fromActor(CMC), Utils.principalToSubaccountBlob(caller) @@ -314,6 +320,10 @@ actor StablecoinMinter { Utils.toHex(Blob.toArray accountIdentitifier); }; + public query func get_failed_to_update_xdr_usd_rate() : async [UpdateXdrUsdRateError] { + Buffer.toArray failedToUpdateXdrUsdRate; + }; + ////////// Private functions ////////// private func calculateMintFee(amount : Nat) : Nat { let fixedFee : Nat = 1_000_000_000; @@ -334,7 +344,7 @@ actor StablecoinMinter { if (Principal.isAnonymous(caller)) Debug.trap("Anonymous principal cannot mint"); }; func trapWhenXRateIsZero() : async () { - if (xdrUsd.rate == 0) { let result = await updateXdrUsdRate() }; + if (xdrUsd.rate == 0) { let _result = await updateXdrUsdRate() }; if (xdrUsd.rate == 0) Debug.trap("XDR to USD rate is zero"); }; @@ -357,15 +367,15 @@ actor StablecoinMinter { }; // Check "to" in TX is AccountIdentifier of Stablecoin Minter with default subaccount - let accountIdentifierOfStablecoinMinter : IcpLedger.AccountIdentifier = Utils.accountIdentifierDefault(Principal.fromActor(StablecoinMinter)); + let accountIdentifierOfStablecoinMinter : IcpLedger.AccountIdentifier = Blob.fromArray(Utils.accountIdentifierDefault(Principal.fromActor(StablecoinMinter))); if (accountIdentifierOfStablecoinMinter != to) { - return #err(#InvalidTransaction("The destination account (" # Utils.toHex(to) # ") in the transaction is not the stablecoin minter's account (#" # Utils.toHex(accountIdentifierOfStablecoinMinter) # ")")); + return #err(#InvalidTransaction("The destination account (" # Utils.toHex(Blob.toArray to) # ") in the transaction is not the stablecoin minter's account (" # Utils.toHex(Blob.toArray accountIdentifierOfStablecoinMinter) # ")")); }; // Check "from" in TX is the caller principal with default subaccount - let accountIdentifierOfCaller : IcpLedger.AccountIdentifier = Utils.accountIdentifierDefault(caller); + let accountIdentifierOfCaller : IcpLedger.AccountIdentifier = Blob.fromArray(Utils.accountIdentifierDefault(caller)); if ((accountIdentifierOfCaller != from) /* and (spender == null) */) { - return #err(#InvalidTransaction("Notifier account (" # Utils.toHex(accountIdentifierOfCaller) # ") and transaction origin account (" # Utils.toHex(from) # ") are not the same")); + return #err(#InvalidTransaction("Notifier account (" # Utils.toHex(Blob.toArray accountIdentifierOfCaller) # ") and transaction origin account (" # Utils.toHex(Blob.toArray from) # ") are not the same")); }; // if there is spender in Tx check spender is notifying [ But they have to provide caller principal or Account to check with from] @@ -391,56 +401,57 @@ actor StablecoinMinter { // 382623823; // }; - private func validateCyclesLedgerBlock(blockIndex_ : CLBlockIndex, caller : Principal) : async Result<(Nat, Account), NotifyError> { - let getBlocksResult = await CycleLedger.icrc3_get_blocks([{ - length = 1; - start = blockIndex_; - }]); - - let blocks = getBlocksResult.blocks; - if (blocks.size() == 0) { - return #err(#InvalidTransaction("Block " # Nat.toText(blockIndex_) # " not found log_length is" # Nat.toText(getBlocksResult.log_length))); - }; - let block = blocks[0].block; - - let transferTx : CyclesLedgerTransferTX = switch (getCyclesLedgerTransferTX(block)) { - case (#ok(value)) { value }; - case (#err(error)) { return #err(error) }; - }; - - // check principal of stablecoin minter with to - let transactionToPrincipal : Principal = Principal.fromBlob(transferTx.transaction.to[0]); - let stablecoinMinterPrincipal : Principal = Principal.fromActor(StablecoinMinter); - if (stablecoinMinterPrincipal != transactionToPrincipal) { - return #err( - #InvalidTransaction( - "The cycle destination account (" # Principal.toText(transactionToPrincipal) # - ") in the transaction is not the stablecoin minter's account (#" # Principal.toText(stablecoinMinterPrincipal) # ")" - ) - ); - }; - - // check pricnipal of caller with from - let transactionFromPrincipal : Principal = Principal.fromBlob(transferTx.transaction.from[0]); - if (caller != transactionFromPrincipal) { - return #err( - #InvalidTransaction( - "Notifier principal (" # Principal.toText(caller) # - ") and transaction origin principal (" # Principal.toText(transactionFromPrincipal) # ") are not the same" - ) - ); - }; - - // check amount is above minimum 1_000_000_000_000 - let amount : Int = transferTx.transaction.amount; - if (amount < 1_000_000_000_000) { - return #err(#InvalidTransaction("Transaction amount is less than 1 Trillion cycles")); - } else { - #ok(Int.abs(amount), getAccountFromCyclesLedgerTxFrom(transferTx.transaction.from)); - }; - - }; - + /* + private func validateCyclesLedgerBlock(blockIndex_ : CLBlockIndex, caller : Principal) : async Result<(Nat, Account), NotifyError> { + let getBlocksResult = await CyclesLedger.icrc3_get_blocks([{ + length = 1; + start = blockIndex_; + }]); + + let blocks = getBlocksResult.blocks; + if (blocks.size() == 0) { + return #err(#InvalidTransaction("Block " # Nat.toText(blockIndex_) # " not found log_length is" # Nat.toText(getBlocksResult.log_length))); + }; + let block = blocks[0].block; + + let transferTx : CyclesLedgerTransferTX = switch (getCyclesLedgerTransferTX(block)) { + case (#ok(value)) { value }; + case (#err(error)) { return #err(error) }; + }; + + // check principal of stablecoin minter with to + let transactionToPrincipal : Principal = Principal.fromBlob(transferTx.transaction.to[0]); + let stablecoinMinterPrincipal : Principal = Principal.fromActor(StablecoinMinter); + if (stablecoinMinterPrincipal != transactionToPrincipal) { + return #err( + #InvalidTransaction( + "The cycle destination account (" # Principal.toText(transactionToPrincipal) # + ") in the transaction is not the stablecoin minter's account (#" # Principal.toText(stablecoinMinterPrincipal) # ")" + ) + ); + }; + + // check pricnipal of caller with from + let transactionFromPrincipal : Principal = Principal.fromBlob(transferTx.transaction.from[0]); + if (caller != transactionFromPrincipal) { + return #err( + #InvalidTransaction( + "Notifier principal (" # Principal.toText(caller) # + ") and transaction origin principal (" # Principal.toText(transactionFromPrincipal) # ") are not the same" + ) + ); + }; + + // check amount is above minimum 1_000_000_000_000 + let amount : Int = transferTx.transaction.amount; + if (amount < 1_000_000_000_000) { + return #err(#InvalidTransaction("Transaction amount is less than 1 Trillion cycles")); + } else { + #ok(Int.abs(amount), getAccountFromCyclesLedgerTxFrom(transferTx.transaction.from)); + }; + + }; + */ func getCyclesLedgerTransferTX(block_ : Value) : Result { let hmap : HashMap = Utils.formatValueOfBlock(block_); @@ -562,7 +573,7 @@ actor StablecoinMinter { stopXrcFetchTimer(); }; system func postupgrade() : () { - restartXrcFetchTimer(); + restartXrcFetchTimer(); }; // Fetch XDR/USD rate from XRC and update xdrUsd @@ -572,8 +583,8 @@ actor StablecoinMinter { let quote_asset = { symbol = "USD"; class_ = #FiatCurrency }; let timestamp = null; - Cycles.add(10_000_000_000); - let getExchangeRateResult = await XRC.get_exchange_rate({ + Cycles.add(10_000_000_000); + let _getExchangeRateResult = await XRC.get_exchange_rate({ base_asset; quote_asset; timestamp; @@ -613,25 +624,25 @@ actor StablecoinMinter { let oneHourInNanoSec = 3600_000_000_000; let nextFetchTime = oneHourInNanoSec - (Time.now() % oneHourInNanoSec); - Timer.setTimer( + Timer.setTimer( #nanoseconds(Int.abs nextFetchTime), func() : async () { - xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); + xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); await timerUpdateXdrUsdRate(); } ); }; - private func restartXrcFetchTimer() : () { + private func restartXrcFetchTimer() : () { // let currentTime = Time.now(); let oneHourInNanoSec = 3600_000_000_000; let nextFetchTime = oneHourInNanoSec - (Time.now() % oneHourInNanoSec); - xrcFetchTimerId := Timer.setTimer( + xrcFetchTimerId := Timer.setTimer( #nanoseconds(Int.abs nextFetchTime), func() : async () { - xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); + xrcFetchTimerId := Timer.recurringTimer(#seconds 3600, timerUpdateXdrUsdRate); await timerUpdateXdrUsdRate(); } );