Environment variables
This commit is contained in:
+57
-1
@@ -11,6 +11,9 @@ import { CUSTODY_POLICY, MOCK_CREDIT_ATOMIC, custodyCommandSchema, custodyMessag
|
||||
type CustodyWithdrawal, type CustodyCommand } from '../shared/custody.ts';
|
||||
import { NETWORK } from '../shared/testnet.ts';
|
||||
import { address, CHAIN_ID } from '../server/testnet/consensus.ts';
|
||||
import { operatorMarketPolicy, validateMarketPolicy, type MarketPolicy } from '../server/market/policy.ts';
|
||||
import { ADDITIONAL_MARKET_FEE } from '../shared/market.ts';
|
||||
import { keccak } from 'hash-wasm';
|
||||
|
||||
function identity() {
|
||||
const keys = generateKeyPairSync('ec', { namedCurve: 'prime256v1' });
|
||||
@@ -42,9 +45,62 @@ async function fixture(t: { after(fn: () => Promise<void>): void }) {
|
||||
priceAtomicXmr: l.priceAtomicXmr, feeBps: l.feeBps });
|
||||
return { dir, envelope, execute, activity, credit, listing, reserve, get market() { return market; },
|
||||
changeCard: () => { present = false; }, advance: (ms: number) => { now += ms; },
|
||||
restart: async () => { market.close(); market = await CustodyMarket.open(dir, options); } };
|
||||
restart: async (policy: MarketPolicy = {}) => { market.close(); market = await CustodyMarket.open(dir, { ...options, ...policy }); } };
|
||||
}
|
||||
|
||||
async function feeAddress() {
|
||||
const prefix = Uint8Array.from([18, ...Array(64).fill(2)]);
|
||||
const data = Buffer.concat([prefix, Buffer.from((await keccak(prefix, 256)).slice(0, 8), 'hex')]);
|
||||
const alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
||||
const sizes: Record<number, number> = { 1: 2, 2: 3, 3: 5, 4: 6, 5: 7, 6: 9, 7: 10, 8: 11 };
|
||||
let encoded = '';
|
||||
for (let i = 0; i < data.length; i += 8) {
|
||||
const chunk = data.subarray(i, i + 8); let n = BigInt(`0x${chunk.toString('hex')}`), text = '';
|
||||
while (n) { text = alphabet[Number(n % 58n)] + text; n /= 58n; }
|
||||
encoded += text.padStart(sizes[chunk.length], '1');
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
test('operator market switches are strict and host fee defaults to fixed author with checksum validation', async () => {
|
||||
assert.deepEqual(operatorMarketPolicy({}), { marketEnabled: true, tradingEnabled: true, hostFeeAddress: ADDITIONAL_MARKET_FEE.address });
|
||||
for (const v of ['', 'true', 'false', 'yes', '2']) {
|
||||
assert.throws(() => operatorMarketPolicy({ WIKIDECK_MARKET_ENABLED: v }));
|
||||
assert.throws(() => operatorMarketPolicy({ WIKIDECK_MARKET_TRADING_ENABLED: v }));
|
||||
}
|
||||
await validateMarketPolicy(operatorMarketPolicy({}));
|
||||
await assert.rejects(validateMarketPolicy({ hostFeeAddress: '4'.repeat(95) }), /checksum|network/);
|
||||
});
|
||||
test('market disable hides public listings and rejects new business but retains private balances and safe release', async t => {
|
||||
const f = await fixture(t); f.credit(); const l = f.listing(), o = f.reserve(l);
|
||||
const w = f.execute<CustodyWithdrawal>(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '100', maxFeeAtomicXmr: '1' });
|
||||
await f.restart({ marketEnabled: false });
|
||||
assert.deepEqual(f.market.browse().listings, []); assert.equal(f.market.info().marketEnabled, false);
|
||||
assert.throws(() => f.listing('2'), /disabled/);
|
||||
assert.throws(() => f.credit(stranger), /disabled/);
|
||||
f.execute(buyer, { type: 'cancel-reservation', orderId: o.id });
|
||||
f.execute(buyer, { type: 'cancel-withdrawal', withdrawalId: w.id });
|
||||
assert.equal(f.activity().availableAtomicXmr, MOCK_CREDIT_ATOMIC);
|
||||
const withdrawal = f.execute<CustodyWithdrawal>(buyer, { type: 'withdraw', destination: 'mock-external', amountAtomicXmr: '1', maxFeeAtomicXmr: '0' });
|
||||
assert.equal(withdrawal.status, 'reserved');
|
||||
});
|
||||
test('paused trading keeps browse and cancellations available, and fee recipients are signed and snapshotted', async t => {
|
||||
const f = await fixture(t), recipient = await feeAddress(); f.credit();
|
||||
await f.restart({ hostFeeAddress: recipient });
|
||||
assert.throws(() => f.listing(), /recipient/);
|
||||
const action = { type: 'list' as const, cardHash, sequence: 0, priceAtomicXmr: '1000', feeBps: 250, hostFeeAddress: recipient };
|
||||
const c = f.envelope(seller, action);
|
||||
assert.throws(() => f.market.execute({ ...c, action: { ...action, hostFeeAddress: ADDITIONAL_MARKET_FEE.address } }), /signature/);
|
||||
const l = f.market.execute(c) as CustodyListing;
|
||||
await f.restart({ hostFeeAddress: ADDITIONAL_MARKET_FEE.address, tradingEnabled: false });
|
||||
assert.equal(f.market.browse().listings[0].hostFeeAddress, recipient);
|
||||
assert.throws(() => f.reserve(l), /disabled/);
|
||||
await f.restart();
|
||||
assert.throws(() => f.reserve(l), /recipient/);
|
||||
const o = f.execute<CustodyOrder>(buyer, { type: 'reserve', listingId: l.id, priceAtomicXmr: l.priceAtomicXmr, feeBps: 250, hostFeeAddress: recipient });
|
||||
assert.equal(o.hostFeeAddress, recipient); assert.equal(o.authorAddress, ADDITIONAL_MARKET_FEE.address);
|
||||
f.market.settleFixture(o.id, evidence); assert.equal(f.market.audit().assetsAtomicXmr, MOCK_CREDIT_ATOMIC);
|
||||
});
|
||||
|
||||
test('custody mode is off by default and rejects all live configurations', () => {
|
||||
assert.equal(custodyMode({}), 'off'); assert.equal(custodyMode({ WIKIDECK_CUSTODY_MODE: 'sandbox' }), 'sandbox');
|
||||
for (const mode of ['live', 'mainnet', '', 'shkeeper']) assert.throws(() => custodyMode({ WIKIDECK_CUSTODY_MODE: mode }));
|
||||
|
||||
Reference in New Issue
Block a user