XMR native
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { ADDITIONAL_MARKET_FEE } from '../../shared/market.ts';
|
||||
import { formatXmr, XMR_ATOMIC_UNITS } from '../../shared/xmr-amount.ts';
|
||||
import { allocateXmrSale } from '../../shared/xmr-pricing.ts';
|
||||
|
||||
/** No live payment adapter is reachable in this release. Never return a payable mock address. */
|
||||
export function paymentMode(env: NodeJS.ProcessEnv = process.env): 'mock' {
|
||||
@@ -7,16 +9,15 @@ export function paymentMode(env: NodeJS.ProcessEnv = process.env): 'mock' {
|
||||
return mode;
|
||||
}
|
||||
export function xmrString(atomic: bigint): string {
|
||||
return `${atomic / 1_000_000_000_000n}.${(atomic % 1_000_000_000_000n).toString().padStart(12, '0')}`;
|
||||
return formatXmr(atomic);
|
||||
}
|
||||
/** Deterministic simulation ONLY. Round gross up and both independent seller fees down. */
|
||||
export function mockQuote(priceCents: number, feeBps: number, usdCentsPerXmr: number) {
|
||||
const rate = BigInt(usdCentsPerXmr);
|
||||
const gross = (BigInt(priceCents) * 1_000_000_000_000n + rate - 1n) / rate;
|
||||
const fee = gross * BigInt(feeBps) / 10_000n;
|
||||
const additionalFee = gross * BigInt(ADDITIONAL_MARKET_FEE.bps) / 10_000n;
|
||||
const gross = (BigInt(priceCents) * XMR_ATOMIC_UNITS + rate - 1n) / rate;
|
||||
const { hosting: fee, author: additionalFee, sellerBeforeNetworkCosts } = allocateXmrSale(gross, feeBps);
|
||||
return { invoice: { provider: 'mock' as const, amountXmr: xmrString(gross), address: null,
|
||||
label: 'SIMULATION ONLY — no Monero payment address. Never send real XMR.' },
|
||||
feeXmr: xmrString(fee), sellerNetXmr: xmrString(gross - fee - additionalFee),
|
||||
feeXmr: xmrString(fee), sellerNetXmr: xmrString(sellerBeforeNetworkCosts),
|
||||
additionalFee: { ...ADDITIONAL_MARKET_FEE, amountXmr: xmrString(additionalFee), payout: 'not-started' as const } };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { mkdir,open,unlink,chmod } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { z } from 'zod';
|
||||
import { ADDITIONAL_MARKET_FEE } from '../../shared/market.ts';
|
||||
import { allocateXmrSale } from '../../shared/xmr-pricing.ts';
|
||||
import { NETWORK } from '../../shared/network.ts';
|
||||
import { CHAIN_ID,sha256,verifySignature,transactionId } from '../testnet/consensus.ts';
|
||||
import { atomicXmr,fiatCents,validateXmrAddress } from './xmr.ts';
|
||||
@@ -107,9 +108,9 @@ export class SettlementLab {
|
||||
s.quote=await this.processor.createInvoice(id,s.terms.priceCents);
|
||||
await validateXmrAddress(s.quote.address);const gross=atomicXmr(s.quote.amountXmr);
|
||||
if(!s.quote.id||gross<=0n)throw new Error('Invalid quote');
|
||||
const hosting=gross*BigInt(s.terms.feeBps)/10000n,fee=gross*100n/10000n;
|
||||
const {hosting,author:fee,sellerBeforeNetworkCosts}=allocateXmrSale(gross,s.terms.feeBps);
|
||||
s.hostingXmr=xmrString(hosting);
|
||||
s.seller={amount:xmrString(gross-hosting-fee),destination:s.terms.sellerXmr,state:'ready'};
|
||||
s.seller={amount:xmrString(sellerBeforeNetworkCosts),destination:s.terms.sellerXmr,state:'ready'};
|
||||
s.fee={amount:xmrString(fee),destination:ADDITIONAL_MARKET_FEE.address,state:fee===0n?'skipped':'ready'};
|
||||
s.quoteDigest=sha256(buyerInvoiceMessage(s));s.status='quote';this.save(s);return s;
|
||||
}catch{return this.hold(s,'invoice-create-ambiguous-or-invalid');}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { keccak } from 'hash-wasm';
|
||||
import { parseXmr } from '../../shared/xmr-amount.ts';
|
||||
|
||||
const alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
const decodedSizes:Record<number,number>={2:1,3:2,5:3,6:4,7:5,9:6,10:7,11:8};
|
||||
@@ -21,10 +22,9 @@ export async function validateXmrAddress(address:string):Promise<string>{
|
||||
return address;
|
||||
}
|
||||
export function atomicXmr(value:string):bigint{
|
||||
if(typeof value!=='string'||!/^(0|[1-9]\d{0,9})(\.\d{1,12})?$/.test(value))throw new Error('Invalid bounded XMR decimal');
|
||||
const [whole,fraction='']=value.split('.');return BigInt(whole)*1_000_000_000_000n+BigInt(fraction.padEnd(12,'0'));
|
||||
return parseXmr(value);
|
||||
}
|
||||
export function fiatCents(value:string):number{
|
||||
if(typeof value!=='string'||!/^(0|[1-9]\d{0,6})(\.\d{1,2})?$/.test(value))throw new Error('Invalid USD decimal');
|
||||
if(typeof value!=='string'||value.length>10||!/^(0|[1-9]\d{0,6})(\.\d{1,2})?(?![\s\S])/.test(value))throw new Error('Invalid USD decimal');
|
||||
const [whole,fraction='']=value.split('.');return Number(whole)*100+Number(fraction.padEnd(2,'0'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user