@seasonkoh/webaz 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/layer0-foundation/L0-2-state-machine/engine.js +81 -93
- package/dist/layer1-agent/L1-1-mcp-server/server.js +682 -236
- package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +138 -168
- package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +6 -4
- package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +12 -8
- package/dist/ledger.js +58 -0
- package/dist/money.js +106 -0
- package/dist/pwa/acp-feed.js +103 -0
- package/dist/pwa/economic-participation.js +1 -1
- package/dist/pwa/integration-contract.js +4 -0
- package/dist/pwa/public/app.js +8 -7
- package/dist/pwa/public/index.html +27 -1
- package/dist/pwa/routes/auction.js +9 -6
- package/dist/pwa/routes/disputes-write.js +12 -11
- package/dist/pwa/routes/orders-create.js +24 -13
- package/dist/pwa/routes/public-utils.js +28 -0
- package/dist/pwa/routes/referral.js +81 -0
- package/dist/pwa/server.js +57 -39
- package/dist/settlement-math.js +27 -0
- package/package.json +1 -1
|
@@ -55,4 +55,85 @@ export function registerReferralRoutes(app, deps) {
|
|
|
55
55
|
logAdminAction(admin.id, 'invite_rotation_toggle', 'system', 'invite_rotation_enabled', { value: v });
|
|
56
56
|
res.json({ success: true, enabled: !!enabled });
|
|
57
57
|
});
|
|
58
|
+
// RFC-003 #1122: 生成商品分享链接(把 MCP webaz_share_link 的本地计算搬到服务端,
|
|
59
|
+
// 让 MCP NETWORK 模式可代理)。RFC-002 §3.5 valuation-layer gate:需 rewards opt-in。
|
|
60
|
+
// 与 PWA pickPreferredSide 对齐的 side 选择(team_count | pv_count)。
|
|
61
|
+
app.get('/api/share-link', (req, res) => {
|
|
62
|
+
const user = auth(req, res);
|
|
63
|
+
if (!user)
|
|
64
|
+
return;
|
|
65
|
+
const userId = user.id;
|
|
66
|
+
const productId = String(req.query.product_id || '');
|
|
67
|
+
const sideArg = String(req.query.side || 'auto');
|
|
68
|
+
if (!productId)
|
|
69
|
+
return void res.status(400).json({ error: 'product_id required', error_code: 'PRODUCT_ID_REQUIRED' });
|
|
70
|
+
const optIn = db.prepare("SELECT rewards_opted_in FROM users WHERE id = ?").get(userId)?.rewards_opted_in ?? 0;
|
|
71
|
+
if (optIn !== 1) {
|
|
72
|
+
const getParam = (key, def) => {
|
|
73
|
+
const r = db.prepare("SELECT value FROM protocol_params WHERE key = ?").get(key);
|
|
74
|
+
return r ? Number(r.value) : def;
|
|
75
|
+
};
|
|
76
|
+
const minOrders = getParam('rewards_opt_in.min_completed_orders', 1);
|
|
77
|
+
const requirePasskey = getParam('rewards_opt_in.require_passkey', 1);
|
|
78
|
+
const totalCompleted = db.prepare("SELECT COUNT(*) as n FROM orders WHERE buyer_id = ? AND status = 'completed'").get(userId).n;
|
|
79
|
+
const passkeyCount = db.prepare("SELECT COUNT(*) as n FROM webauthn_credentials WHERE user_id = ?").get(userId).n;
|
|
80
|
+
const missing = [];
|
|
81
|
+
if (totalCompleted < minOrders)
|
|
82
|
+
missing.push(`completed_orders ${totalCompleted}/${minOrders}`);
|
|
83
|
+
if (requirePasskey === 1 && passkeyCount === 0)
|
|
84
|
+
missing.push('passkey_not_registered');
|
|
85
|
+
if (missing.length === 0)
|
|
86
|
+
missing.push('application_not_submitted');
|
|
87
|
+
return void res.status(403).json({
|
|
88
|
+
error: 'rewards_opt_in_required',
|
|
89
|
+
message: 'Share-link generation is a valuation-layer action — requires builder-identity opt-in (RFC-002 §3.5)',
|
|
90
|
+
missing_requirements: missing,
|
|
91
|
+
next_steps: [
|
|
92
|
+
'Open PWA #me → tap "申请共建身份 / Apply for builder identity"',
|
|
93
|
+
'Read the 8-second disclosure (cannot skip)',
|
|
94
|
+
'Submit application — pre-checks run server-side',
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const product = db.prepare("SELECT id, title, price, commission_rate FROM products WHERE id = ? AND status='active'").get(productId);
|
|
99
|
+
if (!product)
|
|
100
|
+
return void res.status(404).json({ error: '商品不存在或已下架', error_code: 'PRODUCT_NOT_FOUND' });
|
|
101
|
+
let side = 'right';
|
|
102
|
+
if (sideArg === 'left' || sideArg === 'right') {
|
|
103
|
+
side = sideArg;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const u = db.prepare("SELECT placement_pref, total_left_pv, total_right_pv, left_count, right_count FROM users WHERE id = ?")
|
|
107
|
+
.get(userId);
|
|
108
|
+
const pref = u?.placement_pref || 'team_count';
|
|
109
|
+
if (pref === 'pv_count') {
|
|
110
|
+
const since = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' ');
|
|
111
|
+
const w = db.prepare(`SELECT COALESCE(SUM(consumed_left_pv),0) AS l, COALESCE(SUM(consumed_right_pv),0) AS r
|
|
112
|
+
FROM binary_score_records WHERE user_id = ? AND created_at >= ?`)
|
|
113
|
+
.get(userId, since);
|
|
114
|
+
const leftPv = Number(u?.total_left_pv ?? 0) + Number(w.l);
|
|
115
|
+
const rightPv = Number(u?.total_right_pv ?? 0) + Number(w.r);
|
|
116
|
+
side = leftPv <= rightPv ? 'left' : 'right';
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
side = (Number(u?.left_count ?? 0) <= Number(u?.right_count ?? 0)) ? 'left' : 'right';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const completed = db.prepare("SELECT COUNT(*) as n FROM orders WHERE buyer_id = ? AND status = 'completed'").get(userId).n;
|
|
123
|
+
const override = db.prepare("SELECT l1_share_override FROM users WHERE id = ?").get(userId)?.l1_share_override ?? 0;
|
|
124
|
+
const canL1 = override === 1 || (override === 0 && completed > 0);
|
|
125
|
+
const rate = Number(product.commission_rate ?? 0);
|
|
126
|
+
const link = `/?ref=${userId}&side=${side}#order-product/${productId}`;
|
|
127
|
+
res.json({
|
|
128
|
+
product: { id: product.id, title: product.title, price: product.price, commission_rate: rate },
|
|
129
|
+
share_link: link,
|
|
130
|
+
full_url_hint: 'Prepend webaz.xyz (production) to get the absolute URL',
|
|
131
|
+
side,
|
|
132
|
+
binary_explanation: `New user via this link → placed in your ${side === 'left' ? '🔵 left' : '🟢 right'} subtree (tail anchor)`,
|
|
133
|
+
commission_eligibility: canL1
|
|
134
|
+
? `You will earn 3-tier commission: L1=${(rate * 0.70 * 100).toFixed(1)}% L2=${(rate * 0.20 * 100).toFixed(1)}% L3=${(rate * 0.10 * 100).toFixed(1)}% of sale price`
|
|
135
|
+
: 'You are NOT verified yet (need 1 completed purchase). 3-tier commission will be skipped, but points-matching still builds.',
|
|
136
|
+
next_steps: 'Share on TikTok / WeChat / Telegram. New user clicks → 30-day attribution window starts.',
|
|
137
|
+
});
|
|
138
|
+
});
|
|
58
139
|
}
|
package/dist/pwa/server.js
CHANGED
|
@@ -24,6 +24,10 @@ import { initSystemUser, transition, getOrderStatus, checkTimeouts, settleFault
|
|
|
24
24
|
import { endpointToAction, endpointToReadAction } from './endpoint-actions.js';
|
|
25
25
|
import { AGENT_RATE_PER_MIN_DEFAULTS, CROSS_USER_READ_DAILY_CAP, MASS_ACTION_TYPES, MASS_ACTION_DAILY_CAPS } from './limits.js';
|
|
26
26
|
import { initOrderChainSchema, appendOrderEvent, getOrderChain, verifyOrderChain } from '../layer0-foundation/L0-2-state-machine/order-chain.js';
|
|
27
|
+
// RFC-014 PR4 — 正常成交结算走整数 base-units + allocate + 绝对值落库。
|
|
28
|
+
import { toUnits, toDecimal, mulRate, allocate } from '../money.js';
|
|
29
|
+
import { applyWalletDelta, creditColumns } from '../ledger.js';
|
|
30
|
+
import { computeSettlementSplit } from '../settlement-math.js';
|
|
27
31
|
import { initSnfSchema, snfSend, snfCleanup } from '../layer2-business/L2-7-snf/snf-engine.js';
|
|
28
32
|
import { initExternalAnchorSchema } from '../layer1-agent/L1-2-external-anchor/anchor-engine.js';
|
|
29
33
|
import { ensureEvidenceColumns, uploadEvidence, listEvidence as listEvidenceFiles, markEvidenceExpiry, cleanupExpiredEvidence, EVIDENCE_MAX_BYTES, EVIDENCE_ALLOWED_MIME, } from '../layer3-trust/L3-1-dispute-engine/evidence-storage.js';
|
|
@@ -8667,7 +8671,9 @@ function settleCommission(orderId, effectiveBase) {
|
|
|
8667
8671
|
region = buyer?.region ?? 'global';
|
|
8668
8672
|
}
|
|
8669
8673
|
const maxLevels = getRegionMaxLevels(region);
|
|
8670
|
-
|
|
8674
|
+
// RFC-014:佣金池 + 三级拆分走整数 base-units;allocate 保证 L1+L2+L3 ≡ pool(精确,修旧版逐项 round2 不守恒暗缝)。
|
|
8675
|
+
const poolU = mulRate(toUnits(total), rate);
|
|
8676
|
+
const pool = toDecimal(poolU);
|
|
8671
8677
|
// max_levels=0 → 完全禁 MLM,整个 commission pool 入 commission_reserve(三级公池,只进不出)
|
|
8672
8678
|
// 2026-06-04 修双计 bug:旧版既 redirectToCharity 又 return redirected:pool 让 depositToFund 再入 global_fund → 印钱。
|
|
8673
8679
|
// 现统一入 commission_reserve 一次,return redirected:0(depositToFund 只拿 1% base)。
|
|
@@ -8706,9 +8712,12 @@ function settleCommission(orderId, effectiveBase) {
|
|
|
8706
8712
|
// 2026-06-04:所有兜底统一入 commission_reserve(三级公池,独立科目,只进不出)。
|
|
8707
8713
|
// commission 不再回流 global_fund(PV 资金)—— 三套科目解耦,redirected 始终 0。
|
|
8708
8714
|
let toCommissionReserve = 0; // → commission_reserve(仅作日志/返回信息用)
|
|
8715
|
+
// 三级金额一次性 allocate(精确求和 ≡ poolU);各级再按 gate 路由到钱包/公池/escrow。
|
|
8716
|
+
const levelAmtU = allocate(poolU, [LEVEL_RATES[1], LEVEL_RATES[2], LEVEL_RATES[3]]);
|
|
8709
8717
|
for (const { level, beneficiary } of recipients) {
|
|
8710
|
-
const
|
|
8711
|
-
|
|
8718
|
+
const amountU = levelAmtU[level - 1];
|
|
8719
|
+
const amount = toDecimal(amountU);
|
|
8720
|
+
if (amountU <= 0)
|
|
8712
8721
|
continue;
|
|
8713
8722
|
// ① region 截断 (level>maxLevels) → 三级公池
|
|
8714
8723
|
if (level > maxLevels) {
|
|
@@ -8756,8 +8765,7 @@ function settleCommission(orderId, effectiveBase) {
|
|
|
8756
8765
|
db.prepare(`INSERT INTO commission_records (id, order_id, beneficiary_id, source_buyer_id, level, amount, rate, region, source, source_type)
|
|
8757
8766
|
VALUES (?,?,?,?,?,?,?,?,?,?)`)
|
|
8758
8767
|
.run(generateId('comm'), orderId, beneficiary, order.buyer_id, level, amount, rate, region, routeSource, srcType);
|
|
8759
|
-
db
|
|
8760
|
-
.run(amount, amount, beneficiary);
|
|
8768
|
+
applyWalletDelta(db, beneficiary, { balance: amountU, earned: amountU });
|
|
8761
8769
|
}
|
|
8762
8770
|
catch (e) { /* UNIQUE 冲突 */ }
|
|
8763
8771
|
}
|
|
@@ -8829,22 +8837,30 @@ function depositToFund(orderId, extraFromCommission = 0, effectiveBase) {
|
|
|
8829
8837
|
const total = effectiveBase != null ? Number(effectiveBase) : Number(order.total_amount);
|
|
8830
8838
|
if (total <= 0)
|
|
8831
8839
|
return { base: 0, redirect: 0, total: 0 };
|
|
8832
|
-
|
|
8833
|
-
const
|
|
8840
|
+
// RFC-014:整数 base-units + 绝对值落库
|
|
8841
|
+
const amountBaseU = mulRate(toUnits(total), FUND_BASE_RATE());
|
|
8842
|
+
const amountRedirectU = toUnits(Number(extraFromCommission || 0));
|
|
8834
8843
|
const region = order.buyer_region || 'global';
|
|
8835
|
-
const
|
|
8836
|
-
|
|
8844
|
+
const totalDepositU = amountBaseU + amountRedirectU;
|
|
8845
|
+
const amountBase = toDecimal(amountBaseU);
|
|
8846
|
+
const amountRedirect = toDecimal(amountRedirectU);
|
|
8847
|
+
const totalDeposit = toDecimal(totalDepositU);
|
|
8848
|
+
if (totalDepositU > 0) {
|
|
8837
8849
|
// fund_deposits.amount_l3 字段语义已扩为「commission 端回流总额」(区域裁决 + 未 verified)
|
|
8838
8850
|
db.prepare(`INSERT INTO fund_deposits (id, order_id, amount_base, amount_l3, buyer_region)
|
|
8839
8851
|
VALUES (?,?,?,?,?)`).run(generateId('fd'), orderId, amountBase, amountRedirect, region);
|
|
8840
|
-
db
|
|
8852
|
+
creditColumns(db, 'global_fund', 'id = 1', [], { pool_balance: totalDepositU });
|
|
8841
8853
|
}
|
|
8842
8854
|
return { base: amountBase, redirect: amountRedirect, total: totalDeposit };
|
|
8843
8855
|
}
|
|
8844
8856
|
function redirectToCommissionReserve(amount, kind, args = {}) {
|
|
8845
8857
|
if (!Number.isFinite(amount) || amount <= 0)
|
|
8846
8858
|
return;
|
|
8847
|
-
|
|
8859
|
+
// RFC-014:整数 base-units + 绝对值落库(防 REAL `col = col + ?` 浮点 dust)。
|
|
8860
|
+
const aU = toUnits(amount);
|
|
8861
|
+
if (aU <= 0)
|
|
8862
|
+
return;
|
|
8863
|
+
const a = toDecimal(aU);
|
|
8848
8864
|
// Aggregate column mapping (commission_reserve_txns.kind records exact kind):
|
|
8849
8865
|
// chain_gap → total_chain_gap
|
|
8850
8866
|
// orphan_sponsor / opt_out_deactivated / escrow_expired → total_orphan_sponsor (no-eligible-recipient bucket)
|
|
@@ -8853,7 +8869,8 @@ function redirectToCommissionReserve(amount, kind, args = {}) {
|
|
|
8853
8869
|
: kind === 'redirect_region_cap' ? 'total_region_cap'
|
|
8854
8870
|
: 'total_orphan_sponsor';
|
|
8855
8871
|
db.transaction(() => {
|
|
8856
|
-
db
|
|
8872
|
+
creditColumns(db, 'commission_reserve', "id = 'main'", [], { balance: aU, [totalCol]: aU });
|
|
8873
|
+
db.prepare(`UPDATE commission_reserve SET updated_at = datetime('now') WHERE id = 'main'`).run();
|
|
8857
8874
|
db.prepare(`INSERT INTO commission_reserve_txns (id, kind, from_user_id, amount, related_order_id, note)
|
|
8858
8875
|
VALUES (?,?,?,?,?,?)`).run(generateId('crt'), kind, args.fromUserId || null, a, args.orderId || null, args.note || null);
|
|
8859
8876
|
})();
|
|
@@ -8872,19 +8889,11 @@ function settleOrder(orderId) {
|
|
|
8872
8889
|
: db.prepare('SELECT stake_amount, stake_locked_at, category_id FROM products WHERE id = ?').get(order.product_id);
|
|
8873
8890
|
// M8:协议费率二手 1% (vs 商家 2%) — 鼓励个人发布
|
|
8874
8891
|
const feeRate = isSecondhand ? 0.01 : 0.02;
|
|
8875
|
-
const protocolFee = Math.round(total * feeRate * 100) / 100;
|
|
8876
|
-
const protocolToBonus = Math.round(protocolFee * 0.5 * 100) / 100;
|
|
8877
|
-
const protocolToOps = Math.round((protocolFee - protocolToBonus) * 100) / 100;
|
|
8878
|
-
// 面交无三方物流 → logisticsFee = 0
|
|
8879
|
-
const logisticsFee = isInPerson ? 0 : Math.round(total * 0.05 * 100) / 100;
|
|
8880
|
-
// P0 2026-05-28:self-fulfill(logistics_id NULL)订单 → 不收 5% 物流费,全额给 seller
|
|
8881
|
-
// 之前 sellerAmount 用 logisticsFee 扣,但 line 7780 不付任何人 → 每笔黑洞 5% × total
|
|
8882
|
-
const logisticsActual = order.logistics_id ? logisticsFee : 0;
|
|
8883
8892
|
// QA 轮 9.4 P0:settleOrder default 0 vs settleCommission default 0.10 不一致 → 印钱漏洞
|
|
8884
8893
|
// 修:统一两处 default = 0.10(跟 PWA orders-create.ts:277 一致)
|
|
8885
8894
|
const commissionRate = Number(order.snapshot_commission_rate ?? 0.10);
|
|
8886
|
-
|
|
8887
|
-
|
|
8895
|
+
// RFC-014:实际拆分在下方 computeSettlementSplit(整数 base-units,卖家净额 residual 吸收 → 精确守恒)。
|
|
8896
|
+
// self-fulfill / 面交不收物流费(chargeLogistics=false);协议费 50/50 由 allocate 精确拆。
|
|
8888
8897
|
// M7.2.6 方案 3 + M-4 fix:首单锁定 stake(trusted+ 跳过)
|
|
8889
8898
|
// 用单语句 UPDATE ... WHERE stake_locked_at IS NULL 原子拿"是否首次"判定,
|
|
8890
8899
|
// 避免读后写竞态(旧实现两条语句,迁 PG / 多 worker 时可能双锁)
|
|
@@ -8902,21 +8911,30 @@ function settleOrder(orderId) {
|
|
|
8902
8911
|
stakeToLock = product.stake_amount;
|
|
8903
8912
|
}
|
|
8904
8913
|
}
|
|
8905
|
-
//
|
|
8906
|
-
const
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8914
|
+
// RFC-014:整数 base-units 精确拆分(卖家净额 = total − 其余各项,residual 吸收 → Σ ≡ total)。
|
|
8915
|
+
const totalU = toUnits(total);
|
|
8916
|
+
const split = computeSettlementSplit({
|
|
8917
|
+
totalU,
|
|
8918
|
+
feeRate,
|
|
8919
|
+
logisticsRate: 0.05,
|
|
8920
|
+
chargeLogistics: !isInPerson && !!order.logistics_id, // self-fulfill / 面交 = 不收物流费
|
|
8921
|
+
commissionRate,
|
|
8922
|
+
fundRate: FUND_BASE_RATE(),
|
|
8923
|
+
stakeToLockU: toUnits(stakeToLock),
|
|
8924
|
+
});
|
|
8925
|
+
// 买家 escrow 释放 + 卖家净额 + 物流费(实扣即实付)+ 首单 stake 锁定 —— 全绝对值落库
|
|
8926
|
+
applyWalletDelta(db, order.buyer_id, { escrowed: -totalU });
|
|
8927
|
+
applyWalletDelta(db, order.seller_id, { balance: split.sellerAmountU, earned: split.sellerAmountU });
|
|
8928
|
+
if (order.logistics_id && split.logisticsActualU > 0) {
|
|
8929
|
+
applyWalletDelta(db, order.logistics_id, { balance: split.logisticsActualU, earned: split.logisticsActualU });
|
|
8930
|
+
}
|
|
8931
|
+
if (split.stakeToLockU > 0)
|
|
8932
|
+
applyWalletDelta(db, order.seller_id, { staked: split.stakeToLockU });
|
|
8915
8933
|
// 协议费拆分:50% 注入管理津贴池,50% 入 sys_protocol 运营
|
|
8916
|
-
if (
|
|
8917
|
-
db
|
|
8918
|
-
if (
|
|
8919
|
-
db
|
|
8934
|
+
if (split.protocolToBonusU > 0)
|
|
8935
|
+
creditColumns(db, 'management_bonus_pool', 'id = 1', [], { balance: split.protocolToBonusU });
|
|
8936
|
+
if (split.protocolToOpsU > 0)
|
|
8937
|
+
applyWalletDelta(db, 'sys_protocol', { balance: split.protocolToOpsU });
|
|
8920
8938
|
// 推土机分享分润:正常分账 → 钱包;兜底 → commission_reserve(三级公池,独立科目)
|
|
8921
8939
|
settleCommission(orderId);
|
|
8922
8940
|
// 原子能:PV 资金池入金 = 仅 1% base(2026-06-04 起 commission 不再回流此池,三科目解耦)
|
|
@@ -8987,9 +9005,9 @@ function settleOrder(orderId) {
|
|
|
8987
9005
|
}
|
|
8988
9006
|
// P0.1:RFQ 订单完单成功 → 释放卖家 bid_stake_held(staked → balance)+ synthetic product 转 warehouse(P1.2 顺手修)
|
|
8989
9007
|
try {
|
|
8990
|
-
const
|
|
8991
|
-
if (
|
|
8992
|
-
db.
|
|
9008
|
+
const heldStakeU = toUnits(Number(order.bid_stake_held || 0));
|
|
9009
|
+
if (heldStakeU > 0 && order.source === 'rfq') {
|
|
9010
|
+
applyWalletDelta(db, order.seller_id, { balance: heldStakeU, staked: -heldStakeU });
|
|
8993
9011
|
}
|
|
8994
9012
|
// synthetic RFQ product(描述以 [RFQ 开头)卖完后转 warehouse,防 active feed 污染
|
|
8995
9013
|
const pInfo = db.prepare('SELECT description, stock FROM products WHERE id = ?').get(order.product_id);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settlement split — settleOrder 正常成交的资金拆分【纯函数】(RFC-014,可测)。
|
|
3
|
+
*
|
|
4
|
+
* 把买家托管的 total 拆成:协议费(再 50/50 拆津贴/运营)+ 物流费 + 佣金池 + 基金 1% + 首单锁定 stake
|
|
5
|
+
* + 卖家净额。卖家净额 = total − 其余各项(residual 吸收)→ 整数单位下【精确守恒】(Σ ≡ total)。
|
|
6
|
+
*
|
|
7
|
+
* server.ts settleOrder 直接调用本函数拿数字,再落库(applyWalletDelta/creditColumns)。
|
|
8
|
+
* 守恒由 tests/test-settlement-math.ts 守(此前 settleOrder 在 server.ts 巨石里、绑 db 闭包,无法隔离单测)。
|
|
9
|
+
*/
|
|
10
|
+
import { mulRate, allocate } from './money.js';
|
|
11
|
+
export function computeSettlementSplit(i) {
|
|
12
|
+
const protocolFeeU = mulRate(i.totalU, i.feeRate);
|
|
13
|
+
const [protocolToBonusU, protocolToOpsU] = allocate(protocolFeeU, [1, 1]); // 精确 50/50
|
|
14
|
+
const logisticsFeeU = mulRate(i.totalU, i.logisticsRate);
|
|
15
|
+
const logisticsActualU = i.chargeLogistics ? logisticsFeeU : 0;
|
|
16
|
+
const commissionPoolU = mulRate(i.totalU, i.commissionRate);
|
|
17
|
+
const fundBaseU = mulRate(i.totalU, i.fundRate);
|
|
18
|
+
const stakeToLockU = i.stakeToLockU;
|
|
19
|
+
// 卖家净额 = 残值 → 保证 Σ ≡ total(整数减法精确,不增发/不丢)
|
|
20
|
+
const sellerAmountU = i.totalU - protocolFeeU - logisticsActualU - commissionPoolU - fundBaseU - stakeToLockU;
|
|
21
|
+
return { protocolFeeU, protocolToBonusU, protocolToOpsU, logisticsFeeU, logisticsActualU, commissionPoolU, fundBaseU, stakeToLockU, sellerAmountU };
|
|
22
|
+
}
|
|
23
|
+
/** 守恒校验(供测试 + 运行期可选断言):各去向之和 ≡ total。 */
|
|
24
|
+
export function settlementConserves(totalU, s) {
|
|
25
|
+
return s.protocolToBonusU + s.protocolToOpsU === s.protocolFeeU
|
|
26
|
+
&& s.protocolFeeU + s.logisticsActualU + s.commissionPoolU + s.fundBaseU + s.stakeToLockU + s.sellerAmountU === totalU;
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seasonkoh/webaz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "[PRE-LAUNCH] Agent-native decentralized commerce protocol. Humans and AI agents trade on the same protocol via MCP tools. ⚠️ Repository currently private until W8 public launch — GitHub links may return 404. See https://webaz.xyz for status.",
|
|
5
5
|
"main": "dist/mcp.js",
|
|
6
6
|
"bin": {
|