parkingsv-contract
Version:
ParkingSV library
189 lines (131 loc) • 6.93 kB
Markdown
# 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.