feat(settlement): production Revolut, Wise, and Binance external rails
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 59s
CI/CD Pipeline / Security Scanning (push) Successful in 2m34s
CI/CD Pipeline / Lint and Format (push) Failing after 49s
CI/CD Pipeline / Terraform Validation (push) Failing after 22s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 26s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 19s
Validation / validate-genesis (push) Successful in 31s
Validation / validate-terraform (push) Failing after 27s
Validation / validate-kubernetes (push) Failing after 10s
Validation / validate-smart-contracts (push) Failing after 12s
Validation / validate-security (push) Failing after 1m26s
Validation / validate-documentation (push) Failing after 18s
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 59s
CI/CD Pipeline / Security Scanning (push) Successful in 2m34s
CI/CD Pipeline / Lint and Format (push) Failing after 49s
CI/CD Pipeline / Terraform Validation (push) Failing after 22s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 26s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 19s
Validation / validate-genesis (push) Successful in 31s
Validation / validate-terraform (push) Failing after 27s
Validation / validate-kubernetes (push) Failing after 10s
Validation / validate-smart-contracts (push) Failing after 12s
Validation / validate-security (push) Failing after 1m26s
Validation / validate-documentation (push) Failing after 18s
Wire Business/Platform/Spot adapters with fail-closed live gates, status/probe/dispatch APIs, smoke script, and portal rails UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
147
scripts/rails/smoke-revolut-wise-binance.mjs
Normal file
147
scripts/rails/smoke-revolut-wise-binance.mjs
Normal file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Smoke-test Revolut Business + Wise + Binance credentials.
|
||||
* Loads secrets/{revolut,wise,binance}/credentials.env then process.env.
|
||||
* Usage: node scripts/rails/smoke-revolut-wise-binance.mjs
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import crypto from 'crypto';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, '../..');
|
||||
|
||||
function loadEnvFile(rel) {
|
||||
const p = path.join(root, rel);
|
||||
if (!fs.existsSync(p)) return {};
|
||||
const out = {};
|
||||
for (const line of fs.readFileSync(p, 'utf8').split(/\r?\n/)) {
|
||||
if (!line || line.trim().startsWith('#')) continue;
|
||||
const i = line.indexOf('=');
|
||||
if (i < 0) continue;
|
||||
out[line.slice(0, i).trim()] = line.slice(i + 1).trim().replace(/^["']|["']$/g, '');
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function applyEnv(obj) {
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (v && (!(k in process.env) || !process.env[k])) process.env[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
function redact(s) {
|
||||
if (!s) return '(empty)';
|
||||
if (s.length <= 10) return '***';
|
||||
return `${s.slice(0, 4)}…${s.slice(-4)} (len=${s.length})`;
|
||||
}
|
||||
|
||||
applyEnv(loadEnvFile('.env'));
|
||||
applyEnv(loadEnvFile('secrets/revolut/credentials.env'));
|
||||
applyEnv(loadEnvFile('secrets/wise/credentials.env'));
|
||||
applyEnv(loadEnvFile('secrets/binance/credentials.env'));
|
||||
|
||||
async function smokeRevolut() {
|
||||
const token = process.env.REVOLUT_ACCESS_TOKEN || process.env.REVOLUT_API_TOKEN;
|
||||
const base = (process.env.REVOLUT_API_BASE_URL || 'https://b2b.revolut.com/api/1.0').replace(/\/$/, '');
|
||||
if (!token) return { ok: false, configured: false, error: 'REVOLUT_ACCESS_TOKEN missing' };
|
||||
const res = await fetch(`${base}/accounts`, {
|
||||
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
|
||||
});
|
||||
const text = await res.text();
|
||||
let body;
|
||||
try {
|
||||
body = JSON.parse(text);
|
||||
} catch {
|
||||
body = { raw: text.slice(0, 160) };
|
||||
}
|
||||
const accounts = Array.isArray(body) ? body : body?.accounts ?? [];
|
||||
return {
|
||||
ok: res.status >= 200 && res.status < 300,
|
||||
configured: true,
|
||||
http: res.status,
|
||||
token: redact(token),
|
||||
accountCount: Array.isArray(accounts) ? accounts.length : null,
|
||||
accountIdSet: Boolean(process.env.REVOLUT_ACCOUNT_ID),
|
||||
liveGate: process.env.SETTLEMENT_ALLOW_REVOLUT_PRODUCTION === '1',
|
||||
error: res.ok ? undefined : body?.message || text.slice(0, 180),
|
||||
};
|
||||
}
|
||||
|
||||
async function smokeWise() {
|
||||
const token = process.env.WISE_API_TOKEN || process.env.TRANSFERWISE_API_TOKEN;
|
||||
const base = (process.env.WISE_API_BASE_URL || 'https://api.transferwise.com').replace(/\/$/, '');
|
||||
if (!token) return { ok: false, configured: false, error: 'WISE_API_TOKEN missing' };
|
||||
const res = await fetch(`${base}/v1/profiles`, {
|
||||
headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
|
||||
});
|
||||
const text = await res.text();
|
||||
let body;
|
||||
try {
|
||||
body = JSON.parse(text);
|
||||
} catch {
|
||||
body = { raw: text.slice(0, 160) };
|
||||
}
|
||||
const profiles = Array.isArray(body) ? body : [];
|
||||
return {
|
||||
ok: res.status >= 200 && res.status < 300,
|
||||
configured: true,
|
||||
http: res.status,
|
||||
token: redact(token),
|
||||
profileCount: profiles.length,
|
||||
profileIdSet: Boolean(process.env.WISE_PROFILE_ID),
|
||||
liveGate: process.env.SETTLEMENT_ALLOW_WISE_PRODUCTION === '1',
|
||||
error: res.ok ? undefined : body?.error || body?.message || text.slice(0, 180),
|
||||
};
|
||||
}
|
||||
|
||||
async function smokeBinance() {
|
||||
const key = process.env.BINANCE_API_KEY;
|
||||
const secret = process.env.BINANCE_API_SECRET;
|
||||
const base = (process.env.BINANCE_API_BASE_URL || 'https://api.binance.com').replace(/\/$/, '');
|
||||
if (!key || !secret) return { ok: false, configured: false, error: 'BINANCE_API_KEY/SECRET missing' };
|
||||
|
||||
const ping = await fetch(`${base}/api/v3/ping`);
|
||||
const timestamp = Date.now();
|
||||
const qs = `timestamp=${timestamp}`;
|
||||
const sig = crypto.createHmac('sha256', secret).update(qs).digest('hex');
|
||||
const res = await fetch(`${base}/api/v3/account?${qs}&signature=${sig}`, {
|
||||
headers: { 'X-MBX-APIKEY': key, Accept: 'application/json' },
|
||||
});
|
||||
const text = await res.text();
|
||||
let body;
|
||||
try {
|
||||
body = JSON.parse(text);
|
||||
} catch {
|
||||
body = { raw: text.slice(0, 160) };
|
||||
}
|
||||
const funded = (body.balances || []).filter((b) => Number(b.free) > 0).length;
|
||||
return {
|
||||
ok: ping.ok && res.status >= 200 && res.status < 300,
|
||||
configured: true,
|
||||
http: res.status,
|
||||
ping: ping.status,
|
||||
apiKey: redact(key),
|
||||
fundedAssets: funded,
|
||||
canTrade: body.canTrade,
|
||||
liveGate: process.env.SETTLEMENT_ALLOW_BINANCE_PRODUCTION === '1',
|
||||
withdrawGate: process.env.BINANCE_ALLOW_WITHDRAW === '1',
|
||||
error: res.ok ? undefined : body?.msg || text.slice(0, 180),
|
||||
};
|
||||
}
|
||||
|
||||
const report = {
|
||||
asOf: new Date().toISOString(),
|
||||
revolut: await smokeRevolut(),
|
||||
wise: await smokeWise(),
|
||||
binance: await smokeBinance(),
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
const anyConfigured = report.revolut.configured || report.wise.configured || report.binance.configured;
|
||||
const allOk =
|
||||
(!report.revolut.configured || report.revolut.ok) &&
|
||||
(!report.wise.configured || report.wise.ok) &&
|
||||
(!report.binance.configured || report.binance.ok);
|
||||
process.exit(anyConfigured && allOk ? 0 : anyConfigured ? 2 : 3);
|
||||
Reference in New Issue
Block a user