GithubHelp home page GithubHelp logo

natlabs / icrc1 Goto Github PK

View Code? Open in Web Editor NEW
32.0 3.0 23.0 169 KB

A full implementation of the ICRC-1 fungible token standard

Home Page: https://natlabs.github.io/icrc1/

License: MIT License

Makefile 0.89% Dhall 1.25% Motoko 97.86%

icrc1's Introduction

ICRC-1 Implementation

This repo contains the implementation of the ICRC-1 token standard.

References and other implementations

Documentation

Getting Started

  • Expose the ICRC-1 token functions from your canister

    • Import the icrc1 lib and expose them in an actor class.

      Take a look at the examples

  • Launch the basic token with all the standard functions for ICRC-1

    • Install the mops package manager
    • Replace the values enclosed in < > with your desired values and run in the terminal
      git clone https://github.com/NatLabs/icrc1
      cd icrc1
      mops install
      dfx start --background --clean
    
      dfx deploy icrc1 --argument '( record {                     
          name = "<Insert Token Name>";                         
          symbol = "<Insert Symbol>";                           
          decimals = 6;                                           
          fee = 1_000_000;                                        
          max_supply = 1_000_000_000_000;                         
          initial_balances = vec {                                
              record {                                            
                  record {                                        
                      owner = principal "<Insert Principal>";   
                      subaccount = null;                          
                  };                                              
                  100_000_000                                 
              }                                                   
          };                                                      
          min_burn_amount = 10_000;                         
          minting_account = null;                                 
          advanced_settings = null;                               
      })'
  • Create a token dynamically from a canister

        import Nat8 "mo:base/Nat8";
        import Token "mo:icrc1/ICRC1/Canisters/Token";
    
        actor{
            let decimals = 8; // replace with your chosen number of decimals
    
            func add_decimals(n: Nat): Nat{
                n * 10 ** decimals
            };
    
            let pre_mint_account = {
                owner = Principal.fromText("<Insert Principal>");
                subaccount = null;
            };
    
            let token_canister = Token.Token({
                name = "<Insert Token Name>";
                symbol = "<Insert Token Symbol>";
                decimals = Nat8.fromNat(decimals);
                fee = add_decimals(1);
                max_supply = add_decimals(1_000_000);
    
                // pre-mint 100,000 tokens for the account
                initial_balances = [(pre_mint_account, add_decimals(100_000))]; 
    
                min_burn_amount = add_decimals(10);
                minting_account = null; // defaults to the canister id of the caller
                advanced_settings = null; 
            });
        }

The fields for the advanced_settings record are documented here

Tests

Internal Tests

  • Download and Install vessel
  • Run make test
  • Run make actor-test
  • Install Rust and Cargo via rustup
    curl https://sh.rustup.rs -sSf | sh
  • Then run the ref-test command
    make ref-test

Funding

This library was initially incentivized by ICDevs. You can view more about the bounty on the forum or website. The bounty was funded by The ICDevs.org community and the DFINITY Foundation and the award was paid to @NatLabs. If you use this library and gain value from it, please consider a donation to ICDevs.

icrc1's People

Contributors

tomijaga avatar skilesare avatar

Stargazers

Omar Hernandez Salmeron avatar  avatar Matt White avatar Marius Landsverk avatar Frederic R. avatar Tevin-Isaac avatar Lewin Kelly avatar Ariel Robotti avatar  avatar dimi avatar noly avatar muqingqing avatar Angkin Winartan avatar Enoch Chirima avatar  avatar tomkoom avatar Sam-the-tutor avatar Alex Shelkovskiy avatar Denizcan Yilmaz avatar Kadircan Bozkurt avatar Denny Weiß avatar yugocabrio avatar ܐܪܡܝܐ ܐܪܡ avatar Moritz Fuller avatar Michael Shapkin avatar Sun avatar  avatar Fer Martinez avatar  avatar  avatar Tanguy avatar  avatar

Watchers

 avatar Keno avatar  avatar

icrc1's Issues

Returned "Result" Type... #Ok or not #ok?

Great work!

While doing some related work, I noticed that the "official" example repository declares its Result type the way the ICP/NNS ledger canister does:

public type Result<T, E> = { #Ok : T; #Err : E };

Which is also how another implementation by @eimolad does it:

public type TransferResult = { #Ok : Tokens; #Err : TransferError; };

I'm not sure why #Ok/#Err is used over the base library's #ok/#err, but this would cause conflicts with future callers if one or the other is not consistently used. Maybe @skilesare knows which should be?

get_transactions bug, fail with Natural subtraction underflow

If It has 3 transactions, call get_transactions fail with Natural subtraction underflow.
you can test it if set a length less than start (by search inside transactions).

// dfx call
call get_transactions '(record{start=2 : nat; length=1 : nat})'

the bug cause by this line.

txs_in_canister:= SB.slice(transactions, tx_start_index, req.length);

function SB.sclice accept argument (buffer, start, end)

StableBuffer with slice = func<A>(buffer : T.StableBuffer<A>, start : Nat, end : Nat) : [A] {

but the code call it , SB.slice(transactions, tx_start_index, req.length).

it should fix to


SB.slice(transactions, tx_start_index,  tx_start_index + req.length).

could you fix this? or may I pull a request.

Archive canister duplicate transactions

Found this implementation bug on accident. My archive canister ran out of cycles, when cycles were readded I noticed a duplicate batch of transactions in the archive.

The theory is, it got up to 2k transcations in the ledger and when it tried to send them the archive was out of cycles so it kept trying to send them after every transaction.

Once the archive was refilled, each transaction will try to send that bulk. So you basically need just 2 transactions one after each other with less than 2-3 seconds to get the ledger to send it twice.

I'm working on a solution to backfill and reindex my archive now. To prevent this in the future, I think some kind of check could be added the the append_transactions function that checks for the existing indexes/batch.

Github Workflow Secrets not being applied

@tomijaga I am seeing the following error when my PR tries to run the Workflow tests.

  echo "" > ./identity.pem
  echo ""
  dfx identity import icrc-ref-test ./identity.pem --storage-mode plaintext
  dfx identity use icrc-ref-test
  dfx identity whoami
  make ref-test
  shell: /usr/bin/bash -e {0}
  env:
    DFX_TELEMETRY_DISABLED: 1

ref-test

Is there some config I am missing to enable secrets for this PR?
I see in the makefile that this is the one trying to be called:

echo "${{ secrets.IDENTITY_SSH }}" > ./identity.pem

I leave the action link below:
https://github.com/NatLabs/icrc1/actions/runs/5099151687/jobs/9166723292

Return from Transfer

While this is technically correct:


        /// Transfers the given amount of tokens from the sender to the recipient
        icrc1_transfer : shared (TransferArgs) -> async Result.Result<Balance, TransferError>;

because Balance is a Nat....it is kind of confusing. There should be a BlockNumber of type Nat and use that here as the return is the block number of the new transaction and not the balance of the account.

icrc1_transfer not decrementing fee from sender

Hello, I am working with the Motoko Bootcamp and helped by building a quick minter for som ICRC1 tokens to be used by the students. you can check it out here :
https://dpzjy-fyaaa-aaaah-abz7a-cai.ic0.app/
That has 2 canisters and the data canister talks to an untouched implementation of your repo... it seems that it is not charging the fee on a successful transaction ... here is the candid of the token canister we launched:
https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app/?id=db3eq-6iaaa-aaaah-abz6a-cai

we will probably take it down after the bootcamp.... I followed through the code and though it does check that the transaction has the correct fee, it is never burned... I would imagine that adding a fee subtraction to this would do the trick, but want to confirm I did not implement it incorrectly or you have a better idea ... would like to use this standard again and your repo was a great help so THANK YOU ...
the following is from src/ICRC1/Account.mo - starting at line 182


    /// Transfers tokens from the sender to the
    /// recipient in the tx request
    public func transfer_balance(
        accounts : T.AccountBalances,
        tx_req : T.TransactionRequest,
    ) {
        let { encoded; amount } = tx_req;

        update_balance(
            accounts,
            encoded.from,
            func(balance) {
                balance - amount;
            },
        );

        update_balance(
            accounts,
            encoded.to,
            func(balance) {
                balance + amount;
            },
        );
    };

please let me know, students are using this as we speak :)

`validate_subaccount` always returns true regardless of size

in Utils.mo

    /// Checks if a subaccount is valid
    public func validate_subaccount(subaccount : ?T.Subaccount) : Bool {
        switch (subaccount) {
            case (?bytes) {
                bytes.size() == 32;
            };
            case (_) true;
        };
    };

Is this intended? The standard does not mention that it SHOULD be exactly 32 bytes, but I recall the reference implementation returns false in case the subaccount is not exactly 32 bytes

Thnx to @v2i0s2h2 for catching this!

Small error in Transfer.mo:217

Currently it is:if (tx_req.amount > balance + token._fee) {

It shall be: if (tx_req.amount + token._fee > balance) {

Luckily right now it traps with a "Natural subtraction underflow" preventing the transfer. But it should return the error #InsufficientFunds instead.

Way to reproduce: transfer the exact balance from the account (without subtracting the fee from it).

Ultimatly it would be nice to have a test for it :)

Archive canister get_transactions query exception

When there are too many transaction records in archive canister, usually more than 1000 records, there will be a situation that the expected number of data does not match the length of the returned data when the transaction record is queried by calling the get_transactions function. After preliminary analysis, the root of the problem is probably located in line 147 of Archive.mo, where the end value is taken from start + length in line 132. Therefore, when the query start is large, The minimum value obtained here is usually bucket.size (). As a result, the query results do not meet expectations.

Iter: = Itertools.fromArraySlice (bucket, start% BUCKET_SIZE, Nat.min (bucket.size (), end))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.