ICRC-17 - Elective KYC Service Standard

ICRC-17 - Elective KYC Service Standard

Data Details

  • icrc: 17
  • title: ICRC-17 Elective KYC Standard
  • author: Austin Fatheree - austin dot fatheree at gmail dot com - @afat on twitter
  • status: Deliberating
  • category: ICRC
  • requires: ICRC-16
  • created: 2023-Mar-10
  • updated: 2023-Mar-10

Context

The proposed ICRC-17 Elective KYC Standard aims to establish an interface for dapps to communicate with KYC Service canisters on the Internet Computer. The standard defines two simple functions that allow dapps to request KYC checks for users and to notify the KYC Service canister when users transact a certain amount of tokens. The standard also defines a set of types that can be used to represent the necessary data for KYC checks and results.

Assumptions

The ICRC-17 standard relies on the following assumptions:

  • KYC should be elective by the stakeholders involved in a transaction, with varying levels of KYC required depending on the stakeholders involved.
  • KYC is important and a practical reality for many users in certain jurisdictions, as well as for dapp providers building in those jurisdictions.
  • The minimal information required for KYC should be known only to the KYC Service canister, which can handle the KYC and AML checks on behalf of the dapp.

Goals and Objectives

The ICRC-17 Elective KYC Standard aims to achieve the following goals and objectives:

  • Provide a simple and standardized interface for dapps to request KYC checks for users.
  • Provide a simple and standardized interface for dapps to notify the KYC Service canister when users transact a certain amount of tokens.
  • Improve interoperability between different KYC Service canisters and dapps.
  • Simplify the integration of KYC checks into dapps.

Interface Details
The ICRC-17 Elective KYC Standard defines two simple functions, icrc17_kyc_request and icrc17_kyc_notification, that can be used to request KYC checks for users and to notify the KYC Service canister when users transact a certain amount of tokens. The standard also defines a set of types, including KYCCanisterRequest, KYCResult, and KYCNotification

The details of the interface are as follows:

service {
   icrc17_kyc_notification: (KYCNotification) -> (); //one shot - notify service of a transaction
   icrc17_kyc_request: (KYCCanisterRequest) -> (KYCResult); // request kyc - aml info
 };

type TokenSpec = 
 variant {
   Extensible: CandyShared; //for future use cases
   IC: ICTokenSpec;
 };
type KYCResult = 
 record {
   aml: variant {
          Fail;
          NA;
          Pass;
        };
   amount: opt nat;
   kyc: variant {
          Fail;
          NA;
          Pass;
        };
   message: opt text;
   extensible: opt CandyShared;
   token: opt TokenSpec;
   timeout: opt nat;
 };
type KYCCanisterRequest = 
 record {
   amount: opt nat;
   counterparty: KYCAccount;
   token: opt TokenSpec;
 };
type KYCNotification = 
 record {
   amount: opt nat;
   counterparty: KYCAccount;
   token: opt TokenSpec;
   metadata: ?CandyShared;
 };
type KYCAccount = 
 variant {
   Account: vec nat8;
   Extensible: CandyShared;
   ICRC1: record {
            owner: principal;
            subaccount: opt vec nat8;
          };
 };
type ICTokenSpec = 
 record {
   canister: principal;
   decimals: nat;
   fee: opt nat;
   id: opt nat; //used for multi-token canisters
   standard: variant {
               DIP20;
               EXTFungible;
               ICRC1;
               Ledger;
               Other: CandyShared; //for future use
             };
   symbol: text;
 };
type PropertyShared = 
 record {
   immutable: bool;
   name: text;
   value: CandyShared;
 };
type CandyShared = 
 variant {
   Array: vec CandyShared;
   Blob: blob;
   Bool: bool;
   Bytes: vec nat8;
   Class: vec PropertyShared;
   Float: float64;
   Floats: vec float64;
   Int: int;
   Int16: int16;
   Int32: int32;
   Int64: int64;
   Int8: int8;
   Map: vec record {
              CandyShared;
              CandyShared;
            };
   Nat: nat;
   Nat16: nat16;
   Nat32: nat32;
   Nat64: nat64;
   Nat8: nat8;
   Nats: vec nat;
   Option: opt CandyShared;
   Principal: principal;
   Set: vec CandyShared;
   Text: text;
 };

The icrc17_kyc_request function allows the user to submit a request for a KYC check. The request includes a principal, a token specification, and an amount that the user wants to transact. Some of these fields are optional, and it is up to the KYC Service canister to determine if enough information has been provided. The response includes whether the user passed KYC and AML checks, as well as the maximum amount of the provided token that the user is allowed to transact. The KYC Service canister may provide a timeout in its response that the dapp should honor and resubmit a request if the timeout period passes.

The icrc17_kyc_notification function is a one-shot function that a service can call to notify the KYC Service canister when a user transacts a certain number of tokens. This allows the KYC Service canister to adjust the allocations based on time periods.

The types used in the ICRC-17 Elective KYC Standard implement the ICRC-15 CandyShared standard, allowing for future changes and extensibility.

Use Cases

The ICRC-17 Elective KYC Standard enables three forms of elective KYC that dapps can implement according to their specific needs:

Dapp level KYC: In this scenario, any asset administered by the dapp will need to have buyers KYCed. This approach can help to ensure that all transactions conducted within the dapp are compliant with KYC regulations. In the instance of an NFT collection, the collection owner may want to elect that transactions that involve their collection involve KYC. Users of the system must decide if they want to engage if the collection owner elects to do this.

Transaction level KYC: Any seller of an asset can indicate that they only want to transact with KYCed users on a per-transaction basis. This approach can provide an additional layer of security for sellers and reduce the risk of fraudulent or tainted transactions.

Elective level KYC: In this scenario, a user may elect broadly that they want to only participate with KYCed users and declare a specific KYC platform to use to validate buyers. This approach can provide users with more control over their KYC data and streamline the process of conducting KYC checks across multiple dapps. This particular service may warrant its own ICRC standard and discussions as to where this registry should reside.

Ultimately, it will be the dapp’s decision and responsibility to implement these forms of elective KYC. By providing a standardized interface for KYC Service canisters, the ICRC-17 Elective KYC Standard can help to simplify the process of conducting KYC checks and support the growth of dapps on the Internet Computer.

Conclusion

The ICRC- 17 Elective KYC Standard proposes a simple interface for dapps to communicate with KYC Service canisters on the Internet Computer. The standard is designed to make KYC elective for stakeholders involved in a transaction, while also allowing KYC to be conducted in a practical and efficient manner for users and dapp developers alike.

The standard relies on the assumption that KYC is important and a practical reality for many users in certain jurisdictions, but that it should not burden dapp developers with the complexity of conducting KYC checks. Instead, all the dapp knows is a principal, and it subscribes or relies on a KYC Service canister that returns only if the principal passes KYC and AML checks and the amount that they are allowed to transact at.

8 Likes

Does the shape of KYCResult support all jurisdictions globally? If not it might make more sense to adopt a more loosely shaped format. Or, go with the opt variant approach to allow implementors to meet their own needs.

2 Likes

Open to suggestions. The proposal was

does the user pass KYC?
does the user pass AML?
How much can they transact?
Which token?
How long is the request good for?

I added a text message…but you are right there should likely be an extensible field for jurisdictional data.

Added: extensible to KYCResult.

2 Likes

I have also added #exetensible to the KYCCanisterRequest Type so that service providers that want more info(or that are required to collect it by their jurisdiction) can do so. It will be the dapp’s responsibility to implement it, but hopefully we can create some helpful standards that make it easy.

Like an ICRC17.1, ICRC17.2, etc that define the shape of this data field.

This is really neat stuff. I just have one question, as a user.

Who verifies the KYC information, to be sure the information given is valid? It must be passed by a human? (sorry if I’m missing something)

Typically there is a trust at some point. In an nft context, either a collection owner establishes a relationship with a provider or a user them selves establishes a relationship. If the user doesn’t qualify, this cannideally be identified before a transaction and the dapp.canndirect the user to the kyc provider to get registered.

This scheme assumes that all that happens outside the contract. We are onou concerned here with getting back a pass. If a user or collection owner needs a report of who their customers are, then they can go to the kyc provider for a report.

1 Like

Added metadata to the KYCNotification so a service can report identifiable info that they can report to the user if they need a KYC report. It is up to the user and service to make sure the data submitted maintains proper levels of privacy.

Hello,

A quick update. Today we are publishing version 0.1.0 of our kyc.mo component that helps Motoko programs call ICRC-17-compliant KYC servers: GitHub - ORIGYN-SA/kyc.mo.

We have only implemented elective KYC at the collection level for the ORIGYN NFT at the moment. Things are working well and we’ve been collaborating with Yumi marketplace to make sure that the implementation is sound. The recent sale of Gold-backed NFTs used this module to query Yumi’s designated KYC canisters and determine if a user had access to the gold sale.

This is just version 0.1.0 and we’ll have more updates in the future, but we wanted to get something out of the door. If your service might NEED kyc, but you don’t want to IMPLEMENT kyc, then this is a good solution. You can find a service provider willing to publish their kyc records(Principal to amount with no PII) and this component will let you speak and interop with that service without having to handle anything more than a principal form your user.

I think the definition of this standard is still too complex.
The ICRC family of standards definitions seems to be caught in a confusion of complex functionality and trying to cover all scenarios.
Defining a standard is not about making a functional perfect product, it is about abstracting generic rules with commonality, simple and easy to implement, it is more like a form that needs someone to implement it.

I would like to give a few suggestions:
1, Please don’t put optional features into the standard, such as CandyShared. the basis of judgment is kyt need to rely on this feature? what is the core purpose of kyt?
2, kyt does not need to be associated with token assets, because kyt checks whether the address is contaminated, it has nothing to do with assets. kyt is usually used to circumvent the transfer of all assets to a contaminated address.

2 Likes

I am looking to find relevant documentation explaining the semantics and purpose of each field in these type declarations? I want to ensure a clear understanding and proper utilization of the defined structures.
Is this information available? If not would it be beneficial to have?

At this point, this is just a draft, so any declaration of purpose would be subjective until we finalize it. That being said I’m happy to answer questions about the intent of any fields you have questions about.

In general, the goal was to be as unopinionated as possible while defining a structure that could be used for a wide array of potential use cases.

Fairly simply, the idea is to allow a KYC provider to expose an endpoint that other canisters can query to see if an account is authorized to transact according to that KYC service. So going in you only need a Token Identifier, the account transacting and the amount of the transaction(sometimes KYC may be up to a certain amount).

The result can return results from KYC, AML, and some other metadata fields that the consuming service can use to decide if that user can transact or not.

Notifications can be sent from KYC Services to known providers if KYC info changes(ie. The user consumes $5k of their $25k per week allotment).

Lots to be discussed and finalized, and given that I’ve actually written one of these being considered by the working group I likely need to rewrite it given the considerations and best practices that we’ve been establishing. (ie. extensible fields are viewed with distaste, batch by default, using the ISO MUST, SHALL, MAY, SHOULD, etc).

1 Like