nx_trust_lib.nx source
↩ module page · 115 lines · 4091 B
1// nx_trust_lib.nx -- Nishi Office: client TRUST ACCOUNTING (F506, IOLTA / Utah RPC 1.15).
2// Append-only immutable trust ledger on the CID plane (history sacred = what bar audits want).
3// RPC 1.15 by construction: NO-COMMINGLING (per-client balances isolated), NO-OVERDRAW (a
4// disbursement exceeding a client's trust balance is REFUSED), THREE-WAY RECONCILIATION
5// (sum of client ledgers == trust account total; leg 3 = the bank statement a human matches).
6// Money = integer CENTS, no float (exact). DRY: reuses nx_matter_lib helpers. LIB (no main).
7// license_tier: ORIGINAL
8
9import "nx_matter_lib.nx"
10
11// parse a decimal string to i64 (cents)
12func tr_atoi(s: *u8) -> i64 {
13 var v: i64 = 0
14 var i: i64 = 0
15 while s[i] != (0 as u8) {
16 if s[i] >= 48 as u8 { if s[i] <= 57 as u8 { v = v * 10 + (s[i] - 48) } }
17 i = i + 1
18 }
19 return v
20}
21
22// format cents as "$X.XX"
23func tr_dollars(cents: i64, out: *u8) -> i64 {
24 var o: i64 = 0
25 out[o] = 36 as u8
26 o = o + 1
27 o = mt_catn(out, o, cents / 100)
28 out[o] = 46 as u8
29 o = o + 1
30 let c: i64 = cents % 100
31 out[o] = (48 + (c / 10)) as u8
32 o = o + 1
33 out[o] = (48 + (c % 10)) as u8
34 o = o + 1
35 out[o] = 0 as u8
36 return o
37}
38
39// append a trust ledger entry (deposit|disburse) for a client; key "<client>:<seq>".
40func trust_put(prefix: *u8, client: *u8, seq: i64, matter: *u8, ttype: *u8, cents: i64) -> i64 {
41 let id: *u8 = sys_mmap(128)
42 var o: i64 = mt_catcopy(id, 0, client)
43 id[o] = 58 as u8
44 o = o + 1
45 o = mt_catn(id, o, seq)
46 id[o] = 0 as u8
47 let amt: *u8 = sys_mmap(32)
48 var ao: i64 = mt_catn(amt, 0, cents)
49 amt[ao] = 0 as u8
50 let k: *i64 = sys_mmap(8 * 4) as *i64
51 let v: *i64 = sys_mmap(8 * 4) as *i64
52 k[0] = ("amount" as *u8) as i64
53 v[0] = amt as i64
54 k[1] = ("client" as *u8) as i64
55 v[1] = client as i64
56 k[2] = ("matter" as *u8) as i64
57 v[2] = matter as i64
58 k[3] = ("type" as *u8) as i64
59 v[3] = ttype as i64
60 let rec: *u8 = sys_mmap(1024)
61 let rl: i64 = canon_encode(k, v, 4, rec)
62 return reg_put(prefix, "trust:" as *u8, "trust:__idx__" as *u8, id, rec, rl)
63}
64
65// a client's trust balance in cents (sum deposits - disbursements) -- per-client, no commingling.
66func trust_balance(prefix: *u8, client: *u8) -> i64 {
67 let idx: *u8 = sys_mmap(1048576)
68 let ilen: i64 = reg_index(prefix, "trust:__idx__" as *u8, idx, 1048576)
69 let cp: *u8 = sys_mmap(128)
70 var cpl: i64 = mt_catcopy(cp, 0, client)
71 cp[cpl] = 58 as u8
72 cpl = cpl + 1
73 cp[cpl] = 0 as u8
74 let idb: *u8 = sys_mmap(256)
75 let po: *i64 = sys_mmap(16) as *i64
76 let lo: *i64 = sys_mmap(16) as *i64
77 let af: *u8 = sys_mmap(64)
78 let tf: *u8 = sys_mmap(32)
79 var bal: i64 = 0
80 var ls: i64 = 0
81 var i: i64 = 0
82 while i <= ilen {
83 var eol: i64 = 0
84 if i == ilen { eol = 1 }
85 if i < ilen { if idx[i] == 10 as u8 { eol = 1 } }
86 if eol == 1 {
87 if i > ls {
88 var c: i64 = 0
89 while ls + c < i { idb[c] = idx[ls + c]; c = c + 1 }
90 idb[c] = 0 as u8
91 var mtch: i64 = 1
92 var x: i64 = 0
93 while cp[x] != (0 as u8) { if idb[x] != cp[x] { mtch = 0 } x = x + 1 }
94 if mtch == 1 {
95 if reg_get(prefix, "trust:" as *u8, idb, po, lo) == 1 {
96 mt_field(po[0] as *u8, lo[0], "amount" as *u8, 6, af)
97 mt_field(po[0] as *u8, lo[0], "type" as *u8, 4, tf)
98 let amt: i64 = tr_atoi(af)
99 if mt_streq(tf, "deposit" as *u8) == 1 { bal = bal + amt }
100 if mt_streq(tf, "disburse" as *u8) == 1 { bal = bal - amt }
101 }
102 }
103 }
104 ls = i + 1
105 }
106 i = i + 1
107 }
108 return bal
109}
110
111// RPC 1.15 no-overdraw gate: 1 if client can disburse `cents`, else 0.
112func trust_disburse_ok(prefix: *u8, client: *u8, cents: i64) -> i64 {
113 if trust_balance(prefix, client) >= cents { return 1 }
114 return 0
115}