phantasma-sdk-ts 0.5.1 → 0.5.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/dist/cjs/core/tx/ContractArtifacts.js +106 -0
- package/dist/cjs/core/tx/ContractTxHelper.js +120 -0
- package/dist/cjs/core/tx/index.js +2 -0
- package/dist/cjs/core/utils/Hex.js +1 -1
- package/dist/esm/core/tx/ContractArtifacts.js +96 -0
- package/dist/esm/core/tx/ContractTxHelper.js +116 -0
- package/dist/esm/core/tx/index.js +2 -0
- package/dist/esm/core/utils/Hex.js +1 -1
- package/dist/types/core/tx/ContractArtifacts.d.ts +53 -0
- package/dist/types/core/tx/ContractArtifacts.d.ts.map +1 -0
- package/dist/types/core/tx/ContractTxHelper.d.ts +41 -0
- package/dist/types/core/tx/ContractTxHelper.d.ts.map +1 -0
- package/dist/types/core/tx/index.d.ts +2 -0
- package/dist/types/core/tx/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildContractArtifactBundle = exports.buildContractArtifactManifest = exports.coerceContractBytes = exports.normalizeContractName = void 0;
|
|
7
|
+
const sha256_js_1 = __importDefault(require("crypto-js/sha256.js"));
|
|
8
|
+
const enc_hex_js_1 = __importDefault(require("crypto-js/enc-hex.js"));
|
|
9
|
+
const index_js_1 = require("../utils/index.js");
|
|
10
|
+
function sha256Hex(bytes) {
|
|
11
|
+
return (0, sha256_js_1.default)(enc_hex_js_1.default.parse((0, index_js_1.bytesToHex)(bytes))).toString(enc_hex_js_1.default);
|
|
12
|
+
}
|
|
13
|
+
function buildFileEntry(path, bytes) {
|
|
14
|
+
const trimmedPath = path.trim();
|
|
15
|
+
if (!trimmedPath) {
|
|
16
|
+
throw new Error('artifact path cannot be empty');
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
path: trimmedPath,
|
|
20
|
+
size: bytes.length,
|
|
21
|
+
sha256: sha256Hex(bytes),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function normalizeContractName(contractName) {
|
|
25
|
+
const trimmed = contractName.trim();
|
|
26
|
+
if (!trimmed) {
|
|
27
|
+
throw new Error('contractName cannot be empty');
|
|
28
|
+
}
|
|
29
|
+
return trimmed;
|
|
30
|
+
}
|
|
31
|
+
exports.normalizeContractName = normalizeContractName;
|
|
32
|
+
function coerceContractBytes(input, label) {
|
|
33
|
+
if (input instanceof Uint8Array) {
|
|
34
|
+
if (input.length === 0) {
|
|
35
|
+
throw new Error(`${label} cannot be empty`);
|
|
36
|
+
}
|
|
37
|
+
// Copy mutable buffers so callers can keep reusing their original arrays safely.
|
|
38
|
+
return new Uint8Array(input);
|
|
39
|
+
}
|
|
40
|
+
if (typeof input === 'string') {
|
|
41
|
+
const trimmed = input.trim();
|
|
42
|
+
if (!trimmed) {
|
|
43
|
+
throw new Error(`${label} cannot be empty`);
|
|
44
|
+
}
|
|
45
|
+
const bytes = (0, index_js_1.hexToBytes)(trimmed);
|
|
46
|
+
if (bytes.length === 0) {
|
|
47
|
+
throw new Error(`${label} cannot be empty`);
|
|
48
|
+
}
|
|
49
|
+
return bytes;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`${label} must be Uint8Array or hex string`);
|
|
52
|
+
}
|
|
53
|
+
exports.coerceContractBytes = coerceContractBytes;
|
|
54
|
+
function buildContractArtifactManifest(params) {
|
|
55
|
+
// The manifest is the stable handoff contract between compiler, CLI, frontend, and runbooks.
|
|
56
|
+
const contractName = normalizeContractName(params.contractName);
|
|
57
|
+
const compilerName = params.compilerName.trim();
|
|
58
|
+
const compilerVersion = params.compilerVersion.trim();
|
|
59
|
+
if (!compilerName) {
|
|
60
|
+
throw new Error('compilerName cannot be empty');
|
|
61
|
+
}
|
|
62
|
+
if (!compilerVersion) {
|
|
63
|
+
throw new Error('compilerVersion cannot be empty');
|
|
64
|
+
}
|
|
65
|
+
const scriptBytes = coerceContractBytes(params.script, 'script');
|
|
66
|
+
const abiBytes = coerceContractBytes(params.abi, 'abi');
|
|
67
|
+
let debugEntry;
|
|
68
|
+
if (params.debugPath !== undefined || params.debug !== undefined) {
|
|
69
|
+
if (!params.debugPath || params.debug === undefined) {
|
|
70
|
+
throw new Error('debugPath and debug must be provided together');
|
|
71
|
+
}
|
|
72
|
+
debugEntry = buildFileEntry(params.debugPath, coerceContractBytes(params.debug, 'debug'));
|
|
73
|
+
}
|
|
74
|
+
const manifest = {
|
|
75
|
+
format: 'pha.contract.artifacts/v1',
|
|
76
|
+
contractName,
|
|
77
|
+
createdAtUtc: (params.createdAtUtc ?? new Date().toISOString()).trim(),
|
|
78
|
+
compiler: {
|
|
79
|
+
name: compilerName,
|
|
80
|
+
version: compilerVersion,
|
|
81
|
+
},
|
|
82
|
+
files: {
|
|
83
|
+
script: buildFileEntry(params.scriptPath, scriptBytes),
|
|
84
|
+
abi: buildFileEntry(params.abiPath, abiBytes),
|
|
85
|
+
...(debugEntry ? { debug: debugEntry } : {}),
|
|
86
|
+
},
|
|
87
|
+
...(params.sourceFile ? { sourceFile: params.sourceFile } : {}),
|
|
88
|
+
};
|
|
89
|
+
if (!manifest.createdAtUtc) {
|
|
90
|
+
throw new Error('createdAtUtc cannot be empty');
|
|
91
|
+
}
|
|
92
|
+
return manifest;
|
|
93
|
+
}
|
|
94
|
+
exports.buildContractArtifactManifest = buildContractArtifactManifest;
|
|
95
|
+
function buildContractArtifactBundle(params) {
|
|
96
|
+
// Keep bundle materialization small and explicit so higher layers can choose their own IO/layout policy.
|
|
97
|
+
const bundle = {
|
|
98
|
+
contractName: normalizeContractName(params.contractName),
|
|
99
|
+
script: coerceContractBytes(params.script, 'script'),
|
|
100
|
+
abi: coerceContractBytes(params.abi, 'abi'),
|
|
101
|
+
...(params.debug !== undefined ? { debug: coerceContractBytes(params.debug, 'debug') } : {}),
|
|
102
|
+
...(params.manifest ? { manifest: params.manifest } : {}),
|
|
103
|
+
};
|
|
104
|
+
return bundle;
|
|
105
|
+
}
|
|
106
|
+
exports.buildContractArtifactBundle = buildContractArtifactBundle;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContractTxHelper = void 0;
|
|
4
|
+
const ProofOfWork_js_1 = require("../link/interfaces/ProofOfWork.js");
|
|
5
|
+
const DomainSettings_js_1 = require("../types/DomainSettings.js");
|
|
6
|
+
const index_js_1 = require("../utils/index.js");
|
|
7
|
+
const index_js_2 = require("../vm/index.js");
|
|
8
|
+
const Transaction_js_1 = require("./Transaction.js");
|
|
9
|
+
const ContractArtifacts_js_1 = require("./ContractArtifacts.js");
|
|
10
|
+
class ContractTxHelper {
|
|
11
|
+
static buildDeployScript(params) {
|
|
12
|
+
return this.buildContractLifecycleScript('Runtime.DeployContract', params);
|
|
13
|
+
}
|
|
14
|
+
static buildUpgradeScript(params) {
|
|
15
|
+
return this.buildContractLifecycleScript('Runtime.UpgradeContract', params);
|
|
16
|
+
}
|
|
17
|
+
static buildDeployTransaction(params) {
|
|
18
|
+
return this.buildContractLifecycleTransaction(this.buildDeployScript(params), params);
|
|
19
|
+
}
|
|
20
|
+
static buildUpgradeTransaction(params) {
|
|
21
|
+
return this.buildContractLifecycleTransaction(this.buildUpgradeScript(params), params);
|
|
22
|
+
}
|
|
23
|
+
static buildDeployTransactionAndEncode(params) {
|
|
24
|
+
const tx = this.buildDeployTransaction(params);
|
|
25
|
+
this.signTransaction(tx, params.signer, params.proofOfWork);
|
|
26
|
+
return tx.ToStringEncoded(true).toUpperCase();
|
|
27
|
+
}
|
|
28
|
+
static buildUpgradeTransactionAndEncode(params) {
|
|
29
|
+
const tx = this.buildUpgradeTransaction(params);
|
|
30
|
+
this.signTransaction(tx, params.signer, params.proofOfWork);
|
|
31
|
+
return tx.ToStringEncoded(true).toUpperCase();
|
|
32
|
+
}
|
|
33
|
+
static buildDeployScriptFromBundle(bundle, from, gasPrice, gasLimit) {
|
|
34
|
+
return this.buildDeployScript({
|
|
35
|
+
from,
|
|
36
|
+
contractName: bundle.contractName,
|
|
37
|
+
script: bundle.script,
|
|
38
|
+
abi: bundle.abi,
|
|
39
|
+
gasPrice,
|
|
40
|
+
gasLimit,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
static buildUpgradeScriptFromBundle(bundle, from, gasPrice, gasLimit) {
|
|
44
|
+
return this.buildUpgradeScript({
|
|
45
|
+
from,
|
|
46
|
+
contractName: bundle.contractName,
|
|
47
|
+
script: bundle.script,
|
|
48
|
+
abi: bundle.abi,
|
|
49
|
+
gasPrice,
|
|
50
|
+
gasLimit,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
static encodePayloadText(text) {
|
|
54
|
+
return (0, index_js_1.bytesToHex)(new TextEncoder().encode(text));
|
|
55
|
+
}
|
|
56
|
+
static buildContractLifecycleScript(interopName, params) {
|
|
57
|
+
// Keep deploy/upgrade on the same legacy VM script path so CLI and wallet flows generate identical transactions.
|
|
58
|
+
const fromAddress = this.normalizeAddress(params.from);
|
|
59
|
+
const contractName = (0, ContractArtifacts_js_1.normalizeContractName)(params.contractName);
|
|
60
|
+
const scriptBytes = (0, ContractArtifacts_js_1.coerceContractBytes)(params.script, 'script');
|
|
61
|
+
const abiBytes = (0, ContractArtifacts_js_1.coerceContractBytes)(params.abi, 'abi');
|
|
62
|
+
const gasPrice = this.normalizeSmallInteger(params.gasPrice ?? this.DefaultGasPrice, 'gasPrice');
|
|
63
|
+
const gasLimit = this.normalizeSmallInteger(params.gasLimit ?? this.DefaultGasLimit, 'gasLimit');
|
|
64
|
+
const nullAddress = new index_js_2.ScriptBuilder().NullAddress;
|
|
65
|
+
return new index_js_2.ScriptBuilder()
|
|
66
|
+
.BeginScript()
|
|
67
|
+
.AllowGas(fromAddress, nullAddress, gasPrice, gasLimit)
|
|
68
|
+
.CallInterop(interopName, [fromAddress, contractName, scriptBytes, abiBytes])
|
|
69
|
+
.SpendGas(fromAddress)
|
|
70
|
+
.EndScript();
|
|
71
|
+
}
|
|
72
|
+
static buildContractLifecycleTransaction(scriptHex, params) {
|
|
73
|
+
// Transaction construction stays separate from signing so callers can inspect or override the unsigned tx first.
|
|
74
|
+
const nexus = params.nexus.trim();
|
|
75
|
+
const chain = (params.chain ?? DomainSettings_js_1.DomainSettings.RootChainName).trim();
|
|
76
|
+
if (!nexus) {
|
|
77
|
+
throw new Error('nexus cannot be empty');
|
|
78
|
+
}
|
|
79
|
+
if (!chain) {
|
|
80
|
+
throw new Error('chain cannot be empty');
|
|
81
|
+
}
|
|
82
|
+
const payloadHex = params.payloadHex?.trim() ?? '';
|
|
83
|
+
return new Transaction_js_1.Transaction(nexus, chain, scriptHex, params.expiration ?? new Date(Date.now() + 5 * 60 * 1000), payloadHex);
|
|
84
|
+
}
|
|
85
|
+
static signTransaction(tx, signer, proofOfWork = ProofOfWork_js_1.ProofOfWork.Minimal) {
|
|
86
|
+
// Mining/signing is the only mutation stage; everything above should remain deterministic and side-effect free.
|
|
87
|
+
if (proofOfWork > 0) {
|
|
88
|
+
tx.mineTransaction(proofOfWork);
|
|
89
|
+
}
|
|
90
|
+
if (typeof signer === 'string') {
|
|
91
|
+
const trimmedWif = signer.trim();
|
|
92
|
+
if (!trimmedWif) {
|
|
93
|
+
throw new Error('signer WIF cannot be empty');
|
|
94
|
+
}
|
|
95
|
+
tx.sign(trimmedWif);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
tx.signWithKeys(signer);
|
|
99
|
+
}
|
|
100
|
+
static normalizeAddress(address) {
|
|
101
|
+
if (typeof address === 'string') {
|
|
102
|
+
const trimmed = address.trim();
|
|
103
|
+
if (!trimmed) {
|
|
104
|
+
throw new Error('from cannot be empty');
|
|
105
|
+
}
|
|
106
|
+
return trimmed;
|
|
107
|
+
}
|
|
108
|
+
return address;
|
|
109
|
+
}
|
|
110
|
+
static normalizeSmallInteger(value, label) {
|
|
111
|
+
const numeric = typeof value === 'bigint' ? Number(value) : value;
|
|
112
|
+
if (!Number.isSafeInteger(numeric) || numeric <= 0) {
|
|
113
|
+
throw new Error(`${label} must be a positive safe integer`);
|
|
114
|
+
}
|
|
115
|
+
return numeric;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.ContractTxHelper = ContractTxHelper;
|
|
119
|
+
ContractTxHelper.DefaultGasPrice = DomainSettings_js_1.DomainSettings.DefaultMinimumGasFee;
|
|
120
|
+
ContractTxHelper.DefaultGasLimit = 100000;
|
|
@@ -15,5 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./ExecutionState.js"), exports);
|
|
18
|
+
__exportStar(require("./ContractArtifacts.js"), exports);
|
|
19
|
+
__exportStar(require("./ContractTxHelper.js"), exports);
|
|
18
20
|
__exportStar(require("./Transaction.js"), exports);
|
|
19
21
|
__exportStar(require("./utils.js"), exports);
|
|
@@ -18,7 +18,7 @@ function bytesToHex(bytes) {
|
|
|
18
18
|
exports.bytesToHex = bytesToHex;
|
|
19
19
|
function hexToBytes(hex) {
|
|
20
20
|
const trimmed = hex.trim();
|
|
21
|
-
const normalized = trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed;
|
|
21
|
+
const normalized = trimmed.startsWith('0x') || trimmed.startsWith('0X') ? trimmed.slice(2) : trimmed;
|
|
22
22
|
if (normalized.length === 0) {
|
|
23
23
|
return new Uint8Array();
|
|
24
24
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import SHA256 from 'crypto-js/sha256.js';
|
|
2
|
+
import hexEncoding from 'crypto-js/enc-hex.js';
|
|
3
|
+
import { bytesToHex, hexToBytes } from '../utils/index.js';
|
|
4
|
+
function sha256Hex(bytes) {
|
|
5
|
+
return SHA256(hexEncoding.parse(bytesToHex(bytes))).toString(hexEncoding);
|
|
6
|
+
}
|
|
7
|
+
function buildFileEntry(path, bytes) {
|
|
8
|
+
const trimmedPath = path.trim();
|
|
9
|
+
if (!trimmedPath) {
|
|
10
|
+
throw new Error('artifact path cannot be empty');
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
path: trimmedPath,
|
|
14
|
+
size: bytes.length,
|
|
15
|
+
sha256: sha256Hex(bytes),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function normalizeContractName(contractName) {
|
|
19
|
+
const trimmed = contractName.trim();
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
throw new Error('contractName cannot be empty');
|
|
22
|
+
}
|
|
23
|
+
return trimmed;
|
|
24
|
+
}
|
|
25
|
+
export function coerceContractBytes(input, label) {
|
|
26
|
+
if (input instanceof Uint8Array) {
|
|
27
|
+
if (input.length === 0) {
|
|
28
|
+
throw new Error(`${label} cannot be empty`);
|
|
29
|
+
}
|
|
30
|
+
// Copy mutable buffers so callers can keep reusing their original arrays safely.
|
|
31
|
+
return new Uint8Array(input);
|
|
32
|
+
}
|
|
33
|
+
if (typeof input === 'string') {
|
|
34
|
+
const trimmed = input.trim();
|
|
35
|
+
if (!trimmed) {
|
|
36
|
+
throw new Error(`${label} cannot be empty`);
|
|
37
|
+
}
|
|
38
|
+
const bytes = hexToBytes(trimmed);
|
|
39
|
+
if (bytes.length === 0) {
|
|
40
|
+
throw new Error(`${label} cannot be empty`);
|
|
41
|
+
}
|
|
42
|
+
return bytes;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`${label} must be Uint8Array or hex string`);
|
|
45
|
+
}
|
|
46
|
+
export function buildContractArtifactManifest(params) {
|
|
47
|
+
// The manifest is the stable handoff contract between compiler, CLI, frontend, and runbooks.
|
|
48
|
+
const contractName = normalizeContractName(params.contractName);
|
|
49
|
+
const compilerName = params.compilerName.trim();
|
|
50
|
+
const compilerVersion = params.compilerVersion.trim();
|
|
51
|
+
if (!compilerName) {
|
|
52
|
+
throw new Error('compilerName cannot be empty');
|
|
53
|
+
}
|
|
54
|
+
if (!compilerVersion) {
|
|
55
|
+
throw new Error('compilerVersion cannot be empty');
|
|
56
|
+
}
|
|
57
|
+
const scriptBytes = coerceContractBytes(params.script, 'script');
|
|
58
|
+
const abiBytes = coerceContractBytes(params.abi, 'abi');
|
|
59
|
+
let debugEntry;
|
|
60
|
+
if (params.debugPath !== undefined || params.debug !== undefined) {
|
|
61
|
+
if (!params.debugPath || params.debug === undefined) {
|
|
62
|
+
throw new Error('debugPath and debug must be provided together');
|
|
63
|
+
}
|
|
64
|
+
debugEntry = buildFileEntry(params.debugPath, coerceContractBytes(params.debug, 'debug'));
|
|
65
|
+
}
|
|
66
|
+
const manifest = {
|
|
67
|
+
format: 'pha.contract.artifacts/v1',
|
|
68
|
+
contractName,
|
|
69
|
+
createdAtUtc: (params.createdAtUtc ?? new Date().toISOString()).trim(),
|
|
70
|
+
compiler: {
|
|
71
|
+
name: compilerName,
|
|
72
|
+
version: compilerVersion,
|
|
73
|
+
},
|
|
74
|
+
files: {
|
|
75
|
+
script: buildFileEntry(params.scriptPath, scriptBytes),
|
|
76
|
+
abi: buildFileEntry(params.abiPath, abiBytes),
|
|
77
|
+
...(debugEntry ? { debug: debugEntry } : {}),
|
|
78
|
+
},
|
|
79
|
+
...(params.sourceFile ? { sourceFile: params.sourceFile } : {}),
|
|
80
|
+
};
|
|
81
|
+
if (!manifest.createdAtUtc) {
|
|
82
|
+
throw new Error('createdAtUtc cannot be empty');
|
|
83
|
+
}
|
|
84
|
+
return manifest;
|
|
85
|
+
}
|
|
86
|
+
export function buildContractArtifactBundle(params) {
|
|
87
|
+
// Keep bundle materialization small and explicit so higher layers can choose their own IO/layout policy.
|
|
88
|
+
const bundle = {
|
|
89
|
+
contractName: normalizeContractName(params.contractName),
|
|
90
|
+
script: coerceContractBytes(params.script, 'script'),
|
|
91
|
+
abi: coerceContractBytes(params.abi, 'abi'),
|
|
92
|
+
...(params.debug !== undefined ? { debug: coerceContractBytes(params.debug, 'debug') } : {}),
|
|
93
|
+
...(params.manifest ? { manifest: params.manifest } : {}),
|
|
94
|
+
};
|
|
95
|
+
return bundle;
|
|
96
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { ProofOfWork } from '../link/interfaces/ProofOfWork.js';
|
|
2
|
+
import { DomainSettings } from '../types/DomainSettings.js';
|
|
3
|
+
import { bytesToHex } from '../utils/index.js';
|
|
4
|
+
import { ScriptBuilder } from '../vm/index.js';
|
|
5
|
+
import { Transaction } from './Transaction.js';
|
|
6
|
+
import { coerceContractBytes, normalizeContractName } from './ContractArtifacts.js';
|
|
7
|
+
export class ContractTxHelper {
|
|
8
|
+
static buildDeployScript(params) {
|
|
9
|
+
return this.buildContractLifecycleScript('Runtime.DeployContract', params);
|
|
10
|
+
}
|
|
11
|
+
static buildUpgradeScript(params) {
|
|
12
|
+
return this.buildContractLifecycleScript('Runtime.UpgradeContract', params);
|
|
13
|
+
}
|
|
14
|
+
static buildDeployTransaction(params) {
|
|
15
|
+
return this.buildContractLifecycleTransaction(this.buildDeployScript(params), params);
|
|
16
|
+
}
|
|
17
|
+
static buildUpgradeTransaction(params) {
|
|
18
|
+
return this.buildContractLifecycleTransaction(this.buildUpgradeScript(params), params);
|
|
19
|
+
}
|
|
20
|
+
static buildDeployTransactionAndEncode(params) {
|
|
21
|
+
const tx = this.buildDeployTransaction(params);
|
|
22
|
+
this.signTransaction(tx, params.signer, params.proofOfWork);
|
|
23
|
+
return tx.ToStringEncoded(true).toUpperCase();
|
|
24
|
+
}
|
|
25
|
+
static buildUpgradeTransactionAndEncode(params) {
|
|
26
|
+
const tx = this.buildUpgradeTransaction(params);
|
|
27
|
+
this.signTransaction(tx, params.signer, params.proofOfWork);
|
|
28
|
+
return tx.ToStringEncoded(true).toUpperCase();
|
|
29
|
+
}
|
|
30
|
+
static buildDeployScriptFromBundle(bundle, from, gasPrice, gasLimit) {
|
|
31
|
+
return this.buildDeployScript({
|
|
32
|
+
from,
|
|
33
|
+
contractName: bundle.contractName,
|
|
34
|
+
script: bundle.script,
|
|
35
|
+
abi: bundle.abi,
|
|
36
|
+
gasPrice,
|
|
37
|
+
gasLimit,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
static buildUpgradeScriptFromBundle(bundle, from, gasPrice, gasLimit) {
|
|
41
|
+
return this.buildUpgradeScript({
|
|
42
|
+
from,
|
|
43
|
+
contractName: bundle.contractName,
|
|
44
|
+
script: bundle.script,
|
|
45
|
+
abi: bundle.abi,
|
|
46
|
+
gasPrice,
|
|
47
|
+
gasLimit,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
static encodePayloadText(text) {
|
|
51
|
+
return bytesToHex(new TextEncoder().encode(text));
|
|
52
|
+
}
|
|
53
|
+
static buildContractLifecycleScript(interopName, params) {
|
|
54
|
+
// Keep deploy/upgrade on the same legacy VM script path so CLI and wallet flows generate identical transactions.
|
|
55
|
+
const fromAddress = this.normalizeAddress(params.from);
|
|
56
|
+
const contractName = normalizeContractName(params.contractName);
|
|
57
|
+
const scriptBytes = coerceContractBytes(params.script, 'script');
|
|
58
|
+
const abiBytes = coerceContractBytes(params.abi, 'abi');
|
|
59
|
+
const gasPrice = this.normalizeSmallInteger(params.gasPrice ?? this.DefaultGasPrice, 'gasPrice');
|
|
60
|
+
const gasLimit = this.normalizeSmallInteger(params.gasLimit ?? this.DefaultGasLimit, 'gasLimit');
|
|
61
|
+
const nullAddress = new ScriptBuilder().NullAddress;
|
|
62
|
+
return new ScriptBuilder()
|
|
63
|
+
.BeginScript()
|
|
64
|
+
.AllowGas(fromAddress, nullAddress, gasPrice, gasLimit)
|
|
65
|
+
.CallInterop(interopName, [fromAddress, contractName, scriptBytes, abiBytes])
|
|
66
|
+
.SpendGas(fromAddress)
|
|
67
|
+
.EndScript();
|
|
68
|
+
}
|
|
69
|
+
static buildContractLifecycleTransaction(scriptHex, params) {
|
|
70
|
+
// Transaction construction stays separate from signing so callers can inspect or override the unsigned tx first.
|
|
71
|
+
const nexus = params.nexus.trim();
|
|
72
|
+
const chain = (params.chain ?? DomainSettings.RootChainName).trim();
|
|
73
|
+
if (!nexus) {
|
|
74
|
+
throw new Error('nexus cannot be empty');
|
|
75
|
+
}
|
|
76
|
+
if (!chain) {
|
|
77
|
+
throw new Error('chain cannot be empty');
|
|
78
|
+
}
|
|
79
|
+
const payloadHex = params.payloadHex?.trim() ?? '';
|
|
80
|
+
return new Transaction(nexus, chain, scriptHex, params.expiration ?? new Date(Date.now() + 5 * 60 * 1000), payloadHex);
|
|
81
|
+
}
|
|
82
|
+
static signTransaction(tx, signer, proofOfWork = ProofOfWork.Minimal) {
|
|
83
|
+
// Mining/signing is the only mutation stage; everything above should remain deterministic and side-effect free.
|
|
84
|
+
if (proofOfWork > 0) {
|
|
85
|
+
tx.mineTransaction(proofOfWork);
|
|
86
|
+
}
|
|
87
|
+
if (typeof signer === 'string') {
|
|
88
|
+
const trimmedWif = signer.trim();
|
|
89
|
+
if (!trimmedWif) {
|
|
90
|
+
throw new Error('signer WIF cannot be empty');
|
|
91
|
+
}
|
|
92
|
+
tx.sign(trimmedWif);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
tx.signWithKeys(signer);
|
|
96
|
+
}
|
|
97
|
+
static normalizeAddress(address) {
|
|
98
|
+
if (typeof address === 'string') {
|
|
99
|
+
const trimmed = address.trim();
|
|
100
|
+
if (!trimmed) {
|
|
101
|
+
throw new Error('from cannot be empty');
|
|
102
|
+
}
|
|
103
|
+
return trimmed;
|
|
104
|
+
}
|
|
105
|
+
return address;
|
|
106
|
+
}
|
|
107
|
+
static normalizeSmallInteger(value, label) {
|
|
108
|
+
const numeric = typeof value === 'bigint' ? Number(value) : value;
|
|
109
|
+
if (!Number.isSafeInteger(numeric) || numeric <= 0) {
|
|
110
|
+
throw new Error(`${label} must be a positive safe integer`);
|
|
111
|
+
}
|
|
112
|
+
return numeric;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
ContractTxHelper.DefaultGasPrice = DomainSettings.DefaultMinimumGasFee;
|
|
116
|
+
ContractTxHelper.DefaultGasLimit = 100000;
|
|
@@ -14,7 +14,7 @@ export function bytesToHex(bytes) {
|
|
|
14
14
|
}
|
|
15
15
|
export function hexToBytes(hex) {
|
|
16
16
|
const trimmed = hex.trim();
|
|
17
|
-
const normalized = trimmed.startsWith('0x') ? trimmed.slice(2) : trimmed;
|
|
17
|
+
const normalized = trimmed.startsWith('0x') || trimmed.startsWith('0X') ? trimmed.slice(2) : trimmed;
|
|
18
18
|
if (normalized.length === 0) {
|
|
19
19
|
return new Uint8Array();
|
|
20
20
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type ContractBinaryInput = Uint8Array | string;
|
|
2
|
+
export interface ContractArtifactFileEntry {
|
|
3
|
+
path: string;
|
|
4
|
+
size: number;
|
|
5
|
+
sha256: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ContractArtifactManifest {
|
|
8
|
+
format: 'pha.contract.artifacts/v1';
|
|
9
|
+
contractName: string;
|
|
10
|
+
createdAtUtc: string;
|
|
11
|
+
compiler: {
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
sourceFile?: string;
|
|
16
|
+
files: {
|
|
17
|
+
script: ContractArtifactFileEntry;
|
|
18
|
+
abi: ContractArtifactFileEntry;
|
|
19
|
+
debug?: ContractArtifactFileEntry;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface ContractArtifactBundle {
|
|
23
|
+
contractName: string;
|
|
24
|
+
script: Uint8Array;
|
|
25
|
+
abi: Uint8Array;
|
|
26
|
+
debug?: Uint8Array;
|
|
27
|
+
manifest?: ContractArtifactManifest;
|
|
28
|
+
}
|
|
29
|
+
export interface BuildContractArtifactManifestParams {
|
|
30
|
+
contractName: string;
|
|
31
|
+
compilerName: string;
|
|
32
|
+
compilerVersion: string;
|
|
33
|
+
scriptPath: string;
|
|
34
|
+
script: ContractBinaryInput;
|
|
35
|
+
abiPath: string;
|
|
36
|
+
abi: ContractBinaryInput;
|
|
37
|
+
sourceFile?: string;
|
|
38
|
+
debugPath?: string;
|
|
39
|
+
debug?: ContractBinaryInput;
|
|
40
|
+
createdAtUtc?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface BuildContractArtifactBundleParams {
|
|
43
|
+
contractName: string;
|
|
44
|
+
script: ContractBinaryInput;
|
|
45
|
+
abi: ContractBinaryInput;
|
|
46
|
+
debug?: ContractBinaryInput;
|
|
47
|
+
manifest?: ContractArtifactManifest;
|
|
48
|
+
}
|
|
49
|
+
export declare function normalizeContractName(contractName: string): string;
|
|
50
|
+
export declare function coerceContractBytes(input: ContractBinaryInput, label: string): Uint8Array;
|
|
51
|
+
export declare function buildContractArtifactManifest(params: BuildContractArtifactManifestParams): ContractArtifactManifest;
|
|
52
|
+
export declare function buildContractArtifactBundle(params: BuildContractArtifactBundleParams): ContractArtifactBundle;
|
|
53
|
+
//# sourceMappingURL=ContractArtifacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractArtifacts.d.ts","sourceRoot":"","sources":["../../../../src/core/tx/ContractArtifacts.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,2BAA2B,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QACL,MAAM,EAAE,yBAAyB,CAAC;QAClC,GAAG,EAAE,yBAAyB,CAAC;QAC/B,KAAK,CAAC,EAAE,yBAAyB,CAAC;KACnC,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;AAED,MAAM,WAAW,mCAAmC;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,mBAAmB,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iCAAiC;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,GAAG,EAAE,mBAAmB,CAAC;IACzB,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;AAmBD,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAOlE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAwBzF;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,mCAAmC,GAC1C,wBAAwB,CA6C1B;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,iCAAiC,GACxC,sBAAsB,CAWxB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Address } from '../types/Address.js';
|
|
2
|
+
import { PhantasmaKeys } from '../types/PhantasmaKeys.js';
|
|
3
|
+
import { Transaction } from './Transaction.js';
|
|
4
|
+
import { ContractArtifactBundle, ContractBinaryInput } from './ContractArtifacts.js';
|
|
5
|
+
export interface ContractScriptBuildParams {
|
|
6
|
+
from: string | Address;
|
|
7
|
+
contractName: string;
|
|
8
|
+
script: ContractBinaryInput;
|
|
9
|
+
abi: ContractBinaryInput;
|
|
10
|
+
gasPrice?: number | bigint;
|
|
11
|
+
gasLimit?: number | bigint;
|
|
12
|
+
}
|
|
13
|
+
export interface ContractTransactionBuildParams extends ContractScriptBuildParams {
|
|
14
|
+
nexus: string;
|
|
15
|
+
chain?: string;
|
|
16
|
+
expiration?: Date;
|
|
17
|
+
payloadHex?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ContractTransactionSignParams extends ContractTransactionBuildParams {
|
|
20
|
+
signer: string | PhantasmaKeys;
|
|
21
|
+
proofOfWork?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class ContractTxHelper {
|
|
24
|
+
static readonly DefaultGasPrice: number;
|
|
25
|
+
static readonly DefaultGasLimit = 100000;
|
|
26
|
+
static buildDeployScript(params: ContractScriptBuildParams): string;
|
|
27
|
+
static buildUpgradeScript(params: ContractScriptBuildParams): string;
|
|
28
|
+
static buildDeployTransaction(params: ContractTransactionBuildParams): Transaction;
|
|
29
|
+
static buildUpgradeTransaction(params: ContractTransactionBuildParams): Transaction;
|
|
30
|
+
static buildDeployTransactionAndEncode(params: ContractTransactionSignParams): string;
|
|
31
|
+
static buildUpgradeTransactionAndEncode(params: ContractTransactionSignParams): string;
|
|
32
|
+
static buildDeployScriptFromBundle(bundle: ContractArtifactBundle, from: string | Address, gasPrice?: number | bigint, gasLimit?: number | bigint): string;
|
|
33
|
+
static buildUpgradeScriptFromBundle(bundle: ContractArtifactBundle, from: string | Address, gasPrice?: number | bigint, gasLimit?: number | bigint): string;
|
|
34
|
+
static encodePayloadText(text: string): string;
|
|
35
|
+
private static buildContractLifecycleScript;
|
|
36
|
+
private static buildContractLifecycleTransaction;
|
|
37
|
+
private static signTransaction;
|
|
38
|
+
private static normalizeAddress;
|
|
39
|
+
private static normalizeSmallInteger;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=ContractTxHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContractTxHelper.d.ts","sourceRoot":"","sources":["../../../../src/core/tx/ContractTxHelper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAA8C,MAAM,wBAAwB,CAAC;AAEjI,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,GAAG,EAAE,mBAAmB,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA+B,SAAQ,yBAAyB;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,6BAA8B,SAAQ,8BAA8B;IACnF,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,gBAAgB;IAC3B,MAAM,CAAC,QAAQ,CAAC,eAAe,SAAuC;IACtE,MAAM,CAAC,QAAQ,CAAC,eAAe,UAAW;IAE1C,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM;IAInE,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,GAAG,MAAM;IAIpE,MAAM,CAAC,sBAAsB,CAAC,MAAM,EAAE,8BAA8B,GAAG,WAAW;IAIlF,MAAM,CAAC,uBAAuB,CAAC,MAAM,EAAE,8BAA8B,GAAG,WAAW;IAInF,MAAM,CAAC,+BAA+B,CAAC,MAAM,EAAE,6BAA6B,GAAG,MAAM;IAMrF,MAAM,CAAC,gCAAgC,CAAC,MAAM,EAAE,6BAA6B,GAAG,MAAM;IAMtF,MAAM,CAAC,2BAA2B,CAAC,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAW1J,MAAM,CAAC,4BAA4B,CAAC,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAW3J,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI9C,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAqB3C,OAAO,CAAC,MAAM,CAAC,iCAAiC;IAyBhD,OAAO,CAAC,MAAM,CAAC,eAAe;IAsB9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAY/B,OAAO,CAAC,MAAM,CAAC,qBAAqB;CAQrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/tx/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/tx/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|