@caatinga/core 2.3.1 → 2.4.1
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/{browser-Cq4ZofIq.d.cts → artifact.schema-BH5K-IiZ.d.cts} +2 -1
- package/dist/{browser-Cq4ZofIq.d.ts → artifact.schema-BH5K-IiZ.d.ts} +2 -1
- package/dist/browser.cjs +20 -0
- package/dist/browser.d.cts +5 -1
- package/dist/browser.d.ts +5 -1
- package/dist/browser.js +19 -0
- package/dist/index.cjs +400 -63
- package/dist/index.d.cts +35 -5
- package/dist/index.d.ts +35 -5
- package/dist/index.js +392 -63
- package/package.json +1 -1
- package/scaffolds/soroban-contract-stub/Cargo.toml +24 -0
- package/scaffolds/soroban-contract-stub/src/lib.rs +42 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/errors/CaatingaErrorCode.ts
|
|
2
2
|
var CaatingaErrorCode = {
|
|
3
3
|
CONFIG_NOT_FOUND: "CAATINGA_CONFIG_NOT_FOUND",
|
|
4
|
+
DEPENDENCIES_NOT_INSTALLED: "CAATINGA_DEPENDENCIES_NOT_INSTALLED",
|
|
4
5
|
INVALID_CONFIG: "CAATINGA_INVALID_CONFIG",
|
|
5
6
|
COMMAND_FAILED: "CAATINGA_COMMAND_FAILED",
|
|
6
7
|
UNEXPECTED_ERROR: "CAATINGA_UNEXPECTED_ERROR",
|
|
@@ -105,7 +106,7 @@ function formatCause(cause) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
// src/version.ts
|
|
108
|
-
var CAATINGA_CORE_VERSION = "2.
|
|
109
|
+
var CAATINGA_CORE_VERSION = "2.4.1";
|
|
109
110
|
|
|
110
111
|
// src/config/config.schema.ts
|
|
111
112
|
import { z } from "zod";
|
|
@@ -157,6 +158,28 @@ import { access } from "fs/promises";
|
|
|
157
158
|
import path from "path";
|
|
158
159
|
import { createJiti } from "jiti";
|
|
159
160
|
import { z as z2 } from "zod";
|
|
161
|
+
|
|
162
|
+
// src/config/is-dependencies-not-installed-error.ts
|
|
163
|
+
function isDependenciesNotInstalledError(error) {
|
|
164
|
+
if (!error || typeof error !== "object") {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
const candidate = error;
|
|
168
|
+
const code = candidate.code;
|
|
169
|
+
const message = String(candidate.message ?? error);
|
|
170
|
+
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
171
|
+
return /@caatinga\/core/.test(message);
|
|
172
|
+
}
|
|
173
|
+
if (/Cannot find (module|package).+@caatinga\/core/i.test(message)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
if (candidate.cause !== void 0) {
|
|
177
|
+
return isDependenciesNotInstalledError(candidate.cause);
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/config/load-config.ts
|
|
160
183
|
async function loadConfig(options = {}) {
|
|
161
184
|
const cwd = options.cwd ?? process.cwd();
|
|
162
185
|
const configPath = path.resolve(cwd, options.configPath ?? "caatinga.config.ts");
|
|
@@ -181,6 +204,14 @@ async function loadConfig(options = {}) {
|
|
|
181
204
|
error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
182
205
|
);
|
|
183
206
|
}
|
|
207
|
+
if (isDependenciesNotInstalledError(error)) {
|
|
208
|
+
throw new CaatingaError(
|
|
209
|
+
"Project dependencies are not installed.",
|
|
210
|
+
CaatingaErrorCode.DEPENDENCIES_NOT_INSTALLED,
|
|
211
|
+
"Run npm install (or pnpm install) in the project root, then retry.",
|
|
212
|
+
error
|
|
213
|
+
);
|
|
214
|
+
}
|
|
184
215
|
throw error;
|
|
185
216
|
}
|
|
186
217
|
}
|
|
@@ -587,6 +618,64 @@ function defaultEmitWarning(warning) {
|
|
|
587
618
|
`);
|
|
588
619
|
}
|
|
589
620
|
|
|
621
|
+
// src/shell/resolve-subprocess-env.ts
|
|
622
|
+
import { existsSync } from "fs";
|
|
623
|
+
import os from "os";
|
|
624
|
+
import path6 from "path";
|
|
625
|
+
function uniquePaths(entries) {
|
|
626
|
+
const seen = /* @__PURE__ */ new Set();
|
|
627
|
+
const ordered = [];
|
|
628
|
+
for (const entry of entries) {
|
|
629
|
+
if (!entry || seen.has(entry)) {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
seen.add(entry);
|
|
633
|
+
ordered.push(entry);
|
|
634
|
+
}
|
|
635
|
+
return ordered;
|
|
636
|
+
}
|
|
637
|
+
function hasExecutable(binDir, name) {
|
|
638
|
+
return existsSync(path6.join(binDir, name));
|
|
639
|
+
}
|
|
640
|
+
function toolchainBinDirs(home, env) {
|
|
641
|
+
const candidates = [
|
|
642
|
+
path6.join(home, ".cargo", "bin"),
|
|
643
|
+
env.CARGO_HOME ? path6.join(env.CARGO_HOME, "bin") : void 0
|
|
644
|
+
];
|
|
645
|
+
return candidates.filter((entry) => Boolean(entry && existsSync(entry)));
|
|
646
|
+
}
|
|
647
|
+
function buildToolchainPrepend(existingPath, toolchainBins, executableExists = hasExecutable) {
|
|
648
|
+
const prepend = [];
|
|
649
|
+
for (const binDir of toolchainBins) {
|
|
650
|
+
const externalStellarDir = existingPath.find(
|
|
651
|
+
(entry) => entry !== binDir && executableExists(entry, "stellar")
|
|
652
|
+
);
|
|
653
|
+
if (externalStellarDir && executableExists(binDir, "stellar")) {
|
|
654
|
+
prepend.push(externalStellarDir);
|
|
655
|
+
}
|
|
656
|
+
prepend.push(binDir);
|
|
657
|
+
}
|
|
658
|
+
return prepend;
|
|
659
|
+
}
|
|
660
|
+
function resolveSubprocessEnv(overrides = {}) {
|
|
661
|
+
const env = { ...process.env, ...overrides };
|
|
662
|
+
const home = env.HOME ?? os.homedir();
|
|
663
|
+
const existingPath = (env.PATH ?? "").split(path6.delimiter).filter(Boolean);
|
|
664
|
+
const toolchainBins = toolchainBinDirs(home, env);
|
|
665
|
+
const prepend = buildToolchainPrepend(existingPath, toolchainBins);
|
|
666
|
+
env.PATH = uniquePaths([...prepend, ...existingPath]).join(path6.delimiter);
|
|
667
|
+
return env;
|
|
668
|
+
}
|
|
669
|
+
function isCargoBinMissingFromPath(baseEnv = process.env) {
|
|
670
|
+
const home = baseEnv.HOME ?? os.homedir();
|
|
671
|
+
const cargoBin = path6.join(home, ".cargo", "bin", "cargo");
|
|
672
|
+
if (!existsSync(cargoBin)) {
|
|
673
|
+
return false;
|
|
674
|
+
}
|
|
675
|
+
const pathEntries = (baseEnv.PATH ?? "").split(path6.delimiter);
|
|
676
|
+
return !pathEntries.some((entry) => entry === path6.join(home, ".cargo", "bin"));
|
|
677
|
+
}
|
|
678
|
+
|
|
590
679
|
// src/shell/run-command.ts
|
|
591
680
|
async function runCommand(command, args, options = {}) {
|
|
592
681
|
try {
|
|
@@ -595,7 +684,7 @@ async function runCommand(command, args, options = {}) {
|
|
|
595
684
|
}
|
|
596
685
|
const result = await execa(command, args, {
|
|
597
686
|
cwd: options.cwd,
|
|
598
|
-
env: options.env,
|
|
687
|
+
env: resolveSubprocessEnv(options.env ?? {}),
|
|
599
688
|
input: options.input,
|
|
600
689
|
all: true,
|
|
601
690
|
reject: true
|
|
@@ -793,7 +882,7 @@ function validateSourceShape(source) {
|
|
|
793
882
|
}
|
|
794
883
|
|
|
795
884
|
// src/contracts/resolve-contract.ts
|
|
796
|
-
import
|
|
885
|
+
import path7 from "path";
|
|
797
886
|
function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
798
887
|
const contract = config.contracts[contractName];
|
|
799
888
|
if (!contract) {
|
|
@@ -806,8 +895,8 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
|
806
895
|
return {
|
|
807
896
|
name: contractName,
|
|
808
897
|
config: contract,
|
|
809
|
-
sourcePath:
|
|
810
|
-
wasmPath:
|
|
898
|
+
sourcePath: path7.resolve(cwd, contract.path),
|
|
899
|
+
wasmPath: path7.resolve(cwd, contract.wasm)
|
|
811
900
|
};
|
|
812
901
|
}
|
|
813
902
|
|
|
@@ -827,7 +916,7 @@ function resolveDefaultContractName(config) {
|
|
|
827
916
|
// src/contracts/wasm.ts
|
|
828
917
|
import { createHash } from "crypto";
|
|
829
918
|
import { access as access2, readdir as readdir2, readFile as readFile3, stat } from "fs/promises";
|
|
830
|
-
import
|
|
919
|
+
import path8 from "path";
|
|
831
920
|
var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
|
|
832
921
|
var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
|
|
833
922
|
function toCurrentWasmTargetPath(wasmPath) {
|
|
@@ -838,36 +927,88 @@ function toCurrentWasmTargetPath(wasmPath) {
|
|
|
838
927
|
}
|
|
839
928
|
function wasmNotFoundError(configuredWasmPath, options) {
|
|
840
929
|
const migratedPath = options?.migratedPath;
|
|
841
|
-
const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
930
|
+
const cargoTargetDir = process.env.CARGO_TARGET_DIR;
|
|
931
|
+
const hintParts = ["Run caatinga build before deploy or generate."];
|
|
932
|
+
if (migratedPath !== void 0) {
|
|
933
|
+
hintParts.push(
|
|
934
|
+
`Soroban builds use the "${CURRENT_RUST_WASM_TARGET}" target.`,
|
|
935
|
+
`Update wasm in caatinga.config.ts to "${toConfigRelativeWasmPath(migratedPath)}" or an equivalent path under target/${CURRENT_RUST_WASM_TARGET}/release/.`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
if (cargoTargetDir) {
|
|
939
|
+
hintParts.push(
|
|
940
|
+
`CARGO_TARGET_DIR is set to "${cargoTargetDir}"; the WASM may be under that directory instead of the configured path. Unset CARGO_TARGET_DIR or update wasm in caatinga.config.ts.`
|
|
941
|
+
);
|
|
942
|
+
}
|
|
846
943
|
return new CaatingaError(
|
|
847
944
|
`WASM output was not found at ${configuredWasmPath}.`,
|
|
848
945
|
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
849
|
-
|
|
946
|
+
hintParts.join(" ")
|
|
850
947
|
);
|
|
851
948
|
}
|
|
852
949
|
function toConfigRelativeWasmPath(absoluteWasmPath) {
|
|
853
|
-
const relative =
|
|
854
|
-
return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(
|
|
950
|
+
const relative = path8.relative(process.cwd(), absoluteWasmPath);
|
|
951
|
+
return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(path8.sep).join("/")}`;
|
|
952
|
+
}
|
|
953
|
+
function wasmFileName(configuredWasmPath) {
|
|
954
|
+
return path8.basename(configuredWasmPath);
|
|
955
|
+
}
|
|
956
|
+
function buildAlternateWasmCandidates(configuredWasmPath, options) {
|
|
957
|
+
const fileName = wasmFileName(configuredWasmPath);
|
|
958
|
+
const candidates = [];
|
|
959
|
+
const seen = /* @__PURE__ */ new Set();
|
|
960
|
+
function addCandidate(candidate) {
|
|
961
|
+
const resolved = path8.resolve(candidate);
|
|
962
|
+
if (seen.has(resolved)) {
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
seen.add(resolved);
|
|
966
|
+
candidates.push(resolved);
|
|
967
|
+
}
|
|
968
|
+
const cargoTargetDir = process.env.CARGO_TARGET_DIR;
|
|
969
|
+
if (cargoTargetDir) {
|
|
970
|
+
addCandidate(path8.join(cargoTargetDir, CURRENT_RUST_WASM_TARGET, "release", fileName));
|
|
971
|
+
addCandidate(path8.join(cargoTargetDir, LEGACY_RUST_WASM_TARGET, "release", fileName));
|
|
972
|
+
}
|
|
973
|
+
if (options?.sourcePath) {
|
|
974
|
+
addCandidate(path8.join(options.sourcePath, "target", CURRENT_RUST_WASM_TARGET, "release", fileName));
|
|
975
|
+
addCandidate(path8.join(options.sourcePath, "target", LEGACY_RUST_WASM_TARGET, "release", fileName));
|
|
976
|
+
}
|
|
977
|
+
return candidates;
|
|
855
978
|
}
|
|
856
|
-
async function
|
|
979
|
+
async function firstExistingPath(paths) {
|
|
980
|
+
for (const candidate of paths) {
|
|
981
|
+
try {
|
|
982
|
+
await access2(candidate);
|
|
983
|
+
return candidate;
|
|
984
|
+
} catch {
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return void 0;
|
|
989
|
+
}
|
|
990
|
+
async function resolveWasmArtifactPath(configuredWasmPath, options) {
|
|
991
|
+
const resolvedConfiguredPath = path8.resolve(configuredWasmPath);
|
|
857
992
|
try {
|
|
858
|
-
await access2(
|
|
859
|
-
return
|
|
993
|
+
await access2(resolvedConfiguredPath);
|
|
994
|
+
return resolvedConfiguredPath;
|
|
860
995
|
} catch {
|
|
861
|
-
const currentTargetPath = toCurrentWasmTargetPath(
|
|
862
|
-
if (currentTargetPath
|
|
863
|
-
|
|
996
|
+
const currentTargetPath = toCurrentWasmTargetPath(resolvedConfiguredPath);
|
|
997
|
+
if (currentTargetPath !== resolvedConfiguredPath) {
|
|
998
|
+
const migratedPath = await firstExistingPath([currentTargetPath]);
|
|
999
|
+
if (migratedPath) {
|
|
1000
|
+
return migratedPath;
|
|
1001
|
+
}
|
|
864
1002
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1003
|
+
const alternatePath = await firstExistingPath(
|
|
1004
|
+
buildAlternateWasmCandidates(resolvedConfiguredPath, options)
|
|
1005
|
+
);
|
|
1006
|
+
if (alternatePath) {
|
|
1007
|
+
return alternatePath;
|
|
870
1008
|
}
|
|
1009
|
+
throw wasmNotFoundError(resolvedConfiguredPath, {
|
|
1010
|
+
migratedPath: currentTargetPath !== resolvedConfiguredPath ? currentTargetPath : void 0
|
|
1011
|
+
});
|
|
871
1012
|
}
|
|
872
1013
|
}
|
|
873
1014
|
async function hashWasm(wasmPath) {
|
|
@@ -884,7 +1025,7 @@ async function getNewestMtimeInDirectory(directory) {
|
|
|
884
1025
|
async function walk(dir) {
|
|
885
1026
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
886
1027
|
for (const entry of entries) {
|
|
887
|
-
const entryPath =
|
|
1028
|
+
const entryPath = path8.join(dir, entry.name);
|
|
888
1029
|
if (entry.isDirectory()) {
|
|
889
1030
|
await walk(entryPath);
|
|
890
1031
|
continue;
|
|
@@ -900,7 +1041,7 @@ async function getNewestMtimeInDirectory(directory) {
|
|
|
900
1041
|
return newest > 0 ? newest : void 0;
|
|
901
1042
|
}
|
|
902
1043
|
async function isWasmOlderThanSources(input) {
|
|
903
|
-
const srcDir =
|
|
1044
|
+
const srcDir = path8.join(input.contractPath, "src");
|
|
904
1045
|
const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
|
|
905
1046
|
if (newestSourceMtime === void 0) {
|
|
906
1047
|
return false;
|
|
@@ -962,7 +1103,9 @@ async function buildContract(options) {
|
|
|
962
1103
|
}
|
|
963
1104
|
throw error;
|
|
964
1105
|
}
|
|
965
|
-
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath
|
|
1106
|
+
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
|
|
1107
|
+
sourcePath: contract.sourcePath
|
|
1108
|
+
});
|
|
966
1109
|
return {
|
|
967
1110
|
contract: {
|
|
968
1111
|
...contract,
|
|
@@ -973,7 +1116,7 @@ async function buildContract(options) {
|
|
|
973
1116
|
}
|
|
974
1117
|
|
|
975
1118
|
// src/contracts/deploy-contract.ts
|
|
976
|
-
import
|
|
1119
|
+
import path9 from "path";
|
|
977
1120
|
|
|
978
1121
|
// src/contracts/dependency-graph.ts
|
|
979
1122
|
function buildDependencyGraph(contracts) {
|
|
@@ -1030,6 +1173,9 @@ function assertSafeSourceAccount(source) {
|
|
|
1030
1173
|
}
|
|
1031
1174
|
return source;
|
|
1032
1175
|
}
|
|
1176
|
+
function resolveCliSource(explicit) {
|
|
1177
|
+
return assertSafeSourceAccount(explicit ?? process.env.CAATINGA_SOURCE ?? "alice");
|
|
1178
|
+
}
|
|
1033
1179
|
|
|
1034
1180
|
// src/contracts/deploy-contract.ts
|
|
1035
1181
|
function toSnakeCaseFlag(key) {
|
|
@@ -1052,7 +1198,9 @@ async function deployContract(options) {
|
|
|
1052
1198
|
const network = resolveNetwork(options.config, options.networkName);
|
|
1053
1199
|
const source = assertSafeSourceAccount(options.source);
|
|
1054
1200
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga deploy.");
|
|
1055
|
-
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath
|
|
1201
|
+
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath, {
|
|
1202
|
+
sourcePath: contract.sourcePath
|
|
1203
|
+
});
|
|
1056
1204
|
const contractWithWasm = {
|
|
1057
1205
|
...contract,
|
|
1058
1206
|
wasmPath
|
|
@@ -1074,7 +1222,7 @@ async function deployContract(options) {
|
|
|
1074
1222
|
contract: contractWithWasm,
|
|
1075
1223
|
network,
|
|
1076
1224
|
contractId: existing.contractId,
|
|
1077
|
-
artifactsPath:
|
|
1225
|
+
artifactsPath: path9.resolve(cwd, "caatinga.artifacts.json"),
|
|
1078
1226
|
output: "",
|
|
1079
1227
|
skipped: true,
|
|
1080
1228
|
staleWasmWarning
|
|
@@ -1351,13 +1499,13 @@ async function deployContractGraph(options) {
|
|
|
1351
1499
|
|
|
1352
1500
|
// src/contracts/generate-bindings.ts
|
|
1353
1501
|
import { access as access3, mkdir as mkdir2, unlink } from "fs/promises";
|
|
1354
|
-
import
|
|
1502
|
+
import path10 from "path";
|
|
1355
1503
|
function toBindingImportPath(bindingsOutput, contractName) {
|
|
1356
|
-
const normalized = bindingsOutput.replace(/^\.\//, "").split(
|
|
1357
|
-
return `./${
|
|
1504
|
+
const normalized = bindingsOutput.replace(/^\.\//, "").split(path10.sep).join("/");
|
|
1505
|
+
return `./${path10.posix.join(normalized, contractName, "src", "index.js")}`;
|
|
1358
1506
|
}
|
|
1359
1507
|
async function removeLegacyBindingStub(cwd, bindingsOutput, contractName) {
|
|
1360
|
-
const legacyPath =
|
|
1508
|
+
const legacyPath = path10.resolve(cwd, bindingsOutput, `${contractName}.ts`);
|
|
1361
1509
|
try {
|
|
1362
1510
|
await access3(legacyPath);
|
|
1363
1511
|
await unlink(legacyPath);
|
|
@@ -1386,7 +1534,7 @@ async function generateBindings(options) {
|
|
|
1386
1534
|
);
|
|
1387
1535
|
}
|
|
1388
1536
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.");
|
|
1389
|
-
const outputDir =
|
|
1537
|
+
const outputDir = path10.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
|
|
1390
1538
|
await mkdir2(outputDir, { recursive: true });
|
|
1391
1539
|
const result = await runCommand("stellar", [
|
|
1392
1540
|
"contract",
|
|
@@ -1460,8 +1608,8 @@ async function generateBindingsGraph(options) {
|
|
|
1460
1608
|
return { network, results };
|
|
1461
1609
|
}
|
|
1462
1610
|
|
|
1463
|
-
// src/contracts/invoke-
|
|
1464
|
-
var
|
|
1611
|
+
// src/contracts/invoke-target.ts
|
|
1612
|
+
var READ_CALL_FAILURE_REGEX = /this is a read call|read-only/i;
|
|
1465
1613
|
function parseInvokeTarget(target) {
|
|
1466
1614
|
const [contractName, method, extra] = target.split(".");
|
|
1467
1615
|
if (!contractName || !method || extra) {
|
|
@@ -1473,6 +1621,29 @@ function parseInvokeTarget(target) {
|
|
|
1473
1621
|
}
|
|
1474
1622
|
return { contractName, method };
|
|
1475
1623
|
}
|
|
1624
|
+
function buildReadCallHint(target, networkName) {
|
|
1625
|
+
return [
|
|
1626
|
+
`"${target.contractName}.${target.method}" is a read-only contract method.`,
|
|
1627
|
+
"Simulate without signing instead:",
|
|
1628
|
+
` npx caatinga read ${target.contractName}.${target.method} --network ${networkName}`,
|
|
1629
|
+
" (--source is optional; Caatinga resolves CAATINGA_SOURCE or defaults to alice)",
|
|
1630
|
+
"In browser code, use:",
|
|
1631
|
+
` client.contract("${target.contractName}").read("${target.method}")`,
|
|
1632
|
+
` client.contract("${target.contractName}").simulate("${target.method}")`,
|
|
1633
|
+
"Pass method args as the second argument to read() when the contract method takes parameters.",
|
|
1634
|
+
"Only pass Stellar CLI --force when you intentionally need a signed read simulation."
|
|
1635
|
+
].join("\n");
|
|
1636
|
+
}
|
|
1637
|
+
function isReadCallFailure(error) {
|
|
1638
|
+
if (!(error instanceof CaatingaError) || error.code !== CaatingaErrorCode.INVOKE_FAILED) {
|
|
1639
|
+
return false;
|
|
1640
|
+
}
|
|
1641
|
+
return READ_CALL_FAILURE_REGEX.test(`${error.message}
|
|
1642
|
+
${error.hint ?? ""}`);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/contracts/invoke-contract.ts
|
|
1646
|
+
var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
|
|
1476
1647
|
async function invokeContract(options) {
|
|
1477
1648
|
const cwd = options.cwd ?? process.cwd();
|
|
1478
1649
|
const network = resolveNetwork(options.config, options.networkName);
|
|
@@ -1506,6 +1677,14 @@ async function invokeContract(options) {
|
|
|
1506
1677
|
failureCode: CaatingaErrorCode.INVOKE_FAILED
|
|
1507
1678
|
});
|
|
1508
1679
|
} catch (error) {
|
|
1680
|
+
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && isReadCallFailure(error)) {
|
|
1681
|
+
throw new CaatingaError(
|
|
1682
|
+
error.message,
|
|
1683
|
+
error.code,
|
|
1684
|
+
buildReadCallHint(target, network.name),
|
|
1685
|
+
error
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1509
1688
|
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
|
|
1510
1689
|
${error.hint ?? ""}`)) {
|
|
1511
1690
|
throw new CaatingaError(
|
|
@@ -1531,9 +1710,48 @@ ${error.hint ?? ""}`)) {
|
|
|
1531
1710
|
};
|
|
1532
1711
|
}
|
|
1533
1712
|
|
|
1713
|
+
// src/contracts/read-contract.ts
|
|
1714
|
+
async function readContract(options) {
|
|
1715
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1716
|
+
const network = resolveNetwork(options.config, options.networkName);
|
|
1717
|
+
const target = parseInvokeTarget(options.target);
|
|
1718
|
+
const artifacts = await readArtifacts(cwd);
|
|
1719
|
+
const contractArtifact = artifacts.networks[network.name]?.contracts[target.contractName];
|
|
1720
|
+
if (!contractArtifact) {
|
|
1721
|
+
throw new CaatingaError(
|
|
1722
|
+
`No deployed artifact found for "${target.contractName}" on "${network.name}".`,
|
|
1723
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
1724
|
+
"Run caatinga deploy for this contract and network before reading it."
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
await checkBinary("stellar", "Install Stellar CLI before running caatinga read.");
|
|
1728
|
+
const stellarArgs = [
|
|
1729
|
+
"contract",
|
|
1730
|
+
"invoke",
|
|
1731
|
+
"--id",
|
|
1732
|
+
contractArtifact.contractId,
|
|
1733
|
+
"--source-account",
|
|
1734
|
+
resolveCliSource(options.source),
|
|
1735
|
+
"--send=no",
|
|
1736
|
+
...buildStellarNetworkArgs(network),
|
|
1737
|
+
"--",
|
|
1738
|
+
target.method,
|
|
1739
|
+
...options.args ?? []
|
|
1740
|
+
];
|
|
1741
|
+
const result = await runCommand("stellar", stellarArgs, {
|
|
1742
|
+
cwd,
|
|
1743
|
+
failureCode: CaatingaErrorCode.INVOKE_FAILED
|
|
1744
|
+
});
|
|
1745
|
+
return {
|
|
1746
|
+
target,
|
|
1747
|
+
network,
|
|
1748
|
+
result: result.stdout || result.all
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1534
1752
|
// src/templates/create-project-from-template.ts
|
|
1535
1753
|
import { cp, mkdir as mkdir3, readFile as readFile4, readdir as readdir3, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
1536
|
-
import
|
|
1754
|
+
import path11 from "path";
|
|
1537
1755
|
import { z as z7 } from "zod";
|
|
1538
1756
|
|
|
1539
1757
|
// src/templates/template-manifest.schema.ts
|
|
@@ -1609,8 +1827,8 @@ var TEMPLATE_COPY_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
|
1609
1827
|
".git"
|
|
1610
1828
|
]);
|
|
1611
1829
|
async function createProjectFromTemplate(options) {
|
|
1612
|
-
const targetDir =
|
|
1613
|
-
const templateDir =
|
|
1830
|
+
const targetDir = path11.resolve(options.targetDir);
|
|
1831
|
+
const templateDir = path11.resolve(options.templateDir);
|
|
1614
1832
|
try {
|
|
1615
1833
|
await stat2(templateDir);
|
|
1616
1834
|
} catch {
|
|
@@ -1648,7 +1866,7 @@ async function ensureArtifacts(targetDir, projectName) {
|
|
|
1648
1866
|
}
|
|
1649
1867
|
}
|
|
1650
1868
|
async function readTemplateManifest(templateDir) {
|
|
1651
|
-
const manifestPath =
|
|
1869
|
+
const manifestPath = path11.join(templateDir, "caatinga.template.json");
|
|
1652
1870
|
try {
|
|
1653
1871
|
const rawManifest = await readFile4(manifestPath, "utf8");
|
|
1654
1872
|
const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
|
|
@@ -1685,7 +1903,7 @@ async function readTemplateManifest(templateDir) {
|
|
|
1685
1903
|
async function replaceTemplateVariables(dir, projectName) {
|
|
1686
1904
|
const entries = await readdir3(dir);
|
|
1687
1905
|
await Promise.all(entries.map(async (entry) => {
|
|
1688
|
-
const entryPath =
|
|
1906
|
+
const entryPath = path11.join(dir, entry);
|
|
1689
1907
|
const entryStat = await stat2(entryPath);
|
|
1690
1908
|
if (entryStat.isDirectory()) {
|
|
1691
1909
|
await replaceTemplateVariables(entryPath, projectName);
|
|
@@ -1699,15 +1917,15 @@ async function replaceTemplateVariables(dir, projectName) {
|
|
|
1699
1917
|
}));
|
|
1700
1918
|
}
|
|
1701
1919
|
function shouldCopyTemplateEntry(templateDir, source, userFilter) {
|
|
1702
|
-
const relativePath =
|
|
1920
|
+
const relativePath = path11.relative(templateDir, source);
|
|
1703
1921
|
if (!relativePath || relativePath === ".") {
|
|
1704
1922
|
return true;
|
|
1705
1923
|
}
|
|
1706
|
-
const normalizedPath = relativePath.split(
|
|
1924
|
+
const normalizedPath = relativePath.split(path11.sep).join("/");
|
|
1707
1925
|
if (userFilter && !userFilter(normalizedPath)) {
|
|
1708
1926
|
return false;
|
|
1709
1927
|
}
|
|
1710
|
-
return !relativePath.split(
|
|
1928
|
+
return !relativePath.split(path11.sep).some((segment) => TEMPLATE_COPY_EXCLUDED_DIRS.has(segment));
|
|
1711
1929
|
}
|
|
1712
1930
|
function isTextTemplateFile(filePath) {
|
|
1713
1931
|
return [
|
|
@@ -1719,21 +1937,21 @@ function isTextTemplateFile(filePath) {
|
|
|
1719
1937
|
".tsx",
|
|
1720
1938
|
".css",
|
|
1721
1939
|
".html"
|
|
1722
|
-
].includes(
|
|
1940
|
+
].includes(path11.extname(filePath));
|
|
1723
1941
|
}
|
|
1724
1942
|
|
|
1725
1943
|
// src/scaffold/create-zk-project.ts
|
|
1726
1944
|
import { cp as cp2, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
1727
|
-
import { existsSync } from "fs";
|
|
1728
|
-
import
|
|
1945
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1946
|
+
import path12 from "path";
|
|
1729
1947
|
import { fileURLToPath } from "url";
|
|
1730
|
-
var moduleDir = typeof __dirname === "string" ? __dirname :
|
|
1948
|
+
var moduleDir = typeof __dirname === "string" ? __dirname : path12.dirname(fileURLToPath(import.meta.url));
|
|
1731
1949
|
function scaffoldRoot() {
|
|
1732
1950
|
const candidates = [
|
|
1733
|
-
|
|
1734
|
-
|
|
1951
|
+
path12.resolve(moduleDir, "../../scaffolds"),
|
|
1952
|
+
path12.resolve(moduleDir, "../scaffolds")
|
|
1735
1953
|
];
|
|
1736
|
-
const found = candidates.find((candidate) =>
|
|
1954
|
+
const found = candidates.find((candidate) => existsSync2(candidate));
|
|
1737
1955
|
return found ?? candidates[0];
|
|
1738
1956
|
}
|
|
1739
1957
|
function configSource(projectName) {
|
|
@@ -1777,7 +1995,8 @@ function packageJsonSource(projectName) {
|
|
|
1777
1995
|
"zk:build": "caatinga zk build main",
|
|
1778
1996
|
"zk:prove": "caatinga zk prove main",
|
|
1779
1997
|
build: "caatinga build verifier",
|
|
1780
|
-
deploy: "caatinga deploy verifier"
|
|
1998
|
+
deploy: "caatinga deploy verifier --network testnet --source ${CAATINGA_SOURCE:-alice}",
|
|
1999
|
+
doctor: "caatinga doctor --network testnet"
|
|
1781
2000
|
},
|
|
1782
2001
|
devDependencies: {
|
|
1783
2002
|
"@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
|
|
@@ -1797,7 +2016,7 @@ Minimal Caatinga ZK project.
|
|
|
1797
2016
|
npm install
|
|
1798
2017
|
npx caatinga zk build main
|
|
1799
2018
|
npx caatinga build verifier
|
|
1800
|
-
npx caatinga deploy verifier --network testnet
|
|
2019
|
+
npx caatinga deploy verifier --network testnet --source <identity>
|
|
1801
2020
|
npx caatinga zk prove main
|
|
1802
2021
|
\`\`\`
|
|
1803
2022
|
|
|
@@ -1805,25 +2024,25 @@ Replace \`circuits/main.circom\` with your circuit. Keep the entry point named \
|
|
|
1805
2024
|
`;
|
|
1806
2025
|
}
|
|
1807
2026
|
async function createZkProject(options) {
|
|
1808
|
-
const targetDir =
|
|
2027
|
+
const targetDir = path12.resolve(options.targetDir);
|
|
1809
2028
|
const force = options.force ?? false;
|
|
1810
2029
|
const projectFiles = options.projectFiles ?? true;
|
|
1811
2030
|
await mkdir4(targetDir, { recursive: true });
|
|
1812
2031
|
if (projectFiles) {
|
|
1813
2032
|
await Promise.all([
|
|
1814
|
-
writeFile4(
|
|
1815
|
-
writeFile4(
|
|
1816
|
-
writeFile4(
|
|
1817
|
-
writeFile4(
|
|
2033
|
+
writeFile4(path12.join(targetDir, "caatinga.config.ts"), configSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2034
|
+
writeFile4(path12.join(targetDir, "package.json"), packageJsonSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2035
|
+
writeFile4(path12.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2036
|
+
writeFile4(path12.join(targetDir, "README.md"), readmeSource(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
|
|
1818
2037
|
]);
|
|
1819
2038
|
}
|
|
1820
|
-
await mkdir4(
|
|
1821
|
-
await cp2(
|
|
2039
|
+
await mkdir4(path12.join(targetDir, "contracts"), { recursive: true });
|
|
2040
|
+
await cp2(path12.join(scaffoldRoot(), "zk-circuit-stub"), path12.join(targetDir, "circuits"), {
|
|
1822
2041
|
recursive: true,
|
|
1823
2042
|
force,
|
|
1824
2043
|
errorOnExist: !force
|
|
1825
2044
|
});
|
|
1826
|
-
await cp2(
|
|
2045
|
+
await cp2(path12.join(scaffoldRoot(), "zk-verifier"), path12.join(targetDir, "contracts", "verifier"), {
|
|
1827
2046
|
recursive: true,
|
|
1828
2047
|
force,
|
|
1829
2048
|
errorOnExist: !force
|
|
@@ -1834,6 +2053,109 @@ async function createZkProject(options) {
|
|
|
1834
2053
|
return { targetDir };
|
|
1835
2054
|
}
|
|
1836
2055
|
|
|
2056
|
+
// src/scaffold/create-minimal-project.ts
|
|
2057
|
+
import { cp as cp3, mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
|
|
2058
|
+
import { existsSync as existsSync3 } from "fs";
|
|
2059
|
+
import path13 from "path";
|
|
2060
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2061
|
+
var moduleDir2 = typeof __dirname === "string" ? __dirname : path13.dirname(fileURLToPath2(import.meta.url));
|
|
2062
|
+
function scaffoldRoot2() {
|
|
2063
|
+
const candidates = [
|
|
2064
|
+
path13.resolve(moduleDir2, "../../scaffolds"),
|
|
2065
|
+
path13.resolve(moduleDir2, "../scaffolds")
|
|
2066
|
+
];
|
|
2067
|
+
const found = candidates.find((candidate) => existsSync3(candidate));
|
|
2068
|
+
return found ?? candidates[0];
|
|
2069
|
+
}
|
|
2070
|
+
function configSource2(projectName) {
|
|
2071
|
+
return `import { defineConfig } from "@caatinga/core";
|
|
2072
|
+
|
|
2073
|
+
export default defineConfig({
|
|
2074
|
+
project: "${projectName}",
|
|
2075
|
+
defaultNetwork: "testnet",
|
|
2076
|
+
contracts: {
|
|
2077
|
+
app: {
|
|
2078
|
+
path: "./contracts/app",
|
|
2079
|
+
wasm: "./contracts/app/target/wasm32v1-none/release/app.wasm"
|
|
2080
|
+
}
|
|
2081
|
+
},
|
|
2082
|
+
networks: {
|
|
2083
|
+
testnet: {
|
|
2084
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
2085
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
`;
|
|
2090
|
+
}
|
|
2091
|
+
function packageJsonSource2(projectName) {
|
|
2092
|
+
return `${JSON.stringify({
|
|
2093
|
+
name: projectName,
|
|
2094
|
+
version: "0.1.0",
|
|
2095
|
+
private: true,
|
|
2096
|
+
type: "module",
|
|
2097
|
+
scripts: {
|
|
2098
|
+
build: "caatinga build app",
|
|
2099
|
+
deploy: "caatinga deploy app --network testnet --source ${CAATINGA_SOURCE:-alice}",
|
|
2100
|
+
doctor: "caatinga doctor --network testnet",
|
|
2101
|
+
"read:hello": "caatinga read app.hello --network testnet --source ${CAATINGA_SOURCE:-alice}",
|
|
2102
|
+
"read:version": "caatinga read app.version --network testnet --source ${CAATINGA_SOURCE:-alice}"
|
|
2103
|
+
},
|
|
2104
|
+
devDependencies: {
|
|
2105
|
+
"@caatinga/cli": `^${CAATINGA_CORE_VERSION}`,
|
|
2106
|
+
"@caatinga/core": `^${CAATINGA_CORE_VERSION}`
|
|
2107
|
+
}
|
|
2108
|
+
}, null, 2)}
|
|
2109
|
+
`;
|
|
2110
|
+
}
|
|
2111
|
+
function readmeSource2(projectName) {
|
|
2112
|
+
return `# ${projectName}
|
|
2113
|
+
|
|
2114
|
+
Minimal Caatinga project with a Soroban contract stub (no frontend template).
|
|
2115
|
+
|
|
2116
|
+
## Workflow
|
|
2117
|
+
|
|
2118
|
+
\`\`\`bash
|
|
2119
|
+
npm install
|
|
2120
|
+
npx caatinga doctor
|
|
2121
|
+
npx caatinga build app
|
|
2122
|
+
npx caatinga deploy app --network testnet --source <identity>
|
|
2123
|
+
npx caatinga read app.version --network testnet
|
|
2124
|
+
npx caatinga read app.hello --network testnet
|
|
2125
|
+
\`\`\`
|
|
2126
|
+
|
|
2127
|
+
## Contract
|
|
2128
|
+
|
|
2129
|
+
- \`hello()\` \u2014 read-only; returns Soroban Symbol \`hello\`
|
|
2130
|
+
- \`version()\` \u2014 read-only; returns \`1\`
|
|
2131
|
+
|
|
2132
|
+
Use \`caatinga read\` for read-only methods. Use \`caatinga invoke\` only after you add state-changing methods to the contract.
|
|
2133
|
+
|
|
2134
|
+
Soroban \`Symbol\` parameters are generated as TypeScript \`string\` values with host-specific restrictions \u2014 see the Caatinga docs on [Soroban types](https://github.com/caatinga/caatinga/blob/main/docs/soroban-types.md).
|
|
2135
|
+
|
|
2136
|
+
Edit \`contracts/app/src/lib.rs\` to customize the contract. Add a frontend later with \`@caatinga/client\` and your chosen UI stack.
|
|
2137
|
+
`;
|
|
2138
|
+
}
|
|
2139
|
+
async function createMinimalProject(options) {
|
|
2140
|
+
const targetDir = path13.resolve(options.targetDir);
|
|
2141
|
+
const force = options.force ?? false;
|
|
2142
|
+
await mkdir5(targetDir, { recursive: true });
|
|
2143
|
+
await Promise.all([
|
|
2144
|
+
writeFile5(path13.join(targetDir, "caatinga.config.ts"), configSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2145
|
+
writeFile5(path13.join(targetDir, "package.json"), packageJsonSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2146
|
+
writeFile5(path13.join(targetDir, ".gitignore"), "node_modules\n.artifacts\ntarget\n", { encoding: "utf8", flag: force ? "w" : "wx" }),
|
|
2147
|
+
writeFile5(path13.join(targetDir, "README.md"), readmeSource2(options.projectName), { encoding: "utf8", flag: force ? "w" : "wx" })
|
|
2148
|
+
]);
|
|
2149
|
+
await mkdir5(path13.join(targetDir, "contracts"), { recursive: true });
|
|
2150
|
+
await cp3(path13.join(scaffoldRoot2(), "soroban-contract-stub"), path13.join(targetDir, "contracts", "app"), {
|
|
2151
|
+
recursive: true,
|
|
2152
|
+
force,
|
|
2153
|
+
errorOnExist: !force
|
|
2154
|
+
});
|
|
2155
|
+
await writeArtifacts(createInitialArtifacts(options.projectName, { networks: ["testnet"] }), targetDir);
|
|
2156
|
+
return { targetDir };
|
|
2157
|
+
}
|
|
2158
|
+
|
|
1837
2159
|
// src/ci/is-transient-testnet-smoke-failure.ts
|
|
1838
2160
|
var NO_RETRY_CAATINGA_SUBSTRINGS = [
|
|
1839
2161
|
"CAATINGA_UNSUPPORTED_CLI_VERSION",
|
|
@@ -1862,16 +2184,19 @@ export {
|
|
|
1862
2184
|
CaatingaConfigSchema,
|
|
1863
2185
|
CaatingaError,
|
|
1864
2186
|
CaatingaErrorCode,
|
|
2187
|
+
READ_CALL_FAILURE_REGEX,
|
|
1865
2188
|
STELLAR_CLI_LAST_TESTED_VERSION,
|
|
1866
2189
|
STELLAR_CLI_MIN_VERSION,
|
|
1867
2190
|
TemplateManifestSchema,
|
|
1868
2191
|
WELL_KNOWN_NETWORKS,
|
|
1869
2192
|
buildContract,
|
|
1870
2193
|
buildDependencyGraph,
|
|
2194
|
+
buildReadCallHint,
|
|
1871
2195
|
checkBinary,
|
|
1872
2196
|
checkStellarCliVersion,
|
|
1873
2197
|
collectProjectStatus,
|
|
1874
2198
|
createInitialArtifacts,
|
|
2199
|
+
createMinimalProject,
|
|
1875
2200
|
createProjectFromTemplate,
|
|
1876
2201
|
createZkProject,
|
|
1877
2202
|
defineConfig,
|
|
@@ -1884,6 +2209,8 @@ export {
|
|
|
1884
2209
|
generateBindings,
|
|
1885
2210
|
generateBindingsGraph,
|
|
1886
2211
|
invokeContract,
|
|
2212
|
+
isCargoBinMissingFromPath,
|
|
2213
|
+
isReadCallFailure,
|
|
1887
2214
|
isTransientTestnetSmokeFailure,
|
|
1888
2215
|
loadConfig,
|
|
1889
2216
|
parseContractId,
|
|
@@ -1891,11 +2218,13 @@ export {
|
|
|
1891
2218
|
parseStellarCliVersion,
|
|
1892
2219
|
readArtifacts,
|
|
1893
2220
|
readBindingMarker,
|
|
2221
|
+
readContract,
|
|
1894
2222
|
resolveContract,
|
|
1895
2223
|
resolveDefaultContractName,
|
|
1896
2224
|
resolveDeployArgs,
|
|
1897
2225
|
resolveDeployOrder,
|
|
1898
2226
|
resolveNetwork,
|
|
2227
|
+
resolveSubprocessEnv,
|
|
1899
2228
|
runCommand,
|
|
1900
2229
|
toCaatingaError,
|
|
1901
2230
|
updateArtifact,
|