Validation

Legal Entity Identifiers

Legal Entity Identifiers (ISO 17442) checked by their check digits, taken apart, made from an 18-character base, found in text, and looked up in GLEIF's public register for the company's name and status.

Version 1.0.0 · 5 functions · 1 crate

#finance#banking#compliance#codes

Functions

What use lei.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use lei_check, lei_valid from lei (Quillo 0.6).

lei_check(lei: str) → any

An LEI read: { valid, lei, lou, reserved, entity, check_digits, error }. lou is the issuing organization's prefix (first four characters), entity the twelve that name the company. Spaces and case are ignored.

lei_valid(lei: str) → bool

Whether lei is a well-formed LEI with the right check digits.

lei_make(base: str) → stror an error

An LEI from its first 18 characters, with the two check digits added (for test data and for LOUs issuing codes).

lei_find(text: str) → list

Every valid LEI in a text (a contract, a trade confirmation, a CSV), each once in the order found.

lei_lookup(lei: str, base_url: str) → anyor an error

The company behind an LEI from GLEIF's public register (no key needed): { lei, legal_name, country, city, jurisdiction, legal_form, entity_status, registration_status, next_renewal, managing_lou }. registration_status "ISSUED" is current; "LAPSED" means it was not renewed. base_url "" is GLEIF's API (GLEIF_API_URL changes it).

Get it

$ quillo install lei               # lei.qwer into this folder$ quillo install lei --example     # and its example, lei.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/lei.qwer$ curl -O https://quillolang.org/plugins/files/lei.qwe

Then, in your program, all of it or only what you call:

main.qwe
use lei.qweruse lei_check, lei_valid from lei      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
ureq = "2"

Example

lei.qwe
// Counterparties on a trade confirmation checked by their LEIs before the report goes to the// regulator; a typo is caught offline, and a lookup shows who the LEI belongs to.////   quillo run lei.qwe      then open http://localhost:8080/check and http://localhost:8080/who/HWUPKR0MPOU8FGXBT394 (network)use "lei.qwer"let CONFIRMATION = "Trade 77812. Buyer LEI: HWUPKR0MPOU8FGXBT394. Seller LEI: 5493001KJTIIGC8Y1R12. Broker: 5493001KJTIIGC8Y1R17 (as typed)."route GET /check = {    found: lei_find(CONFIRMATION),    buyer: lei_check("HWUP KR0M POU8 FGXB T394"),    typo: lei_check("5493001KJTIIGC8Y1R17").error,    made: lei_make("5493001KJTIIGC8Y1R")?,    short: lei_check("5493001KJ").error,}route GET /who/:lei = lei_lookup(lei, "")?
$ quillo run lei.qwe$ curl 'localhost:8080/check'

Source

lei.qwer
//! Legal Entity Identifiers (ISO 17442), the 20-character codes banks and regulators use for companies: checked by their ISO 7064 check digits, taken apart, made from an 18-character base, found in text, and looked up in GLEIF's public register for the company's name, country and whether the LEI is current.//! version: 1.0.0//!//! [dependencies]//! ureq = "2"use serde_json::{json, Value};fn clean(s: &str) -> String {    s.chars().filter(|c| c.is_ascii_alphanumeric()).map(|c| c.to_ascii_uppercase()).collect()}/// The number mod 97, letters as 10 to 35 (ISO 7064 MOD 97-10), worked out piece by piece.fn mod97(s: &str) -> Option<u32> {    let mut r: u64 = 0;    for c in s.chars() {        let v = c.to_digit(36)? as u64;        r = if v < 10 { (r * 10 + v) % 97 } else { (r * 100 + v) % 97 };    }    Some(r as u32)}/// An LEI read: `{ valid, lei, lou, reserved, entity, check_digits, error }`. `lou` is the issuing organization's prefix (first/// four characters), `entity` the twelve that name the company. Spaces and case are ignored.pub fn lei_check(lei: &str) -> Value {    let l = clean(lei);    let fail = |e: &str| json!({ "valid": false, "lei": l, "error": e });    if l.len() != 20 {        return fail(&format!("an LEI has 20 characters, not {}", l.len()));    }    if !l[18..].chars().all(|c| c.is_ascii_digit()) {        return fail("the last two characters are check digits");    }    if mod97(&l) != Some(1) {        return fail("the check digits do not match: a typo");    }    json!({ "valid": true, "lei": l, "lou": &l[..4], "reserved": &l[4..6], "entity": &l[6..18], "check_digits": &l[18..], "error": null })}/// Whether `lei` is a well-formed LEI with the right check digits.pub fn lei_valid(lei: &str) -> bool {    lei_check(lei)["valid"] == json!(true)}/// An LEI from its first 18 characters, with the two check digits added (for test data and for LOUs issuing codes).pub fn lei_make(base: &str) -> Result<String, String> {    let b = clean(base);    if b.len() != 18 {        return Err(format!("the base is 18 characters, not {}", b.len()));    }    let r = mod97(&format!("{}00", b)).ok_or("the base has letters and digits only")?;    Ok(format!("{}{:02}", b, 98 - r))}/// Every valid LEI in a text (a contract, a trade confirmation, a CSV), each once in the order found.pub fn lei_find(text: &str) -> Vec<String> {    let mut out: Vec<String> = vec![];    for w in text.split(|c: char| !c.is_ascii_alphanumeric()) {        if w.len() == 20 && lei_valid(w) {            let u = w.to_ascii_uppercase();            if !out.contains(&u) {                out.push(u);            }        }    }    out}/// The company behind an LEI from GLEIF's public register (no key needed): `{ lei, legal_name, country, city, jurisdiction,/// legal_form, entity_status, registration_status, next_renewal, managing_lou }`. `registration_status` "ISSUED" is current;/// "LAPSED" means it was not renewed. `base_url` "" is GLEIF's API (GLEIF_API_URL changes it).pub fn lei_lookup(lei: &str, base_url: &str) -> Result<Value, String> {    let c = lei_check(lei);    if c["valid"] != json!(true) {        return Err(c["error"].as_str().unwrap_or("not an LEI").to_string());    }    let l = c["lei"].as_str().unwrap_or_default();    let base = if base_url.is_empty() { std::env::var("GLEIF_API_URL").unwrap_or_else(|_| "https://api.gleif.org/api/v1".into()) } else { base_url.to_string() };    let url = format!("{}/lei-records/{}", base.trim_end_matches('/'), l);    let res = ureq::get(&url).timeout(std::time::Duration::from_secs(15)).set("Accept", "application/vnd.api+json").call();    let text = match res {        Ok(r) => r.into_string().unwrap_or_default(),        Err(ureq::Error::Status(404, _)) => return Err(format!("{} is not in GLEIF's register", l)),        Err(e) => return Err(format!("GLEIF: {}", e)),    };    let v: Value = serde_json::from_str(&text).map_err(|_| "GLEIF did not answer JSON".to_string())?;    let a = &v["data"]["attributes"];    let e = &a["entity"];    Ok(json!({        "lei": l,        "legal_name": e["legalName"]["name"],        "country": e["legalAddress"]["country"],        "city": e["legalAddress"]["city"],        "jurisdiction": e["jurisdiction"],        "legal_form": e["legalForm"]["id"],        "entity_status": e["status"],        "registration_status": a["registration"]["status"],        "next_renewal": a["registration"]["nextRenewalDate"],        "managing_lou": a["registration"]["managingLou"],    }))}
IBAN and BIC iban.qwer · 1.0.0IBANs checked (country, length, check digits) and printed as banks print them, and BICs checked.formsbankingpaymentsfinance9 views · 8 downloadsSecurity identifiers securities_ids.qwer · 1.0.0ISIN, CUSIP and CINS, SEDOL and FIGI checked by their check digits and told apart, ISINs made from a CUSIP, SEDOL or other national number, and ISINs found in text.financevalidationcodesimport7 views · 6 downloadsUS routing numbers aba_routing.qwer · 1.0.0US bank routing numbers checked by their check digit and read for the Federal Reserve district and kind of institution, a check's MICR line read into routing, account and check number, and Canadian transit numbers.bankingpaymentsvalidationfinance7 views · 6 downloadsOFX statements ofx.qwer · 1.0.0Bank and credit card statements in OFX and QFX, old SGML or XML: accounts, balances and each transaction with its bank id, ready to import without doubles.bankingimportaccountingfinance6 views · 8 downloadsISBN isbn.qwer · 1.0.0ISBNs checked, converted between 10 and 13 digits, hyphenated by the ISBN Agency's ranges into group, publisher and title parts with the group's language or country named, and every ISBN in a text found.booksvalidationcodesebooks6 views · 6 downloadsNational ID numbers national_ids.qwer · 1.0.0National identity numbers of 29 countries checked by their own rules (US SSN, UK NINO, CPF, Aadhaar, PH TIN, DNI and NIE, codice fiscale, PESEL...), with the birth date and sex read out where the number holds them.validationidentitycomplianceforms7 views · 7 downloads
Thread
89Useful? Like it, or add to its thread below.

Thread 0 posts

On Legal Entity Identifiers: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.