cashscript 0.11.4 → 0.12.0
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 +18 -6
- package/dist/Argument.js +12 -8
- package/dist/Contract.d.ts +3 -9
- package/dist/Contract.js +4 -31
- package/dist/Errors.d.ts +2 -2
- package/dist/SignatureTemplate.d.ts +1 -0
- package/dist/SignatureTemplate.js +13 -4
- package/dist/TransactionBuilder.d.ts +8 -4
- package/dist/TransactionBuilder.js +74 -22
- package/dist/debugging.js +53 -20
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/interfaces.d.ts +10 -6
- package/dist/interfaces.js +6 -3
- package/dist/libauth-template/LibauthTemplate.d.ts +6 -0
- package/dist/libauth-template/LibauthTemplate.js +445 -0
- package/dist/libauth-template/utils.d.ts +27 -0
- package/dist/libauth-template/utils.js +89 -0
- package/dist/network/MockNetworkProvider.d.ts +4 -3
- package/dist/network/MockNetworkProvider.js +4 -14
- package/dist/test/JestExtensions.js +5 -26
- package/dist/types/type-inference.d.ts +8 -6
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +3 -23
- package/package.json +4 -7
- package/dist/LibauthTemplate.d.ts +0 -32
- package/dist/LibauthTemplate.js +0 -324
- package/dist/Transaction.d.ts +0 -45
- package/dist/Transaction.js +0 -385
- package/dist/advanced/LibauthTemplate.d.ts +0 -45
- package/dist/advanced/LibauthTemplate.js +0 -426
|
@@ -5,13 +5,11 @@ expect.extend({
|
|
|
5
5
|
loggerSpy.mockClear();
|
|
6
6
|
// silence actual stdout output
|
|
7
7
|
loggerSpy.mockImplementation(() => { });
|
|
8
|
+
// Run debug, ignoring any errors because we only care about the logs, even if the transaction fails
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
if (error instanceof OldTransactionBuilderError)
|
|
13
|
-
throw error;
|
|
10
|
+
transaction.debug();
|
|
14
11
|
}
|
|
12
|
+
catch (error) { }
|
|
15
13
|
// We concatenate all the logs into a single string - if no logs are present, we set received to undefined
|
|
16
14
|
const receivedBase = loggerSpy.mock.calls.reduce((acc, [log]) => `${acc}\n${log}`, '').trim();
|
|
17
15
|
const received = receivedBase === '' ? undefined : receivedBase;
|
|
@@ -43,14 +41,12 @@ expect.extend({
|
|
|
43
41
|
expect.extend({
|
|
44
42
|
toFailRequireWith(transaction, match) {
|
|
45
43
|
try {
|
|
46
|
-
|
|
44
|
+
transaction.debug();
|
|
47
45
|
const matcherHint = this.utils.matcherHint('.toFailRequireWith', undefined, match.toString(), { isNot: this.isNot });
|
|
48
46
|
const message = () => `${matcherHint}\n\nContract function did not fail a require statement.`;
|
|
49
47
|
return { message, pass: false };
|
|
50
48
|
}
|
|
51
49
|
catch (transactionError) {
|
|
52
|
-
if (transactionError instanceof OldTransactionBuilderError)
|
|
53
|
-
throw transactionError;
|
|
54
50
|
const matcherHint = this.utils.matcherHint('toFailRequireWith', 'received', 'expected', { isNot: this.isNot });
|
|
55
51
|
const expectedText = `Expected pattern: ${this.isNot ? 'not ' : ''}${this.utils.printExpected(match)}`;
|
|
56
52
|
const receivedText = `Received string: ${this.utils.printReceived(transactionError?.message ?? '')}`;
|
|
@@ -66,33 +62,16 @@ expect.extend({
|
|
|
66
62
|
},
|
|
67
63
|
toFailRequire(transaction) {
|
|
68
64
|
try {
|
|
69
|
-
|
|
65
|
+
transaction.debug();
|
|
70
66
|
const message = () => 'Contract function did not fail a require statement.';
|
|
71
67
|
return { message, pass: false };
|
|
72
68
|
}
|
|
73
69
|
catch (transactionError) {
|
|
74
|
-
if (transactionError instanceof OldTransactionBuilderError)
|
|
75
|
-
throw transactionError;
|
|
76
70
|
const receivedText = `Received string: ${this.utils.printReceived(transactionError?.message ?? '')}`;
|
|
77
71
|
const message = () => `Contract function failed a require statement.\n${receivedText}`;
|
|
78
72
|
return { message, pass: true };
|
|
79
73
|
}
|
|
80
74
|
},
|
|
81
75
|
});
|
|
82
|
-
// Wrapper function with custom error in case people use it with the old transaction builder
|
|
83
|
-
// This is a temporary solution until we fully remove the old transaction builder from the SDK
|
|
84
|
-
const executeDebug = (transaction) => {
|
|
85
|
-
const debugResults = transaction.debug();
|
|
86
|
-
if (debugResults instanceof Promise) {
|
|
87
|
-
debugResults.catch(() => { });
|
|
88
|
-
throw new OldTransactionBuilderError();
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
class OldTransactionBuilderError extends Error {
|
|
92
|
-
constructor() {
|
|
93
|
-
super('The CashScript JestExtensions do not support the old transaction builder since v0.11.0. Please use the new TransactionBuilder class.');
|
|
94
|
-
this.name = 'OldTransactionBuilderError';
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
76
|
export {};
|
|
98
77
|
//# sourceMappingURL=JestExtensions.js.map
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type SignatureTemplate from '../SignatureTemplate.js';
|
|
2
|
+
type BytesType = Uint8Array | string;
|
|
3
|
+
type SignatureType = SignatureTemplate | BytesType;
|
|
2
4
|
type TypeMap = {
|
|
3
|
-
[k: `bytes${number}`]:
|
|
5
|
+
[k: `bytes${number}`]: BytesType;
|
|
4
6
|
} & {
|
|
5
|
-
byte:
|
|
6
|
-
bytes:
|
|
7
|
+
byte: BytesType;
|
|
8
|
+
bytes: BytesType;
|
|
7
9
|
bool: boolean;
|
|
8
10
|
int: bigint;
|
|
9
11
|
string: string;
|
|
10
|
-
pubkey:
|
|
11
|
-
sig:
|
|
12
|
-
datasig:
|
|
12
|
+
pubkey: BytesType;
|
|
13
|
+
sig: SignatureType;
|
|
14
|
+
datasig: BytesType;
|
|
13
15
|
};
|
|
14
16
|
type ProcessParam<Param> = Param extends {
|
|
15
17
|
type: infer Type;
|
package/dist/utils.d.ts
CHANGED
|
@@ -38,3 +38,4 @@ export declare const extendedStringify: (obj: any, spaces?: number) => string;
|
|
|
38
38
|
export declare const zip: <T, U>(a: readonly T[], b: readonly U[]) => [T, U][];
|
|
39
39
|
export declare const isFungibleTokenUtxo: (utxo: Utxo) => boolean;
|
|
40
40
|
export declare const isNonTokenUtxo: (utxo: Utxo) => boolean;
|
|
41
|
+
export declare const delay: (ms: number) => Promise<void>;
|
package/dist/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLockingBytecode, lockingBytecodeToCashAddress, binToHex, generateSigningSerializationBch, utf8ToBin, hexToBin,
|
|
2
|
-
import { encodeInt, hash160, hash256, sha256, Op, scriptToBytecode, } from '@cashscript/utils';
|
|
1
|
+
import { cashAddressToLockingBytecode, decodeCashAddress, addressContentsToLockingBytecode, lockingBytecodeToCashAddress, binToHex, generateSigningSerializationBch, utf8ToBin, hexToBin, LockingBytecodeType, encodeTransactionOutput, isHex, bigIntToCompactUint, NonFungibleTokenCapability, bigIntToVmNumber, } from '@bitauth/libauth';
|
|
2
|
+
import { encodeInt, hash160, hash256, sha256, Op, scriptToBytecode, encodeNullDataScript, } from '@cashscript/utils';
|
|
3
3
|
import { Network, } from './interfaces.js';
|
|
4
4
|
import { VERSION_SIZE, LOCKTIME_SIZE } from './constants.js';
|
|
5
5
|
import { OutputSatoshisTooSmallError, OutputTokenAmountTooSmallError, TokensToNonTokenAddressError, UndefinedInputError, } from './Errors.js';
|
|
@@ -220,27 +220,6 @@ export function getNetworkPrefix(network) {
|
|
|
220
220
|
return 'bitcoincash';
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
-
// ////////////////////////////////////////////////////////////////////////////
|
|
224
|
-
// For encoding OP_RETURN data (doesn't require BIP62.3 / MINIMALDATA)
|
|
225
|
-
function encodeNullDataScript(chunks) {
|
|
226
|
-
return flattenBinArray(chunks.map((chunk) => {
|
|
227
|
-
if (typeof chunk === 'number') {
|
|
228
|
-
return new Uint8Array([chunk]);
|
|
229
|
-
}
|
|
230
|
-
const pushdataOpcode = getPushDataOpcode(chunk);
|
|
231
|
-
return new Uint8Array([...pushdataOpcode, ...chunk]);
|
|
232
|
-
}));
|
|
233
|
-
}
|
|
234
|
-
function getPushDataOpcode(data) {
|
|
235
|
-
const { byteLength } = data;
|
|
236
|
-
if (byteLength === 0)
|
|
237
|
-
return Uint8Array.from([0x4c, 0x00]);
|
|
238
|
-
if (byteLength < 76)
|
|
239
|
-
return Uint8Array.from([byteLength]);
|
|
240
|
-
if (byteLength < 256)
|
|
241
|
-
return Uint8Array.from([0x4c, byteLength]);
|
|
242
|
-
throw new Error('Pushdata too large');
|
|
243
|
-
}
|
|
244
223
|
const randomInt = () => BigInt(Math.floor(Math.random() * 10000));
|
|
245
224
|
export const randomUtxo = (defaults) => ({
|
|
246
225
|
...{
|
|
@@ -290,4 +269,5 @@ export const extendedStringify = (obj, spaces) => JSON.stringify(obj, (_, v) =>
|
|
|
290
269
|
export const zip = (a, b) => (Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]));
|
|
291
270
|
export const isFungibleTokenUtxo = (utxo) => (utxo.token !== undefined && utxo.token.amount > 0n && utxo.token.nft === undefined);
|
|
292
271
|
export const isNonTokenUtxo = (utxo) => utxo.token === undefined;
|
|
272
|
+
export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
293
273
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cashscript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Easily write and interact with Bitcoin Cash contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -45,13 +45,10 @@
|
|
|
45
45
|
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@bitauth/libauth": "^3.1.0-next.
|
|
49
|
-
"@cashscript/utils": "^0.
|
|
48
|
+
"@bitauth/libauth": "^3.1.0-next.8",
|
|
49
|
+
"@cashscript/utils": "^0.12.0",
|
|
50
50
|
"@electrum-cash/network": "^4.1.3",
|
|
51
51
|
"@mr-zwets/bchn-api-wrapper": "^1.0.1",
|
|
52
|
-
"@types/node": "^22.17.0",
|
|
53
|
-
"delay": "^6.0.0",
|
|
54
|
-
"fast-deep-equal": "^3.1.3",
|
|
55
52
|
"pako": "^2.1.0",
|
|
56
53
|
"semver": "^7.7.2"
|
|
57
54
|
},
|
|
@@ -64,5 +61,5 @@
|
|
|
64
61
|
"jest": "^29.7.0",
|
|
65
62
|
"typescript": "^5.9.2"
|
|
66
63
|
},
|
|
67
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "dfa7ea1cfe1c369ab1dd9a919ab89b9a98331872"
|
|
68
65
|
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { AbiFunction, AbiInput, Artifact } from '@cashscript/utils';
|
|
2
|
-
import { WalletTemplate, WalletTemplateScenarioBytecode } from '@bitauth/libauth';
|
|
3
|
-
import { Utxo, TokenDetails, LibauthTokenDetails, Output, SignatureAlgorithm, HashType } from './interfaces.js';
|
|
4
|
-
import { Transaction } from './Transaction.js';
|
|
5
|
-
import { EncodedFunctionArgument } from './Argument.js';
|
|
6
|
-
import { Contract } from './Contract.js';
|
|
7
|
-
interface BuildTemplateOptions {
|
|
8
|
-
transaction: Transaction;
|
|
9
|
-
transactionHex?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare const buildTemplate: ({ transaction, transactionHex, }: BuildTemplateOptions) => Promise<WalletTemplate>;
|
|
12
|
-
export declare const getBitauthUri: (template: WalletTemplate) => string;
|
|
13
|
-
export declare const generateTemplateScenarioTransactionOutputLockingBytecode: (csOutput: Output, contract: Contract) => string | {};
|
|
14
|
-
export declare const generateTemplateScenarioBytecode: (input: Utxo, inputIndex: number, p2pkhScriptNameTemplate: string, insertSlot?: boolean) => WalletTemplateScenarioBytecode | ["slot"];
|
|
15
|
-
export declare const generateTemplateScenarioParametersValues: (types: readonly AbiInput[], encodedArgs: EncodedFunctionArgument[]) => Record<string, string>;
|
|
16
|
-
export declare const generateTemplateScenarioParametersFunctionIndex: (abiFunction: AbiFunction, abi: readonly AbiFunction[]) => Record<string, string>;
|
|
17
|
-
export declare const addHexPrefixExceptEmpty: (value: string) => string;
|
|
18
|
-
export declare const generateTemplateScenarioKeys: (types: readonly AbiInput[], encodedArgs: EncodedFunctionArgument[]) => Record<string, string>;
|
|
19
|
-
export declare const formatParametersForDebugging: (types: readonly AbiInput[], args: EncodedFunctionArgument[]) => string;
|
|
20
|
-
export declare const getSignatureAlgorithmName: (signatureAlgorithm: SignatureAlgorithm) => string;
|
|
21
|
-
export declare const getHashTypeName: (hashType: HashType) => string;
|
|
22
|
-
export declare const formatBytecodeForDebugging: (artifact: Artifact) => string;
|
|
23
|
-
export declare const serialiseTokenDetails: (token?: TokenDetails | LibauthTokenDetails) => LibauthTemplateTokenDetails | undefined;
|
|
24
|
-
export interface LibauthTemplateTokenDetails {
|
|
25
|
-
amount: string;
|
|
26
|
-
category: string;
|
|
27
|
-
nft?: {
|
|
28
|
-
capability: 'none' | 'mutable' | 'minting';
|
|
29
|
-
commitment: string;
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
export {};
|
package/dist/LibauthTemplate.js
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { bytecodeToScript, formatBitAuthScript, } from '@cashscript/utils';
|
|
2
|
-
import { hexToBin, decodeTransaction, binToHex, binToBase64, utf8ToBin, isHex, } from '@bitauth/libauth';
|
|
3
|
-
import { deflate } from 'pako';
|
|
4
|
-
import { isUtxoP2PKH, SignatureAlgorithm, HashType, isUnlockableUtxo, isStandardUnlockableUtxo, } from './interfaces.js';
|
|
5
|
-
import SignatureTemplate from './SignatureTemplate.js';
|
|
6
|
-
import { addressToLockScript, extendedStringify, zip } from './utils.js';
|
|
7
|
-
import { generateUnlockingScriptParams } from './advanced/LibauthTemplate.js';
|
|
8
|
-
export const buildTemplate = async ({ transaction, transactionHex = undefined, // set this argument to prevent unnecessary call `transaction.build()`
|
|
9
|
-
}) => {
|
|
10
|
-
const contract = transaction.contract;
|
|
11
|
-
const txHex = transactionHex ?? await transaction.build();
|
|
12
|
-
const template = {
|
|
13
|
-
$schema: 'https://ide.bitauth.com/authentication-template-v0.schema.json',
|
|
14
|
-
description: 'Imported from cashscript',
|
|
15
|
-
name: 'CashScript Generated Debugging Template',
|
|
16
|
-
supported: ['BCH_2025_05'],
|
|
17
|
-
version: 0,
|
|
18
|
-
entities: generateTemplateEntities(contract.artifact, transaction.abiFunction, transaction.encodedFunctionArgs),
|
|
19
|
-
scripts: generateTemplateScripts(contract.artifact, contract.addressType, transaction.abiFunction, transaction.encodedFunctionArgs, contract.encodedConstructorArgs),
|
|
20
|
-
scenarios: generateTemplateScenarios(contract, transaction, txHex, contract.artifact, transaction.abiFunction, transaction.encodedFunctionArgs, contract.encodedConstructorArgs),
|
|
21
|
-
};
|
|
22
|
-
transaction.inputs
|
|
23
|
-
.forEach((input, index) => {
|
|
24
|
-
if (!isUtxoP2PKH(input))
|
|
25
|
-
return;
|
|
26
|
-
const lockScriptName = `p2pkh_placeholder_lock_${index}`;
|
|
27
|
-
const unlockScriptName = `p2pkh_placeholder_unlock_${index}`;
|
|
28
|
-
const placeholderKeyName = `placeholder_key_${index}`;
|
|
29
|
-
const signatureAlgorithmName = getSignatureAlgorithmName(input.template.getSignatureAlgorithm());
|
|
30
|
-
const hashtypeName = getHashTypeName(input.template.getHashType(false));
|
|
31
|
-
const signatureString = `${placeholderKeyName}.${signatureAlgorithmName}.${hashtypeName}`;
|
|
32
|
-
template.entities[contract.name + '_parameters'].scripts.push(lockScriptName, unlockScriptName);
|
|
33
|
-
template.entities[contract.name + '_parameters'].variables = {
|
|
34
|
-
...template.entities[contract.name + '_parameters'].variables,
|
|
35
|
-
[placeholderKeyName]: {
|
|
36
|
-
description: placeholderKeyName,
|
|
37
|
-
name: placeholderKeyName,
|
|
38
|
-
type: 'Key',
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
// add extra unlocking and locking script for P2PKH inputs spent alongside our contract
|
|
42
|
-
// this is needed for correct cross-references in the template
|
|
43
|
-
template.scripts[unlockScriptName] = {
|
|
44
|
-
name: unlockScriptName,
|
|
45
|
-
script: `<${signatureString}>\n<${placeholderKeyName}.public_key>`,
|
|
46
|
-
unlocks: lockScriptName,
|
|
47
|
-
};
|
|
48
|
-
template.scripts[lockScriptName] = {
|
|
49
|
-
lockingType: 'standard',
|
|
50
|
-
name: lockScriptName,
|
|
51
|
-
script: `OP_DUP\nOP_HASH160 <$(<${placeholderKeyName}.public_key> OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG`,
|
|
52
|
-
};
|
|
53
|
-
});
|
|
54
|
-
return template;
|
|
55
|
-
};
|
|
56
|
-
export const getBitauthUri = (template) => {
|
|
57
|
-
const base64toBase64Url = (base64) => base64.replace(/\+/g, '-').replace(/\//g, '_');
|
|
58
|
-
const payload = base64toBase64Url(binToBase64(deflate(utf8ToBin(extendedStringify(template)))));
|
|
59
|
-
return `https://ide.bitauth.com/import-template/${payload}`;
|
|
60
|
-
};
|
|
61
|
-
const generateTemplateEntities = (artifact, abiFunction, encodedFunctionArgs) => {
|
|
62
|
-
const functionParameters = Object.fromEntries(abiFunction.inputs.map((input, index) => ([
|
|
63
|
-
input.name,
|
|
64
|
-
{
|
|
65
|
-
description: `"${input.name}" parameter of function "${abiFunction.name}"`,
|
|
66
|
-
name: input.name,
|
|
67
|
-
type: encodedFunctionArgs[index] instanceof SignatureTemplate ? 'Key' : 'WalletData',
|
|
68
|
-
},
|
|
69
|
-
])));
|
|
70
|
-
const constructorParameters = Object.fromEntries(artifact.constructorInputs.map((input) => ([
|
|
71
|
-
input.name,
|
|
72
|
-
{
|
|
73
|
-
description: `"${input.name}" parameter of this contract`,
|
|
74
|
-
name: input.name,
|
|
75
|
-
type: 'WalletData',
|
|
76
|
-
},
|
|
77
|
-
])));
|
|
78
|
-
const entities = {
|
|
79
|
-
[artifact.contractName + '_parameters']: {
|
|
80
|
-
description: 'Contract creation and function parameters',
|
|
81
|
-
name: artifact.contractName + '_parameters',
|
|
82
|
-
scripts: [
|
|
83
|
-
artifact.contractName + '_lock',
|
|
84
|
-
artifact.contractName + '_unlock',
|
|
85
|
-
],
|
|
86
|
-
variables: {
|
|
87
|
-
...functionParameters,
|
|
88
|
-
...constructorParameters,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
// function_index is a special variable that indicates the function to execute
|
|
93
|
-
if (artifact.abi.length > 1) {
|
|
94
|
-
entities[artifact.contractName + '_parameters'].variables.function_index = {
|
|
95
|
-
description: 'Script function index to execute',
|
|
96
|
-
name: 'function_index',
|
|
97
|
-
type: 'WalletData',
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return entities;
|
|
101
|
-
};
|
|
102
|
-
const generateTemplateScripts = (artifact, addressType, abiFunction, encodedFunctionArgs, encodedConstructorArgs) => {
|
|
103
|
-
// definition of locking scripts and unlocking scripts with their respective bytecode
|
|
104
|
-
return {
|
|
105
|
-
[artifact.contractName + '_unlock']: generateTemplateUnlockScript(artifact, abiFunction, encodedFunctionArgs),
|
|
106
|
-
[artifact.contractName + '_lock']: generateTemplateLockScript(artifact, addressType, encodedConstructorArgs),
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
const generateTemplateLockScript = (artifact, addressType, constructorArguments) => {
|
|
110
|
-
return {
|
|
111
|
-
lockingType: addressType,
|
|
112
|
-
name: artifact.contractName + '_lock',
|
|
113
|
-
script: [
|
|
114
|
-
`// "${artifact.contractName}" contract constructor parameters`,
|
|
115
|
-
formatParametersForDebugging(artifact.constructorInputs, constructorArguments),
|
|
116
|
-
'',
|
|
117
|
-
'// bytecode',
|
|
118
|
-
formatBytecodeForDebugging(artifact),
|
|
119
|
-
].join('\n'),
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
const generateTemplateUnlockScript = (artifact, abiFunction, encodedFunctionArgs) => {
|
|
123
|
-
const functionIndex = artifact.abi.findIndex((func) => func.name === abiFunction.name);
|
|
124
|
-
const functionIndexString = artifact.abi.length > 1
|
|
125
|
-
? ['// function index in contract', `<function_index> // int = <${functionIndex}>`, '']
|
|
126
|
-
: [];
|
|
127
|
-
return {
|
|
128
|
-
// this unlocking script must pass our only scenario
|
|
129
|
-
passes: [artifact.contractName + '_evaluate'],
|
|
130
|
-
name: artifact.contractName + '_unlock',
|
|
131
|
-
script: [
|
|
132
|
-
`// "${abiFunction.name}" function parameters`,
|
|
133
|
-
formatParametersForDebugging(abiFunction.inputs, encodedFunctionArgs),
|
|
134
|
-
'',
|
|
135
|
-
...functionIndexString,
|
|
136
|
-
].join('\n'),
|
|
137
|
-
unlocks: artifact.contractName + '_lock',
|
|
138
|
-
};
|
|
139
|
-
};
|
|
140
|
-
const generateTemplateScenarios = (contract, transaction, transactionHex, artifact, abiFunction, encodedFunctionArgs, encodedConstructorArgs) => {
|
|
141
|
-
const libauthTransaction = decodeTransaction(hexToBin(transactionHex));
|
|
142
|
-
if (typeof libauthTransaction === 'string')
|
|
143
|
-
throw Error(libauthTransaction);
|
|
144
|
-
const scenarios = {
|
|
145
|
-
// single scenario to spend out transaction under test given the CashScript parameters provided
|
|
146
|
-
[artifact.contractName + '_evaluate']: {
|
|
147
|
-
name: artifact.contractName + '_evaluate',
|
|
148
|
-
description: 'An example evaluation where this script execution passes.',
|
|
149
|
-
data: {
|
|
150
|
-
// encode values for the variables defined above in `entities` property
|
|
151
|
-
bytecode: {
|
|
152
|
-
...generateTemplateScenarioParametersFunctionIndex(abiFunction, artifact.abi),
|
|
153
|
-
...generateTemplateScenarioParametersValues(abiFunction.inputs, encodedFunctionArgs),
|
|
154
|
-
...generateTemplateScenarioParametersValues(artifact.constructorInputs, encodedConstructorArgs),
|
|
155
|
-
},
|
|
156
|
-
currentBlockHeight: 2,
|
|
157
|
-
currentBlockTime: Math.round(+new Date() / 1000),
|
|
158
|
-
keys: {
|
|
159
|
-
privateKeys: generateTemplateScenarioKeys(abiFunction.inputs, encodedFunctionArgs),
|
|
160
|
-
},
|
|
161
|
-
},
|
|
162
|
-
transaction: generateTemplateScenarioTransaction(contract, libauthTransaction, transaction),
|
|
163
|
-
sourceOutputs: generateTemplateScenarioSourceOutputs(transaction),
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
return scenarios;
|
|
167
|
-
};
|
|
168
|
-
const generateTemplateScenarioTransaction = (contract, libauthTransaction, csTransaction) => {
|
|
169
|
-
const slotIndex = csTransaction.inputs.findIndex((input) => !isUtxoP2PKH(input));
|
|
170
|
-
const inputs = libauthTransaction.inputs.map((input, inputIndex) => {
|
|
171
|
-
const csInput = csTransaction.inputs[inputIndex];
|
|
172
|
-
return {
|
|
173
|
-
outpointIndex: input.outpointIndex,
|
|
174
|
-
outpointTransactionHash: binToHex(input.outpointTransactionHash),
|
|
175
|
-
sequenceNumber: input.sequenceNumber,
|
|
176
|
-
unlockingBytecode: generateTemplateScenarioBytecode(csInput, inputIndex, 'p2pkh_placeholder_unlock', inputIndex === slotIndex),
|
|
177
|
-
};
|
|
178
|
-
});
|
|
179
|
-
const locktime = libauthTransaction.locktime;
|
|
180
|
-
const outputs = libauthTransaction.outputs.map((output, index) => {
|
|
181
|
-
const csOutput = csTransaction.outputs[index];
|
|
182
|
-
return {
|
|
183
|
-
lockingBytecode: generateTemplateScenarioTransactionOutputLockingBytecode(csOutput, contract),
|
|
184
|
-
token: serialiseTokenDetails(output.token),
|
|
185
|
-
valueSatoshis: Number(output.valueSatoshis),
|
|
186
|
-
};
|
|
187
|
-
});
|
|
188
|
-
const version = libauthTransaction.version;
|
|
189
|
-
return { inputs, locktime, outputs, version };
|
|
190
|
-
};
|
|
191
|
-
export const generateTemplateScenarioTransactionOutputLockingBytecode = (csOutput, contract) => {
|
|
192
|
-
if (csOutput.to instanceof Uint8Array)
|
|
193
|
-
return binToHex(csOutput.to);
|
|
194
|
-
if ([contract.address, contract.tokenAddress].includes(csOutput.to))
|
|
195
|
-
return {};
|
|
196
|
-
return binToHex(addressToLockScript(csOutput.to));
|
|
197
|
-
};
|
|
198
|
-
const generateTemplateScenarioSourceOutputs = (csTransaction) => {
|
|
199
|
-
const slotIndex = csTransaction.inputs.findIndex((input) => !isUtxoP2PKH(input));
|
|
200
|
-
return csTransaction.inputs.map((input, inputIndex) => {
|
|
201
|
-
return {
|
|
202
|
-
lockingBytecode: generateTemplateScenarioBytecode(input, inputIndex, 'p2pkh_placeholder_lock', inputIndex === slotIndex),
|
|
203
|
-
valueSatoshis: Number(input.satoshis),
|
|
204
|
-
token: serialiseTokenDetails(input.token),
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
};
|
|
208
|
-
// Used for generating the locking / unlocking bytecode for source outputs and inputs
|
|
209
|
-
export const generateTemplateScenarioBytecode = (input, inputIndex, p2pkhScriptNameTemplate, insertSlot) => {
|
|
210
|
-
if (insertSlot)
|
|
211
|
-
return ['slot'];
|
|
212
|
-
const p2pkhScriptName = `${p2pkhScriptNameTemplate}_${inputIndex}`;
|
|
213
|
-
const placeholderKeyName = `placeholder_key_${inputIndex}`;
|
|
214
|
-
// This is for P2PKH inputs in the old transaction builder (TODO: remove when we remove old transaction builder)
|
|
215
|
-
if (isUtxoP2PKH(input)) {
|
|
216
|
-
return {
|
|
217
|
-
script: p2pkhScriptName,
|
|
218
|
-
overrides: {
|
|
219
|
-
keys: {
|
|
220
|
-
privateKeys: {
|
|
221
|
-
[placeholderKeyName]: binToHex(input.template.privateKey),
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
if (isUnlockableUtxo(input) && isStandardUnlockableUtxo(input)) {
|
|
228
|
-
return generateUnlockingScriptParams(input, p2pkhScriptNameTemplate, inputIndex);
|
|
229
|
-
}
|
|
230
|
-
// 'slot' means that we are currently evaluating this specific input,
|
|
231
|
-
// {} means that it is the same script type, but not being evaluated
|
|
232
|
-
return {};
|
|
233
|
-
};
|
|
234
|
-
export const generateTemplateScenarioParametersValues = (types, encodedArgs) => {
|
|
235
|
-
const typesAndArguments = zip(types, encodedArgs);
|
|
236
|
-
const entries = typesAndArguments
|
|
237
|
-
// SignatureTemplates are handled by the 'keys' object in the scenario
|
|
238
|
-
.filter(([, arg]) => !(arg instanceof SignatureTemplate))
|
|
239
|
-
.map(([input, arg]) => {
|
|
240
|
-
const encodedArgumentHex = binToHex(arg);
|
|
241
|
-
const prefixedEncodedArgument = addHexPrefixExceptEmpty(encodedArgumentHex);
|
|
242
|
-
return [input.name, prefixedEncodedArgument];
|
|
243
|
-
});
|
|
244
|
-
return Object.fromEntries(entries);
|
|
245
|
-
};
|
|
246
|
-
export const generateTemplateScenarioParametersFunctionIndex = (abiFunction, abi) => {
|
|
247
|
-
const functionIndex = abi.length > 1
|
|
248
|
-
? abi.findIndex((func) => func.name === abiFunction.name)
|
|
249
|
-
: undefined;
|
|
250
|
-
return functionIndex !== undefined ? { function_index: functionIndex.toString() } : {};
|
|
251
|
-
};
|
|
252
|
-
export const addHexPrefixExceptEmpty = (value) => {
|
|
253
|
-
return value.length > 0 ? `0x${value}` : '';
|
|
254
|
-
};
|
|
255
|
-
export const generateTemplateScenarioKeys = (types, encodedArgs) => {
|
|
256
|
-
const typesAndArguments = zip(types, encodedArgs);
|
|
257
|
-
const entries = typesAndArguments
|
|
258
|
-
.filter(([, arg]) => arg instanceof SignatureTemplate)
|
|
259
|
-
.map(([input, arg]) => [input.name, binToHex(arg.privateKey)]);
|
|
260
|
-
return Object.fromEntries(entries);
|
|
261
|
-
};
|
|
262
|
-
export const formatParametersForDebugging = (types, args) => {
|
|
263
|
-
if (types.length === 0)
|
|
264
|
-
return '// none';
|
|
265
|
-
// We reverse the arguments because the order of the arguments in the bytecode is reversed
|
|
266
|
-
const typesAndArguments = zip(types, args).reverse();
|
|
267
|
-
return typesAndArguments.map(([input, arg]) => {
|
|
268
|
-
if (arg instanceof SignatureTemplate) {
|
|
269
|
-
const signatureAlgorithmName = getSignatureAlgorithmName(arg.getSignatureAlgorithm());
|
|
270
|
-
const hashtypeName = getHashTypeName(arg.getHashType(false));
|
|
271
|
-
return `<${input.name}.${signatureAlgorithmName}.${hashtypeName}> // ${input.type}`;
|
|
272
|
-
}
|
|
273
|
-
const typeStr = input.type === 'bytes' ? `bytes${arg.length}` : input.type;
|
|
274
|
-
// we output these values as pushdata, comment will contain the type and the value of the variable
|
|
275
|
-
// e.g. <timeout> // int = <0xa08601>
|
|
276
|
-
return `<${input.name}> // ${typeStr} = <${`0x${binToHex(arg)}`}>`;
|
|
277
|
-
}).join('\n');
|
|
278
|
-
};
|
|
279
|
-
export const getSignatureAlgorithmName = (signatureAlgorithm) => {
|
|
280
|
-
const signatureAlgorithmNames = {
|
|
281
|
-
[SignatureAlgorithm.SCHNORR]: 'schnorr_signature',
|
|
282
|
-
[SignatureAlgorithm.ECDSA]: 'ecdsa_signature',
|
|
283
|
-
};
|
|
284
|
-
return signatureAlgorithmNames[signatureAlgorithm];
|
|
285
|
-
};
|
|
286
|
-
export const getHashTypeName = (hashType) => {
|
|
287
|
-
const hashtypeNames = {
|
|
288
|
-
[HashType.SIGHASH_ALL]: 'all_outputs',
|
|
289
|
-
[HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY]: 'all_outputs_single_input',
|
|
290
|
-
[HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS]: 'all_outputs_all_utxos',
|
|
291
|
-
[HashType.SIGHASH_ALL | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'all_outputs_single_input_INVALID_all_utxos',
|
|
292
|
-
[HashType.SIGHASH_SINGLE]: 'corresponding_output',
|
|
293
|
-
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY]: 'corresponding_output_single_input',
|
|
294
|
-
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_UTXOS]: 'corresponding_output_all_utxos',
|
|
295
|
-
[HashType.SIGHASH_SINGLE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'corresponding_output_single_input_INVALID_all_utxos',
|
|
296
|
-
[HashType.SIGHASH_NONE]: 'no_outputs',
|
|
297
|
-
[HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY]: 'no_outputs_single_input',
|
|
298
|
-
[HashType.SIGHASH_NONE | HashType.SIGHASH_UTXOS]: 'no_outputs_all_utxos',
|
|
299
|
-
[HashType.SIGHASH_NONE | HashType.SIGHASH_ANYONECANPAY | HashType.SIGHASH_UTXOS]: 'no_outputs_single_input_INVALID_all_utxos',
|
|
300
|
-
};
|
|
301
|
-
return hashtypeNames[hashType];
|
|
302
|
-
};
|
|
303
|
-
export const formatBytecodeForDebugging = (artifact) => {
|
|
304
|
-
if (!artifact.debug) {
|
|
305
|
-
return artifact.bytecode
|
|
306
|
-
.split(' ')
|
|
307
|
-
.map((asmElement) => (isHex(asmElement) ? `<0x${asmElement}>` : asmElement))
|
|
308
|
-
.join('\n');
|
|
309
|
-
}
|
|
310
|
-
return formatBitAuthScript(bytecodeToScript(hexToBin(artifact.debug.bytecode)), artifact.debug.sourceMap, artifact.source);
|
|
311
|
-
};
|
|
312
|
-
export const serialiseTokenDetails = (token) => {
|
|
313
|
-
if (!token)
|
|
314
|
-
return undefined;
|
|
315
|
-
return {
|
|
316
|
-
amount: token.amount.toString(),
|
|
317
|
-
category: token.category instanceof Uint8Array ? binToHex(token.category) : token.category,
|
|
318
|
-
nft: token.nft ? {
|
|
319
|
-
capability: token.nft.capability,
|
|
320
|
-
commitment: token.nft.commitment instanceof Uint8Array ? binToHex(token.nft.commitment) : token.nft.commitment,
|
|
321
|
-
} : undefined,
|
|
322
|
-
};
|
|
323
|
-
};
|
|
324
|
-
//# sourceMappingURL=LibauthTemplate.js.map
|
package/dist/Transaction.d.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { WalletTemplate } from '@bitauth/libauth';
|
|
2
|
-
import { AbiFunction } from '@cashscript/utils';
|
|
3
|
-
import { Utxo, Output, Recipient, TokenDetails, TransactionDetails, Unlocker } from './interfaces.js';
|
|
4
|
-
import SignatureTemplate from './SignatureTemplate.js';
|
|
5
|
-
import { Contract } from './Contract.js';
|
|
6
|
-
import { DebugResults } from './debugging.js';
|
|
7
|
-
import { EncodedFunctionArgument } from './Argument.js';
|
|
8
|
-
export declare class Transaction {
|
|
9
|
-
contract: Contract;
|
|
10
|
-
private unlocker;
|
|
11
|
-
abiFunction: AbiFunction;
|
|
12
|
-
encodedFunctionArgs: EncodedFunctionArgument[];
|
|
13
|
-
private selector?;
|
|
14
|
-
inputs: Utxo[];
|
|
15
|
-
outputs: Output[];
|
|
16
|
-
private sequence;
|
|
17
|
-
private locktime;
|
|
18
|
-
private feePerByte;
|
|
19
|
-
private hardcodedFee;
|
|
20
|
-
private minChange;
|
|
21
|
-
private tokenChange;
|
|
22
|
-
constructor(contract: Contract, unlocker: Unlocker, abiFunction: AbiFunction, encodedFunctionArgs: EncodedFunctionArgument[], selector?: number | undefined);
|
|
23
|
-
from(input: Utxo): this;
|
|
24
|
-
from(inputs: Utxo[]): this;
|
|
25
|
-
fromP2PKH(input: Utxo, template: SignatureTemplate): this;
|
|
26
|
-
fromP2PKH(inputs: Utxo[], template: SignatureTemplate): this;
|
|
27
|
-
to(to: string, amount: bigint, token?: TokenDetails): this;
|
|
28
|
-
to(outputs: Recipient[]): this;
|
|
29
|
-
withOpReturn(chunks: string[]): this;
|
|
30
|
-
withAge(age: number): this;
|
|
31
|
-
withTime(time: number): this;
|
|
32
|
-
withHardcodedFee(hardcodedFee: bigint): this;
|
|
33
|
-
withFeePerByte(feePerByte: number): this;
|
|
34
|
-
withMinChange(minChange: bigint): this;
|
|
35
|
-
withoutChange(): this;
|
|
36
|
-
withoutTokenChange(): this;
|
|
37
|
-
build(): Promise<string>;
|
|
38
|
-
send(): Promise<TransactionDetails>;
|
|
39
|
-
send(raw: true): Promise<string>;
|
|
40
|
-
debug(): Promise<DebugResults>;
|
|
41
|
-
bitauthUri(): Promise<string>;
|
|
42
|
-
getLibauthTemplate(): Promise<WalletTemplate>;
|
|
43
|
-
private getTxDetails;
|
|
44
|
-
private setInputsAndOutputs;
|
|
45
|
-
}
|