UNPKG

parkingsv-contract

Version:

ParkingSV library

189 lines (131 loc) 6.93 kB
# scryptlib > Javascript SDK for integration of Bitcoin SV Smart Contracts written in the sCrypt language. ## Installation You can install `scryptlib` in your project as usual: ``` $ npm i scryptlib ``` Then ``` import { buildContractClass, ... } from 'scryptlib'; ``` ## Usage A smart contract is compiled to a locking script template. A contract function call is transformed to an unlocking script. Developers are responsible for setting the locking and unlocking scripts of a transaction properly before sending it to the Bitcoin network. This may include some actions described below: * Instantiate locking script: replace the constructor formal parameters, represented by placeholders in the locking script template, with actual parameters/arguments to form the complete locking script. * Assemble unlocking script: convert the arguments of a contract function call to script format and concatenate them to form the unlocking script. By using `scryptlib`, both scripts can be obtained with ease. ### Contract Description File The compiler outputs results in a JSON file. It’s a representation used to build locking and unlocking scripts. We call this file a **contract description file**. Here's its structure: ``` { "compilerVersion": "0.1.0+commit.312f643", // version of compiler used to produce this file "contract": "DemoP2PKH", // name of the contract "md5": "01234...", // md5 of the contract source code file "abi": [ // ABI of the contract: interfaces of its public functions and constructor. { "type": "constructor", "name": "constructor", "params": [ { "name": "pubKeyHash", "type": "Ripemd160" } ] }, { "type": "function", "name": "unlock", "index": 0, "params": [ { "name": "sig", "type": "Sig" }, { "name": "pubKey", "type": "PubKey" } ] }, ... ], "asm": "$pubKeyHash OP_OVER OP_HASH160 ..." // locking script of the contract in ASM format, including placeholders for constructor parameters } ``` There are two ways to generate this file (named as `xxx_desc.json`): 1. Use **sCrypt VSC extension**; 2. Use the function `compile` in `scryptlib` like: > ``` import { compile } from 'scryptlib'; ... compile( { path: contractFilePath // the file path of the contract }, { desc: true // set this flag to be `true` to get the description file output } ); ``` ### Deploy A Contract and Call Its Function Both **deploying a contract** and **calling a contract function**are achieved by sending a transaction. Generally speaking, * Deploying a contract needs the locking script in the output of this transaction to be set properly; * Calling a contract function needs the unlocking script in the input of this transaction to be set properly; You can use the description file to build a reflected contract class in Javascript like this: > `const MyContract = buildContractClass(JSON.parse(descFileContent));` To create an instance of the contract class, for example: > `const instance = new MyContract(1234, true, ...parameters);` To get the locking script, use: > `const lockingScript = instance.lockingScript;` To convert it to ASM/hex format > `const lockingScriptASM = lockingScript.toASM();` > `const lockingScriptHex = lockingScript.toHex();` Additionally, you can access OP_RETURN data of the contract locking script by using an accessor named `dataLoad`, for example: > `instance.dataLoad = dataInASM;` After that, the `instance.lockingScript` would include the dataLoad automatically. If you want to access the code part of the contract's locking script without `dataLoad` data, use: > `const codePart = instance.codePart;` > `const codePartASM = instance.codePart.toASM();` > `const codePartHex = instance.codePart.toHex();` Also to access the data part (in OP_RETURN) of the contract locking script, use: > `const dataPart = instance.dataPart;` > `const dataPartASM = instance.dataPart.toASM();` > `const dataPartHex = instance.dataPart.toHex();` To get the unlocking script, just call the function and turn the result to bsv script object, for example: > `const unlockingScript = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters).toScript();` To convert it to ASM/hex format > `const unlockingScriptASM = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters).toASM();` > `const unlockingScriptHex = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters).toHex();` Note that `parameters` in both constructor and function call are mapped to sCrypt types as follows: * `boolean`: mapped to sCrypt `bool` * `number`: mapped to sCrypt `int` * `new Bytes(x)` / `new Sig(x)` / `new PubKey(x)` / `new Ripemd160(x)` / : mapped to sCrypt `bytes` / `Sig` / `PubKey` / `Ripemd160` / , where `x` is hex string In this way, the type of parameters could be checked and potential bugs can be detected before running. ### Local Unit Tests Another very useful functionality is provided by a method: `verify(txContext)`. It would run a contract function call with certain arguments locally. If they satisfy all the constraints of the contract function being called, the return value would be `true`, meaning the call succeeds; otherwise it would throw an exception which has a `context` property like: ``` { 'lockingScriptASM': 'OP_1 OP_2 ...', // contract locking script in asm format 'unlockingScriptASM': 'OP_3 OP4 ...', // unlocking script generated by the function call in asm format 'txHex': '1234567890abcdef...', // current transaction represented in hex format inputIndex: 0, // input index flags: 12345, // sighash type of current transaction inputSatoshis: 10000 // input amount in satoshis } ``` It usually appears in unit tests, like: ``` const pass = instance.someFunc(...params).verify( { inputSatoshis, tx } ); expect(pass).to.equal(true) ``` The `txContext` argument provides some context information of the current transaction. It will be required only if `checkSig` or `checkMultiSig` is called inside the function. ``` { inputSatoshis?: number; // input amount in satoshis tx?: any; // current transaction represented in bsv.Transaction object hex?: string; // current transaction represented in hex format inputIndex?: number; // input index, default value: 0 sighashFlags?: number; // sighash type of current transaction, default value: SIGHASH_ALL | SIGHASH_FORKID } ``` You could find more examples using `scryptlib` in the [boilerplate](https://github.com/scrypt-sv/boilerplate) project mentioned above.