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