What the draft says
On 12 November 2026 Thailand's SEC published a draft notification on stablecoin transfers through licensed platforms. Comments close on 26 November. The Block summarised the two rules that matter most for anyone writing the code:
- An individual customer can deposit or withdraw up to 5,000,000 THB in stablecoins per operator, per day, in each direction (about $147,750).
- A deposit or withdrawal may only involve an account or wallet that belongs to the same person.
Some flows are exempt from the cap, for example transfers between a customer's own accounts on SEC-supervised platforms and stablecoin market-maker activity. Pure peer-to-peer on-chain transfers outside registered platforms are not covered.
This is a draft and it can change. The script below models it; it is not legal advice.
The check
Each row is one transfer: time, customer, operator, direction (in or out), amount in USD, the owner of the external wallet if known, and an optional exempt flag. The script keeps a running total per customer, operator, UTC day and direction, converts it to baht, and reports two kinds of findings.
// check-transfers.mjs import { readFileSync } from 'node:fs'; const CAP_THB = 5_000_000; export function check(rows, thbPerUsd) { const findings = []; const used = new Map(); // customer|operator|day|direction -> THB used so far for (const [i, r] of rows.entries()) { const where = `row ${i + 1} (${r.customer}, ${r.direction} ${r.amountUsd} USD at ${r.operator})`; if (!r.walletOwner) { findings.push(`${where}: wallet owner unknown, the same-owner rule can't be checked`); } else if (r.walletOwner !== r.customer) { findings.push(`${where}: wallet belongs to ${r.walletOwner}, not the customer`); } if (r.exempt) continue; const day = r.time.slice(0, 10); const key = [r.customer, r.operator, day, r.direction].join('|'); const total = (used.get(key) || 0) + r.amountUsd * thbPerUsd; used.set(key, total); if (total > CAP_THB) { findings.push(`${where}: ${r.direction === 'in' ? 'deposit' : 'withdrawal'} total ${fmt(total)} THB on ${day} passes the ${fmt(CAP_THB)} THB cap`); } } return findings; } const fmt = (n) => Math.round(n).toLocaleString('en-US'); const [file, rate] = process.argv.slice(2); if (file) { const thbPerUsd = Number(rate || 33.84); // the rate implied by 5,000,000 THB ≈ $147,750 const findings = check(JSON.parse(readFileSync(file, 'utf8')), thbPerUsd); if (!findings.length) console.log('No findings.'); for (const f of findings) console.log('-', f); process.exit(findings.length ? 2 : 0); } Node 18 or newer, no dependencies. Pass your own THB per USD rate as the second argument; the default is the one implied by The Block's dollar figure.
A sample day
[ { "time": "2026-11-20T09:15:00Z", "customer": "anan", "operator": "exchange-a", "direction": "in", "amountUsd": 90000, "walletOwner": "anan" }, { "time": "2026-11-20T13:40:00Z", "customer": "anan", "operator": "exchange-a", "direction": "in", "amountUsd": 70000, "walletOwner": "anan" }, { "time": "2026-11-20T14:05:00Z", "customer": "anan", "operator": "exchange-b", "direction": "in", "amountUsd": 70000, "walletOwner": "anan" }, { "time": "2026-11-20T16:30:00Z", "customer": "anan", "operator": "exchange-a", "direction": "out", "amountUsd": 20000, "walletOwner": "somchai" }, { "time": "2026-11-20T18:00:00Z", "customer": "anan", "operator": "exchange-a", "direction": "out", "amountUsd": 200000, "walletOwner": "anan", "exempt": true }, { "time": "2026-11-21T08:00:00Z", "customer": "anan", "operator": "exchange-a", "direction": "in", "amountUsd": 90000 } ] $ node check-transfers.mjs transfers.json - row 2 (anan, in 70000 USD at exchange-a): deposit total 5,414,400 THB on 2026-11-20 passes the 5,000,000 THB cap - row 4 (anan, out 20000 USD at exchange-a): wallet belongs to somchai, not the customer - row 6 (anan, in 90000 USD at exchange-a): wallet owner unknown, the same-owner rule can't be checked Row 3 passes because the cap counts per operator, so the same customer depositing at a second platform starts from zero. Row 5 is large but flagged exempt, so it skips the cap and still gets the ownership check. The script exits with 2 when there are findings, which makes it easy to drop into CI or a nightly job.
What the code can't do
The hard part of the real rule is the walletOwner field. The draft asks platforms to verify ownership, apply the Travel Rule and use blockchain analytics to find links to high-risk wallets. A script can only check the data you give it. Treat an unknown owner as a finding, as the script does, rather than a pass.
Two more details to watch when the final text comes out: which timezone "per day" uses (this script uses UTC dates; Thailand is UTC+7), and the exact list of exemptions.
Sources: The Block, "Thailand seeks $148,000 daily cap on certain stablecoin transfers to curb money laundering" (13 Nov 2026); crypto.news on the September consultation (13 Sep 2026).
