@caatinga/core 0.2.1 → 0.2.3
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/README.md +1 -1
- package/dist/browser-CuET2GqM.d.cts +174 -0
- package/dist/browser-CuET2GqM.d.ts +174 -0
- package/dist/browser-DUcSR5D3.d.cts +173 -0
- package/dist/browser-DUcSR5D3.d.ts +173 -0
- package/dist/browser.cjs +107 -0
- package/dist/browser.d.cts +2 -0
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +10 -0
- package/dist/chunk-EKHNKCJV.js +79 -0
- package/dist/chunk-GMABXVEY.js +78 -0
- package/dist/index.cjs +400 -153
- package/dist/index.d.cts +50 -177
- package/dist/index.d.ts +50 -177
- package/dist/index.js +401 -223
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -63,6 +63,7 @@ __export(index_exports, {
|
|
|
63
63
|
runCommand: () => runCommand,
|
|
64
64
|
toCaatingaError: () => toCaatingaError,
|
|
65
65
|
updateArtifact: () => updateArtifact,
|
|
66
|
+
validateSourceShape: () => validateSourceShape,
|
|
66
67
|
writeArtifacts: () => writeArtifacts
|
|
67
68
|
});
|
|
68
69
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -92,6 +93,7 @@ var CaatingaErrorCode = {
|
|
|
92
93
|
CONTRACT_DEPENDENCY_NOT_FOUND: "CAATINGA_CONTRACT_DEPENDENCY_NOT_FOUND",
|
|
93
94
|
CONTRACT_DEPENDENCY_CYCLE: "CAATINGA_CONTRACT_DEPENDENCY_CYCLE",
|
|
94
95
|
CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND: "CAATINGA_CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND",
|
|
96
|
+
DEPENDENCY_CONTRACT_NOT_FOUND: "CAATINGA_DEPENDENCY_CONTRACT_NOT_FOUND",
|
|
95
97
|
DEPLOY_ARG_PLACEHOLDER_INVALID: "CAATINGA_DEPLOY_ARG_PLACEHOLDER_INVALID",
|
|
96
98
|
DEPLOY_ARG_PLACEHOLDER_UNRESOLVED: "CAATINGA_DEPLOY_ARG_PLACEHOLDER_UNRESOLVED",
|
|
97
99
|
BINDING_CLIENT_NOT_FOUND: "CAATINGA_BINDING_CLIENT_NOT_FOUND",
|
|
@@ -101,14 +103,20 @@ var CaatingaErrorCode = {
|
|
|
101
103
|
XDR_SIGN_FAILED: "CAATINGA_XDR_SIGN_FAILED",
|
|
102
104
|
XDR_SUBMIT_FAILED: "CAATINGA_XDR_SUBMIT_FAILED",
|
|
103
105
|
XDR_RESULT_FAILED: "CAATINGA_XDR_RESULT_FAILED",
|
|
106
|
+
READ_RESULT_MISSING: "CAATINGA_READ_RESULT_MISSING",
|
|
104
107
|
WALLET_NOT_CONNECTED: "CAATINGA_WALLET_NOT_CONNECTED",
|
|
108
|
+
WALLET_TIMEOUT: "CAATINGA_WALLET_TIMEOUT",
|
|
105
109
|
SOURCE_ACCOUNT_REQUIRED: "CAATINGA_SOURCE_ACCOUNT_REQUIRED",
|
|
110
|
+
SOURCE_IS_SECRET_KEY: "CAATINGA_SOURCE_IS_SECRET_KEY",
|
|
111
|
+
SOURCE_IS_SEED_PHRASE: "CAATINGA_SOURCE_IS_SEED_PHRASE",
|
|
112
|
+
SOURCE_IS_PUBLIC_KEY: "CAATINGA_SOURCE_IS_PUBLIC_KEY",
|
|
106
113
|
UNSAFE_SOURCE_ACCOUNT: "CAATINGA_UNSAFE_SOURCE_ACCOUNT",
|
|
107
114
|
INVOKE_TARGET_INVALID: "CAATINGA_INVOKE_TARGET_INVALID",
|
|
108
115
|
TEMPLATE_NOT_FOUND: "CAATINGA_TEMPLATE_NOT_FOUND",
|
|
109
116
|
INVALID_TEMPLATE_MANIFEST: "CAATINGA_INVALID_TEMPLATE_MANIFEST",
|
|
110
117
|
TEMPLATE_MANIFEST_NOT_FOUND: "CAATINGA_TEMPLATE_MANIFEST_NOT_FOUND",
|
|
111
|
-
TEMPLATE_INCOMPATIBLE: "CAATINGA_TEMPLATE_INCOMPATIBLE"
|
|
118
|
+
TEMPLATE_INCOMPATIBLE: "CAATINGA_TEMPLATE_INCOMPATIBLE",
|
|
119
|
+
DOCTOR_PARTIAL_DEPLOY: "CAATINGA_DOCTOR_PARTIAL_DEPLOY"
|
|
112
120
|
};
|
|
113
121
|
|
|
114
122
|
// src/errors/CaatingaError.ts
|
|
@@ -135,7 +143,7 @@ function toCaatingaError(error) {
|
|
|
135
143
|
}
|
|
136
144
|
|
|
137
145
|
// src/version.ts
|
|
138
|
-
var CAATINGA_CORE_VERSION = "0.2.
|
|
146
|
+
var CAATINGA_CORE_VERSION = "0.2.3";
|
|
139
147
|
|
|
140
148
|
// src/config/config.schema.ts
|
|
141
149
|
var import_zod = require("zod");
|
|
@@ -323,7 +331,7 @@ var import_execa = require("execa");
|
|
|
323
331
|
|
|
324
332
|
// src/stellar-cli/version.ts
|
|
325
333
|
var import_semver = __toESM(require("semver"), 1);
|
|
326
|
-
var STELLAR_CLI_MIN_VERSION = "
|
|
334
|
+
var STELLAR_CLI_MIN_VERSION = "23.0.0";
|
|
327
335
|
var STELLAR_CLI_TESTED_MAX_VERSION = "25.2.0";
|
|
328
336
|
var STELLAR_CLI_SEMVER_REGEX = /\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?)\b/;
|
|
329
337
|
function parseStellarCliVersion(output) {
|
|
@@ -456,6 +464,146 @@ function parseContractId(output) {
|
|
|
456
464
|
return match[0];
|
|
457
465
|
}
|
|
458
466
|
|
|
467
|
+
// src/stellar-cli/build-stellar-network-args.ts
|
|
468
|
+
function matchesWellKnownNetwork(name, config) {
|
|
469
|
+
const known = WELL_KNOWN_NETWORKS[name];
|
|
470
|
+
if (!known) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
return known.rpcUrl === config.rpcUrl && known.networkPassphrase === config.networkPassphrase;
|
|
474
|
+
}
|
|
475
|
+
function buildRpcNetworkArgs(config) {
|
|
476
|
+
return [
|
|
477
|
+
"--rpc-url",
|
|
478
|
+
config.rpcUrl,
|
|
479
|
+
"--network-passphrase",
|
|
480
|
+
config.networkPassphrase
|
|
481
|
+
];
|
|
482
|
+
}
|
|
483
|
+
function buildStellarNetworkArgsFromConfig(config) {
|
|
484
|
+
for (const [name, known] of Object.entries(WELL_KNOWN_NETWORKS)) {
|
|
485
|
+
if (known.rpcUrl === config.rpcUrl && known.networkPassphrase === config.networkPassphrase) {
|
|
486
|
+
return ["--network", name];
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return buildRpcNetworkArgs(config);
|
|
490
|
+
}
|
|
491
|
+
function buildStellarNetworkArgs(network) {
|
|
492
|
+
if (matchesWellKnownNetwork(network.name, network.config)) {
|
|
493
|
+
return ["--network", network.name];
|
|
494
|
+
}
|
|
495
|
+
return buildStellarNetworkArgsFromConfig(network.config);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/stellar-cli/recover-deploy-contract-id.ts
|
|
499
|
+
var TX_HASH_REGEX = /Transaction hash is ([a-f0-9]{64})/i;
|
|
500
|
+
var DEPLOY_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
|
|
501
|
+
var HORIZON_URL_BY_PASSPHRASE = {
|
|
502
|
+
"Test SDF Network ; September 2015": "https://horizon-testnet.stellar.org",
|
|
503
|
+
"Public Global Stellar Network ; September 2015": "https://horizon.stellar.org"
|
|
504
|
+
};
|
|
505
|
+
function isLikelyPublicKeySource(source) {
|
|
506
|
+
return /^G[A-Z2-7]{55}$/.test(source);
|
|
507
|
+
}
|
|
508
|
+
function decimalSaltToHex(salt) {
|
|
509
|
+
return BigInt(salt).toString(16).padStart(64, "0");
|
|
510
|
+
}
|
|
511
|
+
function resolveHorizonUrl(network) {
|
|
512
|
+
const horizonUrl = HORIZON_URL_BY_PASSPHRASE[network.networkPassphrase];
|
|
513
|
+
if (!horizonUrl) {
|
|
514
|
+
throw new CaatingaError(
|
|
515
|
+
`No Horizon URL mapping for network passphrase "${network.networkPassphrase}".`,
|
|
516
|
+
CaatingaErrorCode.NETWORK_NOT_FOUND,
|
|
517
|
+
"Use testnet or mainnet, or extend Caatinga network metadata."
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
return horizonUrl;
|
|
521
|
+
}
|
|
522
|
+
async function fetchCreateContractSalt(horizonUrl, transactionHash, fetchImpl = fetch) {
|
|
523
|
+
const response = await fetchImpl(`${horizonUrl}/transactions/${transactionHash}/operations`);
|
|
524
|
+
if (!response.ok) {
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
const body = await response.json();
|
|
528
|
+
const operation = body._embedded?.records?.find(
|
|
529
|
+
(record) => record.transaction_successful === true && record.type === "invoke_host_function" && record.function === "HostFunctionTypeHostFunctionTypeCreateContract" && typeof record.salt === "string"
|
|
530
|
+
);
|
|
531
|
+
return operation?.salt ?? null;
|
|
532
|
+
}
|
|
533
|
+
async function resolveContractIdFromDeploySalt(options) {
|
|
534
|
+
const saltHex = decimalSaltToHex(options.salt);
|
|
535
|
+
const result = await runCommand("stellar", [
|
|
536
|
+
"contract",
|
|
537
|
+
"id",
|
|
538
|
+
"wasm",
|
|
539
|
+
"--salt",
|
|
540
|
+
saltHex,
|
|
541
|
+
"--source-account",
|
|
542
|
+
options.source,
|
|
543
|
+
...buildStellarNetworkArgsFromConfig(options.network)
|
|
544
|
+
], {
|
|
545
|
+
cwd: options.cwd,
|
|
546
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
547
|
+
skipStellarVersionCheck: true
|
|
548
|
+
});
|
|
549
|
+
return parseContractId(result.all || `${result.stdout}
|
|
550
|
+
${result.stderr}`);
|
|
551
|
+
}
|
|
552
|
+
async function tryRecoverContractIdFromDeployFailure(options) {
|
|
553
|
+
if (!DEPLOY_SIGNING_FAILURE_REGEX.test(options.output)) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
const hashMatch = options.output.match(TX_HASH_REGEX);
|
|
557
|
+
if (!hashMatch) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
const horizonUrl = resolveHorizonUrl(options.network);
|
|
561
|
+
const salt = await fetchCreateContractSalt(horizonUrl, hashMatch[1], options.fetchImpl);
|
|
562
|
+
if (!salt) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
return resolveContractIdFromDeploySalt({
|
|
566
|
+
salt,
|
|
567
|
+
source: options.source,
|
|
568
|
+
network: options.network,
|
|
569
|
+
cwd: options.cwd,
|
|
570
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/contracts/validate-source-shape.ts
|
|
575
|
+
function validateSourceShape(source) {
|
|
576
|
+
if (source.startsWith("S")) {
|
|
577
|
+
return new CaatingaError(
|
|
578
|
+
"Refusing to accept a Stellar secret key as --source.",
|
|
579
|
+
CaatingaErrorCode.SOURCE_IS_SECRET_KEY,
|
|
580
|
+
"Use a Stellar CLI identity alias instead, for example: --source alice"
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (source.trim().includes(" ")) {
|
|
584
|
+
return new CaatingaError(
|
|
585
|
+
"Refusing to accept a seed phrase as --source.",
|
|
586
|
+
CaatingaErrorCode.SOURCE_IS_SEED_PHRASE,
|
|
587
|
+
"Use a Stellar CLI identity alias instead, for example: --source alice"
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
if (isLikelyPublicKeySource(source)) {
|
|
591
|
+
return new CaatingaError(
|
|
592
|
+
`Public account address cannot sign transactions: ${source}`,
|
|
593
|
+
CaatingaErrorCode.SOURCE_IS_PUBLIC_KEY,
|
|
594
|
+
"Use a Stellar CLI identity with a secret key. Example: stellar keys generate alice --fund --network testnet, then --source alice"
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (source.startsWith("G")) {
|
|
598
|
+
return new CaatingaError(
|
|
599
|
+
"Refusing to accept a public account address as --source.",
|
|
600
|
+
CaatingaErrorCode.UNSAFE_SOURCE_ACCOUNT,
|
|
601
|
+
"Use a Stellar CLI identity alias, not a public address. Example: --source alice"
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return void 0;
|
|
605
|
+
}
|
|
606
|
+
|
|
459
607
|
// src/contracts/resolve-contract.ts
|
|
460
608
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
461
609
|
function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
@@ -478,24 +626,98 @@ function resolveContract(config, contractName, cwd = process.cwd()) {
|
|
|
478
626
|
// src/contracts/wasm.ts
|
|
479
627
|
var import_node_crypto = require("crypto");
|
|
480
628
|
var import_promises4 = require("fs/promises");
|
|
481
|
-
|
|
629
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
630
|
+
var LEGACY_RUST_WASM_TARGET = "wasm32-unknown-unknown";
|
|
631
|
+
var CURRENT_RUST_WASM_TARGET = "wasm32v1-none";
|
|
632
|
+
function toCurrentWasmTargetPath(wasmPath) {
|
|
633
|
+
if (!wasmPath.includes(LEGACY_RUST_WASM_TARGET)) {
|
|
634
|
+
return wasmPath;
|
|
635
|
+
}
|
|
636
|
+
return wasmPath.replaceAll(LEGACY_RUST_WASM_TARGET, CURRENT_RUST_WASM_TARGET);
|
|
637
|
+
}
|
|
638
|
+
function wasmNotFoundError(configuredWasmPath, options) {
|
|
639
|
+
const migratedPath = options?.migratedPath;
|
|
640
|
+
const hint = migratedPath === void 0 ? "Run caatinga build before deploy or generate." : [
|
|
641
|
+
"Run caatinga build before deploy or generate.",
|
|
642
|
+
`Soroban builds use the "${CURRENT_RUST_WASM_TARGET}" target.`,
|
|
643
|
+
`Update wasm in caatinga.config.ts to "${toConfigRelativeWasmPath(migratedPath)}" or an equivalent path under target/${CURRENT_RUST_WASM_TARGET}/release/.`
|
|
644
|
+
].join(" ");
|
|
645
|
+
return new CaatingaError(
|
|
646
|
+
`WASM output was not found at ${configuredWasmPath}.`,
|
|
647
|
+
CaatingaErrorCode.ARTIFACT_NOT_FOUND,
|
|
648
|
+
hint
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
function toConfigRelativeWasmPath(absoluteWasmPath) {
|
|
652
|
+
const relative = import_node_path5.default.relative(process.cwd(), absoluteWasmPath);
|
|
653
|
+
return relative.startsWith("..") ? absoluteWasmPath : `./${relative.split(import_node_path5.default.sep).join("/")}`;
|
|
654
|
+
}
|
|
655
|
+
async function resolveWasmArtifactPath(configuredWasmPath) {
|
|
482
656
|
try {
|
|
483
|
-
await (0, import_promises4.access)(
|
|
657
|
+
await (0, import_promises4.access)(configuredWasmPath);
|
|
658
|
+
return configuredWasmPath;
|
|
484
659
|
} catch {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
660
|
+
const currentTargetPath = toCurrentWasmTargetPath(configuredWasmPath);
|
|
661
|
+
if (currentTargetPath === configuredWasmPath) {
|
|
662
|
+
throw wasmNotFoundError(configuredWasmPath);
|
|
663
|
+
}
|
|
664
|
+
try {
|
|
665
|
+
await (0, import_promises4.access)(currentTargetPath);
|
|
666
|
+
return currentTargetPath;
|
|
667
|
+
} catch {
|
|
668
|
+
throw wasmNotFoundError(configuredWasmPath, { migratedPath: currentTargetPath });
|
|
669
|
+
}
|
|
490
670
|
}
|
|
491
671
|
}
|
|
492
672
|
async function hashWasm(wasmPath) {
|
|
493
673
|
const bytes = await (0, import_promises4.readFile)(wasmPath);
|
|
494
674
|
return (0, import_node_crypto.createHash)("sha256").update(bytes).digest("hex");
|
|
495
675
|
}
|
|
676
|
+
async function getNewestMtimeInDirectory(directory) {
|
|
677
|
+
try {
|
|
678
|
+
await (0, import_promises4.access)(directory);
|
|
679
|
+
} catch {
|
|
680
|
+
return void 0;
|
|
681
|
+
}
|
|
682
|
+
let newest = 0;
|
|
683
|
+
async function walk(dir) {
|
|
684
|
+
const entries = await (0, import_promises4.readdir)(dir, { withFileTypes: true });
|
|
685
|
+
for (const entry of entries) {
|
|
686
|
+
const entryPath = import_node_path5.default.join(dir, entry.name);
|
|
687
|
+
if (entry.isDirectory()) {
|
|
688
|
+
await walk(entryPath);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (!entry.isFile()) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const fileStat = await (0, import_promises4.stat)(entryPath);
|
|
695
|
+
newest = Math.max(newest, fileStat.mtimeMs);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
await walk(directory);
|
|
699
|
+
return newest > 0 ? newest : void 0;
|
|
700
|
+
}
|
|
701
|
+
async function isWasmOlderThanSources(input) {
|
|
702
|
+
const srcDir = import_node_path5.default.join(input.contractPath, "src");
|
|
703
|
+
const newestSourceMtime = await getNewestMtimeInDirectory(srcDir);
|
|
704
|
+
if (newestSourceMtime === void 0) {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
let wasmStat;
|
|
708
|
+
try {
|
|
709
|
+
wasmStat = await (0, import_promises4.stat)(input.wasmPath);
|
|
710
|
+
} catch {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
return wasmStat.mtimeMs < newestSourceMtime;
|
|
714
|
+
}
|
|
715
|
+
function formatStaleWasmWarning(contractName) {
|
|
716
|
+
return `WASM for "${contractName}" may be stale: contract sources under src/ are newer than the WASM file. Run \`caatinga build\` before deploy.`;
|
|
717
|
+
}
|
|
496
718
|
|
|
497
719
|
// src/contracts/build-contract.ts
|
|
498
|
-
var RUST_WASM_TARGET = "
|
|
720
|
+
var RUST_WASM_TARGET = "wasm32v1-none";
|
|
499
721
|
var MISSING_WASM_TARGET_HINT_SUBSTRINGS = [
|
|
500
722
|
"not installed",
|
|
501
723
|
"not found",
|
|
@@ -537,100 +759,24 @@ async function buildContract(options) {
|
|
|
537
759
|
throw new CaatingaError(
|
|
538
760
|
`Required Rust wasm target "${RUST_WASM_TARGET}" is missing.`,
|
|
539
761
|
CaatingaErrorCode.RUST_TARGET_NOT_FOUND,
|
|
540
|
-
`Run \`rustup target add ${RUST_WASM_TARGET}\` and retry
|
|
762
|
+
`Run \`rustup target add ${RUST_WASM_TARGET}\` and retry.`,
|
|
541
763
|
error
|
|
542
764
|
);
|
|
543
765
|
}
|
|
544
766
|
throw error;
|
|
545
767
|
}
|
|
546
|
-
await
|
|
768
|
+
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath);
|
|
547
769
|
return {
|
|
548
|
-
contract
|
|
770
|
+
contract: {
|
|
771
|
+
...contract,
|
|
772
|
+
wasmPath
|
|
773
|
+
},
|
|
549
774
|
output: result.all || result.stdout
|
|
550
775
|
};
|
|
551
776
|
}
|
|
552
777
|
|
|
553
778
|
// src/contracts/deploy-contract.ts
|
|
554
|
-
var
|
|
555
|
-
|
|
556
|
-
// src/stellar-cli/recover-deploy-contract-id.ts
|
|
557
|
-
var TX_HASH_REGEX = /Transaction hash is ([a-f0-9]{64})/i;
|
|
558
|
-
var DEPLOY_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
|
|
559
|
-
var HORIZON_URL_BY_PASSPHRASE = {
|
|
560
|
-
"Test SDF Network ; September 2015": "https://horizon-testnet.stellar.org",
|
|
561
|
-
"Public Global Stellar Network ; September 2015": "https://horizon.stellar.org"
|
|
562
|
-
};
|
|
563
|
-
function isLikelyPublicKeySource(source) {
|
|
564
|
-
return /^G[A-Z2-7]{55}$/.test(source);
|
|
565
|
-
}
|
|
566
|
-
function decimalSaltToHex(salt) {
|
|
567
|
-
return BigInt(salt).toString(16).padStart(64, "0");
|
|
568
|
-
}
|
|
569
|
-
function resolveHorizonUrl(network) {
|
|
570
|
-
const horizonUrl = HORIZON_URL_BY_PASSPHRASE[network.networkPassphrase];
|
|
571
|
-
if (!horizonUrl) {
|
|
572
|
-
throw new CaatingaError(
|
|
573
|
-
`No Horizon URL mapping for network passphrase "${network.networkPassphrase}".`,
|
|
574
|
-
CaatingaErrorCode.NETWORK_NOT_FOUND,
|
|
575
|
-
"Use testnet or mainnet, or extend Caatinga network metadata."
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
return horizonUrl;
|
|
579
|
-
}
|
|
580
|
-
async function fetchCreateContractSalt(horizonUrl, transactionHash, fetchImpl = fetch) {
|
|
581
|
-
const response = await fetchImpl(`${horizonUrl}/transactions/${transactionHash}/operations`);
|
|
582
|
-
if (!response.ok) {
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
const body = await response.json();
|
|
586
|
-
const operation = body._embedded?.records?.find(
|
|
587
|
-
(record) => record.transaction_successful === true && record.type === "invoke_host_function" && record.function === "HostFunctionTypeHostFunctionTypeCreateContract" && typeof record.salt === "string"
|
|
588
|
-
);
|
|
589
|
-
return operation?.salt ?? null;
|
|
590
|
-
}
|
|
591
|
-
async function resolveContractIdFromDeploySalt(options) {
|
|
592
|
-
const saltHex = decimalSaltToHex(options.salt);
|
|
593
|
-
const result = await runCommand("stellar", [
|
|
594
|
-
"contract",
|
|
595
|
-
"id",
|
|
596
|
-
"wasm",
|
|
597
|
-
"--salt",
|
|
598
|
-
saltHex,
|
|
599
|
-
"--source-account",
|
|
600
|
-
options.source,
|
|
601
|
-
"--rpc-url",
|
|
602
|
-
options.network.rpcUrl,
|
|
603
|
-
"--network-passphrase",
|
|
604
|
-
options.network.networkPassphrase
|
|
605
|
-
], {
|
|
606
|
-
cwd: options.cwd,
|
|
607
|
-
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
608
|
-
skipStellarVersionCheck: true
|
|
609
|
-
});
|
|
610
|
-
return parseContractId(result.all || `${result.stdout}
|
|
611
|
-
${result.stderr}`);
|
|
612
|
-
}
|
|
613
|
-
async function tryRecoverContractIdFromDeployFailure(options) {
|
|
614
|
-
if (!DEPLOY_SIGNING_FAILURE_REGEX.test(options.output)) {
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
const hashMatch = options.output.match(TX_HASH_REGEX);
|
|
618
|
-
if (!hashMatch) {
|
|
619
|
-
return null;
|
|
620
|
-
}
|
|
621
|
-
const horizonUrl = resolveHorizonUrl(options.network);
|
|
622
|
-
const salt = await fetchCreateContractSalt(horizonUrl, hashMatch[1], options.fetchImpl);
|
|
623
|
-
if (!salt) {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
626
|
-
return resolveContractIdFromDeploySalt({
|
|
627
|
-
salt,
|
|
628
|
-
source: options.source,
|
|
629
|
-
network: options.network,
|
|
630
|
-
cwd: options.cwd,
|
|
631
|
-
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
632
|
-
});
|
|
633
|
-
}
|
|
779
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
634
780
|
|
|
635
781
|
// src/contracts/dependency-graph.ts
|
|
636
782
|
function buildDependencyGraph(contracts) {
|
|
@@ -681,19 +827,9 @@ function assertSafeSourceAccount(source) {
|
|
|
681
827
|
"Pass a Stellar CLI identity alias, for example: --source alice"
|
|
682
828
|
);
|
|
683
829
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
CaatingaErrorCode.UNSAFE_SOURCE_ACCOUNT,
|
|
688
|
-
"Use a Stellar CLI identity alias instead, for example: --source alice"
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
if (isLikelyPublicKeySource(source)) {
|
|
692
|
-
throw new CaatingaError(
|
|
693
|
-
`Public account address cannot sign transactions: ${source}`,
|
|
694
|
-
CaatingaErrorCode.UNSAFE_SOURCE_ACCOUNT,
|
|
695
|
-
"Use a Stellar CLI identity with a secret key. Example: stellar keys generate alice --fund --network testnet, then --source alice"
|
|
696
|
-
);
|
|
830
|
+
const unsafeSource = validateSourceShape(source);
|
|
831
|
+
if (unsafeSource) {
|
|
832
|
+
throw unsafeSource;
|
|
697
833
|
}
|
|
698
834
|
return source;
|
|
699
835
|
}
|
|
@@ -721,17 +857,32 @@ async function deployContract(options) {
|
|
|
721
857
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga deploy.", {
|
|
722
858
|
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
723
859
|
});
|
|
724
|
-
await
|
|
860
|
+
const wasmPath = await resolveWasmArtifactPath(contract.wasmPath);
|
|
861
|
+
const contractWithWasm = {
|
|
862
|
+
...contract,
|
|
863
|
+
wasmPath
|
|
864
|
+
};
|
|
865
|
+
let staleWasmWarning;
|
|
866
|
+
if (options.checkStaleWasm !== false) {
|
|
867
|
+
const stale = await isWasmOlderThanSources({
|
|
868
|
+
wasmPath,
|
|
869
|
+
contractPath: contract.sourcePath
|
|
870
|
+
});
|
|
871
|
+
if (stale) {
|
|
872
|
+
staleWasmWarning = formatStaleWasmWarning(contract.name);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
725
875
|
const artifactsBefore = await readArtifacts(cwd);
|
|
726
876
|
const existing = artifactsBefore.networks[network.name]?.contracts[contract.name];
|
|
727
877
|
if (existing?.contractId && !options.force) {
|
|
728
878
|
return {
|
|
729
|
-
contract,
|
|
879
|
+
contract: contractWithWasm,
|
|
730
880
|
network,
|
|
731
881
|
contractId: existing.contractId,
|
|
732
|
-
artifactsPath:
|
|
882
|
+
artifactsPath: import_node_path6.default.resolve(cwd, "caatinga.artifacts.json"),
|
|
733
883
|
output: "",
|
|
734
|
-
skipped: true
|
|
884
|
+
skipped: true,
|
|
885
|
+
staleWasmWarning
|
|
735
886
|
};
|
|
736
887
|
}
|
|
737
888
|
const rawDeployArgs = contract.config.deployArgs;
|
|
@@ -762,13 +913,10 @@ async function deployContract(options) {
|
|
|
762
913
|
"contract",
|
|
763
914
|
"deploy",
|
|
764
915
|
"--wasm",
|
|
765
|
-
|
|
916
|
+
wasmPath,
|
|
766
917
|
"--source-account",
|
|
767
918
|
source,
|
|
768
|
-
|
|
769
|
-
network.config.rpcUrl,
|
|
770
|
-
"--network-passphrase",
|
|
771
|
-
network.config.networkPassphrase,
|
|
919
|
+
...buildStellarNetworkArgs(network),
|
|
772
920
|
...constructorArgs
|
|
773
921
|
];
|
|
774
922
|
let output = "";
|
|
@@ -804,7 +952,7 @@ ${error.hint ?? ""}`,
|
|
|
804
952
|
`Contract ID: ${contractId}`
|
|
805
953
|
].filter(Boolean).join("\n");
|
|
806
954
|
}
|
|
807
|
-
const wasmHash = await hashWasm(
|
|
955
|
+
const wasmHash = await hashWasm(wasmPath);
|
|
808
956
|
const dependencyGraph = buildDependencyGraph(options.config.contracts);
|
|
809
957
|
const dependencies = options.dependencies ?? contract.config.dependsOn;
|
|
810
958
|
const nextArtifacts = updateArtifact(
|
|
@@ -824,12 +972,13 @@ ${error.hint ?? ""}`,
|
|
|
824
972
|
);
|
|
825
973
|
const artifactsPath = await writeArtifacts(nextArtifacts, cwd);
|
|
826
974
|
return {
|
|
827
|
-
contract,
|
|
975
|
+
contract: contractWithWasm,
|
|
828
976
|
network,
|
|
829
977
|
contractId,
|
|
830
978
|
artifactsPath,
|
|
831
979
|
output,
|
|
832
|
-
skipped: false
|
|
980
|
+
skipped: false,
|
|
981
|
+
staleWasmWarning
|
|
833
982
|
};
|
|
834
983
|
}
|
|
835
984
|
|
|
@@ -884,6 +1033,53 @@ function resolveDeployOrder(input) {
|
|
|
884
1033
|
}
|
|
885
1034
|
}
|
|
886
1035
|
|
|
1036
|
+
// src/contracts/verify-dependency-contract.ts
|
|
1037
|
+
async function verifyDependencyContract(options) {
|
|
1038
|
+
try {
|
|
1039
|
+
await runCommand("stellar", [
|
|
1040
|
+
"contract",
|
|
1041
|
+
"info",
|
|
1042
|
+
"interface",
|
|
1043
|
+
"--contract-id",
|
|
1044
|
+
options.contractId,
|
|
1045
|
+
...buildStellarNetworkArgs(options.network)
|
|
1046
|
+
], {
|
|
1047
|
+
cwd: options.cwd,
|
|
1048
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
1049
|
+
failureCode: CaatingaErrorCode.DEPENDENCY_CONTRACT_NOT_FOUND
|
|
1050
|
+
});
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.DEPENDENCY_CONTRACT_NOT_FOUND) {
|
|
1053
|
+
throw new CaatingaError(
|
|
1054
|
+
`Dependency "${options.dependencyName}" is not deployed on "${options.network.name}" (contract ID ${options.contractId}).`,
|
|
1055
|
+
CaatingaErrorCode.DEPENDENCY_CONTRACT_NOT_FOUND,
|
|
1056
|
+
"Deploy the dependency on this network, fix caatinga.artifacts.json, or omit --verify-deps.",
|
|
1057
|
+
error.cause
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
throw error;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
async function verifyDependencyContracts(options) {
|
|
1064
|
+
for (const dependencyName of options.dependencies) {
|
|
1065
|
+
const contractArtifact = options.artifacts.networks[options.network.name]?.contracts[dependencyName];
|
|
1066
|
+
if (!contractArtifact?.contractId) {
|
|
1067
|
+
throw new CaatingaError(
|
|
1068
|
+
`No dependency artifact found for "${dependencyName}" on "${options.network.name}".`,
|
|
1069
|
+
CaatingaErrorCode.CONTRACT_DEPENDENCY_ARTIFACT_NOT_FOUND,
|
|
1070
|
+
"Deploy the dependency first or run deploy without --no-deps."
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
await verifyDependencyContract({
|
|
1074
|
+
dependencyName,
|
|
1075
|
+
contractId: contractArtifact.contractId,
|
|
1076
|
+
network: options.network,
|
|
1077
|
+
cwd: options.cwd,
|
|
1078
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
887
1083
|
// src/contracts/deploy-contract-graph.ts
|
|
888
1084
|
async function deployContractGraph(options) {
|
|
889
1085
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -894,17 +1090,33 @@ async function deployContractGraph(options) {
|
|
|
894
1090
|
includeDependencies: options.includeDependencies
|
|
895
1091
|
});
|
|
896
1092
|
const deployedContracts = [];
|
|
1093
|
+
const skippedContracts = [];
|
|
1094
|
+
const staleWasmWarnings = [];
|
|
897
1095
|
for (const contractName of order) {
|
|
898
1096
|
const artifacts = await readArtifacts(cwd);
|
|
899
1097
|
const existing = artifacts.networks[network.name]?.contracts[contractName];
|
|
900
1098
|
const contractConfig = options.config.contracts[contractName];
|
|
1099
|
+
if (options.verifyDeps && contractConfig.dependsOn.length > 0) {
|
|
1100
|
+
await verifyDependencyContracts({
|
|
1101
|
+
dependencies: contractConfig.dependsOn,
|
|
1102
|
+
artifacts,
|
|
1103
|
+
network,
|
|
1104
|
+
cwd,
|
|
1105
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
901
1108
|
const resolvedDeployArgs = resolveDeployArgs({
|
|
902
1109
|
deployArgs: contractConfig.deployArgs,
|
|
903
1110
|
artifacts,
|
|
904
1111
|
network: network.name
|
|
905
1112
|
});
|
|
906
1113
|
if (existing?.contractId && !options.force) {
|
|
907
|
-
|
|
1114
|
+
skippedContracts.push({
|
|
1115
|
+
name: contractName,
|
|
1116
|
+
contractId: existing.contractId,
|
|
1117
|
+
network: network.name,
|
|
1118
|
+
reason: "already-deployed"
|
|
1119
|
+
});
|
|
908
1120
|
continue;
|
|
909
1121
|
}
|
|
910
1122
|
const result = await deployContract({
|
|
@@ -915,20 +1127,38 @@ async function deployContractGraph(options) {
|
|
|
915
1127
|
cwd,
|
|
916
1128
|
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
917
1129
|
force: options.force,
|
|
1130
|
+
checkStaleWasm: options.checkStaleWasm,
|
|
918
1131
|
resolvedDeployArgs,
|
|
919
1132
|
dependencies: contractConfig.dependsOn
|
|
920
1133
|
});
|
|
921
|
-
|
|
1134
|
+
if (result.staleWasmWarning) {
|
|
1135
|
+
staleWasmWarnings.push({
|
|
1136
|
+
contract: contractName,
|
|
1137
|
+
message: result.staleWasmWarning
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
if (result.skipped) {
|
|
1141
|
+
skippedContracts.push({
|
|
1142
|
+
name: contractName,
|
|
1143
|
+
contractId: result.contractId,
|
|
1144
|
+
network: network.name,
|
|
1145
|
+
reason: "already-deployed"
|
|
1146
|
+
});
|
|
1147
|
+
} else {
|
|
1148
|
+
deployedContracts.push({ name: contractName, contractId: result.contractId });
|
|
1149
|
+
}
|
|
922
1150
|
}
|
|
923
1151
|
return {
|
|
924
1152
|
network,
|
|
925
|
-
deployedContracts
|
|
1153
|
+
deployedContracts,
|
|
1154
|
+
skippedContracts,
|
|
1155
|
+
staleWasmWarnings
|
|
926
1156
|
};
|
|
927
1157
|
}
|
|
928
1158
|
|
|
929
1159
|
// src/contracts/generate-bindings.ts
|
|
930
1160
|
var import_promises5 = require("fs/promises");
|
|
931
|
-
var
|
|
1161
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
932
1162
|
async function generateBindings(options) {
|
|
933
1163
|
const cwd = options.cwd ?? process.cwd();
|
|
934
1164
|
const network = resolveNetwork(options.config, options.networkName);
|
|
@@ -944,7 +1174,7 @@ async function generateBindings(options) {
|
|
|
944
1174
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga generate.", {
|
|
945
1175
|
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
946
1176
|
});
|
|
947
|
-
const outputDir =
|
|
1177
|
+
const outputDir = import_node_path7.default.resolve(cwd, options.config.frontend.bindingsOutput, options.contractName);
|
|
948
1178
|
await (0, import_promises5.mkdir)(outputDir, { recursive: true });
|
|
949
1179
|
const result = await runCommand("stellar", [
|
|
950
1180
|
"contract",
|
|
@@ -955,10 +1185,7 @@ async function generateBindings(options) {
|
|
|
955
1185
|
"--output-dir",
|
|
956
1186
|
outputDir,
|
|
957
1187
|
"--overwrite",
|
|
958
|
-
|
|
959
|
-
network.config.rpcUrl,
|
|
960
|
-
"--network-passphrase",
|
|
961
|
-
network.config.networkPassphrase
|
|
1188
|
+
...buildStellarNetworkArgs(network)
|
|
962
1189
|
], {
|
|
963
1190
|
cwd,
|
|
964
1191
|
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
@@ -973,6 +1200,7 @@ async function generateBindings(options) {
|
|
|
973
1200
|
}
|
|
974
1201
|
|
|
975
1202
|
// src/contracts/invoke-contract.ts
|
|
1203
|
+
var INVOKE_SIGNING_FAILURE_REGEX = /xdr processing error: xdr value invalid/i;
|
|
976
1204
|
function parseInvokeTarget(target) {
|
|
977
1205
|
const [contractName, method, extra] = target.split(".");
|
|
978
1206
|
if (!contractName || !method || extra) {
|
|
@@ -1001,25 +1229,43 @@ async function invokeContract(options) {
|
|
|
1001
1229
|
await checkBinary("stellar", "Install Stellar CLI before running caatinga invoke.", {
|
|
1002
1230
|
allowUntestedStellarCli: options.allowUntestedStellarCli
|
|
1003
1231
|
});
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
"
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
})
|
|
1232
|
+
let result;
|
|
1233
|
+
try {
|
|
1234
|
+
result = await runCommand("stellar", [
|
|
1235
|
+
"contract",
|
|
1236
|
+
"invoke",
|
|
1237
|
+
"--id",
|
|
1238
|
+
contractArtifact.contractId,
|
|
1239
|
+
"--source-account",
|
|
1240
|
+
source,
|
|
1241
|
+
...buildStellarNetworkArgs(network),
|
|
1242
|
+
"--",
|
|
1243
|
+
target.method,
|
|
1244
|
+
...options.args ?? []
|
|
1245
|
+
], {
|
|
1246
|
+
cwd,
|
|
1247
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli,
|
|
1248
|
+
failureCode: CaatingaErrorCode.INVOKE_FAILED
|
|
1249
|
+
});
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
if (error instanceof CaatingaError && error.code === CaatingaErrorCode.INVOKE_FAILED && INVOKE_SIGNING_FAILURE_REGEX.test(`${error.message}
|
|
1252
|
+
${error.hint ?? ""}`)) {
|
|
1253
|
+
throw new CaatingaError(
|
|
1254
|
+
error.message,
|
|
1255
|
+
error.code,
|
|
1256
|
+
[
|
|
1257
|
+
"Stellar CLI could not sign the invoke transaction (xdr value invalid).",
|
|
1258
|
+
"Stellar CLI 22.x has a known invoke signing bug; upgrade to 23.0.0 or newer (25.2.0 recommended).",
|
|
1259
|
+
" stellar --version",
|
|
1260
|
+
"Then retry with a funded identity, for example:",
|
|
1261
|
+
" stellar keys generate alice --fund --network testnet",
|
|
1262
|
+
" npx caatinga invoke counter.increment --network testnet --source alice"
|
|
1263
|
+
].join("\n"),
|
|
1264
|
+
error
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
throw error;
|
|
1268
|
+
}
|
|
1023
1269
|
return {
|
|
1024
1270
|
target,
|
|
1025
1271
|
network,
|
|
@@ -1029,7 +1275,7 @@ async function invokeContract(options) {
|
|
|
1029
1275
|
|
|
1030
1276
|
// src/templates/create-project-from-template.ts
|
|
1031
1277
|
var import_promises6 = require("fs/promises");
|
|
1032
|
-
var
|
|
1278
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
1033
1279
|
var import_zod6 = require("zod");
|
|
1034
1280
|
|
|
1035
1281
|
// src/templates/template-manifest.schema.ts
|
|
@@ -1099,8 +1345,8 @@ function formatTemplateCompatibilityHint(issue) {
|
|
|
1099
1345
|
|
|
1100
1346
|
// src/templates/create-project-from-template.ts
|
|
1101
1347
|
async function createProjectFromTemplate(options) {
|
|
1102
|
-
const targetDir =
|
|
1103
|
-
const templateDir =
|
|
1348
|
+
const targetDir = import_node_path8.default.resolve(options.targetDir);
|
|
1349
|
+
const templateDir = import_node_path8.default.resolve(options.templateDir);
|
|
1104
1350
|
try {
|
|
1105
1351
|
await (0, import_promises6.stat)(templateDir);
|
|
1106
1352
|
} catch {
|
|
@@ -1122,7 +1368,7 @@ async function createProjectFromTemplate(options) {
|
|
|
1122
1368
|
return { targetDir, template: manifest };
|
|
1123
1369
|
}
|
|
1124
1370
|
async function readTemplateManifest(templateDir) {
|
|
1125
|
-
const manifestPath =
|
|
1371
|
+
const manifestPath = import_node_path8.default.join(templateDir, "caatinga.template.json");
|
|
1126
1372
|
try {
|
|
1127
1373
|
const rawManifest = await (0, import_promises6.readFile)(manifestPath, "utf8");
|
|
1128
1374
|
const manifest = TemplateManifestSchema.parse(JSON.parse(rawManifest));
|
|
@@ -1159,7 +1405,7 @@ async function readTemplateManifest(templateDir) {
|
|
|
1159
1405
|
async function replaceTemplateVariables(dir, projectName) {
|
|
1160
1406
|
const entries = await (0, import_promises6.readdir)(dir);
|
|
1161
1407
|
await Promise.all(entries.map(async (entry) => {
|
|
1162
|
-
const entryPath =
|
|
1408
|
+
const entryPath = import_node_path8.default.join(dir, entry);
|
|
1163
1409
|
const entryStat = await (0, import_promises6.stat)(entryPath);
|
|
1164
1410
|
if (entryStat.isDirectory()) {
|
|
1165
1411
|
await replaceTemplateVariables(entryPath, projectName);
|
|
@@ -1182,7 +1428,7 @@ function isTextTemplateFile(filePath) {
|
|
|
1182
1428
|
".tsx",
|
|
1183
1429
|
".css",
|
|
1184
1430
|
".html"
|
|
1185
|
-
].includes(
|
|
1431
|
+
].includes(import_node_path8.default.extname(filePath));
|
|
1186
1432
|
}
|
|
1187
1433
|
|
|
1188
1434
|
// src/ci/is-transient-testnet-smoke-failure.ts
|
|
@@ -1241,5 +1487,6 @@ function isTransientTestnetSmokeFailure(logText) {
|
|
|
1241
1487
|
runCommand,
|
|
1242
1488
|
toCaatingaError,
|
|
1243
1489
|
updateArtifact,
|
|
1490
|
+
validateSourceShape,
|
|
1244
1491
|
writeArtifacts
|
|
1245
1492
|
});
|