Very excited to share this library (changed to middleware later) with you.
We extracted it from our latest project after noticing some mechanisms inside can be reused in a lot of the DeFi canisters - not only DeFi Vectors.
It allows devs to use the library synchronously while it handles asynchronicity with icrc ledgers.
Example usage:
import L "mo:devefi-icrc-ledger";
import Principal "mo:base/Principal";
actor class() = this {
stable let lmem = L.LMem();
let ledger = L.Ledger(lmem, "mxzaz-hqaaa-aaaar-qaada-cai");
public func start() : async () { ledger.start(this) };
ledger.onReceive(func (t) = ignore ledger.send({ to = t.from; amount = t.amount; from_subaccount = t.to.subaccount; }));
}
Previously hard to achieve:
Getting notified automatically when there is a transfer to your canister (any subaccount)
ledger.onReceive(func (t : Ledger.Transfer) {
// handle incoming transfers to your canister
});
To do that it follows ledger history using timers.
Sending tokens with certainty without worrying about async calls
ignore ledger.send({
to = {owner; subaccount};
amount = t.amount;
from_subaccount = t.to.subaccount;
}) // returns #ok(local_tx_id) or #err(#insufficient_funds)
This basically adds the transaction to a BTree queue and removes the amount from the local accountâs (in_transit) balance. Local accounts are a Hashmap with {balance: Nat, in_transit: Nat}. The library wonât allow you to use the amount that is in transit.
When transfers come and go to your canister they automatically update the balance and in_transit. When the transfer gets confirmed after finding the block inside the ledger log, the amount gets reduced from in_transit
Get the balance of canister accounts synchronously
ledger.balance(subaccount) : Nat
This basically returns balance
- in_transit
. It keeps only accounts owned by the canister.
A bit more about how sending works
Itâs done with oneway calls and doesnât wait for a callback.
Transactions are retried indefinitely every X minutes until they arrive and retrying relies on deduplication.
Once a transaction is sent, there is no need for the developer to handle confirmations manually. They should consider it done. The transaction will be retried forever. The funds for that transaction are locked and inaccessible through the library. If the canister is DAO governed or black-holed and audited, I believe that will provide near atomic transaction security.
You could queue up to thousands of messages - as much as you can add inside a BTree in one call.
If you want to use 30 different ledgers inside one canister, itâs probably better if someone makes a ledger notification service that brings the info to this library.
https://mops.one/devefi-icrc-ledger
It uses https://mops.one/devefi-icrc-sender and https://mops.one/devefi-icrc-reader
Disclaimer! This library is not audited and not ready for production use. It may change its interface a lot. Currently, itâs a proof of concept. There is a lot of room for improvement and if someone wants to help with it let us know.