Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ src/.DS_Store
# Never commit private keys
*.key
*.secret
.env.local
scripts/.env.local
*.env.local

frontend/node_modules/
frontend/dist/
Expand Down
3 changes: 3 additions & 0 deletions SECURITY-TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Security TODO

- 2026-05-19: Removed the committed testnet deployer seed from strategy scripts and retired it from all scripted deploy/test flows. Treat the old testnet key as compromised and do not fund or reuse it. Generate and fund a fresh testnet account, then keep the replacement seed only in `TESTNET_SECRET` via a local `.env.local` file or shell environment.
37 changes: 33 additions & 4 deletions scripts/deploy_strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/**
* Deploy the BlendLeverageStrategy contract to testnet.
*
* Usage: npx tsx scripts/deploy_strategy.ts
* Usage:
* TESTNET_SECRET=S... npx tsx scripts/deploy_strategy.ts
* # or put TESTNET_SECRET=S... in .env.local / scripts/.env.local
*/
import {
BASE_FEE,
Expand All @@ -14,16 +16,43 @@ import {
xdr,
Address,
nativeToScVal,
StrKey,
} from "@stellar/stellar-sdk";
import * as fs from "fs";
import * as crypto from "crypto";

const RPC_URL = "https://soroban-testnet.stellar.org";
const PASSPHRASE = Networks.TESTNET;
const SECRET = "SCX6RZDDWLKWLSUEKDROH3PCXDPAVVJ355YA4FEHQB3MJMA6K762E527";

const keypair = Keypair.fromSecret(SECRET);
function loadEnvLocal() {
for (const file of [".env.local", "scripts/.env.local"]) {
if (!fs.existsSync(file)) continue;
const lines = fs.readFileSync(file, "utf8").split(/\r?\n/);
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed);
if (!match || process.env[match[1]]) continue;
process.env[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
}
}
}

function getTestnetKeypair(): Keypair {
loadEnvLocal();
const secret = process.env.TESTNET_SECRET?.trim();
if (!secret) {
console.error("Missing TESTNET_SECRET. Add it to your shell env or local .env.local file; never commit secret keys.");
process.exit(1);
}
try {
return Keypair.fromSecret(secret);
} catch {
console.error("Invalid TESTNET_SECRET. Expected a Stellar secret seed beginning with S.");
process.exit(1);
}
}

const keypair = getTestnetKeypair();
const account = keypair.publicKey();
const server = new SorobanRpc.Server(RPC_URL);

Expand Down
1 change: 1 addition & 0 deletions scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@stellar/stellar-sdk": "^14.6.1"
},
"devDependencies": {
"@types/node": "^25.9.1",
"tsx": "^4.19.2",
"typescript": "^5.7.3"
}
Expand Down
36 changes: 33 additions & 3 deletions scripts/test_strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
* 4. Withdraw
*
* Usage:
* npx tsx scripts/test_strategy.ts
* TESTNET_SECRET=S... npx tsx scripts/test_strategy.ts
* # or put TESTNET_SECRET=S... in .env.local / scripts/.env.local
*/

import {
Expand All @@ -26,6 +27,7 @@ import {
nativeToScVal,
scValToNative,
} from "@stellar/stellar-sdk";
import * as fs from "fs";

// ── Constants ────────────────────────────────────────────────────────────────

Expand All @@ -38,8 +40,36 @@ const RPC_URL = "https://soroban-testnet.stellar.org";
const HORIZON_URL = "https://horizon-testnet.stellar.org";
const PASSPHRASE = Networks.TESTNET;

const SECRET = "SCX6RZDDWLKWLSUEKDROH3PCXDPAVVJ355YA4FEHQB3MJMA6K762E527";
const keypair = Keypair.fromSecret(SECRET);
function loadEnvLocal() {
for (const file of [".env.local", "scripts/.env.local"]) {
if (!fs.existsSync(file)) continue;
const lines = fs.readFileSync(file, "utf8").split(/\r?\n/);
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed);
if (!match || process.env[match[1]]) continue;
process.env[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
}
}
}

function getTestnetKeypair(): Keypair {
loadEnvLocal();
const secret = process.env.TESTNET_SECRET?.trim();
if (!secret) {
console.error("Missing TESTNET_SECRET. Add it to your shell env or local .env.local file; never commit secret keys.");
process.exit(1);
}
try {
return Keypair.fromSecret(secret);
} catch {
console.error("Invalid TESTNET_SECRET. Expected a Stellar secret seed beginning with S.");
process.exit(1);
}
}

const keypair = getTestnetKeypair();
const account = keypair.publicKey();

const server = new SorobanRpc.Server(RPC_URL);
Expand Down