mobilestacks 0.1.3 → 0.1.5
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/mobilestacks.config.d.ts +3 -5
- package/dist/mobilestacks.config.d.ts.map +1 -1
- package/dist/mobilestacks.config.js +4 -17
- package/dist/src/cli/init.d.ts.map +1 -1
- package/dist/src/cli/init.js +37 -15
- package/dist/src/core/runtime-environment.d.ts +1 -0
- package/dist/src/core/runtime-environment.d.ts.map +1 -1
- package/dist/src/core/runtime-environment.js +19 -16
- package/dist/src/types/config.d.ts +2 -22
- package/dist/src/types/config.d.ts.map +1 -1
- package/dist/src/types/config.js +4 -3
- package/package.json +2 -1
- package/src/cli/init.ts +50 -18
- package/src/core/runtime-environment.ts +25 -18
- package/src/types/config.ts +4 -6
|
@@ -3,18 +3,16 @@ declare const _default: {
|
|
|
3
3
|
mainnet: {
|
|
4
4
|
url: string;
|
|
5
5
|
name: string;
|
|
6
|
-
explorerUrl: string;
|
|
7
|
-
faucetUrl: null;
|
|
8
6
|
};
|
|
9
7
|
testnet: {
|
|
10
8
|
url: string;
|
|
11
9
|
name: string;
|
|
12
|
-
explorerUrl: string;
|
|
13
|
-
faucetUrl: string;
|
|
14
10
|
};
|
|
15
11
|
};
|
|
16
12
|
defaultNetwork: string;
|
|
17
|
-
wallet: {
|
|
13
|
+
wallet: {
|
|
14
|
+
derivationPath: string;
|
|
15
|
+
};
|
|
18
16
|
};
|
|
19
17
|
export default _default;
|
|
20
18
|
//# sourceMappingURL=mobilestacks.config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobilestacks.config.d.ts","sourceRoot":"","sources":["../mobilestacks.config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mobilestacks.config.d.ts","sourceRoot":"","sources":["../mobilestacks.config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wBAWE"}
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
// Example mobilestacks.config.ts for testing
|
|
4
3
|
exports.default = {
|
|
5
4
|
networks: {
|
|
6
|
-
mainnet: {
|
|
7
|
-
|
|
8
|
-
name: "mainnet",
|
|
9
|
-
explorerUrl: "https://explorer.stacks.co",
|
|
10
|
-
faucetUrl: null
|
|
11
|
-
},
|
|
12
|
-
testnet: {
|
|
13
|
-
url: "https://stacks-node-api.testnet.stacks.co",
|
|
14
|
-
name: "testnet",
|
|
15
|
-
explorerUrl: "https://explorer.stacks.co?chain=testnet",
|
|
16
|
-
faucetUrl: "https://stacks-node-api.testnet.stacks.co/extended/v1/faucet/stx"
|
|
17
|
-
}
|
|
5
|
+
mainnet: { url: 'https://stacks-node-api.mainnet.stacks.co', name: 'mainnet' },
|
|
6
|
+
testnet: { url: 'https://stacks-node-api.testnet.stacks.co', name: 'testnet' }
|
|
18
7
|
},
|
|
19
|
-
defaultNetwork:
|
|
8
|
+
defaultNetwork: 'testnet',
|
|
20
9
|
wallet: {
|
|
21
|
-
|
|
22
|
-
// seedPhrase: "your twelve word phrase here",
|
|
23
|
-
// derivationPath: "m/44'/5757'/0'/0/0"
|
|
10
|
+
derivationPath: "m/44'/5757'/0'/0/0"
|
|
24
11
|
}
|
|
25
12
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/init.ts"],"names":[],"mappings":"AAIA,wBAAsB,OAAO,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/init.ts"],"names":[],"mappings":"AAIA,wBAAsB,OAAO,kBAoF5B"}
|
package/dist/src/cli/init.js
CHANGED
|
@@ -28,34 +28,56 @@ async function runInit() {
|
|
|
28
28
|
message: 'Stacks testnet node URL:',
|
|
29
29
|
default: 'https://stacks-node-api.testnet.stacks.co',
|
|
30
30
|
},
|
|
31
|
-
{
|
|
32
|
-
type: 'input',
|
|
33
|
-
name: 'privateKey',
|
|
34
|
-
message: 'Your wallet private key (leave blank to use seed phrase):',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: 'input',
|
|
38
|
-
name: 'seedPhrase',
|
|
39
|
-
message: 'Your wallet seed phrase (leave blank if using private key):',
|
|
40
|
-
},
|
|
41
31
|
{
|
|
42
32
|
type: 'input',
|
|
43
33
|
name: 'derivationPath',
|
|
44
|
-
message:
|
|
34
|
+
message: "Derivation path (default: m/44'/5757'/0'/0/0):",
|
|
45
35
|
default: "m/44'/5757'/0'/0/0",
|
|
46
36
|
},
|
|
47
37
|
]);
|
|
48
|
-
|
|
38
|
+
// ── Config file: references env vars, never embeds secrets ──
|
|
39
|
+
const config = `import 'dotenv/config';
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
networks: {
|
|
43
|
+
mainnet: { url: process.env.STACKS_MAINNET_URL || '${answers.mainnetUrl}', name: 'mainnet' },
|
|
44
|
+
testnet: { url: process.env.STACKS_TESTNET_URL || '${answers.testnetUrl}', name: 'testnet' },
|
|
45
|
+
},
|
|
46
|
+
defaultNetwork: 'testnet',
|
|
47
|
+
wallet: {
|
|
48
|
+
// Secrets are read from environment variables — never hard-code them here.
|
|
49
|
+
privateKey: process.env.MOBILESTACKS_PRIVATE_KEY || '',
|
|
50
|
+
seedPhrase: process.env.MOBILESTACKS_SEED_PHRASE || '',
|
|
51
|
+
derivationPath: '${answers.derivationPath}',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
`;
|
|
49
55
|
fs_1.default.writeFileSync(path_1.default.join(process.cwd(), 'mobilestacks.config.ts'), config);
|
|
50
|
-
//
|
|
56
|
+
// ── .env file (if it doesn't exist yet) ──
|
|
57
|
+
const envPath = path_1.default.join(process.cwd(), '.env');
|
|
58
|
+
if (!fs_1.default.existsSync(envPath)) {
|
|
59
|
+
const envContent = `# Mobilestacks secrets — NEVER commit this file!\nMOBILESTACKS_PRIVATE_KEY=\nMOBILESTACKS_SEED_PHRASE=\nSTACKS_MAINNET_URL=${answers.mainnetUrl}\nSTACKS_TESTNET_URL=${answers.testnetUrl}\n`;
|
|
60
|
+
fs_1.default.writeFileSync(envPath, envContent, { mode: 0o600 }); // owner-only permissions
|
|
61
|
+
}
|
|
62
|
+
// ── Scaffold example contract ──
|
|
51
63
|
const contractsDir = path_1.default.join(process.cwd(), 'contracts');
|
|
52
64
|
if (!fs_1.default.existsSync(contractsDir))
|
|
53
65
|
fs_1.default.mkdirSync(contractsDir);
|
|
54
66
|
fs_1.default.writeFileSync(path_1.default.join(contractsDir, 'sample-contract.clar'), '(define-public (hello-world)\n (ok "Hello, Stacks!"))\n');
|
|
55
|
-
// Scaffold example user task
|
|
67
|
+
// ── Scaffold example user task ──
|
|
56
68
|
const tasksDir = path_1.default.join(process.cwd(), 'src', 'tasks');
|
|
57
69
|
if (!fs_1.default.existsSync(tasksDir))
|
|
58
70
|
fs_1.default.mkdirSync(tasksDir, { recursive: true });
|
|
59
71
|
fs_1.default.writeFileSync(path_1.default.join(tasksDir, 'example-task.ts'), "import { task } from '../core/dsl';\n\ntask('example', 'An example user task for onboarding')\n .addParam('name', 'Your name', { type: 'string', required: false, defaultValue: 'World' })\n .setAction(async (args) => {\n return `Hello, ${args.name}! Welcome to mobilestacks.`;\n });\n");
|
|
60
|
-
|
|
72
|
+
// ── Security notice ──
|
|
73
|
+
console.log('\n✅ Created:');
|
|
74
|
+
console.log(' • mobilestacks.config.ts (reads secrets from env vars)');
|
|
75
|
+
console.log(' • .env (store your secrets here)');
|
|
76
|
+
console.log(' • contracts/sample-contract.clar');
|
|
77
|
+
console.log(' • src/tasks/example-task.ts');
|
|
78
|
+
console.log('\n⚠️ SECURITY WARNING:');
|
|
79
|
+
console.log(' Your wallet secrets belong in the .env file, NOT in source code.');
|
|
80
|
+
console.log(' • .env is already listed in .gitignore — never remove that entry.');
|
|
81
|
+
console.log(' • Set MOBILESTACKS_PRIVATE_KEY or MOBILESTACKS_SEED_PHRASE in .env');
|
|
82
|
+
console.log(' • See .env.example for the full list of supported variables.\n');
|
|
61
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-environment.d.ts","sourceRoot":"","sources":["../../../src/core/runtime-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAiB,MAAM,iBAAiB,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAiB,MAAM,iBAAiB,CAAC;AAG/D,qBAAa,kBAAkB;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;gBAEX,MAAM,EAAE,kBAAkB;
|
|
1
|
+
{"version":3,"file":"runtime-environment.d.ts","sourceRoot":"","sources":["../../../src/core/runtime-environment.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,kBAAkB,EAAiB,MAAM,iBAAiB,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAiB,MAAM,iBAAiB,CAAC;AAG/D,qBAAa,kBAAkB;IACtB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;gBAEX,MAAM,EAAE,kBAAkB;CAkEvC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RuntimeEnvironment = void 0;
|
|
4
|
+
require("dotenv/config");
|
|
4
5
|
const transactions_1 = require("@stacks/transactions");
|
|
5
6
|
const wallet_sdk_1 = require("@stacks/wallet-sdk");
|
|
6
7
|
const network_1 = require("@stacks/network");
|
|
@@ -27,25 +28,26 @@ class RuntimeEnvironment {
|
|
|
27
28
|
client: { baseUrl: networkConfig.url }
|
|
28
29
|
});
|
|
29
30
|
}
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
// ── Resolve wallet secrets (env vars take priority over config) ──
|
|
32
|
+
const privateKey = process.env.MOBILESTACKS_PRIVATE_KEY ||
|
|
33
|
+
process.env.STACKS_PRIVATE_KEY ||
|
|
34
|
+
config.wallet.privateKey ||
|
|
35
|
+
'';
|
|
36
|
+
const seedPhrase = process.env.MOBILESTACKS_SEED_PHRASE ||
|
|
37
|
+
process.env.STACKS_SEED_PHRASE ||
|
|
38
|
+
config.wallet.seedPhrase ||
|
|
39
|
+
'';
|
|
40
|
+
if (privateKey) {
|
|
41
|
+
const address = (0, transactions_1.getAddressFromPrivateKey)(privateKey, networkName === 'mainnet' ? 'mainnet' : 'testnet');
|
|
42
|
+
this.wallet = { privateKey, address };
|
|
38
43
|
}
|
|
39
|
-
else if (
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const index = parseInt(path.split('/').pop() || '0', 10);
|
|
44
|
+
else if (seedPhrase) {
|
|
45
|
+
const derivPath = config.wallet.derivationPath || "m/44'/5757'/0'/0/0";
|
|
46
|
+
(0, wallet_sdk_1.generateWallet)({ secretKey: seedPhrase, password: '' }).then(wallet => {
|
|
47
|
+
const index = parseInt(derivPath.split('/').pop() || '0', 10);
|
|
44
48
|
const account = wallet.accounts[index] || wallet.accounts[0];
|
|
45
|
-
// Use getStxAddress to get the address
|
|
46
49
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
47
50
|
const { getStxAddress } = require('@stacks/wallet-sdk/dist/models/account');
|
|
48
|
-
// network.addressVersion has singleSig/multiSig lines.
|
|
49
51
|
const address = getStxAddress(account, this.network.transactionVersion);
|
|
50
52
|
this.wallet = {
|
|
51
53
|
privateKey: account.stxPrivateKey,
|
|
@@ -57,7 +59,8 @@ class RuntimeEnvironment {
|
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
61
|
else {
|
|
60
|
-
|
|
62
|
+
// No secrets anywhere — warn but don't crash (devnet may not need a wallet)
|
|
63
|
+
console.warn('⚠️ No wallet secret found. Set MOBILESTACKS_PRIVATE_KEY or MOBILESTACKS_SEED_PHRASE in your .env file.');
|
|
61
64
|
}
|
|
62
65
|
this.stacks = {};
|
|
63
66
|
// Apply extensions
|
|
@@ -15,7 +15,7 @@ export declare const NetworkConfigSchema: z.ZodObject<{
|
|
|
15
15
|
explorerUrl?: string | undefined;
|
|
16
16
|
faucetUrl?: string | null | undefined;
|
|
17
17
|
}>;
|
|
18
|
-
export declare const WalletConfigSchema: z.
|
|
18
|
+
export declare const WalletConfigSchema: z.ZodObject<{
|
|
19
19
|
privateKey: z.ZodOptional<z.ZodString>;
|
|
20
20
|
seedPhrase: z.ZodOptional<z.ZodString>;
|
|
21
21
|
derivationPath: z.ZodOptional<z.ZodString>;
|
|
@@ -30,16 +30,6 @@ export declare const WalletConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
30
30
|
seedPhrase?: string | undefined;
|
|
31
31
|
derivationPath?: string | undefined;
|
|
32
32
|
address?: string | undefined;
|
|
33
|
-
}>, {
|
|
34
|
-
privateKey?: string | undefined;
|
|
35
|
-
seedPhrase?: string | undefined;
|
|
36
|
-
derivationPath?: string | undefined;
|
|
37
|
-
address?: string | undefined;
|
|
38
|
-
}, {
|
|
39
|
-
privateKey?: string | undefined;
|
|
40
|
-
seedPhrase?: string | undefined;
|
|
41
|
-
derivationPath?: string | undefined;
|
|
42
|
-
address?: string | undefined;
|
|
43
33
|
}>;
|
|
44
34
|
export declare const MobilestacksConfigSchema: z.ZodObject<{
|
|
45
35
|
networks: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
@@ -59,7 +49,7 @@ export declare const MobilestacksConfigSchema: z.ZodObject<{
|
|
|
59
49
|
faucetUrl?: string | null | undefined;
|
|
60
50
|
}>>;
|
|
61
51
|
defaultNetwork: z.ZodString;
|
|
62
|
-
wallet: z.
|
|
52
|
+
wallet: z.ZodObject<{
|
|
63
53
|
privateKey: z.ZodOptional<z.ZodString>;
|
|
64
54
|
seedPhrase: z.ZodOptional<z.ZodString>;
|
|
65
55
|
derivationPath: z.ZodOptional<z.ZodString>;
|
|
@@ -74,16 +64,6 @@ export declare const MobilestacksConfigSchema: z.ZodObject<{
|
|
|
74
64
|
seedPhrase?: string | undefined;
|
|
75
65
|
derivationPath?: string | undefined;
|
|
76
66
|
address?: string | undefined;
|
|
77
|
-
}>, {
|
|
78
|
-
privateKey?: string | undefined;
|
|
79
|
-
seedPhrase?: string | undefined;
|
|
80
|
-
derivationPath?: string | undefined;
|
|
81
|
-
address?: string | undefined;
|
|
82
|
-
}, {
|
|
83
|
-
privateKey?: string | undefined;
|
|
84
|
-
seedPhrase?: string | undefined;
|
|
85
|
-
derivationPath?: string | undefined;
|
|
86
|
-
address?: string | undefined;
|
|
87
67
|
}>;
|
|
88
68
|
}, "strip", z.ZodTypeAny, {
|
|
89
69
|
networks: Record<string, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAC;AAKH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAInC,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
package/dist/src/types/config.js
CHANGED
|
@@ -8,13 +8,14 @@ exports.NetworkConfigSchema = zod_1.z.object({
|
|
|
8
8
|
explorerUrl: zod_1.z.string().url().optional(),
|
|
9
9
|
faucetUrl: zod_1.z.string().url().nullable().optional(),
|
|
10
10
|
});
|
|
11
|
-
// Wallet config:
|
|
11
|
+
// Wallet config: secrets are resolved at runtime from env vars.
|
|
12
|
+
// Config values are optional fallbacks.
|
|
12
13
|
exports.WalletConfigSchema = zod_1.z.object({
|
|
13
|
-
privateKey: zod_1.z.string().
|
|
14
|
+
privateKey: zod_1.z.string().optional(),
|
|
14
15
|
seedPhrase: zod_1.z.string().optional(),
|
|
15
16
|
derivationPath: zod_1.z.string().optional(),
|
|
16
17
|
address: zod_1.z.string().optional(),
|
|
17
|
-
})
|
|
18
|
+
});
|
|
18
19
|
exports.MobilestacksConfigSchema = zod_1.z.object({
|
|
19
20
|
networks: zod_1.z.record(exports.NetworkConfigSchema),
|
|
20
21
|
defaultNetwork: zod_1.z.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mobilestacks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Professional Task Runner & CLI for the Stacks Blockchain",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"commander": "^11.0.0",
|
|
67
67
|
"dotenv": "^17.3.1",
|
|
68
68
|
"inquirer": "^8.2.7",
|
|
69
|
+
"mobilestacks": "^0.1.3",
|
|
69
70
|
"node-fetch": "^2.7.0",
|
|
70
71
|
"ts-node": "^10.9.1",
|
|
71
72
|
"zod": "^3.25.76"
|
package/src/cli/init.ts
CHANGED
|
@@ -23,35 +23,67 @@ export async function runInit() {
|
|
|
23
23
|
message: 'Stacks testnet node URL:',
|
|
24
24
|
default: 'https://stacks-node-api.testnet.stacks.co',
|
|
25
25
|
},
|
|
26
|
-
{
|
|
27
|
-
type: 'input',
|
|
28
|
-
name: 'privateKey',
|
|
29
|
-
message: 'Your wallet private key (leave blank to use seed phrase):',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
type: 'input',
|
|
33
|
-
name: 'seedPhrase',
|
|
34
|
-
message: 'Your wallet seed phrase (leave blank if using private key):',
|
|
35
|
-
},
|
|
36
26
|
{
|
|
37
27
|
type: 'input',
|
|
38
28
|
name: 'derivationPath',
|
|
39
|
-
message:
|
|
29
|
+
message: "Derivation path (default: m/44'/5757'/0'/0/0):",
|
|
40
30
|
default: "m/44'/5757'/0'/0/0",
|
|
41
31
|
},
|
|
42
32
|
]);
|
|
43
33
|
|
|
44
|
-
|
|
34
|
+
// ── Config file: references env vars, never embeds secrets ──
|
|
35
|
+
const config = `import 'dotenv/config';
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
networks: {
|
|
39
|
+
mainnet: { url: process.env.STACKS_MAINNET_URL || '${answers.mainnetUrl}', name: 'mainnet' },
|
|
40
|
+
testnet: { url: process.env.STACKS_TESTNET_URL || '${answers.testnetUrl}', name: 'testnet' },
|
|
41
|
+
},
|
|
42
|
+
defaultNetwork: 'testnet',
|
|
43
|
+
wallet: {
|
|
44
|
+
// Secrets are read from environment variables — never hard-code them here.
|
|
45
|
+
privateKey: process.env.MOBILESTACKS_PRIVATE_KEY || '',
|
|
46
|
+
seedPhrase: process.env.MOBILESTACKS_SEED_PHRASE || '',
|
|
47
|
+
derivationPath: '${answers.derivationPath}',
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
`;
|
|
45
51
|
|
|
46
52
|
fs.writeFileSync(path.join(process.cwd(), 'mobilestacks.config.ts'), config);
|
|
47
|
-
|
|
53
|
+
|
|
54
|
+
// ── .env file (if it doesn't exist yet) ──
|
|
55
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
56
|
+
if (!fs.existsSync(envPath)) {
|
|
57
|
+
const envContent = `# Mobilestacks secrets — NEVER commit this file!\nMOBILESTACKS_PRIVATE_KEY=\nMOBILESTACKS_SEED_PHRASE=\nSTACKS_MAINNET_URL=${answers.mainnetUrl}\nSTACKS_TESTNET_URL=${answers.testnetUrl}\n`;
|
|
58
|
+
fs.writeFileSync(envPath, envContent, { mode: 0o600 }); // owner-only permissions
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Scaffold example contract ──
|
|
48
62
|
const contractsDir = path.join(process.cwd(), 'contracts');
|
|
49
63
|
if (!fs.existsSync(contractsDir)) fs.mkdirSync(contractsDir);
|
|
50
|
-
fs.writeFileSync(
|
|
51
|
-
|
|
64
|
+
fs.writeFileSync(
|
|
65
|
+
path.join(contractsDir, 'sample-contract.clar'),
|
|
66
|
+
'(define-public (hello-world)\n (ok "Hello, Stacks!"))\n',
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// ── Scaffold example user task ──
|
|
52
70
|
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
53
71
|
if (!fs.existsSync(tasksDir)) fs.mkdirSync(tasksDir, { recursive: true });
|
|
54
|
-
fs.writeFileSync(
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
fs.writeFileSync(
|
|
73
|
+
path.join(tasksDir, 'example-task.ts'),
|
|
74
|
+
"import { task } from '../core/dsl';\n\ntask('example', 'An example user task for onboarding')\n .addParam('name', 'Your name', { type: 'string', required: false, defaultValue: 'World' })\n .setAction(async (args) => {\n return `Hello, ${args.name}! Welcome to mobilestacks.`;\n });\n",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// ── Security notice ──
|
|
78
|
+
console.log('\n✅ Created:');
|
|
79
|
+
console.log(' • mobilestacks.config.ts (reads secrets from env vars)');
|
|
80
|
+
console.log(' • .env (store your secrets here)');
|
|
81
|
+
console.log(' • contracts/sample-contract.clar');
|
|
82
|
+
console.log(' • src/tasks/example-task.ts');
|
|
83
|
+
|
|
84
|
+
console.log('\n⚠️ SECURITY WARNING:');
|
|
85
|
+
console.log(' Your wallet secrets belong in the .env file, NOT in source code.');
|
|
86
|
+
console.log(' • .env is already listed in .gitignore — never remove that entry.');
|
|
87
|
+
console.log(' • Set MOBILESTACKS_PRIVATE_KEY or MOBILESTACKS_SEED_PHRASE in .env');
|
|
88
|
+
console.log(' • See .env.example for the full list of supported variables.\n');
|
|
57
89
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
1
2
|
import { MobilestacksConfig, NetworkConfig } from '../types/config';
|
|
2
3
|
import { getAddressFromPrivateKey } from '@stacks/transactions';
|
|
3
4
|
import { generateWallet } from '@stacks/wallet-sdk';
|
|
@@ -33,26 +34,29 @@ export class RuntimeEnvironment {
|
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
// ── Resolve wallet secrets (env vars take priority over config) ──
|
|
38
|
+
const privateKey =
|
|
39
|
+
process.env.MOBILESTACKS_PRIVATE_KEY ||
|
|
40
|
+
process.env.STACKS_PRIVATE_KEY ||
|
|
41
|
+
config.wallet.privateKey ||
|
|
42
|
+
'';
|
|
43
|
+
const seedPhrase =
|
|
44
|
+
process.env.MOBILESTACKS_SEED_PHRASE ||
|
|
45
|
+
process.env.STACKS_SEED_PHRASE ||
|
|
46
|
+
config.wallet.seedPhrase ||
|
|
47
|
+
'';
|
|
48
|
+
|
|
49
|
+
if (privateKey) {
|
|
50
|
+
const address = getAddressFromPrivateKey(privateKey, networkName === 'mainnet' ? 'mainnet' : 'testnet');
|
|
51
|
+
this.wallet = { privateKey, address };
|
|
52
|
+
} else if (seedPhrase) {
|
|
53
|
+
const derivPath = config.wallet.derivationPath || "m/44'/5757'/0'/0/0";
|
|
54
|
+
generateWallet({ secretKey: seedPhrase, password: '' }).then(wallet => {
|
|
55
|
+
const index = parseInt(derivPath.split('/').pop() || '0', 10);
|
|
49
56
|
const account = wallet.accounts[index] || wallet.accounts[0];
|
|
50
|
-
// Use getStxAddress to get the address
|
|
51
57
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
52
58
|
const { getStxAddress } = require('@stacks/wallet-sdk/dist/models/account');
|
|
53
|
-
|
|
54
|
-
const address = getStxAddress(account, this.network.transactionVersion);
|
|
55
|
-
|
|
59
|
+
const address = getStxAddress(account, this.network.transactionVersion);
|
|
56
60
|
this.wallet = {
|
|
57
61
|
privateKey: account.stxPrivateKey,
|
|
58
62
|
address,
|
|
@@ -62,7 +66,10 @@ export class RuntimeEnvironment {
|
|
|
62
66
|
throw new Error('Failed to generate wallet from seed phrase');
|
|
63
67
|
});
|
|
64
68
|
} else {
|
|
65
|
-
|
|
69
|
+
// No secrets anywhere — warn but don't crash (devnet may not need a wallet)
|
|
70
|
+
console.warn(
|
|
71
|
+
'⚠️ No wallet secret found. Set MOBILESTACKS_PRIVATE_KEY or MOBILESTACKS_SEED_PHRASE in your .env file.',
|
|
72
|
+
);
|
|
66
73
|
}
|
|
67
74
|
this.stacks = {};
|
|
68
75
|
|
package/src/types/config.ts
CHANGED
|
@@ -8,16 +8,14 @@ export const NetworkConfigSchema = z.object({
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
// Wallet config:
|
|
11
|
+
// Wallet config: secrets are resolved at runtime from env vars.
|
|
12
|
+
// Config values are optional fallbacks.
|
|
12
13
|
export const WalletConfigSchema = z.object({
|
|
13
|
-
privateKey: z.string().
|
|
14
|
+
privateKey: z.string().optional(),
|
|
14
15
|
seedPhrase: z.string().optional(),
|
|
15
16
|
derivationPath: z.string().optional(),
|
|
16
17
|
address: z.string().optional(),
|
|
17
|
-
})
|
|
18
|
-
(data) => data.privateKey || data.seedPhrase,
|
|
19
|
-
{ message: "Either privateKey or seedPhrase is required" }
|
|
20
|
-
);
|
|
18
|
+
});
|
|
21
19
|
|
|
22
20
|
export const MobilestacksConfigSchema = z.object({
|
|
23
21
|
networks: z.record(NetworkConfigSchema),
|