From 9bec0724e1f7a1ace5b5478e2e559e165b902dde Mon Sep 17 00:00:00 2001 From: zhfnjust Date: Tue, 17 Oct 2023 20:26:16 +0000 Subject: [PATCH] Deploy website - based on 6be8479c17b89c8d19b3388af46b07debe77050e --- 404.html | 4 ++-- advanced/codeseparator/index.html | 4 ++-- advanced/how-to-add-a-provider/index.html | 4 ++-- advanced/how-to-add-a-signer/index.html | 4 ++-- advanced/how-to-call-multiple-contracts/index.html | 4 ++-- advanced/how-to-debug-scriptcontext/index.html | 4 ++-- advanced/how-to-integrate-scrypt-service/index.html | 4 ++-- advanced/how-to-replay-instance/index.html | 4 ++-- advanced/inline-asm/index.html | 4 ++-- advanced/sighash-type/index.html | 4 ++-- advanced/timeLock/index.html | 4 ++-- assets/js/{505c1d49.f35a9721.js => 505c1d49.25a7d2cf.js} | 2 +- assets/js/572d3f0b.313df141.js | 1 + assets/js/572d3f0b.ff5487e6.js | 1 - assets/js/{6c2dbd7a.dc2dded2.js => 6c2dbd7a.2ff382bb.js} | 2 +- assets/js/{8c9a110f.0e63a235.js => 8c9a110f.f68bc347.js} | 2 +- assets/js/{910cd6a4.4d925563.js => 910cd6a4.16e54899.js} | 2 +- assets/js/9277e5e6.3c068833.js | 1 + assets/js/9277e5e6.8cc741d3.js | 1 - assets/js/a5c7ba44.150ee82c.js | 1 + assets/js/a5c7ba44.446cc638.js | 1 - assets/js/{c150b9b5.2109f321.js => c150b9b5.ebaa03c2.js} | 2 +- assets/js/{db37e9c8.2dd1c583.js => db37e9c8.686b3333.js} | 2 +- assets/js/fa14be71.6613587d.js | 1 + assets/js/fa14be71.f45edac9.js | 1 - ...{runtime~main.dcb92ac9.js => runtime~main.28adef74.js} | 2 +- bitcoin-basics/bsv/index.html | 4 ++-- bitcoin-basics/index.html | 4 ++-- category/advanced/index.html | 4 ++-- category/tutorials/index.html | 4 ++-- ethereum-devs/index.html | 4 ++-- faq/index.html | 4 ++-- how-to-debug-a-contract/index.html | 4 ++-- .../call-deployed/index.html | 4 ++-- how-to-deploy-and-call-a-contract/deploy-cli/index.html | 4 ++-- how-to-deploy-and-call-a-contract/faucet/index.html | 4 ++-- .../how-to-customize-a-contract-tx/index.html | 4 ++-- how-to-deploy-and-call-a-contract/index.html | 4 ++-- .../how-to-integrate-dotwallet/index.html | 4 ++-- how-to-integrate-a-frontend/index.html | 4 ++-- how-to-publish-a-contract/index.html | 4 ++-- how-to-test-a-contract/index.html | 4 ++-- how-to-verify-a-contract/index.html | 4 ++-- how-to-write-a-contract/built-ins/index.html | 4 ++-- how-to-write-a-contract/index.html | 4 ++-- how-to-write-a-contract/scriptcontext/index.html | 4 ++-- how-to-write-a-contract/stateful-contract/index.html | 4 ++-- index.html | 4 ++-- installation/index.html | 4 ++-- overview/index.html | 4 ++-- reference/classes/ActionError/index.html | 4 ++-- reference/classes/BsvApi/index.html | 4 ++-- reference/classes/Constants/index.html | 4 ++-- reference/classes/ContractApi/index.html | 4 ++-- reference/classes/DefaultProvider/index.html | 4 ++-- reference/classes/DotwalletSigner/index.html | 4 ++-- reference/classes/DummyProvider/index.html | 4 ++-- reference/classes/FunctionCall/index.html | 4 ++-- reference/classes/GorillapoolProvider/index.html | 4 ++-- reference/classes/HashedMap/index.html | 4 ++-- reference/classes/HashedSet/index.html | 4 ++-- reference/classes/OpCode/index.html | 4 ++-- reference/classes/Provider/index.html | 4 ++-- reference/classes/ScryptProvider/index.html | 4 ++-- reference/classes/SensibleProvider/index.html | 4 ++-- reference/classes/SensiletSigner/index.html | 4 ++-- reference/classes/SigHash/index.html | 4 ++-- reference/classes/Signer/index.html | 4 ++-- reference/classes/SmartContract/index.html | 4 ++-- reference/classes/SmartContractLib/index.html | 4 ++-- reference/classes/TAALSigner/index.html | 4 ++-- reference/classes/TaalProvider/index.html | 4 ++-- reference/classes/TestWallet/index.html | 4 ++-- reference/classes/Utils/index.html | 4 ++-- reference/classes/VarIntReader/index.html | 4 ++-- reference/classes/VarIntWriter/index.html | 4 ++-- reference/classes/WhatsonchainProvider/index.html | 4 ++-- reference/classes/bsv.Address/index.html | 4 ++-- reference/classes/bsv.Block/index.html | 4 ++-- reference/classes/bsv.BlockHeader/index.html | 4 ++-- reference/classes/bsv.ECIES/index.html | 4 ++-- reference/classes/bsv.HDPrivateKey/index.html | 4 ++-- reference/classes/bsv.HDPublicKey/index.html | 4 ++-- reference/classes/bsv.MerkleBlock/index.html | 4 ++-- reference/classes/bsv.Message/index.html | 4 ++-- reference/classes/bsv.Mnemonic/index.html | 4 ++-- reference/classes/bsv.Opcode/index.html | 4 ++-- reference/classes/bsv.PrivateKey/index.html | 4 ++-- reference/classes/bsv.PublicKey/index.html | 4 ++-- reference/classes/bsv.Script-1/index.html | 4 ++-- reference/classes/bsv.Script.Interpreter-1/index.html | 4 ++-- reference/classes/bsv.Transaction-1/index.html | 4 ++-- reference/classes/bsv.Transaction.Input-1/index.html | 4 ++-- .../bsv.Transaction.Input.PublicKeyHash/index.html | 4 ++-- reference/classes/bsv.Transaction.Output/index.html | 4 ++-- reference/classes/bsv.Transaction.Signature/index.html | 4 ++-- .../classes/bsv.Transaction.UnspentOutput/index.html | 4 ++-- reference/classes/bsv.Unit/index.html | 4 ++-- reference/classes/bsv.crypto.BN/index.html | 4 ++-- reference/classes/bsv.crypto.Point/index.html | 4 ++-- reference/classes/bsv.crypto.Signature/index.html | 4 ++-- reference/classes/bsv.encoding.Base58/index.html | 4 ++-- reference/classes/bsv.encoding.Base58Check/index.html | 4 ++-- reference/classes/bsv.encoding.BufferReader/index.html | 4 ++-- reference/classes/bsv.encoding.BufferWriter/index.html | 4 ++-- reference/classes/bsv.encoding.Varint/index.html | 4 ++-- reference/enums/ProviderEvent/index.html | 4 ++-- reference/enums/SignatureHashType/index.html | 4 ++-- reference/index.html | 4 ++-- reference/interfaces/Artifact/index.html | 4 ++-- reference/interfaces/ContractCalledEvent/index.html | 4 ++-- reference/interfaces/ContractTransaction/index.html | 4 ++-- reference/interfaces/DefaultProviderOption/index.html | 4 ++-- reference/interfaces/LogConfig/index.html | 4 ++-- reference/interfaces/MethodCallOptions/index.html | 4 ++-- reference/interfaces/MethodCallTxBuilder/index.html | 4 ++-- reference/interfaces/MultiContractCallOptions/index.html | 4 ++-- reference/interfaces/MultiContractTransaction/index.html | 4 ++-- reference/interfaces/Outpoint/index.html | 4 ++-- reference/interfaces/RequestConfig/index.html | 4 ++-- reference/interfaces/ScriptContext/index.html | 4 ++-- reference/interfaces/ScryptConfig/index.html | 4 ++-- reference/interfaces/SignTransactionOptions/index.html | 4 ++-- reference/interfaces/SignatureRequest/index.html | 4 ++-- reference/interfaces/SignatureResponse/index.html | 4 ++-- reference/interfaces/StatefulNext/index.html | 4 ++-- reference/interfaces/SubScription/index.html | 4 ++-- reference/interfaces/SubscribeOptions/index.html | 4 ++-- reference/interfaces/TransactionResponse/index.html | 4 ++-- reference/interfaces/TxContext/index.html | 4 ++-- reference/interfaces/TxInputRef/index.html | 4 ++-- reference/interfaces/TxOutputRef/index.html | 4 ++-- reference/interfaces/UtxoQueryOptions/index.html | 4 ++-- reference/interfaces/VerifyResult/index.html | 4 ++-- reference/interfaces/bsv.Networks.Network/index.html | 4 ++-- reference/interfaces/bsv.Script.IOpChunk/index.html | 4 ++-- .../bsv.Script.Interpreter.InterpretState/index.html | 4 ++-- .../interfaces/bsv.Transaction.IUnspentOutput/index.html | 4 ++-- reference/interfaces/bsv.Util/index.html | 4 ++-- reference/interfaces/bsv.crypto.IOpts/index.html | 4 ++-- reference/modules/bsv.Networks/index.html | 4 ++-- reference/modules/bsv.Script.Interpreter/index.html | 4 ++-- reference/modules/bsv.Script/index.html | 4 ++-- reference/modules/bsv.Transaction.Input/index.html | 4 ++-- reference/modules/bsv.Transaction.Sighash/index.html | 4 ++-- reference/modules/bsv.Transaction/index.html | 4 ++-- reference/modules/bsv.crypto.ECDSA/index.html | 4 ++-- reference/modules/bsv.crypto.Hash/index.html | 4 ++-- reference/modules/bsv.crypto.Random/index.html | 4 ++-- reference/modules/bsv.crypto/index.html | 4 ++-- reference/modules/bsv.encoding/index.html | 4 ++-- reference/modules/bsv/index.html | 4 ++-- search/index.html | 4 ++-- tokens/ft/buildstateoutputft/index.html | 4 ++-- tokens/ft/existing/index.html | 6 +++--- tokens/ft/index.html | 8 ++++---- tokens/ft/multiple/index.html | 6 +++--- tokens/index.html | 6 +++--- tokens/nft/buildstateoutputnft/index.html | 4 ++-- tokens/nft/existing/index.html | 6 +++--- tokens/nft/index.html | 8 ++++---- tutorials/auction/index.html | 4 ++-- tutorials/escrow/index.html | 4 ++-- tutorials/hello-world/index.html | 4 ++-- tutorials/inscribe-image/index.html | 6 +++--- tutorials/mint-bsv20-v1/index.html | 6 +++--- tutorials/mint-bsv20-v2/index.html | 6 +++--- tutorials/oracle/index.html | 4 ++-- tutorials/ordinal-lock/index.html | 6 +++--- tutorials/tic-tac-toe/index.html | 4 ++-- tutorials/voting/index.html | 4 ++-- tutorials/zkp/index.html | 4 ++-- 172 files changed, 337 insertions(+), 337 deletions(-) rename assets/js/{505c1d49.f35a9721.js => 505c1d49.25a7d2cf.js} (86%) create mode 100644 assets/js/572d3f0b.313df141.js delete mode 100644 assets/js/572d3f0b.ff5487e6.js rename assets/js/{6c2dbd7a.dc2dded2.js => 6c2dbd7a.2ff382bb.js} (80%) rename assets/js/{8c9a110f.0e63a235.js => 8c9a110f.f68bc347.js} (80%) rename assets/js/{910cd6a4.4d925563.js => 910cd6a4.16e54899.js} (50%) create mode 100644 assets/js/9277e5e6.3c068833.js delete mode 100644 assets/js/9277e5e6.8cc741d3.js create mode 100644 assets/js/a5c7ba44.150ee82c.js delete mode 100644 assets/js/a5c7ba44.446cc638.js rename assets/js/{c150b9b5.2109f321.js => c150b9b5.ebaa03c2.js} (79%) rename assets/js/{db37e9c8.2dd1c583.js => db37e9c8.686b3333.js} (72%) create mode 100644 assets/js/fa14be71.6613587d.js delete mode 100644 assets/js/fa14be71.f45edac9.js rename assets/js/{runtime~main.dcb92ac9.js => runtime~main.28adef74.js} (72%) diff --git a/404.html b/404.html index 2904edb18..34af5ef6a 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | sCrypt - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/advanced/codeseparator/index.html b/advanced/codeseparator/index.html index 6f9f9a479..4d7ba6d50 100644 --- a/advanced/codeseparator/index.html +++ b/advanced/codeseparator/index.html @@ -4,7 +4,7 @@ Use Code Separators | sCrypt - + @@ -14,7 +14,7 @@ This is because conventionally, the signature covers the entire locking script, instead of a subscript with everything before OCS removed. We can achieve this by passing the index of insertCodeSeparator as a method call parameter, to specify which OP_CODESEPARATOR divides the locking script. Let's take a look at an example for the smart contract above:

// Create array of signature options, each for a separate public key.
const pubKeyOrAddrToSign: SignaturesOption = []
for (let i = 0; i < publicKeys.length; i++) {
const pubKey = publicKeys[i]
pubKeyOrAddrToSign.push({
pubKeyOrAddr: pubKey, // The public key for which a signature will be created.
csIdx: i // Index of the `insertCodeSeparator` call, starting from 0
// I.e., if csIdx = 1, then only the code starting from and including
// the second occurence of `insertCodeSeparator` will be signed.
})
}
const callContract = async () => await demo.methods.unlock(
(sigResps) => {
// Inside the signature responses we can observe,
// which instance of the `insertCodeSeparator` the signature
// takes into account:
console.log(sigResps)
return findSigs(sigResps, publicKeys)
},
publicKeys.map((publicKey) => PubKey(toHex(publicKey))) as FixedArray<PubKey, 3>,
{
pubKeyOrAddrToSign
} as MethodCallOptions<CodeSeparator>
)
expect(callContract()).not.throw
- + \ No newline at end of file diff --git a/advanced/how-to-add-a-provider/index.html b/advanced/how-to-add-a-provider/index.html index 8f55848e2..fddd42b71 100644 --- a/advanced/how-to-add-a-provider/index.html +++ b/advanced/how-to-add-a-provider/index.html @@ -4,13 +4,13 @@ How to Add a Provider | sCrypt - +
Skip to main content

How to Add a Provider

In the contract testing section, we learned about the Provider class in sCrypt. This class serves as an abstraction of a Bitcoin node, allowing your application to communicate with the Bitcoin network.

sCrypt provides the following providers by default:

  • DummyProvider: A mockup provider intended for local testing. It does not connect to the Bitcoin blockchain and thus cannot send transactions.

  • DefaultProvider: The default provider is the safest and easiest way to begin developing on Bitcoin, and is also robust enough for use in production. It can be used in testnet as well as mainnet.

  • For a full list of providers, see here.

Implementation

Base Class Provider

To implement your own provider, you must extend the base class Provider. Here's the definition of this class:

/**
* A Provider is an abstraction of non-account-based operations on a blockchain and is generally not directly involved in signing transaction or data.
*/
export abstract class Provider extends EventEmitter {

constructor() {
super()
this._isProvider = true;
}

/**
* check if provider is ready
*/
abstract isConnected(): boolean;

/**
* Implement the connection provider, for example, verify the api key during the connection process.
* @returns a connected provider. Throw an exception if the connection fails.
*/
abstract connect(): Promise<this>;

/**
* update provider network
* @param network Network type to be updated
*/
abstract updateNetwork(network: bsv.Networks.Network): Promise<boolean>;

/**
* @returns The network this provider is connected to.
*/
abstract getNetwork(): Promise<bsv.Networks.Network>;

/**
* @returns The fee rate for sending transactions through this provider.
*/
abstract getFeePerKb(): Promise<number>;

/**
* Get a best guess of the fee for a transaction.
* @param tx A transaction object to estimate.
* @returns The estimated fee in satoshis.
*/
async getEstimateFee(tx: bsv.Transaction): Promise<number> {
const copy = new bsv.Transaction(tx.uncheckedSerialize());
// use a copy bcoz `feePerKb` resets all the signatures for inputs.
copy.feePerKb(await this.getFeePerKb());
return copy.getEstimateFee();
}

// Executions

/**
* Send a raw transaction hex string.
* @param rawTxHex The raw transaction hex string to send.
* @returns A promise which resolves to the hash of the transaction that has been sent.
*/
abstract sendRawTransaction(rawTxHex: string): Promise<TxHash>;

/**
* Send a transaction object.
* @param tx The transaction object to send.
* @returns A promise which resolves to the hash of the transaction that has been sent.
* @throws If there is a problem with the `tx` object during serialization.
*/
sendTransaction(tx: bsv.Transaction): Promise<TxHash> {
// TODO: fix tx.serialize issue
return this.sendRawTransaction(tx.serialize({ disableIsFullySigned: true }));
}

// Queries

/**
* Get a transaction from the network.
* @param txHash The hash value of the transaction.
* @returns The query result with the transaction information.
*/
abstract getTransaction(txHash: TxHash): Promise<TransactionResponse>

/**
* Get a list of the P2PKH UTXOs.
* @param address The address of the returned UTXOs belongs to.
* @param options The optional query conditions, see details in `UtxoQueryOptions`.
* @returns A promise which resolves to a list of UTXO for the query options.
*/
abstract listUnspent(address: AddressOption, options?: UtxoQueryOptions): Promise<UTXO[]>;

/**
* Get the balance of BSVs in satoshis for an address.
* @param address The query address.
* @returns A promise which resolves to the address balance status.
*/
abstract getBalance(address: AddressOption): Promise<{ confirmed: number, unconfirmed: number }>;

/**
* Get a list of UTXO for a certain contract instance.
* @param genesisTxHash The hash value of deployment transaction of the contract instance.
* @param outputIndex The output index of the deployment transaction of the contract instance.
* @returns A promise which resolves to a list of transaction UTXO.
*/
abstract getContractUTXOs(genesisTxHash: TxHash, outputIndex: number): Promise<UTXO[]>;

// Inspection

readonly _isProvider: boolean;

/**
* Check if an object is a `Provider`
* @param value The target object
* @returns Returns `true` if and only if `object` is a Provider.
*/
static isProvider(value: any): value is Provider {
return !!(value && value._isProvider);
}
}

It is recommended that your provider implements all abstract methods. For non-abstract methods, the default implementation is usually sufficient.

Example: WhatsonchainProvider

Let's walk through the process of implementing our own provider. In this example we'll implement a provider for WhatsOnChain (WoC).

  1. First let's implement the isConnected() and connect() functions. Because WoC doesn't need to maintan an open connection, not does it require any authentication by default, it's simply marked as connected by default. If your chosen provider does, here's probably the place to implement the connection logic.
isConnected(): boolean {
return true;
}

override async connect(): Promise<this> {
this.emit(ProviderEvent.Connected, true);
return Promise.resolve(this);
}
  1. Next, we'll implement the network functions. Here, your providers selected network can be toggled. WoC supports both the Bitcoin mainnet along with testnet, so we don't do further checking:
override async updateNetwork(network: bsv.Networks.Network): Promise<boolean> {
this._network = network;
this.emit(ProviderEvent.NetworkChange, network);
return Promise.resolve(true);
}

override async getNetwork(): Promise<bsv.Networks.Network> {
return Promise.resolve(this._network);
}

If your provider is only meant for the testnet, you could do something like this:

override async updateNetwork(network: bsv.Networks.Network): Promise<boolean> {
if (network != bsv.Networks.testnet) {
throw new Error('Network not supported.')
}
this._network = network;
this.emit(ProviderEvent.NetworkChange, network);
return Promise.resolve(true);
}
  1. Now let's set the transaction fee rate. In our example, we hard-code the value to be 50 satoshis per Kb:
override async getFeePerKb(): Promise<number> {
return Promise.resolve(50);
}
  1. Let's implement the function that will send the transaction data to our provider:
override async sendRawTransaction(rawTxHex: string): Promise<TxHash> {
// 1 second per KB
const size = Math.max(1, rawTxHex.length / 2 / 1024); //KB
const timeout = Math.max(10000, 1000 * size);
try {
const res = await superagent.post(
`${this.apiPrefix}/tx/raw`
)
.timeout({
response: timeout, // Wait 5 seconds for the server to start sending,
deadline: 60000, // but allow 1 minute for the file to finish loading.
})
.set('Content-Type', 'application/json')
.send({ txhex: rawTxHex })
return res.body;
} catch (error) {
if (error.response && error.response.text) {
throw new Error(`WhatsonchainProvider ERROR: ${error.response.text}`)
}
throw new Error(`WhatsonchainProvider ERROR: ${error.message}`)
}
}

In the function we use the superagent to send requests to WoC's HTTP endpoint. Check out their docs for a description of the endpoints they provide.

  1. Now we need to implement some queries. First let's implement the function to get a list of UTXO's for a certain address:
override async listUnspent(
address: AddressOption,
options?: UtxoQueryOptions
): Promise<UTXO[]> {

const res = await superagent.get(`${this.apiPrefix}/address/${address}/unspent`);
const utxos: UTXO[] =
res.body.map(item => ({
txId: item.tx_hash,
outputIndex: item.tx_pos,
satoshis: item.value,
script: bsv.Script.buildPublicKeyHashOut(address).toHex(),
}));

if (options?.minSatoshis && utxos.reduce((s, u) => s + u.satoshis, 0) < options.minSatoshis) {
throw new Error(`WhatsonchainProvider ERROR: not enough utxos for the request amount of ${options.minSatoshis} on address ${address.toString()}`);
}

return utxos;
}

Next, we'll make the getBalance function parse out the addresses balance from the UTXO's:

override async getBalance(
address?: AddressOption
): Promise<{ confirmed: number, unconfirmed: number }> {

return this.listUnspent(address, { minSatoshis: 0 }).then(utxos => {
return {
confirmed: utxos.reduce((acc, utxo) => {
acc += utxo.satoshis;
return acc;
}, 0),
unconfirmed: 0
}
})

}

We also implement the function to query the raw transaction using the transactions ID:

override async getTransaction(txHash: string): Promise<TransactionResponse> {
try {
const res = await superagent.get(`${this.apiPrefix}/tx/${txHash}/hex`);
return new bsv.Transaction(res.text)
} catch (e) {
throw new Error(`WhatsonchainProvider ERROR: failed fetching raw transaction data: ${e.message}`);
}
}

Lastly, if our provider doesn't support a certain query, we can simply throw an error by default:

override async getContractUTXOs(genesisTxHash: string, outputIndex?: number): Promise<UTXO[]> {
throw new Error("Method #getContractUTXOs not implemented in WhatsonchainProvider.");
}

Using the Provider

Providers are usually used by a Signer:

const provider = new WhatsonchainProvider(bsv.Networks.mainnet)
const signer = new TestWallet(privateKey, provider)

await contractInstance.connect(signer);

Here, the signer will use our WhatsonchainProvider for each Bitcoin network operation it needs. The next section describes signers and how we can implement a custom one.

- + \ No newline at end of file diff --git a/advanced/how-to-add-a-signer/index.html b/advanced/how-to-add-a-signer/index.html index 40d860a36..8f4f45322 100644 --- a/advanced/how-to-add-a-signer/index.html +++ b/advanced/how-to-add-a-signer/index.html @@ -4,13 +4,13 @@ How to Add a Signer | sCrypt - +
Skip to main content

How to Add a Signer

As described in this section, a signer is an abstraction of private keys, which can be used to sign messages and transactions. A simple signer would be a single private key, while a complex signer is a wallet.

sCrypt provides the following signers by default:

  1. TestWallet : a simple wallet that can hold multiple private keys, with in-memory utxo management. Should only be used for testing.
  2. SensiletSigner: a signer powered by the popular smart contract wallet Sensilet. Can be used in production.

Implementation

Base Class Signer

If you want to implement your own signer, you must inherit from the base class Signer.

/**
* A `Signer` is a class which in some way directly or indirectly has access to a private key, which can sign messages and transactions to authorize the network to perform operations.
*/
export abstract class Signer {

provider?: Provider;
readonly _isSigner: boolean;

constructor(provider?: Provider) {
this._isSigner = true;
this.provider = provider;
}

/**
* Connect a provider to `this`.
* @param provider The target provider.
* @returns
*/
abstract connect(provider: Provider): Promise<this>;

// Account

/**
*
* @returns A promise which resolves to the address to the default private key of the signer.
*/
abstract getDefaultAddress(): Promise<bsv.Address>;

/**
*
* @returns A promise which resolves to the public key of the default private key of the signer.
*/
abstract getDefaultPubKey(): Promise<bsv.PublicKey>;

/**
*
* @param address The request address, using the default address if omitted.
* @returns The public key result.
* @throws If the private key for the address does not belong this signer.
*/
abstract getPubKey(address?: AddressOption): Promise<bsv.PublicKey>;

// Signing

/**
* Sign a raw transaction hex string.
*
* @param rawTxHex The raw transaction hex to sign.
* @param options The options for signing, see the details of `SignTransactionOptions`.
* @returns A promise which resolves to the signed transaction hex string.
* @throws If any input of the transaction can not be signed properly.
*/
abstract signRawTransaction(rawTxHex: string, options: SignTransactionOptions): Promise<string>;

/**
* Sign a transaction object.
* @param tx The transaction object to sign.
* @param options The options for signing, see the details of `SignTransactionOptions`.
* @returns A promise which resolves to the signed transaction object.
*/
abstract signTransaction(tx: bsv.Transaction, options?: SignTransactionOptions): Promise<bsv.Transaction>;

/**
* Sign a message string.
* @param message The message to be signed.
* @param address The optional address whose private key will be used to sign `message`, using the default private key if omitted.
* @returns A promise which resolves to the signautre of the message.
*/
abstract signMessage(message: string, address?: AddressOption): Promise<string>;

/**
* Get the requested transaction signatures for the raw transaction.
* @param rawTxHex The raw transaction hex to get signatures from.
* @param sigRequests The signature requst informations, see details in `SignatureRequest`.
* @returns A promise which resolves to a list of `SignatureReponse` corresponding to `sigRequests`.
*/
abstract getSignatures(rawTxHex: string, sigRequests: SignatureRequest[]): Promise<SignatureResponse[]>;

/**
* Get the connected provider.
* @returns the connected provider.
* @throws if no provider is connected to `this`.
*/
get connectedProvider(): Provider {
if (!this.provider) {
throw new Error(`the provider of singer ${this.constructor.name} is not set yet!`);
}
if (!this.provider.isConnected()) {
throw new Error(`the provider of singer ${this.constructor.name} is not connected yet!`);
}

return this.provider;
}

/**
* Sign the transaction, then broadcast the transaction
* @param tx A transaction is signed and broadcast
* @param options The options for signing, see the details of `SignTransactionOptions`.
* @returns A promise which resolves to the transaction id.
*/
async signAndsendTransaction(tx: bsv.Transaction, options?: SignTransactionOptions): Promise<TransactionResponse> {
await tx.sealAsync();
const signedTx = await this.signTransaction(tx, options);
await this.connectedProvider.sendTransaction(signedTx);
return signedTx;
};

/**
* Get a list of the P2PKH UTXOs.
* @param address The address of the returned UTXOs belongs to.
* @param options The optional query conditions, see details in `UtxoQueryOptions`.
* @returns A promise which resolves to a list of UTXO for the query options.
*/
listUnspent(address: AddressOption, options?: UtxoQueryOptions): Promise<UTXO[]> {
// default implemention using provider, can be overrided.
return this.connectedProvider.listUnspent(address, options);
}

/**
* Get the balance of BSVs in satoshis for an address.
* @param address The query address.
* @returns A promise which resolves to the address balance status.
*/
getBalance(address?: AddressOption): Promise<{ confirmed: number, unconfirmed: number }> {
// default implemention using provider, can be overrided.
return this.connectedProvider.getBalance(address);
}

// Inspection
/**
* Check if an object is a `Signer`
* @param value The target object
* @returns Returns `true` if and only if `object` is a Provider.
*/
static isSigner(value: any): value is Signer {
return !!(value && value._isSigner);
}

}

It is recommended that your signer implements all abstract methods. For non-abstract methods, the default implementation is usually sufficient.

Example: SensiletSigner

Next, we use the Sensilet wallet as an example to show how to implement a SensiletSigner.

  1. In the connect method, you usually attempt to connect to a provider and save it:
override async connect(provider: Provider): Promise<this> {
// we should make sure sensilet is connected before we connect a provider.
const isSensiletConnected = await this.isSensiletConnected();

if(!isSensiletConnected) {
Promise.reject(new Error('Sensilet is not connected!'))
}

if(!provider.isConnected()) {
// connect the provider
await provider.connect();
}

this.provider = provider;
return this;
}
  1. Returns the address to the default private key of the wallet in getDefaultAddress:
/**
* Get an object that can directly interact with the Sensilet wallet,
* if there is no connection with the wallet, it will request to establish a connection.
* @returns SensiletWalletAPI
*/
async getConnectedTarget(): Promise<SensiletWalletAPI> {

const isSensiletConnected = await this.isSensiletConnected();
if (!isSensiletConnected) {
// trigger connecting to sensilet account when it's not connected.
try {
const addr = await this._target.requestAccount();
this._address = bsv.Address.fromString(addr);
} catch (e) {
throw new Error('Sensilet requestAccount failed')
}
}
return this.getSensilet();
}

override async getDefaultAddress(): Promise<bsv.Address> {
//
const sensilet = await this.getConnectedTarget();
const address = await sensilet.getAddress();
return bsv.Address.fromString(address);
}
  1. Returns the public key to the default private key of the wallet in getDefaultPubKey:
override async getDefaultPubKey(): Promise<PublicKey> {
const sensilet = await this.getConnectedTarget();
const pubKey = await sensilet.getPublicKey();
return Promise.resolve(new bsv.PublicKey(pubKey));
}
  1. Since Sensilet is a single-address wallet, we simply ignore the getPubKey method:
override async getPubKey(address: AddressOption): Promise<PublicKey> {
throw new Error(`Method ${this.constructor.name}#getPubKey not implemented.`);
}
  1. Both signTransaction and signRawTransaction sign the transaction, but their parameters are different. signRawTransaction converts the parameters and delegates the implementation of the signing to signTransaction.

The following are types used in these two functions:


/**
* `SignatureRequest` contains required informations for a signer to sign a certain input of a transaction.
*/
export interface SignatureRequest {
/** The index of input to sign. */
inputIndex: number;
/** The previous output satoshis value of the input to spend. */
satoshis: number;
/** The address(es) of corresponding private key(s) required to sign the input. */
address: AddressesOption;
/** The previous output script of input, default value is a P2PKH locking script for the `address` if omitted. */
scriptHex?: string;
/** The sighash type, default value is `SIGHASH_ALL | SIGHASH_FORKID` if omitted. */
sigHashType?: number;
/** The extra information for signing. */
data?: unknown;
}

/**
* `SignatureResponse` contains the signing result corresponding to a `SignatureRequest`.
*/
export interface SignatureResponse {
/** The index of input. */
inputIndex: number;
/** The signature.*/
sig: string;
/** The public key bound with the `sig`. */
publicKey: string;
/** The sighash type, default value is `SIGHASH_ALL | SIGHASH_FORKID` if omitted. */
sigHashType: number;
}

/**
* `SignTransactionOptions` is the options can be provided when signing a transaction.
*/
export interface SignTransactionOptions {
/** The `SignatureRequest` for the some inputs of the transaction. */
sigRequests?: SignatureRequest[];
/** The address(es) whose corresponding private key(s) should be used to sign the tx. */
address?: AddressesOption;
}

signTransaction will convert the above parameter types to the parameter types required by the sensilet api. And call the sensilet api to complete the signature, which is implemented in getSignatures function.

override async signRawTransaction(rawTxHex: string, options: SignTransactionOptions): Promise<string> {
// convert `rawTxHex` to a transation object
const sigReqsByInputIndex: Map<number, SignatureRequest> = (options?.sigRequests || []).reduce((m, sigReq) => { m.set(sigReq.inputIndex, sigReq); return m; }, new Map());
const tx = new bsv.Transaction(rawTxHex);
tx.inputs.forEach((_, inputIndex) => {
const sigReq = sigReqsByInputIndex.get(inputIndex);
if (!sigReq) {
throw new Error(`\`SignatureRequest\` info should be provided for the input ${inputIndex} to call #signRawTransaction`)
}
const script = sigReq.scriptHex ? new bsv.Script(sigReq.scriptHex) : bsv.Script.buildPublicKeyHashOut(sigReq.address.toString());
// set ref output of the input
tx.inputs[inputIndex].output = new bsv.Transaction.Output({
script,
satoshis: sigReq.satoshis
})
});

const signedTx = await this.signTransaction(tx, options);
return signedTx.toString();
}

override async signTransaction(tx: Transaction, options?: SignTransactionOptions): Promise<Transaction> {

const network = await this.getNetwork();
// Generate default `sigRequests` if not passed by user
const sigRequests: SignatureRequest[] = options?.sigRequests?.length ? options.sigRequests :

tx.inputs.map((input, inputIndex) => {
const useAddressToSign = options && options.address ? options.address :
input.output?.script.isPublicKeyHashOut()
? input.output.script.toAddress(network)
: this._address;

return {
inputIndex,
satoshis: input.output?.satoshis,
address: useAddressToSign,
scriptHex: input.output?.script?.toHex(),
sigHashType: DEFAULT_SIGHASH_TYPE,
}
})

const sigResponses = await this.getSignatures(tx.toString(), sigRequests);

// Set the acquired signature as an unlocking script for the transaction
tx.inputs.forEach((input, inputIndex) => {
const sigResp = sigResponses.find(sigResp => sigResp.inputIndex === inputIndex);
if (sigResp && input.output?.script.isPublicKeyHashOut()) {
var unlockingScript = new bsv.Script("")
.add(Buffer.from(sigResp.sig, 'hex'))
.add(Buffer.from(sigResp.publicKey, 'hex'));

input.setScript(unlockingScript)
}
})

return tx;
}

/**
* Get signatures with sensilet api
* @param rawTxHex a transation raw hex
* @param sigRequests a `SignatureRequest` array for the some inputs of the transaction.
* @returns a `SignatureResponse` array
*/
async getSignatures(rawTxHex: string, sigRequests: SignatureRequest[]): Promise<SignatureResponse[]> {
const network = await this.getNetwork()
// convert `sigRequests` to the parameter type required by sensilet `signTx` api
const inputInfos = sigRequests.flatMap((sigReq) => {
const addresses = parseAddresses(sigReq.address, network);
return addresses.map(address => {
return {
txHex: rawTxHex,
inputIndex: sigReq.inputIndex,
scriptHex: sigReq.scriptHex || bsv.Script.buildPublicKeyHashOut(address).toHex(),
satoshis: sigReq.satoshis,
sigtype: sigReq.sigHashType || DEFAULT_SIGHASH_TYPE,
address: address.toString()
}
});
});

const sensilet = await this.getConnectedTarget();
// call sensilet `signTx` api to sign transaction
// https://doc.sensilet.com/guide/sensilet-api.html#signtx
const sigResults = await sensilet.signTx({
list: inputInfos
});

return inputInfos.map((inputInfo, idx) => {
return {
inputIndex: inputInfo.inputIndex,
sig: sigResults.sigList[idx].sig,
publicKey: sigResults.sigList[idx].publicKey,
sigHashType: sigRequests[idx].sigHashType || DEFAULT_SIGHASH_TYPE
}
})
}
  1. Sensilet supports signing messages, if your wallet does not support it, you can throw an exception in the signMessage function:
override async signMessage(message: string, address?: AddressOption): Promise<string> {
if (address) {
throw new Error(`${this.constructor.name}#signMessge with \`address\` param is not supported!`);
}
const sensilet = await this.getConnectedTarget();
return sensilet.signMessage(message);
}

So far, we have implemented all abstract methods. The remaining non-abstract methods can reuse the default implementation, that is, delegating to the connected provider. If you have a customized implementation, you can override them. For example, we can use the Sensilet api getBsvBalance to obtain the balance of an address.

override getBalance(address?: AddressOption): Promise<{ confirmed: number, unconfirmed: number }> {
if(address) {
return this.connectedProvider.getBalance(address);
}
return this.getConnectedTarget().then(target => target.getBsvBalance()).then(r => r.balance)
}

Now we have implemented SensiletSigner. The full code is here.

Use your signer

Just connect your signer to a smart contract instance like any other signers:

// declare your signer
const your_signer = new YourSigner(new DefaultProvider());
// connect the signer to the contract instance
await instance.connect(your_signer);
- + \ No newline at end of file diff --git a/advanced/how-to-call-multiple-contracts/index.html b/advanced/how-to-call-multiple-contracts/index.html index 7632c50f8..b59a93073 100644 --- a/advanced/how-to-call-multiple-contracts/index.html +++ b/advanced/how-to-call-multiple-contracts/index.html @@ -4,13 +4,13 @@ Call Multiple Contracts in a Single Tx | sCrypt - +
Skip to main content

Call Multiple Contracts in a Single Tx

Up to now, we have only shown how to call one smart contract in a transaction. That is, only one input of the tx spends a smart contract UTXO, and the other inputs, if any, spend Pay-to-Public-Key-Hash (P2PKH) UTXOs, which are generally NOT regarded as smart contracts.

There are cases where it is desirable to spend multiple smart contract UTXOs in different inputs of a tx.

The main differences from calling a single contract are:

  1. Set multiContractCall = true in MethodCallOptions
  2. Each call may only return a partial/incomplete transaction, instead of a complete transaction
  3. A partial tx has to be passed as ContractTransaction in MethodCallOptions in subsequent calls
  4. Finally invoke SmartContract.multiContractCall(partialContractTx: ContractTransaction, signer: Signer) to sign and broadcast the complete transaction

The following is an example code of calling two contracts at the same time:

import { Counter } from '../../src/contracts/counter'
import { getDefaultSigner } from '../utils/helper'
import { HashPuzzle } from '../../src/contracts/hashPuzzle'

async function main() {
await Counter.loadArtifact()
await HashPuzzle.loadArtifact()

const signer = getDefaultSigner()
let counter = new Counter(1n)

// connect to a signer
await counter.connect(signer)

// contract deployment
const deployTx = await counter.deploy(1)
console.log('Counter contract deployed: ', deployTx.id)

counter.bindTxBuilder(
'incrementOnChain',
(
current: Counter,
options: MethodCallOptions<Counter>,
...args: any
): Promise<ContractTransaction> => {
// create the next instance from the current
const nextInstance = current.next()
// apply updates on the next instance locally
nextInstance.count++

const tx = new bsv.Transaction()
tx.addInput(current.buildContractInput()).addOutput(
new bsv.Transaction.Output({
script: nextInstance.lockingScript,
satoshis: current.balance,
})
)

return Promise.resolve({
tx: tx,
atInputIndex: 0,
nexts: [
{
instance: nextInstance,
balance: current.balance,
atOutputIndex: 0,
},
],
})
}
)

const plainText = 'abc'
const byteString = toByteString(plainText, true)
const sha256Data = sha256(byteString)

const hashPuzzle = new HashPuzzle(sha256Data)

// connect to a signer
await hashPuzzle.connect(signer)

const deployTx1 = await hashPuzzle.deploy(1)
console.log('HashPuzzle contract deployed: ', deployTx1.id)

hashPuzzle.bindTxBuilder(
'unlock',
(
current: HashPuzzle,
options: MethodCallOptions<HashPuzzle>,
...args: any
): Promise<ContractTransaction> => {
if (options.partialContractTx) {
const unSignedTx = options.partialContractTx.tx
unSignedTx.addInput(
current.buildContractInput()
)

return Promise.resolve({
tx: unSignedTx,
atInputIndex: 1,
nexts: [],
})
}

throw new Error('no partialContractTx found')
}
)

const partialTx = await counter.methods.incrementOnChain({
multiContractCall: true,
} as MethodCallOptions<Counter>)

const finalTx = await hashPuzzle.methods.unlock(
byteString,
{
multiContractCall: true,
partialContractTx: partialTx,
} as MethodCallOptions<HashPuzzle>
)

const { tx: callTx, nexts } = await SmartContract.multiContractCall(
finalTx,
signer
)

console.log('Counter, HashPuzzle contract `unlock` called: ', callTx.id)

// hashPuzzle has terminated, but counter can still be called
counter = nexts[0].instance
}

await main()

note
  • You must bind a transaction builder to each contract instance, since the default only spends a single contract UTXO.
  • If the called contracts need signatures from different private keys to be called, the signer passed to multiContractCall must have all private keys.
- + \ No newline at end of file diff --git a/advanced/how-to-debug-scriptcontext/index.html b/advanced/how-to-debug-scriptcontext/index.html index 3a189419e..b750263bc 100644 --- a/advanced/how-to-debug-scriptcontext/index.html +++ b/advanced/how-to-debug-scriptcontext/index.html @@ -4,13 +4,13 @@ How to Debug ScriptContext Failure | sCrypt - +
Skip to main content

How to Debug ScriptContext Failure

ScriptContext enables the logic of the contract to be executed correctly according to the agreement, and the state of the contract to be propagated correctly.

When it runs incorrectly, you need to master the following methods to locate the error more efficiently.

hashOutputs assertion failed

The hashOutputs field of ScriptContext is the double SHA256 of the serialization of all output amount (8-byte little endian) with scriptPubKey. Through it, we can agree on how the outputs of the transaction calling the contract should be constructed.

If the output of the transaction is not constructed as required by the contract, then the hashOutputs of ScriptContext field will not match the the double SHA256 of the outputs produced in the code when the contract runs. The following assertion will fail:

assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')

We all know that if the preimage of the hash is inconsistent, the hash value will not match. When an assertion failure occurs, we can only see two mismatched hash values, and cannot visually see the difference between the preimages of the two hash values (that is, the outputs in the contract and the outputs of the transaction).

A function diffOutputs in DebugFunctions Interface is provided to directly compare the difference between the outputs argument and all the outputs of the transaction bound by this.to, which are serialized and hashed to produce the hashOutputs field of ScriptContext.

Just call this.debug.diffOutputs(outputs) in the contract:

this.debug.diffOutputs(outputs) // diff and print the comparison result
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')

and you will see the comparison result:

diffoutputs

Through the printed comparison results, we can intuitively see that the number of satoshis included in the output calculated in the contract is different from the number of satoshis included in the output actually added when constructing the transaction. Now, we have found the source of the error.

- + \ No newline at end of file diff --git a/advanced/how-to-integrate-scrypt-service/index.html b/advanced/how-to-integrate-scrypt-service/index.html index ae2285400..4607e4ead 100644 --- a/advanced/how-to-integrate-scrypt-service/index.html +++ b/advanced/how-to-integrate-scrypt-service/index.html @@ -4,13 +4,13 @@ How to Integrate sCrypt Service | sCrypt - +
Skip to main content

How to Integrate sCrypt Service

Before interacting with a sCrypt contract, we must create a contract instance representing the latest state of the contract on chain. Such an instance can be created by calling the fromTx method. However, this means your application needs to track and record all contract-related transactions, especially for a stateful contract.

An easier alternative is to leverage sCrypt infrastructure service, which tracks such transactions, so you can focus on your application logic.

Get Your API Key

Step 1: Create Your Free Account

Go to the sCrypt homepage to create your free account.

Step 2: Get API Key

Sign in and click on the copy icon to copy your API Key.

Integration

Once you have an API key, you can easily integrate sCrypt service into your app by following these simple steps.

Step 1: Initialize Client

You can pass the API key, along with network, to the Scrypt.init function to initialize an sCrypt client in your app.

import { Scrypt, bsv } from 'scrypt-ts'

Scrypt.init({
apiKey: 'YOUR_API_KEY',
network: bsv.Networks.testnet,
})

Step 2: Connect ScryptProvider with your signer

Connect signer to ScryptProvider, the required provider to use sCrypt service.

const signer = new TestWallet(myPrivateKey)
await signer.connect(new ScryptProvider())

Step 3: Get Contract ID

Each contract is uniquely identified by the transaction that deploy it and the output it is in, which we regard as its ID.

const counter = new Counter(0n)
// connect signer
await counter.connect(signer)

const balance = 1
const deployTx = await counter.deploy(balance)
console.log('contract Counter deployed: ', deployTx.id)

const contractId = {
/** The deployment transaction id */
txId: deployTx.id,
/** The output index */
outputIndex: 0,
}

You can usually get the ID of a contract from its creator, who publicizes it so others can interact with it.

Step 4: Get Contract Instance

Once you have the contract ID, you can easily create a contract instance as follows.

const currentInstance = await Scrypt.contractApi.getLatestInstance(
Counter,
contractId
)

// connect signer
await currentInstance.connect(signer)

For a stateless contract, the instance points to the deployment tx; for a stateful one, it points to the latest tip in a chain of txs, which sCrypt service tracks automatically.

Interact with the Contract

Once you have the instance after following the steps above, you can easily read from the contract, write to it, and listen to it.

Read

You read an instance's properties using the dot operator, like any other object.

// read @prop count
console.log(counter.count)
note

Reading does NOT broadcast a transaction to the blockchain.

Write

To update a contract instance, you call its public method as before, which writes to the blockchain by broadcasting a transaction.

// call the method of current instance to apply the updates on chain
const { tx } = await currentInstance.methods.incrementOnChain()

console.log(`Counter contract called, tx: ${tx.id}`)

Listen to Events

Often, your app needs to be notified when a contract gets called and updated. It is essential to be able to listen to such events in real time that can alert your app whenever something relevant occurs on chain. For example, in your front-end, you can refresh the web page to show the user the latest state of a contract, upon event notifications.

With the sCrypt service, you can easily subscribe to a contract's events by its contract ID, using ethier websockets (client side) or webhooks (server side) per your requirements.

Websockets

To use websockets to listen for contract events, just use the Scrypt.contractApi.subscribe dedicated API in our client SDK, which takes two parameters:

  1. options: SubscribeOptions<T>: it includes a contract class, a contract ID, and a optional list of method names monitored.
interface SubscribeOptions<T> {
clazz: new (...args: any) => T;
id: ContractId;
methodNames?: Array<string>;
}

If methodNames is set, you will be notified only when public functions in the list are called. Otherwise, you will be notified when ANY public function is called.

  1. callback: (event: ContractCalledEvent<T>) => void: a callback funciton upon receiving notifications.

ContractCalledEvent<T> contains relevant information on how the contract is called:

  • methodName: string, which public method is called

  • args: SupportedParamType[], arguments the public method is called with

  • tx: bsv.Transaction, transaction where contract is called from

  • nexts: Array[T], includes the new contract instances created by this call. If a stateful contract is called, nexts contains the contract instances containing the new state generated by this call. You can read the latest state from the new contract instance to, e.g., display the new state to users. If a stateless contract is called, nexts is empty.

Below is an example of listening to events when incrementOnChain method is called.

const subscription = Scrypt.contractApi.subscribe({
clazz: Counter, // contract class
id: contractId, // contract id
methodNames: ['incrementOnChain']
}, (event: ContractCalledEvent<Counter>) => {
// callback when receiving a notification
console.log(`${event.methodName} is called with args: ${event.args}`)
});
note

When using this API, you do not need any backend services of your own; the code usually runs in your users' browsers. There is a security issue because of exposure of your API key. So it’s highly recommended that you just use it in demo projects for trusted users.

Webhooks

There is an alternative for listening to contract events in a more secure and effective way. Just use our webhook service to push event data to your own backend service.

Webhook Management

First, you need to create a valid webhook in our service before trying to receive any event data. You can manage webhooks on the webhooks page of our dashboard.

To create a valid webhook, you need to provide the following information:

  1. Webhook URL

This is the specified URL of your backend service for receving the associated event data.

  1. Network

A webhook can only receive events from a single network. It must be either testnet or mainnet.

  1. Contract ID

A webhook must listen to a certain contract ID. In other words, it will be notified only when this contract is called on chain.

Please note that the contract can only be listened to if it is deployed and called using our SDK or services.

  1. Contract Artifact

A contract artifact is also needed to decode call data on chain. You can usually find it in the artifact folder of your sCrypt project. It is required if the contract ID was newly registered to our service. It becomes optional if it has been registered before. Also, you can only update artifacts registered first by you.

Besides adding webhooks in dashboard, you can add them programmatically.


const fs = require('fs').promises;
const util = require('util');

// Async function to read a JSON file
async function readArtifactFromFile(filePath) {
try {
// Read the file using fs.promises.readFile and await for the result
const data = await fs.readFile(filePath, 'utf8');

// Parse the JSON data
const jsonData = JSON.parse(data);

// Return the parsed JSON object
return jsonData;
} catch (error) {
// Handle errors, e.g., file not found
throw new Error('Error reading JSON file: ' + error.message);
}
}


async function main() {
try {
// Provide the path to your JSON artifact file
const artifactFilePath = 'path_to_your_json_file.json';

// Fetch the JSON artifact data from the file
const artifact = await readArtifactFromFile(artifactFilePath);

const apiKey = '[Your API key]';
const webhookUrl = 'https://api.scrypt.io/webhooks/create'; // Use 'https://testnet-api.scrypt.io' for testnet

const requestBody = {
url: 'http://127.0.0.1:3005/api/webhooks/test_notify',
contractId: {
txId: "1fa604263d2a16f6292f788e391b83ea7037fb9eb2ed0055ab5802ab2d090ef5",
outputIndex: 0
},
desc: "test webhook",
artifact: artifact // Use the fetched artifact data here
};

const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify(requestBody)
});

if (!response.ok) {
throw new Error('Failed to create webhook');
}

const responseData = await response.json();
console.log(responseData);
} catch (error) {
console.error('Error:', error);
}
}

// Call the main function to start the process
main();

Webhook Request and Response

When a contract is called on chain, we will push event data through a http POST request with a body like this to your webhook URL:

{
"webhookId": "wh_EyY2zEnogmK9e57Q",
"createdAt": "2023-07-24T04:00:32.246Z",
"events": [{
"eventType": "utxoSpent",
"spentUtxo": {
"txId": "966a3fb5d46c673ceaef2a476e828b75a6e6eae28839b36c0ff42cddc7a28f5b",
"outputIndex": 0
},
"contractId": {
"txId": "966a3fb5d46c673ceaef2a476e828b75a6e6eae28839b36c0ff42cddc7a28f5b",
"outputIndex": 0
},
"spentBy": {
"txId": "c359669cef68509d8357741e57bdff29f731c28643596d2c49f12dcd633e89f7",
"inputIndex": 0
},
"createdInSpentTxOutputs": [
0
],
"id": "evt_6XnqNUIhoZJ6SaEg5sDGcC",
"methodName": "vote",
"args": [{
"name": "name",
"type": "bytes",
"value": "6950686f6e65"
}]
}]
}

The request details the events data:

  • eventType: The type name of the event. Currently only utxoSpent available.

  • spentUtxo: The specified utxo of the contract spent in the event.

  • contractId: The contract ID that the event belongs to.

  • spentBy: The specified input index of the contract call tx from which the event comes.

  • createdInSpentTxOutputs: Newly generated contract utxo(s) in the spent tx if it's a stateful contract.

  • id: Unique event id.

  • methodName: The method name of the contract call of the event.

  • args: The argument list of the contract call of the event.

You need to return a HTTP code of 200 for a successful acknowledgement. We will automatically pause the webhook after several unsuccessful deliveries. You need to manually reactivate it on the webhooks page before we start pushing notifications to it again. For a single event, there might be more than one notification pushed to the webhook, so make sure you have this situation handled.

Webhook Security

To keep your webhook requests secure, we add a signature header x-scrypt-signature for each request by signing the request data with your own API key using the HMAC-SHA256 algorithm. You can verify it if you want. It can be generated using code like this:

const signature = crypto.createHmac('sha256', apiKey).update(JSON.stringify(body)).digest('hex');
Webhook Limit

The number of webhooks that each user can create is limited. The following is the limit on the number of webhooks that users of different plans can create.

Planlimt on testnetlimt on mainnet
Starter1010
Pro100100
Business200200
Enterprise300300
- + \ No newline at end of file diff --git a/advanced/how-to-replay-instance/index.html b/advanced/how-to-replay-instance/index.html index 39202c99d..d7a2faddb 100644 --- a/advanced/how-to-replay-instance/index.html +++ b/advanced/how-to-replay-instance/index.html @@ -4,13 +4,13 @@ How to Replay an Instance to the Latest States | sCrypt - +
Skip to main content

How to Replay an Instance to the Latest States

Using sCrypt Service and sCrypt client, we can effortlessly create a contract instance reflecting the latest state as follows:

const currentInstance = await Scrypt.contractApi.getLatestInstance(
Counter,
contractId
)

However, this method is ineffective for smart contracts with states of type HashedMap or HashedSet. This is because each instance only contains hashed values, not the original ones.

In this section, we'll use contract CrowdfundReplay located at src/contracts/crowdfundReplay.ts as a reference to explain how to replay these contract instances to their latest states.

This crowdfund contract features a HashedMap donators that records the donors' public key and their respective donation satoshi amounts.

export type Donators = HashedMap<PubKey, bigint>

export class CrowdfundReplay extends SmartContract {

@prop(true)
donators: Donators

...
}

This contract has three public methods:

  • donate adds an entry to the HashedMap.
  • refund removes a specific donator from the map.
  • collect destroys the contract without updating any stateful properties.
export type Donators = HashedMap<PubKey, bigint>

export class CrowdfundReplay extends SmartContract {
...

@method()
public donate(donator: PubKey, amount: bigint) {
...
assert(!this.donators.has(donator), 'donator already exists')
this.donators.set(donator, amount)
...
}

@method()
public refund(donator: PubKey, amount: bigint, sig: Sig) {
...
assert(this.donators.canGet(donator, amount), 'not donated before')
assert(this.donators.delete(donator), 'failed to remove donator')
...
}

@method()
public collect(sig: Sig) {
...
}
}

To replay the contract instance to the latest states, follow these three steps:

Step 1. Offchain Helper Functions

Initially, add helper functions that update stateful properties in a manner identical to the public methods.

These functions are defined within the offchainUpdates object:

class CrowdfundReplay extends SmartContract {

...

offchainUpdates: OffchainUpdates<CrowdfundReplay> = {
'donate': (next: CrowdfundReplay, donator: PubKey, amount: bigint) => {
next.donators.set(donator, amount)
},
'refund': (next: CrowdfundReplay, donator: PubKey) => {
next.donators.delete(donator)
},
}

...
}
note

The object keys must match the public method names precisely.

In our example, we only need two helper functions since the collect method doesn't alter any stateful properties.

Step 2. Create Instance from Deployment Tx

Retrieve the deployment transaction using the contract ID. Subsequently, recover the contract instance from it.

// Recover instance from the deployment transaction
const tx = await provider.getTx(contractId.txId)
const instance = CrowdfundReplay.fromTx(
tx,
contractId.outputIndex,
{
donators: new HashedMap<PubKey, bigint>(),
}
)

Note: For more details on the workings of the fromTx() and getTransaction() functions, refer to the documentation here.

Step 3. Replay Instance to Latest States

Invoke the replayToLatest function to acquire the latest contract instance.

import { replayToLatest } from 'scrypt-ts'

...

const latestInstance = await replayToLatest(instance, contractId)

if (latestInstance) {
// The latest instance is now ready for use.
...
}

Note: If the replayToLatest() function yields null, it indicates that there have been no state changes for the contract instance. This scenario arises if the contract hasn't been interacted with since its deployment or if all state modifications have been reverted.


- + \ No newline at end of file diff --git a/advanced/inline-asm/index.html b/advanced/inline-asm/index.html index d1b4f7cfe..897f9db57 100644 --- a/advanced/inline-asm/index.html +++ b/advanced/inline-asm/index.html @@ -4,14 +4,14 @@ Use Script inside sCrypt | sCrypt - +
Skip to main content

Use Script inside sCrypt

Script is a low-level language and acts as assembly for the Bitcoin Virtual Machine. Usually, developers do not have to deal with it directly and can use high-level languages like sCrypt. However, there are cases where using script is desirable. For example, customized script is optimized and thus smaller and more efficient than Script generated by sCrypt. Or script is generated using external tools like Baguette and needs to be integrated into sCrypt.

To achieve this currently, you have to edit the auto-generated .scrypt files under your project's artifacts directory.

First you create a project called P2PKH:

npx scrypt-cli project P2PKH --asm

Notice the --asm option must be enabled, meaning you are going to use inline assembly format of script.

Your contract is at src/contracts/p2pkh.ts:

export class P2PKH extends SmartContract {
@prop()
readonly address: Addr

constructor(address: Addr) {
super(...arguments)
this.address = address
}

@method()
public unlock(sig: Sig, pubkey: PubKey) {
assert(
pubKey2Addr(pubkey) == this.address,
'public key does not correspond to address'
)
assert(this.checkSig(sig, pubkey), 'signature check failed')
}
}

Say you want to substitute the unlock function with manual script, you edit the file .asm/asm.json.

{
"P2PKH": {
"unlock": "OP_DUP OP_HASH160 $pubKeyHash OP_EQUALVERIFY OP_CHECKSIG"
}
}

Variables can be defined by prefix $, as in $pubKeyHash.

We could also define multiple substitutions for multiple methods, if needed.

Now, you can compile the contracts with --asm option:

npx scrypt-cli compile --asm

Now, after compiling, the function body will be replaced with script, as could be seen in artifacts/P2PKH.scrypt.

Set Inline Assembly Variables

Assembly variables can be replaced with literal Script in ASM format using setAsmVars(). Each variable is prefixed by its unique scope, namely, the contract and the function it is under.

p2pkh = new P2PKH(Addr(myAddress.toByteString()))

// Set ASM variable
// Keep in mind that these are NOT constructor parameters and must be set separately.
asmVarValues = {
'P2PKH.unlock.address': myAddress.toByteString()
}
p2pkh.setAsmVars(asmVarValues)

Full code can be found on GitHub. For more information about inline script/assembly, please refer to here.

note

Inline script bypasses many features of sCrypt such as type checking. Extreme caution has to be taken when using this advanced feature.

- + \ No newline at end of file diff --git a/advanced/sighash-type/index.html b/advanced/sighash-type/index.html index ab03ea89a..6919ed2a5 100644 --- a/advanced/sighash-type/index.html +++ b/advanced/sighash-type/index.html @@ -4,7 +4,7 @@ Sighash Types | sCrypt - + @@ -14,7 +14,7 @@ In this scenario, we can employ the ANYONECANPAY | ALL flag with our signature to unlock the deployed P2PKH contract. This allows our friend to append another input to our transaction, contributing funds to pay the network fee.

To illustrate, we would structure the contract call as follows:

const sighashType = SignatureHashType.ANYONECANPAY_ALL
const { tx } = await p2pkh.methods.unlock(
// Pass the first parameter, the signature, to `unlock`.
// Once the transaction is signed, signatures are returned in `SignatureResponse[]`.
// Identify the required signature(s) using the public key, address, and the sighash type specified.
(sigResps) => findSig(sigResps, publicKey, sighashType),
PubKey(toHex(publicKey)),
{
// Direct the signer to use the private key associated with `publicKey` and the specified sighash type to sign this transaction.
pubKeyOrAddrToSign: {
pubKeyOrAddr: publicKey,
sigHashType: sighashType,
},
// This flag ensures the call tx is only created locally and not broadcasted.
partiallySigned: true,
// Prevents automatic addition of fee inputs.
autoPayFee: false,
} as MethodCallOptions<P2PKH>
)

Executing the above will yield the entire contract call transaction without broadcasting it. We can subsequently pass this transaction to our friend. Since we applied the ANYONECANPAY sighash flag, adding an additional input will not invalidate our signature. This is because network nodes will exclusively use the first input to authenticate our signature.

To further elaborate, we might also use the ANYONECANPAY | SINGLE flag. This would grant our friend the capability to append extra outputs to our transaction. This can be advantageous, for instance, if he wishes to reclaim a portion of his contributed funds as change, especially if he used an UTXO with an excessive amount of locked-in funds.

You can find a full code example in our project boilerplate.

2. Sighash Types in @method() Parameters

In this section, we will introduce how to specify different sighash types in the @method() decorator.

note

Sighash here only affects contracts that access ScriptContext in their public methods.

Counter

Let us use the Counter contract as an example. It simply records how many times it has been called since deployment.

Noted that the @method decorator takes a sighash type as a parameter, whose default is ALL. According to the doc, hashOutputs is the double SHA256 of the serialization of all outputs when the sighash type is ALL. The default calling transaction builder adds a change output when necessary. That's why we need to add a change output when building outputs of the spending transaction in the public method: we need to build all the outputs that are included in hashOutputs. Otherwise, contract call will fail.

The following transaction is a contract calling transaction of Counter. As you can see, it contains two outputs: one for the new state, the other for change.

Advanced Counter

Noted that in the state transition of Counter, there is always only one UTXO that contains the latest contract state. When the contract is called, it spends the UTXO of the current state and creates a UTXO of the new state. Moreover, the contract input index of the spending transaction and the contract output index are the same.

In fact, we only care about the contract-related UTXO in the transaction inputs and outputs when calling Counter, and do not care about other inputs and outputs. Thus, we can use SINGLE | ANYONECANPAY to simplify the contract. SINGLE lets us focus on the contract output itself. ANYONECANPAY allows anyone to add inputs for this contract calling transaction to, e.g., pay fees.

We make two changes to the original Counter.

  1. Using @method(SigHash.ANYONECANPAY_SINGLE)
  2. Build an output that only contains the contract's new state, without the change output.
export class AdvancedCounter extends SmartContract {
...

// 1) add ANYONECANPAY_SINGLE
@method(SigHash.ANYONECANPAY_SINGLE)
public incrementOnChain() {
...

const amount: bigint = this.ctx.utxo.value
// 2) remove change output
const output: ByteString = this.buildStateOutput(amount)
assert(this.ctx.hashOutputs == hash256(output), 'hashOutputs mismatch')
}

...
}

You can check the complete code here.

The following transaction is a contract calling transaction of AdvancedCounter. You can see it also contains two outputs, but we only use one output when checking if it hashes to hashOutputs in the public method, since we use SINGLE.

More examples

Use different sighash types in @method() decorator will change the value of ScriptContext. This is useful in many cases.

You can find these examples in our boilerplate.

- + \ No newline at end of file diff --git a/advanced/timeLock/index.html b/advanced/timeLock/index.html index 926b86219..1918c709f 100644 --- a/advanced/timeLock/index.html +++ b/advanced/timeLock/index.html @@ -4,7 +4,7 @@ Time Lock | sCrypt - + @@ -12,7 +12,7 @@
Skip to main content

Time Lock

Overview

In this section, we will go over how to create a smart contract, which has a public method, that can only be unlocked once a certain point in time has passed.

What is a time lock?

In the context of smart contracts, a time-lock is a feature that restricts the spending of specific bitcoins until a specified future time or block height is reached. sCrypt offers capabilities to implement these types of time-locks in your smart contracts, providing a mechanism to ensure a transaction won't be included in a block before a certain point in time or block height is reached. In other words, the smart contract's method cannot be successfully invoked until that point in time has passed.

For instance, this mechanism could be used to add a withdrawal method to a smart contract. In the event of non-cooperation from other parties, an individual could retrieve their funds locked in the smart contract after some amount of time has passed. This approach is utilized in cross-chain atomic swaps, for example.

Image Credit: bcoin

Implementation

In sCrypt, a time-lock can be enforced by constraining the locktime and sequence values of the script execution context. This context pertains to the execution of the transaction, which includes a call to the smart contract's public method. Thus, if the value is constrained – for example, the locktime needs to be above the value 1690236000 (a Unix timestamp) – then this transaction cannot be included into the blockchain until that point in time.

Note that the value of locktime can either be a Unix timestamp or a block height. For this value to be enforced, sequence also needs to be set to a value less than 0xffffffff.

sCrypt offers a convenient built-in function timeLock to enforce this constraint.

// Time after which our public method can be called.
@prop()
readonly matureTime: bigint // Can be a timestamp or block height.

// ...

@method()
public unlock() {
// The following assertion ensures that the `unlock` method can
// not be successfully invoked until `matureTime` has passed.
assert(this.timeLock(this.matureTime), 'time lock not yet expired')
}

It is important to note that this mechanism can be employed solely to ensure that a method can be called after a specific point in time. In contrast, it cannot be employed to ensure that a method is called before a specific point in time.

Calling

Upon a method call to the unlock method defined above, we need to set the locktime value of the transaction that will call the public method. We can do this by simply setting the locktime paramater of MethodCallOptions.

timeLock.methods.unlock(
{
lockTime: 1673523720
} as MethodCallOptions<TimeLock>
)

Internally this will also set the inputs sequence to a value lower than 0xffffffff. We can also set this value explicitly.

timeLock.methods.unlock(
{
lockTime: 1673523720,
sequence: 0
} as MethodCallOptions<TimeLock>
)

Lastly, if we are using a custom transaction builder we need to set these values for the unsigned transaction that we are building there.

instance.bindTxBuilder('unlock',
async (
current: TimeLock,
options: MethodCallOptions<TimeLock>
) => {

// ...

if (options.lockTime) {
unsignedTx.setLockTime(options.lockTime)
}
unsignedTx.setInputSequence(0, 0)

// ...
}
)

How does it work?

Under the hood, the timeLock function asserts that the sequence value of our calling transaction is less than UINT_MAX. This ensures that the Bitcoin network will enforce the locktime value.

Next, it checks if our target time-lock value indicates a block height or a Unix timestamp. If it's using a block height, i.e. the time-lock value is less than 500,000,000, the method also ensures that the locktime value of the calling transaction corresponds to a block height.

Lastly, the method verifies that the value of locktime is greater than or equal to the time-lock we have passed as an argument.

For more information on how the locktime and sequence values work, please read the BSV wiki page.

- + \ No newline at end of file diff --git a/assets/js/505c1d49.f35a9721.js b/assets/js/505c1d49.25a7d2cf.js similarity index 86% rename from assets/js/505c1d49.f35a9721.js rename to assets/js/505c1d49.25a7d2cf.js index 32110dd2b..d244d33ff 100644 --- a/assets/js/505c1d49.f35a9721.js +++ b/assets/js/505c1d49.25a7d2cf.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[5448],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),h=l(n),m=a,d=h["".concat(c,".").concat(m)]||h[m]||u[m]||i;return n?r.createElement(d,s(s({ref:t},p),{},{components:n})):r.createElement(d,s({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,s=new Array(i);s[0]=h;var o={};for(var c in t)hasOwnProperty.call(t,c)&&(o[c]=t[c]);o.originalType=e,o.mdxType="string"==typeof e?e:a,s[1]=o;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const i={sidebar_position:8},s="Tutorial 8: Inscribe Image",o={unversionedId:"tutorials/inscribe-image",id:"tutorials/inscribe-image",title:"Tutorial 8: Inscribe Image",description:"Overview",source:"@site/docs/tutorials/inscribe-image.md",sourceDirName:"tutorials",slug:"/tutorials/inscribe-image",permalink:"/tutorials/inscribe-image",draft:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Tutorial 7: Escrow",permalink:"/tutorials/escrow"},next:{title:"Tutorial 9: Mint BSV20 V2 Token",permalink:"/tutorials/mint-bsv20-v2"}},c={},l=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Inscribe Image",id:"inscribe-image",level:2},{value:"Transfer the Inscription",id:"transfer-the-inscription",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:l};function u(e){let{components:t,...i}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,i,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"tutorial-8-inscribe-image"},"Tutorial 8: Inscribe Image"),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"In this tutorial, we will use contract ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example to introduce how to inscribe an image on an ordinal, which is locked in a smart contract. It can be transferred by calling the contract."),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"Your wallet must be funded before inscribing the image.")),(0,a.kt)("p",null,"First, you install ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,a.kt)("h2",{id:"contract"},"Contract"),(0,a.kt)("p",null,"The new contract ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT")," is almost the same as the previous ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except it must be derived from ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," instead of ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract"),", which comes with package ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockNFT extends OrdinalNFT {\n ...\n}\n")),(0,a.kt)("p",null,"It also stores a hash value in the contract. It will be unlocked successfully when calling the public method ",(0,a.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct hash preimage."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockNFT extends OrdinalNFT {\n @prop()\n hash: Sha256\n \n ...\n \n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,a.kt)("p",null,"The base class ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," encapsulates helper functions to handle ordinals. If you want to create your own contract that control Ordinal NFTs, you must derive from it."),(0,a.kt)("h2",{id:"inscribe-image"},"Inscribe Image"),(0,a.kt)("p",null,"We first create an instance of contract ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT"),". Next we call ",(0,a.kt)("inlineCode",{parentName:"p"},"inscribeImage")," on the instance to inscribe an image."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockNFT(hash)\n...\n// inscribe image into contract\nconst image = readImage()\nconst mintTx = await hashLock.inscribeImage(image, 'image/png')\n")),(0,a.kt)("p",null,"Execute command ",(0,a.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/inscribeImage.ts")," to run this example."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(1010).Z,width:"1570",height:"186"})),(0,a.kt)("p",null,"Then you can check your inscription on the explorer."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(3904).Z,width:"2064",height:"1674"})),(0,a.kt)("p",null,"Now that the inscription is locked to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5858).Z,width:"2478",height:"1528"})),(0,a.kt)("p",null,"This is different from using a P2PKH address to receive the inscription, where the inscription is controlled by a private key."),(0,a.kt)("h2",{id:"transfer-the-inscription"},"Transfer the Inscription"),(0,a.kt)("p",null,"The contract instance holds the inscription and we transfer it to a bitcoin address."),(0,a.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,a.kt)("p",null,"Class ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdiNFTP2PKH")," represents an address that can hold inscriptions. Its constructor takes one parameter which is the receiving address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const receiver = new OrdiNFTP2PKH(Addr(address.toByteString()))\n")),(0,a.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,a.kt)("p",null,"Similar to ",(0,a.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," before, we call the ",(0,a.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT")," as follows."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await hashLock.methods.unlock(\n message,\n {\n transfer: receiver, // <-----\n } as MethodCallOptions\n)\n")),(0,a.kt)("p",null,"We pass the receiver instance to ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," of struct ",(0,a.kt)("inlineCode",{parentName:"p"},"MethodCallOptions"),"."),(0,a.kt)("h2",{id:"conclusion"},"Conclusion"),(0,a.kt)("p",null,"Great! You have finished the tutorial on how to inscribe and transfer a 1Sat Ordinal with a smart contract."),(0,a.kt)("p",null,"The full complete ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockNFT.ts"},"contract")," and ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/inscribeImage.ts"},"example")," can be found in sCrypt's ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}u.isMDXComponent=!0},3904:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-inscribe-tx-cc0ab5329c121d0e5c660e9e603ba645.png"},5858:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-transfer-tx-6046243c56422851bf2077bf3d535367.png"},1010:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-c8917a4ed68568fe18a0189f0ea9bcd2.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[5448],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,c=e.parentName,p=o(e,["components","mdxType","originalType","parentName"]),h=l(n),m=a,d=h["".concat(c,".").concat(m)]||h[m]||u[m]||i;return n?r.createElement(d,s(s({ref:t},p),{},{components:n})):r.createElement(d,s({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,s=new Array(i);s[0]=h;var o={};for(var c in t)hasOwnProperty.call(t,c)&&(o[c]=t[c]);o.originalType=e,o.mdxType="string"==typeof e?e:a,s[1]=o;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>u,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const i={sidebar_position:8},s="Tutorial 8: Inscribe Image",o={unversionedId:"tutorials/inscribe-image",id:"tutorials/inscribe-image",title:"Tutorial 8: Inscribe Image",description:"Overview",source:"@site/docs/tutorials/inscribe-image.md",sourceDirName:"tutorials",slug:"/tutorials/inscribe-image",permalink:"/tutorials/inscribe-image",draft:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Tutorial 7: Escrow",permalink:"/tutorials/escrow"},next:{title:"Tutorial 9: Mint BSV20 V2 Token",permalink:"/tutorials/mint-bsv20-v2"}},c={},l=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Inscribe Image",id:"inscribe-image",level:2},{value:"Transfer the Inscription",id:"transfer-the-inscription",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:l};function u(e){let{components:t,...i}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,i,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"tutorial-8-inscribe-image"},"Tutorial 8: Inscribe Image"),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"In this tutorial, we will use contract ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example to introduce how to inscribe an image on an ordinal, which is locked in a smart contract. It can be transferred by calling the contract."),(0,a.kt)("admonition",{type:"note"},(0,a.kt)("p",{parentName:"admonition"},"Your wallet must be funded before inscribing the image.")),(0,a.kt)("p",null,"First, you install ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,a.kt)("h2",{id:"contract"},"Contract"),(0,a.kt)("p",null,"The new contract ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT")," is almost the same as the previous ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except it must be derived from ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," instead of ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract"),", which comes with package ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockNFT extends OrdinalNFT {\n ...\n}\n")),(0,a.kt)("p",null,"It also stores a hash value in the contract. It will be unlocked successfully when calling the public method ",(0,a.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct hash preimage."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockNFT extends OrdinalNFT {\n @prop()\n hash: Sha256\n \n ...\n \n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,a.kt)("p",null,"The base class ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," encapsulates helper functions to handle ordinals. If you want to create your own contract that control Ordinal NFTs, you must derive from it."),(0,a.kt)("h2",{id:"inscribe-image"},"Inscribe Image"),(0,a.kt)("p",null,"We first create an instance of contract ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT"),". Next we call ",(0,a.kt)("inlineCode",{parentName:"p"},"inscribeImage")," on the instance to inscribe an image."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockNFT(hash)\n...\n// inscribe image into contract\nconst image = readImage()\nconst mintTx = await hashLock.inscribeImage(image, 'image/png')\n")),(0,a.kt)("p",null,"Execute command ",(0,a.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/inscribeImage.ts")," to run this example."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(1010).Z,width:"1570",height:"186"})),(0,a.kt)("p",null,"Then you can check your inscription on the explorer."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(3904).Z,width:"2064",height:"1674"})),(0,a.kt)("p",null,"Now that the inscription is locked to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5858).Z,width:"2478",height:"1528"})),(0,a.kt)("p",null,"This is different from using a P2PKH address to receive the inscription, where the inscription is controlled by a private key."),(0,a.kt)("h2",{id:"transfer-the-inscription"},"Transfer the Inscription"),(0,a.kt)("p",null,"The contract instance holds the inscription and we transfer it to a bitcoin address."),(0,a.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,a.kt)("p",null,"Class ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdiNFTP2PKH")," represents an address that can hold inscriptions. Its constructor takes one parameter which is the receiving address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const receiver = new OrdiNFTP2PKH(Addr(address.toByteString()))\n")),(0,a.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,a.kt)("p",null,"Similar to ",(0,a.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," before, we call the ",(0,a.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT")," as follows."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await hashLock.methods.unlock(\n message,\n {\n transfer: receiver, // <-----\n } as OrdiMethodCallOptions\n)\n")),(0,a.kt)("p",null,"We pass the receiver instance to ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," of struct ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdiMethodCallOptions"),"."),(0,a.kt)("h2",{id:"conclusion"},"Conclusion"),(0,a.kt)("p",null,"Great! You have finished the tutorial on how to inscribe and transfer a 1Sat Ordinal with a smart contract."),(0,a.kt)("p",null,"The full complete ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockNFT.ts"},"contract")," and ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/inscribeImage.ts"},"example")," can be found in sCrypt's ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}u.isMDXComponent=!0},3904:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-inscribe-tx-cc0ab5329c121d0e5c660e9e603ba645.png"},5858:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-transfer-tx-6046243c56422851bf2077bf3d535367.png"},1010:(e,t,n)=>{n.d(t,{Z:()=>r});const r=n.p+"assets/images/inscribe-image-c8917a4ed68568fe18a0189f0ea9bcd2.png"}}]); \ No newline at end of file diff --git a/assets/js/572d3f0b.313df141.js b/assets/js/572d3f0b.313df141.js new file mode 100644 index 000000000..e59c6ba83 --- /dev/null +++ b/assets/js/572d3f0b.313df141.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6325],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),u=l(n),m=a,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(h,s(s({ref:t},p),{},{components:n})):r.createElement(h,s({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,s=new Array(o);s[0]=u;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={sidebar_position:2},s="Non Funglible Tokens - NFTs",i={unversionedId:"tokens/nft/nft",id:"tokens/nft/nft",title:"Non Funglible Tokens - NFTs",description:"To create a smart contract that will carry an NFT, have your smart contract extend the OrdinalNFT class:",source:"@site/docs/tokens/nft/nft.md",sourceDirName:"tokens/nft",slug:"/tokens/nft/",permalink:"/tokens/nft/",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"The Official sCrypt 1Sat Ordinals SDK",permalink:"/tokens/"},next:{title:"Transfer Existing NFT to a Smart Contract",permalink:"/tokens/nft/existing"}},c={},l=[{value:"Inscribe",id:"inscribe",level:2},{value:"Transfer",id:"transfer",level:2}],p={toc:l};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"non-funglible-tokens---nfts"},"Non Funglible Tokens - NFTs"),(0,a.kt)("p",null,"To create a smart contract that will carry an NFT, have your smart contract extend the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," class:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { method, prop, assert, ByteString, sha256, Sha256 } from "scrypt-ts";\nimport { OrdinalNFT } from "scrypt-ord";\n\nexport class HashLockNFT extends OrdinalNFT {\n @prop()\n hash: Sha256;\n\n constructor(hash: Sha256) {\n super();\n // Important: Call `init` after the `super()` statement.\n this.init(...arguments);\n this.hash = hash;\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash === sha256(message), "hashes are not equal");\n }\n}\n')),(0,a.kt)("p",null,"The contract above represents an NFT that can be unlocked / transferred by providing the secret pre-image of a hash value.\nEach constructor extending the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," class must also call the instances ",(0,a.kt)("inlineCode",{parentName:"p"},"init")," method and pass the constructors arguments. It is important to call this function ",(0,a.kt)("strong",{parentName:"p"},"after")," the call to ",(0,a.kt)("inlineCode",{parentName:"p"},"super"),"."),(0,a.kt)("h2",{id:"inscribe"},"Inscribe"),(0,a.kt)("p",null,"The following code demonstrates how deploy / inscribe the NFT contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'HashLockNFT.loadArtifact();\n\nconst text = "Hello sCrypt and 1Sat Ordinals";\n\nconst message = toByteString(\'secret string\', true);\nconst hash = sha256(message);\n\nconst instance = new HashLockNFT(hash);\n\nconst signer = getDefaultSigner();\nawait instance.connect(signer);\n\nconst inscriptionTx = await instance.inscribeText(text);\nconsole.log("Inscribed NFT: ", inscriptionTx.id);\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"inscribeText")," first inscribes the locking script with the specified text and then deploys the contract."),(0,a.kt)("p",null,"Among text the inscription can contain many other types of data. Here's how you can conveniently inscribe an image:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ...\n\nconst bb = readFileSync(join(__dirname, "..", "..", "logo.png")).toString("base64");\n\nconst tx = await instance.inscribeImage(bb, ContentType.PNG);\nconsole.log("Inscribed NFT: ", tx.id);\n')),(0,a.kt)("p",null,"In fact the data type can be arbitrary. It only depends on the Ordinals wallet you're using to support that data type."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'const tx = await instance.inscribe({\n content: `your content in hex`,\n contentType: `your contentType`,\n});\nconsole.log("Inscribed NFT: ", tx.id);\n')),(0,a.kt)("p",null,"The value ",(0,a.kt)("inlineCode",{parentName:"p"},"contentType")," must be a MIME-type string. The ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/src/contentType.ts"},(0,a.kt)("inlineCode",{parentName:"a"},"ContentType"))," object contains common MIME-types."),(0,a.kt)("h2",{id:"transfer"},"Transfer"),(0,a.kt)("p",null,"You can easily transfer a deployed NFT to an Ordinals address by passing a ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," value via the method call parameters. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"OrdiNFTP2PKH")," is a ",(0,a.kt)("a",{parentName:"p",href:"https://learnmeabitcoin.com/guide/p2pkh"},"P2PKH")," contract for holding ordinals NFTs. Like a normal P2PKH contract, you need an address to instantiate it."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ... deploy code from above\n\nconst { tx: transferTx } = await instance.methods.unlock(\n message, \n {\n transfer: new OrdiNFTP2PKH(\n Addr(recipientAddress.toByteString())\n ),\n }\n);\n\nconsole.log("Transferred NFT: ", transferTx.id);\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," parameter can be any single instance of a contract that extends ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/572d3f0b.ff5487e6.js b/assets/js/572d3f0b.ff5487e6.js deleted file mode 100644 index 96daf4b3d..000000000 --- a/assets/js/572d3f0b.ff5487e6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6325],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},u=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),u=l(n),m=a,h=u["".concat(c,".").concat(m)]||u[m]||d[m]||o;return n?r.createElement(h,s(s({ref:t},p),{},{components:n})):r.createElement(h,s({ref:t},p))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,s=new Array(o);s[0]=u;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={sidebar_position:2},s="Non Funglible Tokens - NFTs",i={unversionedId:"tokens/nft/nft",id:"tokens/nft/nft",title:"Non Funglible Tokens - NFTs",description:"To create a smart contract that will carry an NFT, have your smart contract extend the OrdinalNFT class:",source:"@site/docs/tokens/nft/nft.md",sourceDirName:"tokens/nft",slug:"/tokens/nft/",permalink:"/tokens/nft/",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"The Official sCrypt 1Sat Ordinals SDK",permalink:"/tokens/"},next:{title:"Transfer Existing NFT to a Smart Contract",permalink:"/tokens/nft/existing"}},c={},l=[{value:"Inscribe",id:"inscribe",level:2},{value:"Transfer",id:"transfer",level:2}],p={toc:l};function d(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"non-funglible-tokens---nfts"},"Non Funglible Tokens - NFTs"),(0,a.kt)("p",null,"To create a smart contract that will carry an NFT, have your smart contract extend the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," class:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { method, prop, assert, ByteString, sha256, Sha256 } from "scrypt-ts";\nimport { OrdinalNFT } from "scrypt-ord";\n\nexport class HashPuzzleNFT extends OrdinalNFT {\n @prop()\n hash: Sha256;\n\n constructor(hash: Sha256) {\n super();\n // Important: Call `init` after the `super()` statement.\n this.init(...arguments);\n this.hash = hash;\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash === sha256(message), "hashes are not equal");\n }\n}\n')),(0,a.kt)("p",null,"The contract above represents an NFT that can be unlocked / transferred by providing the secret pre-image of a hash value.\nEach constructor extending the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT")," class must also call the instances ",(0,a.kt)("inlineCode",{parentName:"p"},"init")," method and pass the constructors arguments. It is important to call this function ",(0,a.kt)("strong",{parentName:"p"},"after")," the call to ",(0,a.kt)("inlineCode",{parentName:"p"},"super"),"."),(0,a.kt)("h2",{id:"inscribe"},"Inscribe"),(0,a.kt)("p",null,"The following code demonstrates how deploy / inscribe the NFT contract:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'HashPuzzleNFT.loadArtifact();\n\nconst text = "Hello sCrypt and 1Sat Ordinals";\n\nconst message = toByteString(\'secret string\', true);\nconst hash = sha256(message);\n\nconst instance = new HashPuzzleNFT(hash);\n\nconst signer = getDefaultSigner();\nawait instance.connect(signer);\n\nconst inscriptionTx = await instance.inscribeTextNft(text);\nconsole.log("Inscribed NFT: ", inscriptionTx.id);\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"inscribeTextNft")," first inscribes the locking script with the specified text and then deploys the contract."),(0,a.kt)("p",null,"Among text the inscription can contain many other types of data. Here's how you can conveniently inscribe an image:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ...\n\nconst bb = readFileSync(join(__dirname, "..", "..", "logo.png")).toString("base64");\n\nconst tx = await instance.inscribeImageNft(bb, ContentType.PNG);\nconsole.log("Inscribed NFT: ", tx.id);\n')),(0,a.kt)("p",null,"In fact the data type can be arbitrary. It only depends on the Ordinals wallet you're using to support that data type."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'const tx = await instance.inscribe({\n content: `your content in hex`,\n contentType: `your contentType`,\n});\nconsole.log("Inscribed NFT: ", tx.id);\n')),(0,a.kt)("p",null,"The value ",(0,a.kt)("inlineCode",{parentName:"p"},"contentType")," must be a MIME-type string. The ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/src/contentType.ts"},(0,a.kt)("inlineCode",{parentName:"a"},"ContentType"))," object contains common MIME-types."),(0,a.kt)("h2",{id:"transfer"},"Transfer"),(0,a.kt)("p",null,"You can easily transfer a deployed NFT to an Ordinals address by passing a ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," value via the method call parameters. "),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"OrdNFTP2PKH")," is a ",(0,a.kt)("a",{parentName:"p",href:"https://learnmeabitcoin.com/guide/p2pkh"},"P2PKH")," contract for holding ordinals NFTs. Like a normal P2PKH contract, you need an address to instantiate it."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ... deploy code from above\n\nconst { tx: transferTx } = await instance.methods.unlock(\n message, \n {\n transfer: new OrdNFTP2PKH(\n Addr(recipientAddress.toByteString())\n ),\n }\n);\n\nconsole.log("Transferred NFT: ", transferTx.id);\n')),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"transfer")," parameter can be any single instance of a contract that extends ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalNFT"),"."))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6c2dbd7a.dc2dded2.js b/assets/js/6c2dbd7a.2ff382bb.js similarity index 80% rename from assets/js/6c2dbd7a.dc2dded2.js rename to assets/js/6c2dbd7a.2ff382bb.js index 6f7139022..911000f62 100644 --- a/assets/js/6c2dbd7a.dc2dded2.js +++ b/assets/js/6c2dbd7a.2ff382bb.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6857],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),p=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(i.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=a,f=d["".concat(i,".").concat(m)]||d[m]||u[m]||o;return n?r.createElement(f,l(l({ref:t},c),{},{components:n})):r.createElement(f,l({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s.mdxType="string"==typeof e?e:a,l[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={sidebar_position:1},l="The Official sCrypt 1Sat Ordinals SDK",s={unversionedId:"tokens/tokens",id:"tokens/tokens",title:"The Official sCrypt 1Sat Ordinals SDK",description:"sCrypt offers its official 1Sat Ordinals SDK named scrypt-ord.",source:"@site/docs/tokens/tokens.md",sourceDirName:"tokens",slug:"/tokens/",permalink:"/tokens/",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Tutorial 11: Ordinal Lock",permalink:"/tutorials/ordinal-lock"},next:{title:"Non Funglible Tokens - NFTs",permalink:"/tokens/nft/"}},i={},p=[{value:"Installation",id:"installation",level:2},{value:"Base Classes",id:"base-classes",level:2},{value:"OrdProvider",id:"ordprovider",level:2}],c={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"the-official-scrypt-1sat-ordinals-sdk"},"The Official sCrypt 1Sat Ordinals SDK"),(0,a.kt)("p",null,"sCrypt offers its official ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/"},"1Sat Ordinals")," SDK named ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},(0,a.kt)("inlineCode",{parentName:"a"},"scrypt-ord")),"."),(0,a.kt)("p",null,"The SDK offers an easy to use interface for deploying and transferring 1Sat Ordinals and augmenting them with the power of sCrypt smart contracts."),(0,a.kt)("p",null,"It facilitates the development of smart contracts for both non-fungible tokens (NFTs) and fungible tokens (FTs)."),(0,a.kt)("h2",{id:"installation"},"Installation"),(0,a.kt)("p",null,"It is recommended that you ",(0,a.kt)("a",{parentName:"p",href:"/installation#the-scrypt-cli-tool"},"create an sCrypt project")," using our CLI tool. Once you have the project set up, simply run the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"npm i scrypt-ord\n")),(0,a.kt)("h2",{id:"base-classes"},"Base Classes"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," provides base classes that can be extended with custom smart contract functionality. Unlike the ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract")," class, which you would typically extend for a regular sCrypt smart contract, here you should extend these base classes to integrate your smart contract with 1Sat ordinals functionality."),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Non-fungible tokens (NFTs):")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"OrdinalNFT"))),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Fungible tokens (FTs):")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V1")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V2"))),(0,a.kt)("p",null,"There are also pre-made classes that represent standard 1Sat transfers using the widely employed ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," mechanism:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"OrdNFTP2PKH")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V1P2PKH")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V2P2PKH"))),(0,a.kt)("p",null,"Suppose you wish to lock an ordinal token using a custom hash puzzle contract, you would define the smart contract class as shown below:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashPuzzleNFT extends OrdinalNFT {\n @prop()\n hash: Sha256\n\n constructor(hash: Sha256) {\n super()\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,a.kt)("p",null,"For a deeper exploration, please refer to the respective subsections:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/tokens/nft/"},"Non-Fungible Tokens (NFTs)")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/tokens/ft/"},"Fungible Tokens (FTs)"))),(0,a.kt)("h2",{id:"ordprovider"},(0,a.kt)("inlineCode",{parentName:"h2"},"OrdProvider")),(0,a.kt)("p",null,"When you use sCrypt 1Sat Ordinals SDK, we recommend that you use ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdProvider")," as the ",(0,a.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#provider"},"provider"),". This allows your transaction to be indexed instantly, instead of waiting for it to be mined into a block."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"export function getDefaultSigner(): TestWallet {\n return new TestWallet(\n myPrivateKey,\n new OrdProvider(bsv.Networks.mainnet)\n )\n}\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6857],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>m});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),p=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=p(e.components);return r.createElement(i.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(n),m=a,f=d["".concat(i,".").concat(m)]||d[m]||u[m]||o;return n?r.createElement(f,l(l({ref:t},c),{},{components:n})):r.createElement(f,l({ref:t},c))}));function m(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,l=new Array(o);l[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s.mdxType="string"==typeof e?e:a,l[1]=s;for(var p=2;p{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>l,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>p});var r=n(7462),a=(n(7294),n(3905));const o={sidebar_position:1},l="The Official sCrypt 1Sat Ordinals SDK",s={unversionedId:"tokens/tokens",id:"tokens/tokens",title:"The Official sCrypt 1Sat Ordinals SDK",description:"sCrypt offers its official 1Sat Ordinals SDK named scrypt-ord.",source:"@site/docs/tokens/tokens.md",sourceDirName:"tokens",slug:"/tokens/",permalink:"/tokens/",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Tutorial 11: Ordinal Lock",permalink:"/tutorials/ordinal-lock"},next:{title:"Non Funglible Tokens - NFTs",permalink:"/tokens/nft/"}},i={},p=[{value:"Installation",id:"installation",level:2},{value:"Base Classes",id:"base-classes",level:2},{value:"OrdProvider",id:"ordprovider",level:2}],c={toc:p};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"the-official-scrypt-1sat-ordinals-sdk"},"The Official sCrypt 1Sat Ordinals SDK"),(0,a.kt)("p",null,"sCrypt offers its official ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/"},"1Sat Ordinals")," SDK named ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},(0,a.kt)("inlineCode",{parentName:"a"},"scrypt-ord")),"."),(0,a.kt)("p",null,"The SDK offers an easy to use interface for deploying and transferring 1Sat Ordinals and augmenting them with the power of sCrypt smart contracts."),(0,a.kt)("p",null,"It facilitates the development of smart contracts for both non-fungible tokens (NFTs) and fungible tokens (FTs)."),(0,a.kt)("h2",{id:"installation"},"Installation"),(0,a.kt)("p",null,"It is recommended that you ",(0,a.kt)("a",{parentName:"p",href:"/installation#the-scrypt-cli-tool"},"create an sCrypt project")," using our CLI tool. Once you have the project set up, simply run the following command:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre"},"npm i scrypt-ord\n")),(0,a.kt)("h2",{id:"base-classes"},"Base Classes"),(0,a.kt)("p",null,(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," provides base classes that can be extended with custom smart contract functionality. Unlike the ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract")," class, which you would typically extend for a regular sCrypt smart contract, here you should extend these base classes to integrate your smart contract with 1Sat ordinals functionality."),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Non-fungible tokens (NFTs):")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"OrdinalNFT"))),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"Fungible tokens (FTs):")),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V1")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V2"))),(0,a.kt)("p",null,"There are also pre-made classes that represent standard 1Sat transfers using the widely employed ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," mechanism:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"OrdiNFTP2PKH")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V1P2PKH")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"BSV20V2P2PKH"))),(0,a.kt)("p",null,"Suppose you wish to lock an ordinal token using a custom hash puzzle contract, you would define the smart contract class as shown below:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockNFT extends OrdinalNFT {\n @prop()\n hash: Sha256\n\n constructor(hash: Sha256) {\n super()\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,a.kt)("p",null,"For a deeper exploration, please refer to the respective subsections:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/tokens/nft/"},"Non-Fungible Tokens (NFTs)")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/tokens/ft/"},"Fungible Tokens (FTs)"))),(0,a.kt)("h2",{id:"ordprovider"},(0,a.kt)("inlineCode",{parentName:"h2"},"OrdProvider")),(0,a.kt)("p",null,"When you use sCrypt 1Sat Ordinals SDK, we recommend that you use ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdiProvider")," as the ",(0,a.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#provider"},"provider"),". This allows your transaction to be indexed instantly, instead of waiting for it to be mined into a block."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"export function getDefaultSigner(): TestWallet {\n return new TestWallet(\n myPrivateKey,\n new OrdiProvider(bsv.Networks.mainnet)\n )\n}\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8c9a110f.0e63a235.js b/assets/js/8c9a110f.f68bc347.js similarity index 80% rename from assets/js/8c9a110f.0e63a235.js rename to assets/js/8c9a110f.f68bc347.js index 46444ec9f..312f67610 100644 --- a/assets/js/8c9a110f.0e63a235.js +++ b/assets/js/8c9a110f.f68bc347.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[622],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,i=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),h=c(n),u=r,m=h["".concat(i,".").concat(u)]||h[u]||d[u]||o;return n?a.createElement(m,s(s({ref:t},p),{},{components:n})):a.createElement(m,s({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=h;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l.mdxType="string"==typeof e?e:r,s[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:9},s="Tutorial 9: Mint BSV20 V2 Token",l={unversionedId:"tutorials/mint-bsv20-v2",id:"tutorials/mint-bsv20-v2",title:"Tutorial 9: Mint BSV20 V2 Token",description:"Overview",source:"@site/docs/tutorials/mint-bsv20-v2.md",sourceDirName:"tutorials",slug:"/tutorials/mint-bsv20-v2",permalink:"/tutorials/mint-bsv20-v2",draft:!1,tags:[],version:"current",sidebarPosition:9,frontMatter:{sidebar_position:9},sidebar:"tutorialSidebar",previous:{title:"Tutorial 8: Inscribe Image",permalink:"/tutorials/inscribe-image"},next:{title:"Tutorial 10: Mint BSV20 V1 Token",permalink:"/tutorials/mint-bsv20-v1"}},i={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Deploy Token",id:"deploy-token",level:2},{value:"Transfer Token",id:"transfer-token",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:c};function d(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"tutorial-9-mint-bsv20-v2-token"},"Tutorial 9: Mint BSV20 V2 Token"),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"In this tutorial, we will use contract ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example, to introduce how to mint a BSV20 Token (",(0,r.kt)("strong",{parentName:"p"},"version 2"),") with ",(0,r.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," and transfer it with a Smart Contract."),(0,r.kt)("p",null,"To enable all these features, you should install ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,r.kt)("h2",{id:"contract"},"Contract"),(0,r.kt)("p",null,"The new contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2")," is almost the same as the previous ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except two differences."),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"It must be derived from ",(0,r.kt)("inlineCode",{parentName:"li"},"BSV20V2")," instead of ",(0,r.kt)("inlineCode",{parentName:"li"},"SmartContract"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFTV2 extends BSV20V2 {\n ...\n}\n")),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"The constructor has extra parameters - ",(0,r.kt)("inlineCode",{parentName:"li"},"id"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"max"),", and ",(0,r.kt)("inlineCode",{parentName:"li"},"dec")," - representing ",(0,r.kt)("a",{parentName:"li",href:"https://docs.1satordinals.com/bsv20#v2-deploy+mint-tickerless-mode"},"BSV20 V2 fields"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {\n super(id, max, dec)\n this.init(...arguments)\n this.hash = hash\n}\n")),(0,r.kt)("p",null,"The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct message."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"export class HashLockFTV2 extends BSV20V2 {\n @prop()\n hash: Sha256\n \n ...\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"The base class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2")," encapsulated helper functions to handle BSV20 V2 tokens. If you want to create your own contract that can interact with BSV20 V2 protocol, derive from it."),(0,r.kt)("h2",{id:"deploy-token"},"Deploy Token"),(0,r.kt)("p",null,"We first create an instance of contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2"),", then call function ",(0,r.kt)("inlineCode",{parentName:"p"},"deployToken")," to deploy the new token."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// BSV20 V2 fields\nconst max = 10n\nconst dec = 0n\n// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockFTV2(toByteString(''), max, dec, hash)\n...\n// deploy the new BSV20V2 token\nconst tokenId = await hashLock.deployToken()\n")),(0,r.kt)("p",null,"Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH."),(0,r.kt)("p",null,"In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,r.kt)("h2",{id:"transfer-token"},"Transfer Token"),(0,r.kt)("p",null,"For now, the contract instance holds the token and we try to transfer it to a P2PKH address."),(0,r.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,r.kt)("p",null,"Class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2P2PKH")," represents a P2PKH address that can hold BSV20 V2 tokens. Its constructor takes BSV20 V2 fields and an receiving address as parameters."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"const alice = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressAlice )\nconst bob = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressBob)\n")),(0,r.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,r.kt)("p",null,"Just as other ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," methods we introduced before, we call the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2")," as follows."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// Call the contract\nconst { tx: transferTx } = await hashLock.methods.unlock(message, {\n transfer: [\n {\n instance: alice,\n amt: 2n,\n },\n {\n instance: bob,\n amt: 5n,\n },\n ],\n} as MethodCallOptions)\n")),(0,r.kt)("p",null,"This code will create a transaction that transfers 2 tokens to ",(0,r.kt)("inlineCode",{parentName:"p"},"alice")," and 5 to ",(0,r.kt)("inlineCode",{parentName:"p"},"bob"),"."),(0,r.kt)("p",null,"The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key ",(0,r.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress")," of struct ",(0,r.kt)("inlineCode",{parentName:"p"},"MethodCallOptions"),"."),(0,r.kt)("p",null,"Execute command ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/mintBSV20V2.ts")," to run this example."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(2308).Z,width:"1596",height:"184"})),(0,r.kt)("p",null,"Then you can check your token transfer details on the explorer."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(4650).Z,width:"2202",height:"1204"})),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(434).Z,width:"2198",height:"1670"})),(0,r.kt)("h2",{id:"conclusion"},"Conclusion"),(0,r.kt)("p",null,"Great! You have finished the tutorial on how to mint and transfer the BSV20 V2 Token with a Smart Contract."),(0,r.kt)("p",null,"The full complete ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockFTV2.ts"},"contract")," and ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/mintBSV20V2.ts"},"example")," can be found in sCrypt's ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}d.isMDXComponent=!0},4650:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-mint-tx-cac73a04da37573e0b1543645b134bd9.png"},434:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-transfer-tx-06097a8dd1bc1bb98c9f28773372e882.png"},2308:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-67a561e9f83adef77c00eb14b18122de.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[622],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var i=a.createContext({}),c=function(e){var t=a.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(i.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,i=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),h=c(n),u=r,m=h["".concat(i,".").concat(u)]||h[u]||d[u]||o;return n?a.createElement(m,s(s({ref:t},p),{},{components:n})):a.createElement(m,s({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=h;var l={};for(var i in t)hasOwnProperty.call(t,i)&&(l[i]=t[i]);l.originalType=e,l.mdxType="string"==typeof e?e:r,s[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>i,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:9},s="Tutorial 9: Mint BSV20 V2 Token",l={unversionedId:"tutorials/mint-bsv20-v2",id:"tutorials/mint-bsv20-v2",title:"Tutorial 9: Mint BSV20 V2 Token",description:"Overview",source:"@site/docs/tutorials/mint-bsv20-v2.md",sourceDirName:"tutorials",slug:"/tutorials/mint-bsv20-v2",permalink:"/tutorials/mint-bsv20-v2",draft:!1,tags:[],version:"current",sidebarPosition:9,frontMatter:{sidebar_position:9},sidebar:"tutorialSidebar",previous:{title:"Tutorial 8: Inscribe Image",permalink:"/tutorials/inscribe-image"},next:{title:"Tutorial 10: Mint BSV20 V1 Token",permalink:"/tutorials/mint-bsv20-v1"}},i={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Deploy Token",id:"deploy-token",level:2},{value:"Transfer Token",id:"transfer-token",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:c};function d(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"tutorial-9-mint-bsv20-v2-token"},"Tutorial 9: Mint BSV20 V2 Token"),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"In this tutorial, we will use contract ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example, to introduce how to mint a BSV20 Token (",(0,r.kt)("strong",{parentName:"p"},"version 2"),") with ",(0,r.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," and transfer it with a Smart Contract."),(0,r.kt)("p",null,"To enable all these features, you should install ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,r.kt)("h2",{id:"contract"},"Contract"),(0,r.kt)("p",null,"The new contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2")," is almost the same as the previous ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except two differences."),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"It must be derived from ",(0,r.kt)("inlineCode",{parentName:"li"},"BSV20V2")," instead of ",(0,r.kt)("inlineCode",{parentName:"li"},"SmartContract"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFTV2 extends BSV20V2 {\n ...\n}\n")),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"The constructor has extra parameters - ",(0,r.kt)("inlineCode",{parentName:"li"},"id"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"max"),", and ",(0,r.kt)("inlineCode",{parentName:"li"},"dec")," - representing ",(0,r.kt)("a",{parentName:"li",href:"https://docs.1satordinals.com/bsv20#v2-deploy+mint-tickerless-mode"},"BSV20 V2 fields"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {\n super(id, max, dec)\n this.init(...arguments)\n this.hash = hash\n}\n")),(0,r.kt)("p",null,"The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct message."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"export class HashLockFTV2 extends BSV20V2 {\n @prop()\n hash: Sha256\n \n ...\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"The base class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2")," encapsulated helper functions to handle BSV20 V2 tokens. If you want to create your own contract that can interact with BSV20 V2 protocol, derive from it."),(0,r.kt)("h2",{id:"deploy-token"},"Deploy Token"),(0,r.kt)("p",null,"We first create an instance of contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2"),", then call function ",(0,r.kt)("inlineCode",{parentName:"p"},"deployToken")," to deploy the new token."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// BSV20 V2 fields\nconst max = 10n\nconst dec = 0n\n// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockFTV2(toByteString(''), max, dec, hash)\n...\n// deploy the new BSV20V2 token\nconst tokenId = await hashLock.deployToken()\n")),(0,r.kt)("p",null,"Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH."),(0,r.kt)("p",null,"In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,r.kt)("h2",{id:"transfer-token"},"Transfer Token"),(0,r.kt)("p",null,"For now, the contract instance holds the token and we try to transfer it to a P2PKH address."),(0,r.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,r.kt)("p",null,"Class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2P2PKH")," represents a P2PKH address that can hold BSV20 V2 tokens. Its constructor takes BSV20 V2 fields and an receiving address as parameters."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"const alice = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressAlice )\nconst bob = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressBob)\n")),(0,r.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,r.kt)("p",null,"Just as other ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," methods we introduced before, we call the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFTV2")," as follows."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// Call the contract\nconst { tx: transferTx } = await hashLock.methods.unlock(message, {\n transfer: [\n {\n instance: alice,\n amt: 2n,\n },\n {\n instance: bob,\n amt: 5n,\n },\n ],\n} as OrdiMethodCallOptions)\n")),(0,r.kt)("p",null,"This code will create a transaction that transfers 2 tokens to ",(0,r.kt)("inlineCode",{parentName:"p"},"alice")," and 5 to ",(0,r.kt)("inlineCode",{parentName:"p"},"bob"),"."),(0,r.kt)("p",null,"The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key ",(0,r.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress")," of struct ",(0,r.kt)("inlineCode",{parentName:"p"},"OrdiMethodCallOptions"),"."),(0,r.kt)("p",null,"Execute command ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/mintBSV20V2.ts")," to run this example."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(2308).Z,width:"1596",height:"184"})),(0,r.kt)("p",null,"Then you can check your token transfer details on the explorer."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(4650).Z,width:"2202",height:"1204"})),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(434).Z,width:"2198",height:"1670"})),(0,r.kt)("h2",{id:"conclusion"},"Conclusion"),(0,r.kt)("p",null,"Great! You have finished the tutorial on how to mint and transfer the BSV20 V2 Token with a Smart Contract."),(0,r.kt)("p",null,"The full complete ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockFTV2.ts"},"contract")," and ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/mintBSV20V2.ts"},"example")," can be found in sCrypt's ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}d.isMDXComponent=!0},4650:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-mint-tx-cac73a04da37573e0b1543645b134bd9.png"},434:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-transfer-tx-06097a8dd1bc1bb98c9f28773372e882.png"},2308:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20v2-67a561e9f83adef77c00eb14b18122de.png"}}]); \ No newline at end of file diff --git a/assets/js/910cd6a4.4d925563.js b/assets/js/910cd6a4.16e54899.js similarity index 50% rename from assets/js/910cd6a4.4d925563.js rename to assets/js/910cd6a4.16e54899.js index f3f3d157b..76faf9cc4 100644 --- a/assets/js/910cd6a4.4d925563.js +++ b/assets/js/910cd6a4.16e54899.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[2],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=l(n),f=a,g=d["".concat(c,".").concat(f)]||d[f]||p[f]||o;return n?r.createElement(g,s(s({ref:t},u),{},{components:n})):r.createElement(g,s({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,s=new Array(o);s[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>p,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Transfer Existing FT to a Smart Contract",sidebar_position:1},s=void 0,i={unversionedId:"tokens/ft/existing",id:"tokens/ft/existing",title:"Transfer Existing FT to a Smart Contract",description:"Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract.",source:"@site/docs/tokens/ft/existing.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/existing",permalink:"/tokens/ft/existing",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{title:"Transfer Existing FT to a Smart Contract",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Funglible Tokens - FTs",permalink:"/tokens/ft/"},next:{title:"Multiple Inputs with Different Contracts",permalink:"/tokens/ft/multiple"}},c={},l=[{value:"Handling Change",id:"handling-change",level:2}],u={toc:l};function p(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract."),(0,a.kt)("p",null,"If you would like to unlock a single specific UTXO, you can do the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"HashPuzzleFT.loadArtifact();\n\n// Initialize recipient smart contract.\nconst message = toByteString('super secret', true);\nconst hash = sha256(message);\nconst recipient = new HashPuzzleFT(hash);\nawait recipient.connect(getDefaultSigner());\n\n// Create P2PKH object from an UTXO\n// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.\nconst utxo: UTXO = ...\nconst p2pkh = BSV20V2P2PKH.fromUTXO(utxo);\nawait p2pkh.connect(getDefaultSigner());\n\n// Unlock it and transfer the FTs carried by it.\nconst { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n } as MethodCallOptions\n);\n\nconsole.log(\"Transferred FT: \", transferTx.id);\n")),(0,a.kt)("p",null,"Alternatively, you can unlock multiple FT UTXOs and send them to a smart contract. Using the ",(0,a.kt)("inlineCode",{parentName:"p"},"getBSV20")," function you can simply fetch FT UTXOs for a given Ordinal wallet address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ... initialize recipient smart contract\n\n// Fetch FT UTXOs for given Ordinal address.\n// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.\nconst bsv20P2PKHs = await BSV20V2P2PKH.getBSV20(tokenId, `your ordinal address`);\n\nawait Promise.all(bsv20P2PKHs.map((p) => p.connect(signer)));\n\n// Construct recipient smart contract(s)\nconst recipients: Array = [\n {\n instance: new HashPuzzleFTV2(tokenId, amount, dec, sha256(message)),\n amt: 6n,\n },\n];\n\n// Transfer\nconst { tx, nexts } = await BSV20V2P2PKH.transfer(\n bsv20P2PKHs,\n signer,\n recipients\n);\n\nconsole.log("Transferred FT: ", transferTx.id);\n')),(0,a.kt)("h2",{id:"handling-change"},"Handling Change"),(0,a.kt)("p",null,"Note, how the mechanism above is very similar to a regular Bitcoin transfer. If the FT amount from the inputs exceeds the recipients amount, the leftover will be transferred back to the Ordinal address as change."),(0,a.kt)("p",null,"You can customize the address using the method call option ",(0,a.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n tokenChangeAddress: yourOrdinalAddress\n } as MethodCallOptions\n)\n")),(0,a.kt)("p",null,"You can also skip change altogether by using the ",(0,a.kt)("inlineCode",{parentName:"p"},"skipTokenChange")," option. But be careful! Any leftover tokens will be considered ",(0,a.kt)("strong",{parentName:"p"},"burned")," in this case:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n skipTokenChange: true\n } as MethodCallOptions\n)\n")))}p.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[2],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>f});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function s(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},u=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),d=l(n),f=a,g=d["".concat(c,".").concat(f)]||d[f]||p[f]||o;return n?r.createElement(g,s(s({ref:t},u),{},{components:n})):r.createElement(g,s({ref:t},u))}));function f(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,s=new Array(o);s[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:a,s[1]=i;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>p,frontMatter:()=>o,metadata:()=>i,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Transfer Existing FT to a Smart Contract",sidebar_position:1},s=void 0,i={unversionedId:"tokens/ft/existing",id:"tokens/ft/existing",title:"Transfer Existing FT to a Smart Contract",description:"Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract.",source:"@site/docs/tokens/ft/existing.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/existing",permalink:"/tokens/ft/existing",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{title:"Transfer Existing FT to a Smart Contract",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Funglible Tokens - FTs",permalink:"/tokens/ft/"},next:{title:"Multiple Inputs with Different Contracts",permalink:"/tokens/ft/multiple"}},c={},l=[{value:"Handling Change",id:"handling-change",level:2}],u={toc:l};function p(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract."),(0,a.kt)("p",null,"If you would like to unlock a single specific UTXO, you can do the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"HashLockFT.loadArtifact();\n...\n// Initialize recipient smart contract.\nconst message = toByteString('super secret', true);\nconst hash = sha256(message);\nconst recipient = new HashLockFT(tick, max, lim, dec, hash);\nawait recipient.connect(getDefaultSigner());\n\n// Create P2PKH object from an UTXO\n// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.\nconst utxo: UTXO = ...\nconst p2pkh = BSV20V2P2PKH.fromUTXO(utxo);\nawait p2pkh.connect(getDefaultSigner());\n\n// Unlock it and transfer the FTs carried by it.\nconst { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n } as OrdiMethodCallOptions\n);\n\nconsole.log(\"Transferred FT: \", transferTx.id);\n")),(0,a.kt)("p",null,"Alternatively, you can unlock multiple FT UTXOs and send them to a smart contract. Using the ",(0,a.kt)("inlineCode",{parentName:"p"},"getBSV20")," function you can simply fetch FT UTXOs for a given Ordinal wallet address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// ... initialize recipient smart contract\n\n// Fetch FT UTXOs for given Ordinal address.\n// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.\nconst bsv20P2PKHs = await BSV20V2P2PKH.getBSV20(tokenId, `your ordinal address`);\n\nawait Promise.all(bsv20P2PKHs.map((p) => p.connect(signer)));\n\n// Construct recipient smart contract(s)\nconst recipients: Array = [\n {\n instance: new HashLockFTV2(tokenId, amount, dec, sha256(message)),\n amt: 6n,\n },\n];\n\n// Transfer\nconst { tx, nexts } = await BSV20V2P2PKH.transfer(\n bsv20P2PKHs,\n signer,\n recipients\n);\n\nconsole.log("Transferred FT: ", transferTx.id);\n')),(0,a.kt)("h2",{id:"handling-change"},"Handling Change"),(0,a.kt)("p",null,"Note, how the mechanism above is very similar to a regular Bitcoin transfer. If the FT amount from the inputs exceeds the recipients amount, the leftover will be transferred back to the Ordinal address as change."),(0,a.kt)("p",null,"You can customize the address using the method call option ",(0,a.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n tokenChangeAddress: yourOrdinalAddress\n } as OrdiMethodCallOptions\n)\n")),(0,a.kt)("p",null,"You can also skip change altogether by using the ",(0,a.kt)("inlineCode",{parentName:"p"},"skipTokenChange")," option. But be careful! Any leftover tokens will be considered ",(0,a.kt)("strong",{parentName:"p"},"burned")," in this case:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n skipTokenChange: true\n } as OrdiMethodCallOptions\n)\n")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9277e5e6.3c068833.js b/assets/js/9277e5e6.3c068833.js new file mode 100644 index 000000000..3a7744ff3 --- /dev/null +++ b/assets/js/9277e5e6.3c068833.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[2997],{3905:(t,n,e)=>{e.d(n,{Zo:()=>u,kt:()=>m});var r=e(7294);function a(t,n,e){return n in t?Object.defineProperty(t,n,{value:e,enumerable:!0,configurable:!0,writable:!0}):t[n]=e,t}function o(t,n){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(t,n).enumerable}))),e.push.apply(e,r)}return e}function i(t){for(var n=1;n=0||(a[e]=t[e]);return a}(t,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,e)&&(a[e]=t[e])}return a}var c=r.createContext({}),l=function(t){var n=r.useContext(c),e=n;return t&&(e="function"==typeof t?t(n):i(i({},n),t)),e},u=function(t){var n=l(t.components);return r.createElement(c.Provider,{value:n},t.children)},p={inlineCode:"code",wrapper:function(t){var n=t.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(t,n){var e=t.components,a=t.mdxType,o=t.originalType,c=t.parentName,u=s(t,["components","mdxType","originalType","parentName"]),d=l(e),m=a,f=d["".concat(c,".").concat(m)]||d[m]||p[m]||o;return e?r.createElement(f,i(i({ref:n},u),{},{components:e})):r.createElement(f,i({ref:n},u))}));function m(t,n){var e=arguments,a=n&&n.mdxType;if("string"==typeof t||a){var o=e.length,i=new Array(o);i[0]=d;var s={};for(var c in n)hasOwnProperty.call(n,c)&&(s[c]=n[c]);s.originalType=t,s.mdxType="string"==typeof t?t:a,i[1]=s;for(var l=2;l{e.r(n),e.d(n,{assets:()=>c,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=e(7462),a=(e(7294),e(3905));const o={title:"Multiple Inputs with Different Contracts",sidebar_position:2},i=void 0,s={unversionedId:"tokens/ft/multiple",id:"tokens/ft/multiple",title:"Multiple Inputs with Different Contracts",description:"Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the section for calling multiple contract instances.",source:"@site/docs/tokens/ft/multiple.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/multiple",permalink:"/tokens/ft/multiple",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{title:"Multiple Inputs with Different Contracts",sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Transfer Existing FT to a Smart Contract",permalink:"/tokens/ft/existing"},next:{title:"buildStateOutputFT",permalink:"/tokens/ft/buildstateoutputft"}},c={},l=[],u={toc:l};function p(t){let{components:n,...e}=t;return(0,a.kt)("wrapper",(0,r.Z)({},u,e,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the ",(0,a.kt)("a",{parentName:"p",href:"/advanced/how-to-call-multiple-contracts"},"section for calling multiple contract instances"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// One sender is regular bsv-20 P2PKH.\nconst sender0 = BSV20V2P2PKH.fromUTXO(utxo)\nawait sender0.connect(signer)\n\n// Second sender is a hash lock contract.\nconst sender1 = HashLockFTV2.fromUTXO(utxo)\nawait sender1.connect(signer)\n\n// Recipient will be a single hash lock contract.\nconst recipientAmt = 6n\nconst recipients: Array = [\n {\n instance: new HashLockFTV2(\n tokenId,\n amount,\n dec,\n sha256(toByteString('next super secret', true))\n ),\n amt: recipientAmt,\n },\n];\n\nconst totalTokenAmt = sender0.getAmt() + sender1.getAmt()\nconst tokenChangeAmt = totalTokenAmt - recipientAmt\n\nconst ordPubKey = await signer.getDefaultPubKey()\n\nsender0.bindTxBuilder(\n 'unlock',\n async (\n current: BSV20V2P2PKH,\n options: OrdiMethodCallOptions\n ): Promise => {\n const tx = new bsv.Transaction()\n const nexts: StatefulNext[] = []\n\n for (let i = 0; i < recipients.length; i++) {\n const receiver = recipients[i]\n\n if (receiver.instance instanceof BSV20V2) {\n receiver.instance.setAmt(receiver.amt)\n } else {\n throw new Error('Unsupported receiver, only BSV-20!')\n }\n\n tx.addOutput(\n new bsv.Transaction.Output({\n script: receiver.instance.lockingScript,\n satoshis: 1,\n })\n )\n\n nexts.push({\n instance: receiver.instance,\n balance: 1,\n atOutputIndex: i,\n })\n }\n\n if (tokenChangeAmt > 0n) {\n const p2pkh = new BSV20V2P2PKH(\n tokenId,\n amount,\n dec,\n Addr(ordPubKey.toAddress().toByteString())\n )\n\n p2pkh.setAmt(tokenChangeAmt)\n\n tx.addOutput(\n new bsv.Transaction.Output({\n script: p2pkh.lockingScript,\n satoshis: 1,\n })\n )\n\n nexts.push({\n instance: p2pkh,\n balance: 1,\n atOutputIndex: nexts.length,\n })\n }\n\n tx.change(ordPubKey.toAddress())\n\n tx.addInput(current.buildContractInput())\n\n return Promise.resolve({\n tx: tx,\n atInputIndex: 0,\n nexts,\n })\n }\n)\n\nlet partialContractTx = await sender0.methods.unlock(\n (sigResps) => findSig(sigResps, ordPubKey),\n PubKey(ordPubKey.toByteString()),\n {\n pubKeyOrAddrToSign: ordPubKey,\n multiContractCall: true,\n } as OrdiMethodCallOptions\n)\n\nsender1.bindTxBuilder(\n 'unlock',\n async (\n current: HashLockFTV2,\n options: MethodCallOptions\n ): Promise => {\n if (options.partialContractTx) {\n const tx = options.partialContractTx.tx\n tx.addInput(current.buildContractInput())\n\n return Promise.resolve({\n tx: tx,\n atInputIndex: 1,\n nexts: partialContractTx.nexts,\n })\n }\n\n throw new Error('no partialContractTx')\n }\n)\n\npartialContractTx = await sender1.methods.unlock(message1, {\n partialContractTx,\n transfer: recipients,\n pubKeyOrAddrToSign: ordPubKey,\n multiContractCall: true,\n} as OrdiMethodCallOptions)\n\nconst { tx } = await SmartContract.multiContractCall(\n partialContractTx,\n signer\n)\n\nconsole.log('Transfer tx:', tx.id)\n")),(0,a.kt)("p",null,"In the above code, a partial transaction is constructed, which unlocks the first UTXO containing a ",(0,a.kt)("inlineCode",{parentName:"p"},"BSV20V2P2PKH")," instance. The actual contract call doesn't execute yet, as we set the ",(0,a.kt)("inlineCode",{parentName:"p"},"multiContractCall")," flag within the method call parameters."),(0,a.kt)("p",null,"We then feed that partially constructed transaction via the second contract call, which will unlock the ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockFTV2")," instance. Just like the first call, this call also has the ",(0,a.kt)("inlineCode",{parentName:"p"},"multiContractCall")," flag set."),(0,a.kt)("p",null,"Once the transaction is fully built, we can sign and broadcast it using the ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract.multiContractCall")," function."),(0,a.kt)("p",null,"The above code is an example based on v2, but the same can be achieved using v1."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/9277e5e6.8cc741d3.js b/assets/js/9277e5e6.8cc741d3.js deleted file mode 100644 index 9bbfa6df6..000000000 --- a/assets/js/9277e5e6.8cc741d3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[2997],{3905:(t,n,e)=>{e.d(n,{Zo:()=>u,kt:()=>m});var r=e(7294);function a(t,n,e){return n in t?Object.defineProperty(t,n,{value:e,enumerable:!0,configurable:!0,writable:!0}):t[n]=e,t}function o(t,n){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(t,n).enumerable}))),e.push.apply(e,r)}return e}function i(t){for(var n=1;n=0||(a[e]=t[e]);return a}(t,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,e)&&(a[e]=t[e])}return a}var c=r.createContext({}),l=function(t){var n=r.useContext(c),e=n;return t&&(e="function"==typeof t?t(n):i(i({},n),t)),e},u=function(t){var n=l(t.components);return r.createElement(c.Provider,{value:n},t.children)},p={inlineCode:"code",wrapper:function(t){var n=t.children;return r.createElement(r.Fragment,{},n)}},d=r.forwardRef((function(t,n){var e=t.components,a=t.mdxType,o=t.originalType,c=t.parentName,u=s(t,["components","mdxType","originalType","parentName"]),d=l(e),m=a,f=d["".concat(c,".").concat(m)]||d[m]||p[m]||o;return e?r.createElement(f,i(i({ref:n},u),{},{components:e})):r.createElement(f,i({ref:n},u))}));function m(t,n){var e=arguments,a=n&&n.mdxType;if("string"==typeof t||a){var o=e.length,i=new Array(o);i[0]=d;var s={};for(var c in n)hasOwnProperty.call(n,c)&&(s[c]=n[c]);s.originalType=t,s.mdxType="string"==typeof t?t:a,i[1]=s;for(var l=2;l{e.r(n),e.d(n,{assets:()=>c,contentTitle:()=>i,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=e(7462),a=(e(7294),e(3905));const o={title:"Multiple Inputs with Different Contracts",sidebar_position:2},i=void 0,s={unversionedId:"tokens/ft/multiple",id:"tokens/ft/multiple",title:"Multiple Inputs with Different Contracts",description:"Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the section for calling multiple contract instances.",source:"@site/docs/tokens/ft/multiple.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/multiple",permalink:"/tokens/ft/multiple",draft:!1,tags:[],version:"current",sidebarPosition:2,frontMatter:{title:"Multiple Inputs with Different Contracts",sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Transfer Existing FT to a Smart Contract",permalink:"/tokens/ft/existing"},next:{title:"buildStateOutputFT",permalink:"/tokens/ft/buildstateoutputft"}},c={},l=[],u={toc:l};function p(t){let{components:n,...e}=t;return(0,a.kt)("wrapper",(0,r.Z)({},u,e,{components:n,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the ",(0,a.kt)("a",{parentName:"p",href:"/advanced/how-to-call-multiple-contracts"},"section for calling multiple contract instances"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// One sender is regular bsv-20 P2PKH.\nconst sender0 = BSV20V2P2PKH.fromUTXO(utxo)\nawait sender0.connect(signer)\n\n// Second sender is a hash puzzle contract.\nconst sender1 = HashPuzzleFTV2.fromUTXO(utxo)\nawait sender1.connect(signer)\n\n// Recipient will be a single hash puzzle contract.\nconst recipientAmt = 6n\nconst recipients: Array = [\n {\n instance: new HashPuzzleFT(\n tokenId,\n amount,\n dec,\n sha256(toByteString('next super secret', true))\n ),\n amt: recipientAmt,\n },\n];\n\nconst totalTokenAmt = sender0.getAmt() + sender1.getAmt()\nconst tokenChangeAmt = totalTokenAmt - recipientAmt\n\nconst ordPubKey = await signer.getDefaultPubKey()\n\nsender0.bindTxBuilder(\n 'unlock',\n async (\n current: BSV20V2P2PKH,\n options: MethodCallOptions\n ): Promise => {\n const tx = new bsv.Transaction()\n const nexts: StatefulNext[] = []\n\n for (let i = 0; i < recipients.length; i++) {\n const receiver = recipients[i]\n\n if (receiver.instance instanceof BSV20V2) {\n receiver.instance.setAmt(receiver.amt)\n } else {\n throw new Error('Unsupported receiver, only BSV-20!')\n }\n\n tx.addOutput(\n new bsv.Transaction.Output({\n script: receiver.instance.lockingScript,\n satoshis: 1,\n })\n )\n\n nexts.push({\n instance: receiver.instance,\n balance: 1,\n atOutputIndex: i,\n })\n }\n\n if (tokenChangeAmt > 0n) {\n const p2pkh = new BSV20V2P2PKH(\n tokenId,\n amount,\n dec,\n Addr(ordPubKey.toAddress().toByteString())\n )\n\n p2pkh.setAmt(tokenChangeAmt)\n\n tx.addOutput(\n new bsv.Transaction.Output({\n script: p2pkh.lockingScript,\n satoshis: 1,\n })\n )\n\n nexts.push({\n instance: p2pkh,\n balance: 1,\n atOutputIndex: nexts.length,\n })\n }\n\n tx.change(ordPubKey.toAddress())\n\n tx.addInput(current.buildContractInput())\n\n return Promise.resolve({\n tx: tx,\n atInputIndex: 0,\n nexts,\n })\n }\n)\n\nlet partialContractTx = await sender0.methods.unlock(\n (sigResps) => findSig(sigResps, ordPubKey),\n PubKey(ordPubKey.toByteString()),\n {\n pubKeyOrAddrToSign: ordPubKey,\n multiContractCall: true,\n } as MethodCallOptions\n)\n\nsender1.bindTxBuilder(\n 'unlock',\n async (\n current: HashPuzzleFTV2,\n options: MethodCallOptions\n ): Promise => {\n if (options.partialContractTx) {\n const tx = options.partialContractTx.tx\n tx.addInput(current.buildContractInput())\n\n return Promise.resolve({\n tx: tx,\n atInputIndex: 1,\n nexts: partialContractTx.nexts,\n })\n }\n\n throw new Error('no partialContractTx')\n }\n)\n\npartialContractTx = await sender1.methods.unlock(message1, {\n partialContractTx,\n transfer: recipients,\n pubKeyOrAddrToSign: ordPubKey,\n multiContractCall: true,\n} as MethodCallOptions)\n\nconst { tx } = await SmartContract.multiContractCall(\n partialContractTx,\n signer\n)\n\nconsole.log('Transfer tx:', tx.id)\n")),(0,a.kt)("p",null,"In the above code, a partial transaction is constructed, which unlocks the first UTXO containing a ",(0,a.kt)("inlineCode",{parentName:"p"},"BSV20V2P2PKH")," instance. The actual contract call doesn't execute yet, as we set the ",(0,a.kt)("inlineCode",{parentName:"p"},"multiContractCall")," flag within the method call parameters."),(0,a.kt)("p",null,"We then feed that partially constructed transaction via the second contract call, which will unlock the ",(0,a.kt)("inlineCode",{parentName:"p"},"HashPuzzleFT")," instance. Just like the first call, this call also has the ",(0,a.kt)("inlineCode",{parentName:"p"},"multiContractCall")," flag set."),(0,a.kt)("p",null,"Once the transaction is fully built, we can sign and broadcast it using the ",(0,a.kt)("inlineCode",{parentName:"p"},"SmartContract.multiContractCall")," function."),(0,a.kt)("p",null,"The above code is an example based on v2, but the same can be achieved using v1."))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a5c7ba44.150ee82c.js b/assets/js/a5c7ba44.150ee82c.js new file mode 100644 index 000000000..61d3163e8 --- /dev/null +++ b/assets/js/a5c7ba44.150ee82c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[7062],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>u});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var c=a.createContext({}),l=function(e){var t=a.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return a.createElement(c.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),h=l(n),u=r,m=h["".concat(c,".").concat(u)]||h[u]||d[u]||o;return n?a.createElement(m,i(i({ref:t},p),{},{components:n})):a.createElement(m,i({ref:t},p))}));function u(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=h;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:3},i="Funglible Tokens - FTs",s={unversionedId:"tokens/ft/ft",id:"tokens/ft/ft",title:"Funglible Tokens - FTs",description:"Just like NFTs, scrypt-ord also supports fungible tokens. Under the hood it utilizes the bsv-20 protocol.",source:"@site/docs/tokens/ft/ft.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/",permalink:"/tokens/ft/",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"buildStateOutputNFT",permalink:"/tokens/nft/buildstateoutputnft"},next:{title:"Transfer Existing FT to a Smart Contract",permalink:"/tokens/ft/existing"}},c={},l=[{value:"v1",id:"v1",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Mint and Transfer",id:"mint-and-transfer",level:3},{value:"v2",id:"v2",level:2},{value:"Mint",id:"mint",level:3},{value:"Transfer",id:"transfer",level:3}],p={toc:l};function d(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"funglible-tokens---fts"},"Funglible Tokens - FTs"),(0,r.kt)("p",null,"Just like NFTs, ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," also supports fungible tokens. Under the hood it utilizes the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20"},(0,r.kt)("inlineCode",{parentName:"a"},"bsv-20")," protocol"),"."),(0,r.kt)("p",null,"BSV-20 is a protocol for creating fungible tokens on the Bitcoin SV blockchain. Fungible tokens are interchangeable with each other, and can be used to represent a variety of assets, such as currencies, securities, and in-game items."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," supports both v1 and v2 of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV-20")," FT protocol."),(0,r.kt)("h2",{id:"v1"},"v1"),(0,r.kt)("p",null,"Tokens utilizing the first version of the ",(0,r.kt)("inlineCode",{parentName:"p"},"bsv-20")," must be initialized by a ",(0,r.kt)("strong",{parentName:"p"},"deployment")," inscription, which specifies the token's ticker symbol, amount and mint limit. For more information, refer to the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20#v1-mint-first-is-first-mode"},"1Sat docs"),"."),(0,r.kt)("p",null,"To create a v1 token smart contract, have it extend the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," class:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFT extends BSV20V1 {\n @prop()\n hash: Sha256\n\n constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {\n super(tick, max, lim, dec)\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"As you can see above, the constructor of contract extending the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," takes as parameters all of the needed information for the token deployment, succeeded by other parameters needed you use for your contract (",(0,r.kt)("inlineCode",{parentName:"p"},"hash")," in this particular example).\nEach constructor extending the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," class must also call the instances ",(0,r.kt)("inlineCode",{parentName:"p"},"init")," method and pass the constructors arguments. It is important to call this function ",(0,r.kt)("strong",{parentName:"p"},"after")," the call to ",(0,r.kt)("inlineCode",{parentName:"p"},"super"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"Here's an example of how you would deploy the new FT:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'HashLockFT.loadArtifact();\n\nconst tick = toByteString("DOGE", true);\nconst max = 100000n;\nconst lim = max / 10n;\nconst dec = 0n\n\nconst hashLock = new HashLockFT(\n tick,\n max,\n lim,\n dec,\n sha256(toByteString("secret0", true))\n);\nawait hashLock.connect(getDefaultSigner());\nawait hashLock.deployToken();\n')),(0,r.kt)("h3",{id:"mint-and-transfer"},"Mint and Transfer"),(0,r.kt)("p",null,"Once the deployment transaction was successfully broadcast, the token is ready for minting."),(0,r.kt)("p",null,"Here's how you can mint some amount:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Minting\nconst amt = 1000n;\nconst mintTx = await hashLock.mint(amt);\nconsole.log("Minted tx: ", mintTx.id);\n')),(0,r.kt)("p",null,"Note, that if the amount exceeds the limit set above, or the token was already wholely minted, the transaction won't be considered valid by 1Sat indexers."),(0,r.kt)("p",null,"The minted amount can then be transferred by calling the contract, as in ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"regular sCrypt contracts"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Transfer\nfor (let i = 0; i < 3; i++) {\n // The recipient contract.\n // Because this particular contract doesn\'t enforce subsequent outputs,\n // it could be any other contract or just a P2PKH.\n const receiver = new HashLockFT(\n tick,\n max,\n lim,\n dec,\n sha256(toByteString(`secret${i + 1}`, true))\n );\n const recipients: Array = [\n {\n instance: receiver,\n amt: 10n,\n },\n ];\n\n // Unlock and transfer.\n const { tx } = await hashLock.methods.unlock(\n toByteString(`secret:${i}`, true),\n {\n transfer: recipients,\n }\n );\n console.log("Transfer tx: ", tx.id);\n \n // Update instance for next iteration.\n hashLock = recipients[0].instance as HashLockFT;\n}\n')),(0,r.kt)("p",null,"Note that the new recipient smart contract instance is passed as a parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," during the call to the deployed instance. The ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter is an array of contract instances that extend ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1"),"."),(0,r.kt)("h2",{id:"v2"},"v2"),(0,r.kt)("p",null,"Version 2 of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV-20")," token protocol simplifies the process of minting a new fungible token. In this version, the deployment and minting are done within a single transaction. Unlike v1, v2 lacks a token ticker field. The token is identified by an ",(0,r.kt)("inlineCode",{parentName:"p"},"id")," field, which is the transaction id and output index where the token was minted, in the form of ",(0,r.kt)("inlineCode",{parentName:"p"},"_"),"."),(0,r.kt)("p",null,"Please refer to the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20#new-in-v2-tickerless-mode"},"official 1Sat documentation")," for more info."),(0,r.kt)("p",null,"To create a v2 token smart contract, have it extend the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2")," class:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFTV2 extends BSV20V2 {\n @prop()\n hash: Sha256\n\n constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {\n super(id, max, dec)\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("h3",{id:"mint"},"Mint"),(0,r.kt)("p",null,"In v1, tokens are deployed and minted in separate transactions, but in v2, all tokens are deployed and minted in one transactions. Here's an example of how you would deploy the new v2 FT:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"HashLockFTV2.loadArtifact()\n\nconst max = 10000n // Whole token amount.\nconst dec = 0n // Decimal precision.\n\nhashLock = new HashLockFTV2(\n toByteString(''),\n max,\n dec,\n sha256(toByteString('super secret', true))\n)\nawait hashLock.connect(getDefaultSigner())\n\ntokenId = await hashLock.deployToken()\nconsole.log('token id: ', tokenId)\n")),(0,r.kt)("admonition",{type:"note"},(0,r.kt)("p",{parentName:"admonition"},"Since we cannot know the id of the token deployment transaction at the time of deployment, the id is empty.")),(0,r.kt)("p",null,"The whole token supply is minted within the first transaction, and whoever can unlock the deployment UTXO will gain full control of the whole supply. Additionally, the smart contract itself can enforce rules for the distribution of the tokens."),(0,r.kt)("h3",{id:"transfer"},"Transfer"),(0,r.kt)("p",null,"The minted amount can be transferred by invoking the contract, similar to ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"standard sCrypt contracts"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Transfer\nfor (let i = 0; i < 3; i++) {\n // The recipient contract.\n // Because this particular contract doesn\'t enforce subsequent outputs,\n // it could be any other contract or just a P2PKH.\n const receiver = new HashLockFTV2(\n toByteString(tokenId, true),\n max,\n dec,\n sha256(toByteString(`secret${i + 1}`, true))\n );\n const recipients: Array = [\n {\n instance: receiver,\n amt: 10n,\n },\n ];\n\n // Unlock and transfer.\n const { tx } = await hashLock.methods.unlock(\n toByteString(`secret:${i}`, true),\n {\n transfer: recipients,\n }\n );\n console.log("Transfer tx: ", tx.id);\n \n // Update instance for next iteration.\n hashLock = recipients[0].instance as HashLockFTV2;\n}\n')),(0,r.kt)("p",null,"The new recipient smart contract instance is provided as a ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter when calling the deployed instance. The ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter consists of an array of contract instances derived from ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2"),"."),(0,r.kt)("hr",null))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/a5c7ba44.446cc638.js b/assets/js/a5c7ba44.446cc638.js deleted file mode 100644 index fca14552c..000000000 --- a/assets/js/a5c7ba44.446cc638.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[7062],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>h});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},d=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(n),h=r,m=d["".concat(l,".").concat(h)]||d[h]||u[h]||o;return n?a.createElement(m,i(i({ref:t},p),{},{components:n})):a.createElement(m,i({ref:t},p))}));function h(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,i=new Array(o);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:r,i[1]=s;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:3},i="Funglible Tokens - FTs",s={unversionedId:"tokens/ft/ft",id:"tokens/ft/ft",title:"Funglible Tokens - FTs",description:"Just like NFTs, scrypt-ord also supports fungible tokens. Under the hood it utilizes the bsv-20 protocol.",source:"@site/docs/tokens/ft/ft.md",sourceDirName:"tokens/ft",slug:"/tokens/ft/",permalink:"/tokens/ft/",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3},sidebar:"tutorialSidebar",previous:{title:"buildStateOutputNFT",permalink:"/tokens/nft/buildstateoutputnft"},next:{title:"Transfer Existing FT to a Smart Contract",permalink:"/tokens/ft/existing"}},l={},c=[{value:"v1",id:"v1",level:2},{value:"Deployment",id:"deployment",level:3},{value:"Mint and Transfer",id:"mint-and-transfer",level:3},{value:"v2",id:"v2",level:2},{value:"Mint",id:"mint",level:3},{value:"Transfer",id:"transfer",level:3}],p={toc:c};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"funglible-tokens---fts"},"Funglible Tokens - FTs"),(0,r.kt)("p",null,"Just like NFTs, ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," also supports fungible tokens. Under the hood it utilizes the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20"},(0,r.kt)("inlineCode",{parentName:"a"},"bsv-20")," protocol"),"."),(0,r.kt)("p",null,"BSV-20 is a protocol for creating fungible tokens on the Bitcoin SV blockchain. Fungible tokens are interchangeable with each other, and can be used to represent a variety of assets, such as currencies, securities, and in-game items."),(0,r.kt)("p",null,(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," supports both v1 and v2 of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV-20")," FT protocol."),(0,r.kt)("h2",{id:"v1"},"v1"),(0,r.kt)("p",null,"Tokens utilizing the first version of the ",(0,r.kt)("inlineCode",{parentName:"p"},"bsv-20")," must be initialized by a ",(0,r.kt)("strong",{parentName:"p"},"deployment")," inscription, which specifies the token's ticker symbol, amount and mint limit. For more information, refer to the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20#v1-mint-first-is-first-mode"},"1Sat docs"),"."),(0,r.kt)("p",null,"To create a v1 token smart contract, have it extend the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," class:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashPuzzleFT extends BSV20V1 {\n @prop()\n hash: Sha256\n\n constructor(tick: ByteString, max: bigint, lim: bigint, hash: Sha256) {\n super(tick, max, lim)\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"As you can see above, the constructor of contract extending the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," takes as parameters all of the needed information for the token deployment, succeeded by other parameters needed you use for your contract (",(0,r.kt)("inlineCode",{parentName:"p"},"hash")," in this particular example).\nEach constructor extending the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," class must also call the instances ",(0,r.kt)("inlineCode",{parentName:"p"},"init")," method and pass the constructors arguments. It is important to call this function ",(0,r.kt)("strong",{parentName:"p"},"after")," the call to ",(0,r.kt)("inlineCode",{parentName:"p"},"super"),"."),(0,r.kt)("h3",{id:"deployment"},"Deployment"),(0,r.kt)("p",null,"Here's an example of how you would deploy the new FT:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'HashPuzzleFT.loadArtifact();\n\nconst tick = toByteString("DOGE", true);\nconst max = 100000n;\nconst lim = max / 10n;\n\nconst hashPuzzle = new HashPuzzleFT(\n tick,\n max,\n lim,\n sha256(toByteString("secret0", true))\n);\nawait hashPuzzle.connect(getDefaultSigner());\nawait hashPuzzle.deployToken();\n')),(0,r.kt)("h3",{id:"mint-and-transfer"},"Mint and Transfer"),(0,r.kt)("p",null,"Once the deployment transaction was successfully broadcast, the token is ready for minting."),(0,r.kt)("p",null,"Here's how you can mint some amount:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Minting\nconst amt = 1000n;\nconst mintTx = await hashPuzzle.mint(amt);\nconsole.log("Minted tx: ", mintTx.id);\n')),(0,r.kt)("p",null,"Note, that if the amount exceeds the limit set above, or the token was already wholely minted, the transaction won't be considered valid by 1Sat indexers."),(0,r.kt)("p",null,"The minted amount can then be transferred by calling the contract, as in ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"regular sCrypt contracts"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Transfer\nfor (let i = 0; i < 3; i++) {\n // The recipient contract.\n // Because this particular contract doesn\'t enforce subsequent outputs,\n // it could be any other contract or just a P2PKH.\n const receiver = new HashPuzzleFT(\n tick,\n max,\n lim,\n sha256(toByteString(`secret${i + 1}`, true))\n );\n const recipients: Array = [\n {\n instance: receiver,\n amt: 10n,\n },\n ];\n\n // Unlock and transfer.\n const { tx } = await hashPuzzle.methods.unlock(\n toByteString(`secret:${i}`, true),\n {\n transfer: recipients,\n }\n );\n console.log("Transfer tx: ", tx.id);\n \n // Update instance for next iteration.\n hashPuzzle = recipients[0].instance as HashPuzzleFT;\n}\n')),(0,r.kt)("p",null,"Note that the new recipient smart contract instance is passed as a parameter named ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," during the call to the deployed instance. The ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter is an array of contract instances that extend ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1"),"."),(0,r.kt)("h2",{id:"v2"},"v2"),(0,r.kt)("p",null,"Version 2 of the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV-20")," token protocol simplifies the process of minting a new fungible token. In this version, the deployment and minting are done within a single transaction. Unlike v1, v2 lacks a token ticker field. The token is identified by an ",(0,r.kt)("inlineCode",{parentName:"p"},"id")," field, which is the transaction id and output index where the token was minted, in the form of ",(0,r.kt)("inlineCode",{parentName:"p"},"_"),"."),(0,r.kt)("p",null,"Please refer to the ",(0,r.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/bsv20#new-in-v2-tickerless-mode"},"official 1Sat documentation")," for more info."),(0,r.kt)("p",null,"To create a v2 token smart contract, have it extend the ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2")," class:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashPuzzleFTV2 extends BSV20V2 {\n @prop()\n hash: Sha256\n\n constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {\n super(id, max, dec)\n this.init(...arguments)\n this.hash = hash\n }\n\n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("h3",{id:"mint"},"Mint"),(0,r.kt)("p",null,"In v1, tokens are deployed and minted in separate transactions, but in v2, all tokens are deployed and minted in one transactions. Here's an example of how you would deploy the new v2 FT:"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"HashPuzzleFTV2.loadArtifact()\n\nconst max = 10000n // Whole token amount.\nconst dec = 0n // Decimal precision.\n\nhashPuzzle = new HashPuzzleFTV2(\n toByteString(''),\n max,\n dec,\n sha256(toByteString('super secret', true))\n)\nawait hashPuzzle.connect(getDefaultSigner())\n\ntokenId = await hashPuzzle.deployToken()\nconsole.log('token id: ', tokenId)\n")),(0,r.kt)("admonition",{type:"note"},(0,r.kt)("p",{parentName:"admonition"},"Since we cannot know the id of the token deployment transaction at the time of deployment, the id is empty.")),(0,r.kt)("p",null,"The whole token supply is minted within the first transaction, and whoever can unlock the deployment UTXO will gain full control of the whole supply. Additionally, the smart contract itself can enforce rules for the distribution of the tokens."),(0,r.kt)("h3",{id:"transfer"},"Transfer"),(0,r.kt)("p",null,"The minted amount can be transferred by invoking the contract, similar to ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"standard sCrypt contracts"),":"),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},'// Transfer\nfor (let i = 0; i < 3; i++) {\n // The recipient contract.\n // Because this particular contract doesn\'t enforce subsequent outputs,\n // it could be any other contract or just a P2PKH.\n const receiver = new HashPuzzleFT(\n toByteString(tokenId, true),\n max,\n dec,\n sha256(toByteString(`secret${i + 1}`, true))\n );\n const recipients: Array = [\n {\n instance: receiver,\n amt: 10n,\n },\n ];\n\n // Unlock and transfer.\n const { tx } = await hashPuzzle.methods.unlock(\n toByteString(`secret:${i}`, true),\n {\n transfer: recipients,\n }\n );\n console.log("Transfer tx: ", tx.id);\n \n // Update instance for next iteration.\n hashPuzzle = recipients[0].instance as HashPuzzleFTV2;\n}\n')),(0,r.kt)("p",null,"The new recipient smart contract instance is provided as a ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter when calling the deployed instance. The ",(0,r.kt)("inlineCode",{parentName:"p"},"transfer")," parameter consists of an array of contract instances derived from ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V2"),"."),(0,r.kt)("hr",null))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c150b9b5.2109f321.js b/assets/js/c150b9b5.ebaa03c2.js similarity index 79% rename from assets/js/c150b9b5.2109f321.js rename to assets/js/c150b9b5.ebaa03c2.js index 807b4662f..b4be8a90c 100644 --- a/assets/js/c150b9b5.2109f321.js +++ b/assets/js/c150b9b5.ebaa03c2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[9698],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),h=c(n),m=r,u=h["".concat(l,".").concat(m)]||h[m]||d[m]||o;return n?a.createElement(u,s(s({ref:t},p),{},{components:n})):a.createElement(u,s({ref:t},p))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=h;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:r,s[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:10},s="Tutorial 10: Mint BSV20 V1 Token",i={unversionedId:"tutorials/mint-bsv20-v1",id:"tutorials/mint-bsv20-v1",title:"Tutorial 10: Mint BSV20 V1 Token",description:"Overview",source:"@site/docs/tutorials/mint-bsv20-v1.md",sourceDirName:"tutorials",slug:"/tutorials/mint-bsv20-v1",permalink:"/tutorials/mint-bsv20-v1",draft:!1,tags:[],version:"current",sidebarPosition:10,frontMatter:{sidebar_position:10},sidebar:"tutorialSidebar",previous:{title:"Tutorial 9: Mint BSV20 V2 Token",permalink:"/tutorials/mint-bsv20-v2"},next:{title:"Tutorial 11: Ordinal Lock",permalink:"/tutorials/ordinal-lock"}},l={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Deploy and Mint",id:"deploy-and-mint",level:2},{value:"Transfer Token",id:"transfer-token",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:c};function d(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"tutorial-10-mint-bsv20-v1-token"},"Tutorial 10: Mint BSV20 V1 Token"),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"In this tutorial, we will use contract ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example, to introduce how to mint a BSV20 Token (",(0,r.kt)("strong",{parentName:"p"},"version 1"),") with ",(0,r.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," and transfer it with a Smart Contract."),(0,r.kt)("p",null,"To enable all these features, you should install ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,r.kt)("h2",{id:"contract"},"Contract"),(0,r.kt)("p",null,"The new contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT")," is almost the same as the previous ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except two differences."),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"It must be derived from ",(0,r.kt)("inlineCode",{parentName:"li"},"BSV20V1")," instead of ",(0,r.kt)("inlineCode",{parentName:"li"},"SmartContract"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFT extends BSV20V1 {\n ...\n}\n")),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"The constructor has extra parameters - ",(0,r.kt)("inlineCode",{parentName:"li"},"tick"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"max"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"lim"),", and ",(0,r.kt)("inlineCode",{parentName:"li"},"dec")," - representing ",(0,r.kt)("a",{parentName:"li",href:"https://docs.1satordinals.com/bsv20#v1-deploy-first-is-first-mode-only"},"BSV20 fields"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {\n super(tick, max, lim, dec)\n this.init(...arguments)\n this.hash = hash\n}\n")),(0,r.kt)("p",null,"The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct message."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFT extends BSV20V1 {\n @prop()\n hash: Sha256\n \n ...\n \n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"The base class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," encapsulated helper functions to handle BSV20 (version 1) tokens. If you want to create your own contract that can interact with BSV20 protocol, derive from it."),(0,r.kt)("h2",{id:"deploy-and-mint"},"Deploy and Mint"),(0,r.kt)("p",null,"For BSV20 version 1, tokens must be deployed before mint. We first create an instance of contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT"),", then call function ",(0,r.kt)("inlineCode",{parentName:"p"},"deployToken")," to deploy the new token, and call ",(0,r.kt)("inlineCode",{parentName:"p"},"mint")," at last to mint tokens into the contract instance."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// BSV20 fields\nconst tick = toByteString('HELLO', true)\nconst max = 100n\nconst lim = 10n\nconst dec = 0n\n// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockFT(tick, max, lim, dec, hash)\n...\n// deploy the new BSV20 token $HELLO\nawait hashLock.deployToken()\n// mint 10 $HELLO into contract instance\nconst mintTx = await hashLock.mint(10n)\n")),(0,r.kt)("p",null,"Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH."),(0,r.kt)("p",null,"In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,r.kt)("h2",{id:"transfer-token"},"Transfer Token"),(0,r.kt)("p",null,"For now, the contract instance holds the token and we try to transfer it to a P2PKH address."),(0,r.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,r.kt)("p",null,"Class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1P2PKH")," represents a P2PKH address that can hold BSV20 version 1 tokens. Its constructor takes BSV20 fields and an receiving address as parameters."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"const alice = new BSV20V1P2PKH(tick, max, lim, dec, addressAlice)\nconst bob = new BSV20V1P2PKH(tick, max, lim, dec, addressBob)\n")),(0,r.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,r.kt)("p",null,"Just as other ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," methods we introduced before, we call the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT")," as follows."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// Call the contract\nconst { tx: transferTx } = await hashLock.methods.unlock(message, {\n transfer: [\n {\n instance: alice,\n amt: 2n,\n },\n {\n instance: bob,\n amt: 5n,\n },\n ],\n} as MethodCallOptions)\n")),(0,r.kt)("p",null,"This code will create a transaction that transfers 2 tokens to ",(0,r.kt)("inlineCode",{parentName:"p"},"alice")," and 5 to ",(0,r.kt)("inlineCode",{parentName:"p"},"bob"),"."),(0,r.kt)("p",null,"The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key ",(0,r.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress")," of struct ",(0,r.kt)("inlineCode",{parentName:"p"},"MethodCallOptions"),"."),(0,r.kt)("p",null,"Execute command ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/mintBSV20.ts")," to run this example."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5527).Z,width:"1584",height:"300"})),(0,r.kt)("p",null,"Then you can check your token transfer details on the explorer."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(1899).Z,width:"2466",height:"1352"})),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(7859).Z,width:"2204",height:"1538"})),(0,r.kt)("p",null,"The UTXO model is a powerful feature of BSV20, we can send tokens to multiple receivers in a single transaction, allowing us to create complex and efficient transactions."),(0,r.kt)("h2",{id:"conclusion"},"Conclusion"),(0,r.kt)("p",null,"Great! You have finished the tutorial on how to mint and transfer the BSV20 Token with a Smart Contract."),(0,r.kt)("p",null,"The full complete ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockFT.ts"},"contract")," and ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/mintBSV20.ts"},"example")," can be found in sCrypt's ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}d.isMDXComponent=!0},1899:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-mint-tx-27e81f72e8b13b4337d3a598b77df7f6.png"},7859:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-transfer-tx-39d10c2524b6bf0a9f2bd42f85a66e16.png"},5527:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-457835378ee4536cd5201043011cbede.png"}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[9698],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>m});var a=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function s(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var l=a.createContext({}),c=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=c(e.components);return a.createElement(l.Provider,{value:t},e.children)},d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},h=a.forwardRef((function(e,t){var n=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),h=c(n),m=r,u=h["".concat(l,".").concat(m)]||h[m]||d[m]||o;return n?a.createElement(u,s(s({ref:t},p),{},{components:n})):a.createElement(u,s({ref:t},p))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var o=n.length,s=new Array(o);s[0]=h;var i={};for(var l in t)hasOwnProperty.call(t,l)&&(i[l]=t[l]);i.originalType=e,i.mdxType="string"==typeof e?e:r,s[1]=i;for(var c=2;c{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>c});var a=n(7462),r=(n(7294),n(3905));const o={sidebar_position:10},s="Tutorial 10: Mint BSV20 V1 Token",i={unversionedId:"tutorials/mint-bsv20-v1",id:"tutorials/mint-bsv20-v1",title:"Tutorial 10: Mint BSV20 V1 Token",description:"Overview",source:"@site/docs/tutorials/mint-bsv20-v1.md",sourceDirName:"tutorials",slug:"/tutorials/mint-bsv20-v1",permalink:"/tutorials/mint-bsv20-v1",draft:!1,tags:[],version:"current",sidebarPosition:10,frontMatter:{sidebar_position:10},sidebar:"tutorialSidebar",previous:{title:"Tutorial 9: Mint BSV20 V2 Token",permalink:"/tutorials/mint-bsv20-v2"},next:{title:"Tutorial 11: Ordinal Lock",permalink:"/tutorials/ordinal-lock"}},l={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Deploy and Mint",id:"deploy-and-mint",level:2},{value:"Transfer Token",id:"transfer-token",level:2},{value:"Step 1. Create Receiver Instance",id:"step-1-create-receiver-instance",level:3},{value:"Step 2. Call the Contract",id:"step-2-call-the-contract",level:3},{value:"Conclusion",id:"conclusion",level:2}],p={toc:c};function d(e){let{components:t,...o}=e;return(0,r.kt)("wrapper",(0,a.Z)({},p,o,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"tutorial-10-mint-bsv20-v1-token"},"Tutorial 10: Mint BSV20 V1 Token"),(0,r.kt)("h2",{id:"overview"},"Overview"),(0,r.kt)("p",null,"In this tutorial, we will use contract ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"HashLock")," as an example, to introduce how to mint a BSV20 Token (",(0,r.kt)("strong",{parentName:"p"},"version 1"),") with ",(0,r.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," and transfer it with a Smart Contract."),(0,r.kt)("p",null,"To enable all these features, you should install ",(0,r.kt)("inlineCode",{parentName:"p"},"scrypt-ord")," as an dependency in your project."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-bash"},"npm install scrypt-ord\n")),(0,r.kt)("h2",{id:"contract"},"Contract"),(0,r.kt)("p",null,"The new contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT")," is almost the same as the previous ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/boilerplate/blob/master/src/contracts/hashLock.ts"},"implementation"),", except two differences."),(0,r.kt)("ol",null,(0,r.kt)("li",{parentName:"ol"},"It must be derived from ",(0,r.kt)("inlineCode",{parentName:"li"},"BSV20V1")," instead of ",(0,r.kt)("inlineCode",{parentName:"li"},"SmartContract"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFT extends BSV20V1 {\n ...\n}\n")),(0,r.kt)("ol",{start:2},(0,r.kt)("li",{parentName:"ol"},"The constructor has extra parameters - ",(0,r.kt)("inlineCode",{parentName:"li"},"tick"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"max"),", ",(0,r.kt)("inlineCode",{parentName:"li"},"lim"),", and ",(0,r.kt)("inlineCode",{parentName:"li"},"dec")," - representing ",(0,r.kt)("a",{parentName:"li",href:"https://docs.1satordinals.com/bsv20#v1-deploy-first-is-first-mode-only"},"BSV20 fields"),".")),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {\n super(tick, max, lim, dec)\n this.init(...arguments)\n this.hash = hash\n}\n")),(0,r.kt)("p",null,"The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," with the correct message."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"class HashLockFT extends BSV20V1 {\n @prop()\n hash: Sha256\n \n ...\n \n @method()\n public unlock(message: ByteString) {\n assert(this.hash == sha256(message), 'hashes are not equal')\n }\n}\n")),(0,r.kt)("p",null,"The base class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1")," encapsulated helper functions to handle BSV20 (version 1) tokens. If you want to create your own contract that can interact with BSV20 protocol, derive from it."),(0,r.kt)("h2",{id:"deploy-and-mint"},"Deploy and Mint"),(0,r.kt)("p",null,"For BSV20 version 1, tokens must be deployed before mint. We first create an instance of contract ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT"),", then call function ",(0,r.kt)("inlineCode",{parentName:"p"},"deployToken")," to deploy the new token, and call ",(0,r.kt)("inlineCode",{parentName:"p"},"mint")," at last to mint tokens into the contract instance."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// BSV20 fields\nconst tick = toByteString('HELLO', true)\nconst max = 100n\nconst lim = 10n\nconst dec = 0n\n// create contract instance\nconst message = toByteString('Hello sCrypt', true)\nconst hash = sha256(message)\nconst hashLock = new HashLockFT(tick, max, lim, dec, hash)\n...\n// deploy the new BSV20 token $HELLO\nawait hashLock.deployToken()\n// mint 10 $HELLO into contract instance\nconst mintTx = await hashLock.mint(10n)\n")),(0,r.kt)("p",null,"Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH."),(0,r.kt)("p",null,"In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked."),(0,r.kt)("h2",{id:"transfer-token"},"Transfer Token"),(0,r.kt)("p",null,"For now, the contract instance holds the token and we try to transfer it to a P2PKH address."),(0,r.kt)("h3",{id:"step-1-create-receiver-instance"},"Step 1. Create Receiver Instance"),(0,r.kt)("p",null,"Class ",(0,r.kt)("inlineCode",{parentName:"p"},"BSV20V1P2PKH")," represents a P2PKH address that can hold BSV20 version 1 tokens. Its constructor takes BSV20 fields and an receiving address as parameters."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"const alice = new BSV20V1P2PKH(tick, max, lim, dec, addressAlice)\nconst bob = new BSV20V1P2PKH(tick, max, lim, dec, addressBob)\n")),(0,r.kt)("h3",{id:"step-2-call-the-contract"},"Step 2. Call the Contract"),(0,r.kt)("p",null,"Just as other ",(0,r.kt)("a",{parentName:"p",href:"/how-to-deploy-and-call-a-contract/#contract-call"},"contract calling")," methods we introduced before, we call the public method ",(0,r.kt)("inlineCode",{parentName:"p"},"unlock")," of ",(0,r.kt)("inlineCode",{parentName:"p"},"HashLockFT")," as follows."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-ts"},"// Call the contract\nconst { tx: transferTx } = await hashLock.methods.unlock(message, {\n transfer: [\n {\n instance: alice,\n amt: 2n,\n },\n {\n instance: bob,\n amt: 5n,\n },\n ],\n} as OrdiMethodCallOptions)\n")),(0,r.kt)("p",null,"This code will create a transaction that transfers 2 tokens to ",(0,r.kt)("inlineCode",{parentName:"p"},"alice")," and 5 to ",(0,r.kt)("inlineCode",{parentName:"p"},"bob"),"."),(0,r.kt)("p",null,"The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key ",(0,r.kt)("inlineCode",{parentName:"p"},"tokenChangeAddress")," of struct ",(0,r.kt)("inlineCode",{parentName:"p"},"OrdiMethodCallOptions"),"."),(0,r.kt)("p",null,"Execute command ",(0,r.kt)("inlineCode",{parentName:"p"},"npx ts-node tests/examples/mintBSV20.ts")," to run this example."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(5527).Z,width:"1584",height:"300"})),(0,r.kt)("p",null,"Then you can check your token transfer details on the explorer."),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(1899).Z,width:"2466",height:"1352"})),(0,r.kt)("p",null,(0,r.kt)("img",{src:n(7859).Z,width:"2204",height:"1538"})),(0,r.kt)("p",null,"The UTXO model is a powerful feature of BSV20, we can send tokens to multiple receivers in a single transaction, allowing us to create complex and efficient transactions."),(0,r.kt)("h2",{id:"conclusion"},"Conclusion"),(0,r.kt)("p",null,"Great! You have finished the tutorial on how to mint and transfer the BSV20 Token with a Smart Contract."),(0,r.kt)("p",null,"The full complete ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/hashLockFT.ts"},"contract")," and ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/examples/mintBSV20.ts"},"example")," can be found in sCrypt's ",(0,r.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord"},"repository"),"."))}d.isMDXComponent=!0},1899:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-mint-tx-27e81f72e8b13b4337d3a598b77df7f6.png"},7859:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-transfer-tx-39d10c2524b6bf0a9f2bd42f85a66e16.png"},5527:(e,t,n)=>{n.d(t,{Z:()=>a});const a=n.p+"assets/images/mint-bsv20-457835378ee4536cd5201043011cbede.png"}}]); \ No newline at end of file diff --git a/assets/js/db37e9c8.2dd1c583.js b/assets/js/db37e9c8.686b3333.js similarity index 72% rename from assets/js/db37e9c8.2dd1c583.js rename to assets/js/db37e9c8.686b3333.js index a403b2356..26b1aefdd 100644 --- a/assets/js/db37e9c8.2dd1c583.js +++ b/assets/js/db37e9c8.686b3333.js @@ -1 +1 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[8567],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),f=l(n),d=a,g=f["".concat(c,".").concat(d)]||f[d]||u[d]||o;return n?r.createElement(g,i(i({ref:t},p),{},{components:n})):r.createElement(g,i({ref:t},p))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=f;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Transfer Existing NFT to a Smart Contract",sidebar_position:1},i=void 0,s={unversionedId:"tokens/nft/existing",id:"tokens/nft/existing",title:"Transfer Existing NFT to a Smart Contract",description:"Suppose you would like to transfer an existing NFT that was already inscribed in the past, which is typically locked using a P2PKH lock.",source:"@site/docs/tokens/nft/existing.md",sourceDirName:"tokens/nft",slug:"/tokens/nft/existing",permalink:"/tokens/nft/existing",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{title:"Transfer Existing NFT to a Smart Contract",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Non Funglible Tokens - NFTs",permalink:"/tokens/nft/"},next:{title:"buildStateOutputNFT",permalink:"/tokens/nft/buildstateoutputnft"}},c={},l=[],p={toc:l};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose you would like to transfer an existing NFT that was already inscribed in the past, which is typically locked using a ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," lock.\nYou can fetch all the needed data for the transfer by either using ",(0,a.kt)("inlineCode",{parentName:"p"},"fromUTXO")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"getLatestInstance"),". The former takes the deployed NFT's current UTXO, while the latter takes the NFT's ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/readme/terms#origin"},"origin"),"."),(0,a.kt)("p",null,"If the deployed NFT is locked using a regular ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," you may unlock it like the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const outpoint = '036718e5c603169b9981a55f276adfa7b5d024616ac95e048b05a81258ea2388_0';\n\n// Create a P2PKH object from a UTXO\nconst utxo: UTXO = OneSatApis.fetchUTXOByOutpoint(outpoint);\nconst p2pkh = OrdNFTP2PKH.fromUTXO(utxo);\n// Alternatively, create a P2PKH from an origin\nconst p2pkh = await OrdNFTP2PKH.getLatestInstance(outpoint);\n\n// Construct recipient smart contract\nconst message = toByteString('super secret', true);\nconst hash = sha256(message);\nconst recipient = new HashPuzzleNFT(hash);\nawait recipient.connect(getDefaultSigner());\n\n// Unlock deployed NFT and send it to the recipient hash puzzle contract\nawait p2pkh.connect(getDefaultSigner());\n\nconst { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n } as MethodCallOptions\n);\n\nconsole.log(\"Transferred NFT: \", transferTx.id);\n")),(0,a.kt)("p",null,"Alternatively, if the NFT is locked using a smart contract, i.e. ",(0,a.kt)("inlineCode",{parentName:"p"},"HashPuzzleNFT"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"HashPuzzleNFT.loadArtifact();\n\n// Retrieve `HashPuzzleNFT` instance holding the NFT\nconst nft = await HashPuzzleNFT.getLatestInstance(outpoint);\nawait nft.connect(getDefaultSigner());\n\nconst hash = sha256(toByteString('next super secret', true));\nconst recipient = new HashPuzzleNFT(hash);\nawait recipient.connect(getDefaultSigner());\n\n// Send NFT to recipient\nconst { tx: transferTx } = await nft.methods.unlock(\n toByteString('super secret', true),\n {\n transfer: recipient,\n }\n);\n\nconsole.log(\"Transferred NFT: \", transferTx.id);\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[8567],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>d});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function i(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var c=r.createContext({}),l=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},p=function(e){var t=l(e.components);return r.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,c=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),f=l(n),d=a,g=f["".concat(c,".").concat(d)]||f[d]||u[d]||o;return n?r.createElement(g,i(i({ref:t},p),{},{components:n})):r.createElement(g,i({ref:t},p))}));function d(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=f;var s={};for(var c in t)hasOwnProperty.call(t,c)&&(s[c]=t[c]);s.originalType=e,s.mdxType="string"==typeof e?e:a,i[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>s,toc:()=>l});var r=n(7462),a=(n(7294),n(3905));const o={title:"Transfer Existing NFT to a Smart Contract",sidebar_position:1},i=void 0,s={unversionedId:"tokens/nft/existing",id:"tokens/nft/existing",title:"Transfer Existing NFT to a Smart Contract",description:"Suppose you would like to transfer an existing NFT that was already inscribed in the past, which is typically locked using a P2PKH lock.",source:"@site/docs/tokens/nft/existing.md",sourceDirName:"tokens/nft",slug:"/tokens/nft/existing",permalink:"/tokens/nft/existing",draft:!1,tags:[],version:"current",sidebarPosition:1,frontMatter:{title:"Transfer Existing NFT to a Smart Contract",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Non Funglible Tokens - NFTs",permalink:"/tokens/nft/"},next:{title:"buildStateOutputNFT",permalink:"/tokens/nft/buildstateoutputnft"}},c={},l=[],p={toc:l};function u(e){let{components:t,...n}=e;return(0,a.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"Suppose you would like to transfer an existing NFT that was already inscribed in the past, which is typically locked using a ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," lock.\nYou can fetch all the needed data for the transfer by either using ",(0,a.kt)("inlineCode",{parentName:"p"},"fromUTXO")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"getLatestInstance"),". The former takes the deployed NFT's current UTXO, while the latter takes the NFT's ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/readme/terms#origin"},"origin"),"."),(0,a.kt)("p",null,"If the deployed NFT is locked using a regular ",(0,a.kt)("inlineCode",{parentName:"p"},"P2PKH")," you may unlock it like the following:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const outpoint = '036718e5c603169b9981a55f276adfa7b5d024616ac95e048b05a81258ea2388_0';\n\n// Create a P2PKH object from a UTXO\nconst utxo: UTXO = OneSatApis.fetchUTXOByOutpoint(outpoint);\nconst p2pkh = OrdiNFTP2PKH.fromUTXO(utxo);\n// Alternatively, create a P2PKH from an origin\nconst p2pkh = await OrdiNFTP2PKH.getLatestInstance(outpoint);\n\n// Construct recipient smart contract\nconst message = toByteString('super secret', true);\nconst hash = sha256(message);\nconst recipient = new HashLockNFT(hash);\nawait recipient.connect(getDefaultSigner());\n\n// Unlock deployed NFT and send it to the recipient hash lock contract\nawait p2pkh.connect(getDefaultSigner());\n\nconst { tx: transferTx } = await p2pkh.methods.unlock(\n (sigResps) => findSig(sigResps, `yourPubKey`),\n PubKey(`yourPubKey`.toByteString()),\n {\n transfer: recipient,\n pubKeyOrAddrToSign: `yourPubKey`,\n } as OrdiMethodCallOptions\n);\n\nconsole.log(\"Transferred NFT: \", transferTx.id);\n")),(0,a.kt)("p",null,"Alternatively, if the NFT is locked using a smart contract, i.e. ",(0,a.kt)("inlineCode",{parentName:"p"},"HashLockNFT"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"HashLockNFT.loadArtifact();\n\n// Retrieve `HashLockNFT` instance holding the NFT\nconst nft = await HashLockNFT.getLatestInstance(outpoint);\nawait nft.connect(getDefaultSigner());\n\nconst hash = sha256(toByteString('next super secret', true));\nconst recipient = new HashLockNFT(hash);\nawait recipient.connect(getDefaultSigner());\n\n// Send NFT to recipient\nconst { tx: transferTx } = await nft.methods.unlock(\n toByteString('super secret', true),\n {\n transfer: recipient,\n }\n);\n\nconsole.log(\"Transferred NFT: \", transferTx.id);\n")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fa14be71.6613587d.js b/assets/js/fa14be71.6613587d.js new file mode 100644 index 000000000..6b6409f17 --- /dev/null +++ b/assets/js/fa14be71.6613587d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6279],{3905:(t,e,n)=>{n.d(e,{Zo:()=>d,kt:()=>h});var r=n(7294);function a(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function s(t){for(var e=1;e=0||(a[n]=t[n]);return a}(t,e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(a[n]=t[n])}return a}var l=r.createContext({}),c=function(t){var e=r.useContext(l),n=e;return t&&(n="function"==typeof t?t(e):s(s({},e),t)),n},d=function(t){var e=c(t.components);return r.createElement(l.Provider,{value:e},t.children)},p={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},u=r.forwardRef((function(t,e){var n=t.components,a=t.mdxType,i=t.originalType,l=t.parentName,d=o(t,["components","mdxType","originalType","parentName"]),u=c(n),h=a,m=u["".concat(l,".").concat(h)]||u[h]||p[h]||i;return n?r.createElement(m,s(s({ref:e},d),{},{components:n})):r.createElement(m,s({ref:e},d))}));function h(t,e){var n=arguments,a=e&&e.mdxType;if("string"==typeof t||a){var i=n.length,s=new Array(i);s[0]=u;var o={};for(var l in e)hasOwnProperty.call(e,l)&&(o[l]=e[l]);o.originalType=t,o.mdxType="string"==typeof t?t:a,s[1]=o;for(var c=2;c{n.r(e),n.d(e,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var r=n(7462),a=(n(7294),n(3905));const i={sidebar_position:11},s="Tutorial 11: Ordinal Lock",o={unversionedId:"tutorials/ordinal-lock",id:"tutorials/ordinal-lock",title:"Tutorial 11: Ordinal Lock",description:"Overview",source:"@site/docs/tutorials/ordinal-lock.md",sourceDirName:"tutorials",slug:"/tutorials/ordinal-lock",permalink:"/tutorials/ordinal-lock",draft:!1,tags:[],version:"current",sidebarPosition:11,frontMatter:{sidebar_position:11},sidebar:"tutorialSidebar",previous:{title:"Tutorial 10: Mint BSV20 V1 Token",permalink:"/tutorials/mint-bsv20-v1"},next:{title:"The Official sCrypt 1Sat Ordinals SDK",permalink:"/tokens/"}},l={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Constructor",id:"constructor",level:3},{value:"Methods",id:"methods",level:3},{value:"Frontend",id:"frontend",level:2},{value:"Setup Project",id:"setup-project",level:3},{value:"Install the sCrypt SDK",id:"install-the-scrypt-sdk",level:3},{value:"Compile Contract",id:"compile-contract",level:3},{value:"Load Contract Artifact",id:"load-contract-artifact",level:3},{value:"Connect Signer to OrdiProvider",id:"connect-signer-to-ordiprovider",level:3},{value:"Integrate Wallet",id:"integrate-wallet",level:3},{value:"Load Ordinals",id:"load-ordinals",level:3},{value:"List an Ordinal",id:"list-an-ordinal",level:3},{value:"Buy an Ordinal",id:"buy-an-ordinal",level:3},{value:"Conclusion",id:"conclusion",level:2}],d={toc:c};function p(t){let{components:e,...i}=t;return(0,a.kt)("wrapper",(0,r.Z)({},d,i,{components:e,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"tutorial-11-ordinal-lock"},"Tutorial 11: Ordinal Lock"),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"In this tutorial, we will go over how to use ",(0,a.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," to build a full-stack dApp on Bitcoin to sell ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/"},"1Sat Ordinals"),", including the smart contract and an interactive front-end."),(0,a.kt)("h2",{id:"contract"},"Contract"),(0,a.kt)("p",null,"The contract ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/ordinalLock.ts"},"OrdinalLock")," allows an ordinal to be offered up for sale on a decentralized marketplace. These listings can be purchased by anyone who is able to pay the requested price. Listings can also be cancelled by the person who listed them."),(0,a.kt)("p",null,"To record the seller and price, we need to add two properties to the contract."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"export class OrdinalLock extends OrdinalNFT {\n @prop()\n seller: PubKey\n\n @prop()\n amount: bigint\n \n ...\n}\n")),(0,a.kt)("h3",{id:"constructor"},"Constructor"),(0,a.kt)("p",null,"Initialize all the ",(0,a.kt)("inlineCode",{parentName:"p"},"@prop")," properties in the constructor."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(seller: PubKey, amount: bigint) {\n super()\n this.init(...arguments)\n this.seller = seller\n this.amount = amount\n}\n")),(0,a.kt)("h3",{id:"methods"},"Methods"),(0,a.kt)("p",null,"The public method ",(0,a.kt)("inlineCode",{parentName:"p"},"purchase")," only needs to confine the transaction's outputs to contain:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"transfer ordinal to the buyer"),(0,a.kt)("li",{parentName:"ul"},"payment to the seller")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"@method()\npublic purchase(receiver: Addr) {\n const outputs =\n Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer\n Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller\n this.buildChangeOutput()\n assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs check failed')\n}\n")),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/ordinalLock.ts"},"final complete code")," is as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"import { Addr, prop, method, Utils, hash256, assert, ContractTransaction, bsv, PubKey, hash160, Sig, SigHash } from 'scrypt-ts'\nimport { OrdiMethodCallOptions, OrdinalNFT } from '../scrypt-ord'\n\nexport class OrdinalLock extends OrdinalNFT {\n @prop()\n seller: PubKey\n\n @prop()\n amount: bigint\n\n constructor(seller: PubKey, amount: bigint) {\n super()\n this.init(...arguments)\n this.seller = seller\n this.amount = amount\n }\n\n @method()\n public purchase(receiver: Addr) {\n const outputs =\n Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer\n Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller\n this.buildChangeOutput()\n assert(\n this.ctx.hashOutputs == hash256(outputs),\n 'hashOutputs check failed'\n )\n }\n\n @method(SigHash.ANYONECANPAY_SINGLE)\n public cancel(sig: Sig) {\n assert(this.checkSig(sig, this.seller), 'seller signature check failed')\n const outputs = Utils.buildAddressOutput(hash160(this.seller), 1n) // ordinal back to the seller\n assert(\n this.ctx.hashOutputs == hash256(outputs),\n 'hashOutputs check failed'\n )\n }\n\n static async buildTxForPurchase(\n current: OrdinalLock,\n options: OrdiMethodCallOptions,\n receiver: Addr\n ): Promise {\n const defaultAddress = await current.signer.getDefaultAddress()\n const tx = new bsv.Transaction()\n .addInput(current.buildContractInput())\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(receiver)\n ),\n satoshis: 1,\n })\n )\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(hash160(current.seller))\n ),\n satoshis: Number(current.amount),\n })\n )\n .change(options.changeAddress || defaultAddress)\n return {\n tx,\n atInputIndex: 0,\n nexts: [],\n }\n }\n\n static async buildTxForCancel(\n current: OrdinalLock,\n options: OrdiMethodCallOptions\n ): Promise {\n const defaultAddress = await current.signer.getDefaultAddress()\n const tx = new bsv.Transaction()\n .addInput(current.buildContractInput())\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(hash160(current.seller))\n ),\n satoshis: 1,\n })\n )\n .change(options.changeAddress || defaultAddress)\n return {\n tx,\n atInputIndex: 0,\n nexts: [],\n }\n }\n}\n")),(0,a.kt)("p",null,"Note the customized calling method ",(0,a.kt)("inlineCode",{parentName:"p"},"buildTxForPurchase")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"buildTxForCancel")," ensure the ordinal is in the first input and goes to the first output, which is also a 1sat output."),(0,a.kt)("h2",{id:"frontend"},"Frontend"),(0,a.kt)("p",null,"We will add a frontend to the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalLock")," smart contract accroding to this ",(0,a.kt)("a",{parentName:"p",href:"/how-to-integrate-a-frontend/"},"guide"),"."),(0,a.kt)("h3",{id:"setup-project"},"Setup Project"),(0,a.kt)("p",null,"The front-end will be created using ",(0,a.kt)("a",{parentName:"p",href:"https://create-react-app.dev/"},"Create React App"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npx create-react-app ordinal-lock-demo --template typescript\n")),(0,a.kt)("h3",{id:"install-the-scrypt-sdk"},"Install the sCrypt SDK"),(0,a.kt)("p",null,"The sCrypt SDK enables you to easily compile, test, deploy, and call contracts."),(0,a.kt)("p",null,"Use the ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-cli")," command line to install the SDK."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"cd ordinal-lock-demo\nnpm i scrypt-ord\nnpx scrypt-cli init\n")),(0,a.kt)("p",null,"This command will create a contract under ",(0,a.kt)("inlineCode",{parentName:"p"},"src/contracts"),". Replace the file with the contract written ",(0,a.kt)("a",{parentName:"p",href:"#final-code"},"above"),"."),(0,a.kt)("h3",{id:"compile-contract"},"Compile Contract"),(0,a.kt)("p",null,"Compile the contract with the following command: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npx scrypt-cli compile\n")),(0,a.kt)("p",null,"This command will generate a contract artifact file under ",(0,a.kt)("inlineCode",{parentName:"p"},"artifacts"),"."),(0,a.kt)("h3",{id:"load-contract-artifact"},"Load Contract Artifact"),(0,a.kt)("p",null,"Before writing the front-end code, we need to load the contract artifact in ",(0,a.kt)("inlineCode",{parentName:"p"},"src/index.tsx"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"import { OrdinalLock } from './contracts/ordinalLock'\nimport artifact from '../artifacts/ordinalLock.json'\nOrdinalLock.loadArtifact(artifact)\n")),(0,a.kt)("h3",{id:"connect-signer-to-ordiprovider"},"Connect Signer to ",(0,a.kt)("inlineCode",{parentName:"h3"},"OrdiProvider")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const provider = new OrdiProvider();\nconst signer = new SensiletSigner(provider);\n")),(0,a.kt)("h3",{id:"integrate-wallet"},"Integrate Wallet"),(0,a.kt)("p",null,"Use ",(0,a.kt)("inlineCode",{parentName:"p"},"requestAuth")," method of ",(0,a.kt)("inlineCode",{parentName:"p"},"signer")," to request access to the wallet."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// request authentication\nconst { isAuthenticated, error } = await signer.requestAuth();\nif (!isAuthenticated) {\n // something went wrong, throw an Error with `error` message\n throw new Error(error);\n}\n\n// authenticated\n// ...\n")),(0,a.kt)("h3",{id:"load-ordinals"},"Load Ordinals"),(0,a.kt)("p",null,"After a user connect wallet, we can get the his address. Call the ",(0,a.kt)("a",{parentName:"p",href:"https://v3.ordinals.gorillapool.io/api/docs/"},"1Sat Ordinals API")," to retrieve ordinals on this address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"useEffect(() => {\n loadCollections()\n}, [connectedAddress])\n\nfunction loadCollections() {\n if (connectedAddress) {\n const url = `https://v3.ordinals.gorillapool.io/api/txos/address/${connectedAddress.toString()}/unspent?bsv20=false`\n fetch(url).then(r => r.json()).then(r => r.filter(e => e.origin.data.insc.file.type !== 'application/bsv-20')).then(r => setCollections(r)) }\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7312).Z,width:"2880",height:"1752"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5517).Z,width:"2880",height:"1750"})),(0,a.kt)("h3",{id:"list-an-ordinal"},"List an Ordinal"),(0,a.kt)("p",null,"For each ordinal in the collection list, we can click the ",(0,a.kt)("inlineCode",{parentName:"p"},"Sell")," button to list it after filling in the selling price, in satoshis. Sell an ordinal means we need to create a contract instance, and then transfer the ordinal into it. Afterwards, the ordinal is under the control of the contract, meaning it can be bought by anyone paying the price to the seller."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"async function sell() {\n const signer = new SensiletSigner(new OrdiProvider())\n const publicKey = await signer.getDefaultPubKey()\n\n const instance = new OrdinalLock(PubKey(toHex(publicKey)), amount)\n await instance.connect(signer)\n\n const inscriptionUtxo = await parseUtxo(txid, vout)\n const inscriptionP2PKH = OrdiNFTP2PKH.fromUTXO(inscriptionUtxo)\n await inscriptionP2PKH.connect(signer)\n\n const { tx } = await inscriptionP2PKH.methods.unlock(\n (sigResps) => findSig(sigResps, publicKey),\n PubKey(toHex(publicKey)),\n {\n transfer: instance, // <---- \n pubKeyOrAddrToSign: publicKey,\n } as OrdiMethodCallOptions\n )\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://lucid.app/publicSegments/view/50527d66-0710-4658-b8db-b615d60232f8/image.png",alt:null})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8965).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8375).Z,width:"2880",height:"1750"})),(0,a.kt)("h3",{id:"buy-an-ordinal"},"Buy an Ordinal"),(0,a.kt)("p",null,"To buy an ordinal that is on sale, we only need to call the contract public method ",(0,a.kt)("inlineCode",{parentName:"p"},"purchase"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"async function buy() {\n const signer = new SensiletSigner(new OrdiProvider())\n const address = await signer.getDefaultAddress()\n const { tx } = await instance.methods.purchase(Addr(address.toByteString()))\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://lucid.app/publicSegments/view/0b52243b-bdbc-4a13-b5b6-9386be80e155/image.png",alt:null})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8099).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8831).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7657).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7083).Z,width:"2880",height:"1750"})),(0,a.kt)("h2",{id:"conclusion"},"Conclusion"),(0,a.kt)("p",null,"Congratulations! You have successfully completed a full-stack dApp that can sell 1Sat Ordinals on Bitcoin."),(0,a.kt)("p",null,"The full example repo can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/ordinal-lock-demo"},"here"),"."))}p.isMDXComponent=!0},8099:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy1-0cca34ece542fab6f2681485fd602e1b.png"},8831:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy2-520cf27a5dc308e0f6823f61a1844e08.png"},7657:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy3-f25e3059d0ef019132bd634528f7b71c.png"},7083:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy4-8d286427b76802c0be1e4fda7cb7217d.png"},7312:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/load1-bb1f4c54b02b4f3179f17b1dff13a847.png"},5517:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/load2-56db013c94c63eefd6a7840ea119b580.png"},8965:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/sell1-05b4faee211f41647fecb4b83e4c98f2.png"},8375:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/sell2-3bac875c5790de2b6997a7139621c6b7.png"}}]); \ No newline at end of file diff --git a/assets/js/fa14be71.f45edac9.js b/assets/js/fa14be71.f45edac9.js deleted file mode 100644 index 8a1a9b77c..000000000 --- a/assets/js/fa14be71.f45edac9.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkscrypt_docs=self.webpackChunkscrypt_docs||[]).push([[6279],{3905:(t,e,n)=>{n.d(e,{Zo:()=>d,kt:()=>h});var r=n(7294);function a(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function i(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function s(t){for(var e=1;e=0||(a[n]=t[n]);return a}(t,e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(a[n]=t[n])}return a}var l=r.createContext({}),c=function(t){var e=r.useContext(l),n=e;return t&&(n="function"==typeof t?t(e):s(s({},e),t)),n},d=function(t){var e=c(t.components);return r.createElement(l.Provider,{value:e},t.children)},p={inlineCode:"code",wrapper:function(t){var e=t.children;return r.createElement(r.Fragment,{},e)}},u=r.forwardRef((function(t,e){var n=t.components,a=t.mdxType,i=t.originalType,l=t.parentName,d=o(t,["components","mdxType","originalType","parentName"]),u=c(n),h=a,m=u["".concat(l,".").concat(h)]||u[h]||p[h]||i;return n?r.createElement(m,s(s({ref:e},d),{},{components:n})):r.createElement(m,s({ref:e},d))}));function h(t,e){var n=arguments,a=e&&e.mdxType;if("string"==typeof t||a){var i=n.length,s=new Array(i);s[0]=u;var o={};for(var l in e)hasOwnProperty.call(e,l)&&(o[l]=e[l]);o.originalType=t,o.mdxType="string"==typeof t?t:a,s[1]=o;for(var c=2;c{n.r(e),n.d(e,{assets:()=>l,contentTitle:()=>s,default:()=>p,frontMatter:()=>i,metadata:()=>o,toc:()=>c});var r=n(7462),a=(n(7294),n(3905));const i={sidebar_position:11},s="Tutorial 11: Ordinal Lock",o={unversionedId:"tutorials/ordinal-lock",id:"tutorials/ordinal-lock",title:"Tutorial 11: Ordinal Lock",description:"Overview",source:"@site/docs/tutorials/ordinal-lock.md",sourceDirName:"tutorials",slug:"/tutorials/ordinal-lock",permalink:"/tutorials/ordinal-lock",draft:!1,tags:[],version:"current",sidebarPosition:11,frontMatter:{sidebar_position:11},sidebar:"tutorialSidebar",previous:{title:"Tutorial 10: Mint BSV20 V1 Token",permalink:"/tutorials/mint-bsv20-v1"},next:{title:"The Official sCrypt 1Sat Ordinals SDK",permalink:"/tokens/"}},l={},c=[{value:"Overview",id:"overview",level:2},{value:"Contract",id:"contract",level:2},{value:"Constructor",id:"constructor",level:3},{value:"Methods",id:"methods",level:3},{value:"Frontend",id:"frontend",level:2},{value:"Setup Project",id:"setup-project",level:3},{value:"Install the sCrypt SDK",id:"install-the-scrypt-sdk",level:3},{value:"Compile Contract",id:"compile-contract",level:3},{value:"Load Contract Artifact",id:"load-contract-artifact",level:3},{value:"Connect Signer to OrdiProvider",id:"connect-signer-to-ordiprovider",level:3},{value:"Integrate Wallet",id:"integrate-wallet",level:3},{value:"Load Ordinals",id:"load-ordinals",level:3},{value:"List an Ordinal",id:"list-an-ordinal",level:3},{value:"Buy an Ordinal",id:"buy-an-ordinal",level:3},{value:"Conclusion",id:"conclusion",level:2}],d={toc:c};function p(t){let{components:e,...i}=t;return(0,a.kt)("wrapper",(0,r.Z)({},d,i,{components:e,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"tutorial-11-ordinal-lock"},"Tutorial 11: Ordinal Lock"),(0,a.kt)("h2",{id:"overview"},"Overview"),(0,a.kt)("p",null,"In this tutorial, we will go over how to use ",(0,a.kt)("a",{parentName:"p",href:"https://scrypt.io/"},"sCrypt")," to build a full-stack dApp on Bitcoin to sell ",(0,a.kt)("a",{parentName:"p",href:"https://docs.1satordinals.com/"},"1Sat Ordinals"),", including the smart contract and an interactive front-end."),(0,a.kt)("h2",{id:"contract"},"Contract"),(0,a.kt)("p",null,"The contract ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/ordinalLock.ts"},"OrdinalLock")," allows an ordinal to be offered up for sale on a decentralized marketplace. These listings can be purchased by anyone who is able to pay the requested price. Listings can also be cancelled by the person who listed them."),(0,a.kt)("p",null,"To record the seller and price, we need to add two properties to the contract."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"export class OrdinalLock extends OrdinalNFT {\n @prop()\n seller: PubKey\n\n @prop()\n amount: bigint\n \n ...\n}\n")),(0,a.kt)("h3",{id:"constructor"},"Constructor"),(0,a.kt)("p",null,"Initialize all the ",(0,a.kt)("inlineCode",{parentName:"p"},"@prop")," properties in the constructor."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"constructor(seller: PubKey, amount: bigint) {\n super()\n this.init(...arguments)\n this.seller = seller\n this.amount = amount\n}\n")),(0,a.kt)("h3",{id:"methods"},"Methods"),(0,a.kt)("p",null,"The public method ",(0,a.kt)("inlineCode",{parentName:"p"},"purchase")," only needs to confine the transaction's outputs to contain:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"transfer ordinal to the buyer"),(0,a.kt)("li",{parentName:"ul"},"payment to the seller")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"@method()\npublic purchase(receiver: Addr) {\n const outputs =\n Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer\n Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller\n this.buildChangeOutput()\n assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs check failed')\n}\n")),(0,a.kt)("p",null,"The ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/scrypt-ord/blob/master/tests/contracts/ordinalLock.ts"},"final complete code")," is as follows:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"import { Addr, prop, method, Utils, hash256, assert, MethodCallOptions, ContractTransaction, bsv, PubKey, hash160 } from 'scrypt-ts'\nimport { OrdinalNFT } from 'scrypt-ord'\n\nexport class OrdinalLock extends OrdinalNFT {\n @prop()\n seller: PubKey\n\n @prop()\n amount: bigint\n\n constructor(seller: PubKey, amount: bigint) {\n super()\n this.init(...arguments)\n this.seller = seller\n this.amount = amount\n }\n\n @method()\n public purchase(receiver: Addr) {\n const outputs =\n Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer\n Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller\n this.buildChangeOutput()\n assert(\n this.ctx.hashOutputs == hash256(outputs),\n 'hashOutputs check failed'\n )\n }\n\n @method(SigHash.ANYONECANPAY_SINGLE)\n public cancel(sig: Sig) {\n assert(this.checkSig(sig, this.seller), 'seller signature check failed')\n const outputs = Utils.buildAddressOutput(hash160(this.seller), 1n) // ordinal back to the seller\n assert(\n this.ctx.hashOutputs == hash256(outputs),\n 'hashOutputs check failed'\n )\n }\n\n static async buildTxForPurchase(\n current: OrdinalLock,\n options: MethodCallOptions,\n receiver: Addr\n ): Promise {\n const defaultAddress = await current.signer.getDefaultAddress()\n const tx = new bsv.Transaction()\n .addInput(current.buildContractInput())\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(receiver)\n ),\n satoshis: 1,\n })\n )\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(hash160(current.seller))\n ),\n satoshis: Number(current.amount),\n })\n )\n .change(options.changeAddress || defaultAddress)\n return {\n tx,\n atInputIndex: 0,\n nexts: [],\n }\n }\n\n static async buildTxForCancel(\n current: OrdinalLock,\n options: MethodCallOptions\n ): Promise {\n const defaultAddress = await current.signer.getDefaultAddress()\n const tx = new bsv.Transaction()\n .addInput(current.buildContractInput())\n .addOutput(\n new bsv.Transaction.Output({\n script: bsv.Script.fromHex(\n Utils.buildAddressScript(hash160(current.seller))\n ),\n satoshis: 1,\n })\n )\n .change(options.changeAddress || defaultAddress)\n return {\n tx,\n atInputIndex: 0,\n nexts: [],\n }\n }\n}\n")),(0,a.kt)("p",null,"Note the customized calling method ",(0,a.kt)("inlineCode",{parentName:"p"},"buildTxForPurchase")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"buildTxForCancel")," ensure the ordinal is in the first input and goes to the first output, which is also a 1sat output."),(0,a.kt)("h2",{id:"frontend"},"Frontend"),(0,a.kt)("p",null,"We will add a frontend to the ",(0,a.kt)("inlineCode",{parentName:"p"},"OrdinalLock")," smart contract accroding to this ",(0,a.kt)("a",{parentName:"p",href:"/how-to-integrate-a-frontend/"},"guide"),"."),(0,a.kt)("h3",{id:"setup-project"},"Setup Project"),(0,a.kt)("p",null,"The front-end will be created using ",(0,a.kt)("a",{parentName:"p",href:"https://create-react-app.dev/"},"Create React App"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npx create-react-app ordinal-lock-demo --template typescript\n")),(0,a.kt)("h3",{id:"install-the-scrypt-sdk"},"Install the sCrypt SDK"),(0,a.kt)("p",null,"The sCrypt SDK enables you to easily compile, test, deploy, and call contracts."),(0,a.kt)("p",null,"Use the ",(0,a.kt)("inlineCode",{parentName:"p"},"scrypt-cli")," command line to install the SDK."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"cd ordinal-lock-demo\nnpm i scrypt-ord\nnpx scrypt-cli init\n")),(0,a.kt)("p",null,"This command will create a contract under ",(0,a.kt)("inlineCode",{parentName:"p"},"src/contracts"),". Replace the file with the contract written ",(0,a.kt)("a",{parentName:"p",href:"#final-code"},"above"),"."),(0,a.kt)("h3",{id:"compile-contract"},"Compile Contract"),(0,a.kt)("p",null,"Compile the contract with the following command: "),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-bash"},"npx scrypt-cli compile\n")),(0,a.kt)("p",null,"This command will generate a contract artifact file under ",(0,a.kt)("inlineCode",{parentName:"p"},"artifacts"),"."),(0,a.kt)("h3",{id:"load-contract-artifact"},"Load Contract Artifact"),(0,a.kt)("p",null,"Before writing the front-end code, we need to load the contract artifact in ",(0,a.kt)("inlineCode",{parentName:"p"},"src/index.tsx"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"import { OrdinalLock } from './contracts/ordinalLock'\nimport artifact from '../artifacts/ordinalLock.json'\nOrdinalLock.loadArtifact(artifact)\n")),(0,a.kt)("h3",{id:"connect-signer-to-ordiprovider"},"Connect Signer to ",(0,a.kt)("inlineCode",{parentName:"h3"},"OrdiProvider")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"const provider = new OrdiProvider();\nconst signer = new SensiletSigner(provider);\n")),(0,a.kt)("h3",{id:"integrate-wallet"},"Integrate Wallet"),(0,a.kt)("p",null,"Use ",(0,a.kt)("inlineCode",{parentName:"p"},"requestAuth")," method of ",(0,a.kt)("inlineCode",{parentName:"p"},"signer")," to request access to the wallet."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"// request authentication\nconst { isAuthenticated, error } = await signer.requestAuth();\nif (!isAuthenticated) {\n // something went wrong, throw an Error with `error` message\n throw new Error(error);\n}\n\n// authenticated\n// ...\n")),(0,a.kt)("h3",{id:"load-ordinals"},"Load Ordinals"),(0,a.kt)("p",null,"After a user connect wallet, we can get the his address. Call the ",(0,a.kt)("a",{parentName:"p",href:"https://v3.ordinals.gorillapool.io/api/docs/"},"1Sat Ordinals API")," to retrieve ordinals on this address."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"useEffect(() => {\n loadCollections()\n}, [connectedAddress])\n\nfunction loadCollections() {\n if (connectedAddress) {\n const url = `https://v3.ordinals.gorillapool.io/api/txos/address/${connectedAddress.toString()}/unspent?bsv20=false`\n fetch(url).then(r => r.json()).then(r => r.filter(e => e.origin.data.insc.file.type !== 'application/bsv-20')).then(r => setCollections(r)) }\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7312).Z,width:"2880",height:"1752"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(5517).Z,width:"2880",height:"1750"})),(0,a.kt)("h3",{id:"list-an-ordinal"},"List an Ordinal"),(0,a.kt)("p",null,"For each ordinal in the collection list, we can click the ",(0,a.kt)("inlineCode",{parentName:"p"},"Sell")," button to list it after filling in the selling price, in satoshis. Sell an ordinal means we need to create a contract instance, and then transfer the ordinal into it. Afterwards, the ordinal is under the control of the contract, meaning it can be bought by anyone paying the price to the seller."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"async function sell() {\n const signer = new SensiletSigner(new OrdiProvider())\n const publicKey = await signer.getDefaultPubKey()\n\n const instance = new OrdinalLock(PubKey(toHex(publicKey)), amount)\n await instance.connect(signer)\n\n const inscriptionUtxo = await parseUtxo(txid, vout)\n const inscriptionP2PKH = OrdiNFTP2PKH.fromUTXO(inscriptionUtxo)\n await inscriptionP2PKH.connect(signer)\n\n const { tx } = await inscriptionP2PKH.methods.unlock(\n (sigResps) => findSig(sigResps, publicKey),\n PubKey(toHex(publicKey)),\n {\n transfer: instance, // <---- \n pubKeyOrAddrToSign: publicKey,\n } as MethodCallOptions\n )\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://lucid.app/publicSegments/view/50527d66-0710-4658-b8db-b615d60232f8/image.png",alt:null})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8965).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8375).Z,width:"2880",height:"1750"})),(0,a.kt)("h3",{id:"buy-an-ordinal"},"Buy an Ordinal"),(0,a.kt)("p",null,"To buy an ordinal that is on sale, we only need to call the contract public method ",(0,a.kt)("inlineCode",{parentName:"p"},"purchase"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"async function buy() {\n const signer = new SensiletSigner(new OrdiProvider())\n const address = await signer.getDefaultAddress()\n const { tx } = await instance.methods.purchase(Addr(address.toByteString()))\n}\n")),(0,a.kt)("p",null,(0,a.kt)("img",{parentName:"p",src:"https://lucid.app/publicSegments/view/0b52243b-bdbc-4a13-b5b6-9386be80e155/image.png",alt:null})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8099).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(8831).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7657).Z,width:"2880",height:"1750"})),(0,a.kt)("p",null,(0,a.kt)("img",{src:n(7083).Z,width:"2880",height:"1750"})),(0,a.kt)("h2",{id:"conclusion"},"Conclusion"),(0,a.kt)("p",null,"Congratulations! You have successfully completed a full-stack dApp that can sell 1Sat Ordinals on Bitcoin."),(0,a.kt)("p",null,"The full example repo can be found ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/sCrypt-Inc/ordinal-lock-demo/tree/sensilet"},"here"),"."))}p.isMDXComponent=!0},8099:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy1-0cca34ece542fab6f2681485fd602e1b.png"},8831:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy2-520cf27a5dc308e0f6823f61a1844e08.png"},7657:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy3-f25e3059d0ef019132bd634528f7b71c.png"},7083:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/buy4-8d286427b76802c0be1e4fda7cb7217d.png"},7312:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/load1-bb1f4c54b02b4f3179f17b1dff13a847.png"},5517:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/load2-56db013c94c63eefd6a7840ea119b580.png"},8965:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/sell1-05b4faee211f41647fecb4b83e4c98f2.png"},8375:(t,e,n)=>{n.d(e,{Z:()=>r});const r=n.p+"assets/images/sell2-3bac875c5790de2b6997a7139621c6b7.png"}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.dcb92ac9.js b/assets/js/runtime~main.28adef74.js similarity index 72% rename from assets/js/runtime~main.dcb92ac9.js rename to assets/js/runtime~main.28adef74.js index 1c3667231..17b990328 100644 --- a/assets/js/runtime~main.dcb92ac9.js +++ b/assets/js/runtime~main.28adef74.js @@ -1 +1 @@ -(()=>{"use strict";var e,c,d,a,b,f={},t={};function r(e){var c=t[e];if(void 0!==c)return c.exports;var d=t[e]={id:e,loaded:!1,exports:{}};return f[e].call(d.exports,d,d.exports,r),d.loaded=!0,d.exports}r.m=f,r.c=t,e=[],r.O=(c,d,a,b)=>{if(!d){var f=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](d[o])))?d.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[d,a,b]},r.n=e=>{var c=e&&e.__esModule?()=>e.default:()=>e;return r.d(c,{a:c}),c},d=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var f={};c=c||[null,d({}),d([]),d(d)];for(var t=2&a&&e;"object"==typeof t&&!~c.indexOf(t);t=d(t))Object.getOwnPropertyNames(t).forEach((c=>f[c]=()=>e[c]));return f.default=()=>e,r.d(b,f),b},r.d=(e,c)=>{for(var d in c)r.o(c,d)&&!r.o(e,d)&&Object.defineProperty(e,d,{enumerable:!0,get:c[d]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((c,d)=>(r.f[d](e,c),c)),[])),r.u=e=>"assets/js/"+({2:"910cd6a4",21:"70db8588",53:"935f2afb",100:"6ed84bca",208:"7225f144",275:"73e21f82",284:"f0d18cf5",351:"bb7a85de",462:"14b44c09",589:"7c4be5dd",622:"8c9a110f",666:"e525704c",673:"043d3e45",704:"b47ebff3",759:"e4e10033",836:"0480b142",966:"4d00a19e",980:"9fc92457",1033:"d17b93f5",1112:"74e275e2",1168:"3d8a8d1b",1217:"14155be4",1220:"83fb1adc",1316:"1cb1071b",1372:"1db64337",1403:"ca2c86a8",1448:"c8209f69",1567:"06de43e0",1636:"51d1dba0",1689:"1a6f2b55",1712:"419555a2",1811:"db986b04",1826:"4af4b6e8",2006:"07109bdf",2018:"77dda5d6",2106:"9e9f554b",2133:"bffcfebc",2185:"879ebe09",2217:"10d519e3",2287:"912ef652",2317:"67ff73b9",2328:"0ccfb315",2441:"4c37cee5",2462:"03e490ce",2653:"cef480a7",2705:"f5fbe228",2807:"2ab2aa69",2813:"1788e182",2828:"c023801b",2890:"9fcad7a3",2894:"a7d37132",2895:"331ef79c",2969:"fa01f05c",2997:"9277e5e6",3012:"a7e31d84",3016:"9bda8f72",3040:"56b58987",3139:"80d01892",3204:"82e67270",3217:"3b8c55ea",3317:"874dec39",3392:"9bd2e2d8",3525:"fd8c99ae",3542:"33a4e2e3",3689:"dd80477d",3732:"727923c4",3808:"61e13606",3826:"83f962c1",3831:"c2959c58",3841:"5ba26743",4030:"1b23cebc",4088:"56d75774",4089:"c29255d2",4141:"b99126ff",4337:"9fea62fb",4346:"b16c2f66",4421:"f7fb2808",4428:"83e3cb0c",4560:"924f1c1c",4564:"313836b0",4568:"7b430b5d",4617:"b7851e83",4661:"45c8a2be",4709:"4ace2938",4759:"4d48c635",4831:"c138b1fa",4855:"e38e2f24",4999:"fa011ef2",5032:"c49c0ac4",5065:"33426d98",5090:"cb43a3ec",5120:"5455d950",5294:"3ff43f10",5330:"3d41d66e",5448:"505c1d49",5636:"862d9758",5700:"b40963c8",5937:"66540400",5994:"55201687",6091:"3658c897",6138:"c186e46b",6201:"51cfe601",6240:"17a77d31",6279:"fa14be71",6297:"8e2eb00b",6308:"27431a05",6325:"572d3f0b",6341:"6958f4b4",6364:"69c71146",6387:"f1af8fb8",6647:"c5a48ac7",6672:"b7e6597b",6857:"6c2dbd7a",6938:"2594e5e6",7062:"a5c7ba44",7097:"26d5742a",7105:"ec4bed84",7139:"e37cbbfc",7147:"6484c565",7154:"6eaa7f88",7333:"a1bc6bc5",7653:"adc4c70f",7701:"7381381c",7719:"fe61ad06",7738:"01e59a88",7839:"17b1a180",7918:"17896441",7920:"1a4e3797",7944:"440910b0",7945:"9b05ebac",7966:"4e65a820",8e3:"1b1e1a52",8005:"47d5ee98",8062:"7fde2f23",8142:"74f9bace",8151:"205159e4",8182:"ee294475",8203:"6d80b1c2",8241:"b6325b43",8245:"787f7b6d",8380:"2d3d592f",8413:"31d8d510",8455:"e3a53a02",8567:"db37e9c8",8790:"d795c046",8881:"138eedef",8929:"2920d9d5",8944:"7d4560fc",9016:"e74d5bd3",9100:"19eca43a",9147:"5e70887e",9268:"dcd4f12c",9318:"dc5aa0c6",9322:"e97527cd",9348:"ff282309",9404:"54547e85",9442:"6d1a890e",9514:"1be78505",9517:"f0ad07a9",9615:"3666a527",9698:"c150b9b5",9817:"14eb3368",9968:"ad5627e3"}[e]||e)+"."+{2:"4d925563",21:"e42b4ace",53:"a757abe3",100:"c35870d5",208:"552e90e2",275:"20c8d57a",284:"8e742ce9",351:"2efc2bf7",462:"c38c5807",589:"d488c06f",622:"0e63a235",666:"2cffece2",673:"c9724cca",704:"85ea7a54",759:"1d059a09",836:"a564a48e",966:"1357cb73",980:"32f95b02",1033:"772e273a",1112:"29200481",1168:"0fd08076",1217:"8f02b82d",1220:"36c94b21",1316:"28cb78f3",1372:"e2238602",1403:"2e483d5b",1448:"2bc22202",1567:"e2bac3ab",1636:"43d6b932",1689:"e239431d",1712:"d78ccae2",1811:"02aacecc",1826:"3feafb63",2006:"5d5b337d",2018:"7c382f4d",2106:"10248ea7",2133:"fc0e4956",2185:"27838861",2217:"f2262aae",2287:"61458bb2",2317:"a7437ed1",2328:"a9aca516",2441:"dbcb0926",2462:"a4bce634",2653:"1fa4b24a",2705:"10386e74",2807:"0f53ed3a",2813:"6e713b54",2828:"ef9ec49c",2890:"0ac663c9",2894:"64a2d51b",2895:"dbc13880",2969:"fcb19b25",2997:"8cc741d3",3012:"faef12e2",3016:"328b4bbf",3040:"7374a154",3139:"5b6c8393",3204:"555bfdd2",3217:"3f920915",3317:"f3fbca77",3392:"6903e152",3525:"45acd798",3542:"11b28543",3689:"e6711bbf",3732:"7b30bf18",3808:"68430c52",3826:"c0a98e38",3831:"a48b9ecc",3841:"de66ff57",4030:"0c501a81",4088:"57000555",4089:"31350b51",4141:"38ead6d8",4337:"cddabd42",4346:"f8085c56",4421:"b12df3ce",4428:"f83e914f",4560:"18631fb5",4564:"8471b3f6",4568:"abfb46f8",4617:"1a5b1ed4",4661:"11c5a2cb",4709:"83b58349",4759:"22402cd6",4831:"1fc7ad0c",4855:"b7a614cb",4972:"83820106",4999:"7db27fa7",5032:"84198c02",5065:"15d9f37b",5090:"212a28e3",5120:"313f0955",5294:"68121304",5330:"eb86018a",5448:"f35a9721",5636:"d7a287ad",5700:"f019f4c9",5937:"7b0211b4",5994:"990d914b",6091:"ad179304",6138:"dd2e7832",6201:"1e787386",6240:"2f28fec6",6279:"f45edac9",6297:"ebb61583",6308:"a8d01784",6325:"ff5487e6",6341:"256490c6",6364:"24b13147",6387:"43125fd8",6647:"249d266d",6672:"b3df4774",6780:"a5384721",6857:"dc2dded2",6938:"7b7b3bb6",6945:"39357269",7062:"446cc638",7097:"76670be1",7105:"c4514c39",7139:"e7257f43",7147:"acd51c3c",7154:"183e6942",7333:"8f3d4316",7653:"927188bb",7701:"80cabee0",7719:"effb47ce",7738:"1d0491f0",7839:"5c44e0bb",7918:"e33790d6",7920:"8113720b",7944:"4bb84e54",7945:"f76b2bbc",7966:"96aac556",8e3:"fe2aa2a6",8005:"878701c0",8062:"1f96b25f",8142:"8d5c53e6",8151:"c7aa13cf",8182:"113db6db",8203:"0909e07e",8241:"0b770ac8",8245:"aea9f501",8380:"a92d460e",8413:"7eb99c36",8455:"a574a299",8567:"2dd1c583",8790:"af286e41",8881:"ec4dbbc6",8894:"e3ac420f",8929:"477d669b",8944:"88c51dd0",9016:"aa18ca79",9100:"4e162d77",9147:"a2993e9a",9268:"cb4a67e3",9318:"de179e44",9322:"58725d29",9348:"a50d51a1",9404:"637ebeea",9442:"49c56369",9514:"ea38cb07",9517:"7b92397b",9615:"0f9302d4",9698:"2109f321",9817:"d2291fef",9968:"6bc3fdea"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,c)=>Object.prototype.hasOwnProperty.call(e,c),a={},b="scrypt-docs:",r.l=(e,c,d,f)=>{if(a[e])a[e].push(c);else{var t,o;if(void 0!==d)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=a[e];if(delete a[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(d))),c)return c(d)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918",55201687:"5994",66540400:"5937","910cd6a4":"2","70db8588":"21","935f2afb":"53","6ed84bca":"100","7225f144":"208","73e21f82":"275",f0d18cf5:"284",bb7a85de:"351","14b44c09":"462","7c4be5dd":"589","8c9a110f":"622",e525704c:"666","043d3e45":"673",b47ebff3:"704",e4e10033:"759","0480b142":"836","4d00a19e":"966","9fc92457":"980",d17b93f5:"1033","74e275e2":"1112","3d8a8d1b":"1168","14155be4":"1217","83fb1adc":"1220","1cb1071b":"1316","1db64337":"1372",ca2c86a8:"1403",c8209f69:"1448","06de43e0":"1567","51d1dba0":"1636","1a6f2b55":"1689","419555a2":"1712",db986b04:"1811","4af4b6e8":"1826","07109bdf":"2006","77dda5d6":"2018","9e9f554b":"2106",bffcfebc:"2133","879ebe09":"2185","10d519e3":"2217","912ef652":"2287","67ff73b9":"2317","0ccfb315":"2328","4c37cee5":"2441","03e490ce":"2462",cef480a7:"2653",f5fbe228:"2705","2ab2aa69":"2807","1788e182":"2813",c023801b:"2828","9fcad7a3":"2890",a7d37132:"2894","331ef79c":"2895",fa01f05c:"2969","9277e5e6":"2997",a7e31d84:"3012","9bda8f72":"3016","56b58987":"3040","80d01892":"3139","82e67270":"3204","3b8c55ea":"3217","874dec39":"3317","9bd2e2d8":"3392",fd8c99ae:"3525","33a4e2e3":"3542",dd80477d:"3689","727923c4":"3732","61e13606":"3808","83f962c1":"3826",c2959c58:"3831","5ba26743":"3841","1b23cebc":"4030","56d75774":"4088",c29255d2:"4089",b99126ff:"4141","9fea62fb":"4337",b16c2f66:"4346",f7fb2808:"4421","83e3cb0c":"4428","924f1c1c":"4560","313836b0":"4564","7b430b5d":"4568",b7851e83:"4617","45c8a2be":"4661","4ace2938":"4709","4d48c635":"4759",c138b1fa:"4831",e38e2f24:"4855",fa011ef2:"4999",c49c0ac4:"5032","33426d98":"5065",cb43a3ec:"5090","5455d950":"5120","3ff43f10":"5294","3d41d66e":"5330","505c1d49":"5448","862d9758":"5636",b40963c8:"5700","3658c897":"6091",c186e46b:"6138","51cfe601":"6201","17a77d31":"6240",fa14be71:"6279","8e2eb00b":"6297","27431a05":"6308","572d3f0b":"6325","6958f4b4":"6341","69c71146":"6364",f1af8fb8:"6387",c5a48ac7:"6647",b7e6597b:"6672","6c2dbd7a":"6857","2594e5e6":"6938",a5c7ba44:"7062","26d5742a":"7097",ec4bed84:"7105",e37cbbfc:"7139","6484c565":"7147","6eaa7f88":"7154",a1bc6bc5:"7333",adc4c70f:"7653","7381381c":"7701",fe61ad06:"7719","01e59a88":"7738","17b1a180":"7839","1a4e3797":"7920","440910b0":"7944","9b05ebac":"7945","4e65a820":"7966","1b1e1a52":"8000","47d5ee98":"8005","7fde2f23":"8062","74f9bace":"8142","205159e4":"8151",ee294475:"8182","6d80b1c2":"8203",b6325b43:"8241","787f7b6d":"8245","2d3d592f":"8380","31d8d510":"8413",e3a53a02:"8455",db37e9c8:"8567",d795c046:"8790","138eedef":"8881","2920d9d5":"8929","7d4560fc":"8944",e74d5bd3:"9016","19eca43a":"9100","5e70887e":"9147",dcd4f12c:"9268",dc5aa0c6:"9318",e97527cd:"9322",ff282309:"9348","54547e85":"9404","6d1a890e":"9442","1be78505":"9514",f0ad07a9:"9517","3666a527":"9615",c150b9b5:"9698","14eb3368":"9817",ad5627e3:"9968"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(c,d)=>{var a=r.o(e,c)?e[c]:void 0;if(0!==a)if(a)d.push(a[2]);else if(/^(1303|532)$/.test(c))e[c]=0;else{var b=new Promise(((d,b)=>a=e[c]=[d,b]));d.push(a[2]=b);var f=r.p+r.u(c),t=new Error;r.l(f,(d=>{if(r.o(e,c)&&(0!==(a=e[c])&&(e[c]=void 0),a)){var b=d&&("load"===d.type?"missing":d.type),f=d&&d.target&&d.target.src;t.message="Loading chunk "+c+" failed.\n("+b+": "+f+")",t.name="ChunkLoadError",t.type=b,t.request=f,a[1](t)}}),"chunk-"+c,c)}},r.O.j=c=>0===e[c];var c=(c,d)=>{var a,b,f=d[0],t=d[1],o=d[2],n=0;if(f.some((c=>0!==e[c]))){for(a in t)r.o(t,a)&&(r.m[a]=t[a]);if(o)var i=o(r)}for(c&&c(d);n{"use strict";var e,c,a,d,b,f={},t={};function r(e){var c=t[e];if(void 0!==c)return c.exports;var a=t[e]={id:e,loaded:!1,exports:{}};return f[e].call(a.exports,a,a.exports,r),a.loaded=!0,a.exports}r.m=f,r.c=t,e=[],r.O=(c,a,d,b)=>{if(!a){var f=1/0;for(i=0;i=b)&&Object.keys(r.O).every((e=>r.O[e](a[o])))?a.splice(o--,1):(t=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[a,d,b]},r.n=e=>{var c=e&&e.__esModule?()=>e.default:()=>e;return r.d(c,{a:c}),c},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,r.t=function(e,d){if(1&d&&(e=this(e)),8&d)return e;if("object"==typeof e&&e){if(4&d&&e.__esModule)return e;if(16&d&&"function"==typeof e.then)return e}var b=Object.create(null);r.r(b);var f={};c=c||[null,a({}),a([]),a(a)];for(var t=2&d&&e;"object"==typeof t&&!~c.indexOf(t);t=a(t))Object.getOwnPropertyNames(t).forEach((c=>f[c]=()=>e[c]));return f.default=()=>e,r.d(b,f),b},r.d=(e,c)=>{for(var a in c)r.o(c,a)&&!r.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:c[a]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce(((c,a)=>(r.f[a](e,c),c)),[])),r.u=e=>"assets/js/"+({2:"910cd6a4",21:"70db8588",53:"935f2afb",100:"6ed84bca",208:"7225f144",275:"73e21f82",284:"f0d18cf5",351:"bb7a85de",462:"14b44c09",589:"7c4be5dd",622:"8c9a110f",666:"e525704c",673:"043d3e45",704:"b47ebff3",759:"e4e10033",836:"0480b142",966:"4d00a19e",980:"9fc92457",1033:"d17b93f5",1112:"74e275e2",1168:"3d8a8d1b",1217:"14155be4",1220:"83fb1adc",1316:"1cb1071b",1372:"1db64337",1403:"ca2c86a8",1448:"c8209f69",1567:"06de43e0",1636:"51d1dba0",1689:"1a6f2b55",1712:"419555a2",1811:"db986b04",1826:"4af4b6e8",2006:"07109bdf",2018:"77dda5d6",2106:"9e9f554b",2133:"bffcfebc",2185:"879ebe09",2217:"10d519e3",2287:"912ef652",2317:"67ff73b9",2328:"0ccfb315",2441:"4c37cee5",2462:"03e490ce",2653:"cef480a7",2705:"f5fbe228",2807:"2ab2aa69",2813:"1788e182",2828:"c023801b",2890:"9fcad7a3",2894:"a7d37132",2895:"331ef79c",2969:"fa01f05c",2997:"9277e5e6",3012:"a7e31d84",3016:"9bda8f72",3040:"56b58987",3139:"80d01892",3204:"82e67270",3217:"3b8c55ea",3317:"874dec39",3392:"9bd2e2d8",3525:"fd8c99ae",3542:"33a4e2e3",3689:"dd80477d",3732:"727923c4",3808:"61e13606",3826:"83f962c1",3831:"c2959c58",3841:"5ba26743",4030:"1b23cebc",4088:"56d75774",4089:"c29255d2",4141:"b99126ff",4337:"9fea62fb",4346:"b16c2f66",4421:"f7fb2808",4428:"83e3cb0c",4560:"924f1c1c",4564:"313836b0",4568:"7b430b5d",4617:"b7851e83",4661:"45c8a2be",4709:"4ace2938",4759:"4d48c635",4831:"c138b1fa",4855:"e38e2f24",4999:"fa011ef2",5032:"c49c0ac4",5065:"33426d98",5090:"cb43a3ec",5120:"5455d950",5294:"3ff43f10",5330:"3d41d66e",5448:"505c1d49",5636:"862d9758",5700:"b40963c8",5937:"66540400",5994:"55201687",6091:"3658c897",6138:"c186e46b",6201:"51cfe601",6240:"17a77d31",6279:"fa14be71",6297:"8e2eb00b",6308:"27431a05",6325:"572d3f0b",6341:"6958f4b4",6364:"69c71146",6387:"f1af8fb8",6647:"c5a48ac7",6672:"b7e6597b",6857:"6c2dbd7a",6938:"2594e5e6",7062:"a5c7ba44",7097:"26d5742a",7105:"ec4bed84",7139:"e37cbbfc",7147:"6484c565",7154:"6eaa7f88",7333:"a1bc6bc5",7653:"adc4c70f",7701:"7381381c",7719:"fe61ad06",7738:"01e59a88",7839:"17b1a180",7918:"17896441",7920:"1a4e3797",7944:"440910b0",7945:"9b05ebac",7966:"4e65a820",8e3:"1b1e1a52",8005:"47d5ee98",8062:"7fde2f23",8142:"74f9bace",8151:"205159e4",8182:"ee294475",8203:"6d80b1c2",8241:"b6325b43",8245:"787f7b6d",8380:"2d3d592f",8413:"31d8d510",8455:"e3a53a02",8567:"db37e9c8",8790:"d795c046",8881:"138eedef",8929:"2920d9d5",8944:"7d4560fc",9016:"e74d5bd3",9100:"19eca43a",9147:"5e70887e",9268:"dcd4f12c",9318:"dc5aa0c6",9322:"e97527cd",9348:"ff282309",9404:"54547e85",9442:"6d1a890e",9514:"1be78505",9517:"f0ad07a9",9615:"3666a527",9698:"c150b9b5",9817:"14eb3368",9968:"ad5627e3"}[e]||e)+"."+{2:"16e54899",21:"e42b4ace",53:"a757abe3",100:"c35870d5",208:"552e90e2",275:"20c8d57a",284:"8e742ce9",351:"2efc2bf7",462:"c38c5807",589:"d488c06f",622:"f68bc347",666:"2cffece2",673:"c9724cca",704:"85ea7a54",759:"1d059a09",836:"a564a48e",966:"1357cb73",980:"32f95b02",1033:"772e273a",1112:"29200481",1168:"0fd08076",1217:"8f02b82d",1220:"36c94b21",1316:"28cb78f3",1372:"e2238602",1403:"2e483d5b",1448:"2bc22202",1567:"e2bac3ab",1636:"43d6b932",1689:"e239431d",1712:"d78ccae2",1811:"02aacecc",1826:"3feafb63",2006:"5d5b337d",2018:"7c382f4d",2106:"10248ea7",2133:"fc0e4956",2185:"27838861",2217:"f2262aae",2287:"61458bb2",2317:"a7437ed1",2328:"a9aca516",2441:"dbcb0926",2462:"a4bce634",2653:"1fa4b24a",2705:"10386e74",2807:"0f53ed3a",2813:"6e713b54",2828:"ef9ec49c",2890:"0ac663c9",2894:"64a2d51b",2895:"dbc13880",2969:"fcb19b25",2997:"3c068833",3012:"faef12e2",3016:"328b4bbf",3040:"7374a154",3139:"5b6c8393",3204:"555bfdd2",3217:"3f920915",3317:"f3fbca77",3392:"6903e152",3525:"45acd798",3542:"11b28543",3689:"e6711bbf",3732:"7b30bf18",3808:"68430c52",3826:"c0a98e38",3831:"a48b9ecc",3841:"de66ff57",4030:"0c501a81",4088:"57000555",4089:"31350b51",4141:"38ead6d8",4337:"cddabd42",4346:"f8085c56",4421:"b12df3ce",4428:"f83e914f",4560:"18631fb5",4564:"8471b3f6",4568:"abfb46f8",4617:"1a5b1ed4",4661:"11c5a2cb",4709:"83b58349",4759:"22402cd6",4831:"1fc7ad0c",4855:"b7a614cb",4972:"83820106",4999:"7db27fa7",5032:"84198c02",5065:"15d9f37b",5090:"212a28e3",5120:"313f0955",5294:"68121304",5330:"eb86018a",5448:"25a7d2cf",5636:"d7a287ad",5700:"f019f4c9",5937:"7b0211b4",5994:"990d914b",6091:"ad179304",6138:"dd2e7832",6201:"1e787386",6240:"2f28fec6",6279:"6613587d",6297:"ebb61583",6308:"a8d01784",6325:"313df141",6341:"256490c6",6364:"24b13147",6387:"43125fd8",6647:"249d266d",6672:"b3df4774",6780:"a5384721",6857:"2ff382bb",6938:"7b7b3bb6",6945:"39357269",7062:"150ee82c",7097:"76670be1",7105:"c4514c39",7139:"e7257f43",7147:"acd51c3c",7154:"183e6942",7333:"8f3d4316",7653:"927188bb",7701:"80cabee0",7719:"effb47ce",7738:"1d0491f0",7839:"5c44e0bb",7918:"e33790d6",7920:"8113720b",7944:"4bb84e54",7945:"f76b2bbc",7966:"96aac556",8e3:"fe2aa2a6",8005:"878701c0",8062:"1f96b25f",8142:"8d5c53e6",8151:"c7aa13cf",8182:"113db6db",8203:"0909e07e",8241:"0b770ac8",8245:"aea9f501",8380:"a92d460e",8413:"7eb99c36",8455:"a574a299",8567:"686b3333",8790:"af286e41",8881:"ec4dbbc6",8894:"e3ac420f",8929:"477d669b",8944:"88c51dd0",9016:"aa18ca79",9100:"4e162d77",9147:"a2993e9a",9268:"cb4a67e3",9318:"de179e44",9322:"58725d29",9348:"a50d51a1",9404:"637ebeea",9442:"49c56369",9514:"ea38cb07",9517:"7b92397b",9615:"0f9302d4",9698:"ebaa03c2",9817:"d2291fef",9968:"6bc3fdea"}[e]+".js",r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,c)=>Object.prototype.hasOwnProperty.call(e,c),d={},b="scrypt-docs:",r.l=(e,c,a,f)=>{if(d[e])d[e].push(c);else{var t,o;if(void 0!==a)for(var n=document.getElementsByTagName("script"),i=0;i{t.onerror=t.onload=null,clearTimeout(s);var b=d[e];if(delete d[e],t.parentNode&&t.parentNode.removeChild(t),b&&b.forEach((e=>e(a))),c)return c(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:t}),12e4);t.onerror=l.bind(null,t.onerror),t.onload=l.bind(null,t.onload),o&&document.head.appendChild(t)}},r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.p="/",r.gca=function(e){return e={17896441:"7918",55201687:"5994",66540400:"5937","910cd6a4":"2","70db8588":"21","935f2afb":"53","6ed84bca":"100","7225f144":"208","73e21f82":"275",f0d18cf5:"284",bb7a85de:"351","14b44c09":"462","7c4be5dd":"589","8c9a110f":"622",e525704c:"666","043d3e45":"673",b47ebff3:"704",e4e10033:"759","0480b142":"836","4d00a19e":"966","9fc92457":"980",d17b93f5:"1033","74e275e2":"1112","3d8a8d1b":"1168","14155be4":"1217","83fb1adc":"1220","1cb1071b":"1316","1db64337":"1372",ca2c86a8:"1403",c8209f69:"1448","06de43e0":"1567","51d1dba0":"1636","1a6f2b55":"1689","419555a2":"1712",db986b04:"1811","4af4b6e8":"1826","07109bdf":"2006","77dda5d6":"2018","9e9f554b":"2106",bffcfebc:"2133","879ebe09":"2185","10d519e3":"2217","912ef652":"2287","67ff73b9":"2317","0ccfb315":"2328","4c37cee5":"2441","03e490ce":"2462",cef480a7:"2653",f5fbe228:"2705","2ab2aa69":"2807","1788e182":"2813",c023801b:"2828","9fcad7a3":"2890",a7d37132:"2894","331ef79c":"2895",fa01f05c:"2969","9277e5e6":"2997",a7e31d84:"3012","9bda8f72":"3016","56b58987":"3040","80d01892":"3139","82e67270":"3204","3b8c55ea":"3217","874dec39":"3317","9bd2e2d8":"3392",fd8c99ae:"3525","33a4e2e3":"3542",dd80477d:"3689","727923c4":"3732","61e13606":"3808","83f962c1":"3826",c2959c58:"3831","5ba26743":"3841","1b23cebc":"4030","56d75774":"4088",c29255d2:"4089",b99126ff:"4141","9fea62fb":"4337",b16c2f66:"4346",f7fb2808:"4421","83e3cb0c":"4428","924f1c1c":"4560","313836b0":"4564","7b430b5d":"4568",b7851e83:"4617","45c8a2be":"4661","4ace2938":"4709","4d48c635":"4759",c138b1fa:"4831",e38e2f24:"4855",fa011ef2:"4999",c49c0ac4:"5032","33426d98":"5065",cb43a3ec:"5090","5455d950":"5120","3ff43f10":"5294","3d41d66e":"5330","505c1d49":"5448","862d9758":"5636",b40963c8:"5700","3658c897":"6091",c186e46b:"6138","51cfe601":"6201","17a77d31":"6240",fa14be71:"6279","8e2eb00b":"6297","27431a05":"6308","572d3f0b":"6325","6958f4b4":"6341","69c71146":"6364",f1af8fb8:"6387",c5a48ac7:"6647",b7e6597b:"6672","6c2dbd7a":"6857","2594e5e6":"6938",a5c7ba44:"7062","26d5742a":"7097",ec4bed84:"7105",e37cbbfc:"7139","6484c565":"7147","6eaa7f88":"7154",a1bc6bc5:"7333",adc4c70f:"7653","7381381c":"7701",fe61ad06:"7719","01e59a88":"7738","17b1a180":"7839","1a4e3797":"7920","440910b0":"7944","9b05ebac":"7945","4e65a820":"7966","1b1e1a52":"8000","47d5ee98":"8005","7fde2f23":"8062","74f9bace":"8142","205159e4":"8151",ee294475:"8182","6d80b1c2":"8203",b6325b43:"8241","787f7b6d":"8245","2d3d592f":"8380","31d8d510":"8413",e3a53a02:"8455",db37e9c8:"8567",d795c046:"8790","138eedef":"8881","2920d9d5":"8929","7d4560fc":"8944",e74d5bd3:"9016","19eca43a":"9100","5e70887e":"9147",dcd4f12c:"9268",dc5aa0c6:"9318",e97527cd:"9322",ff282309:"9348","54547e85":"9404","6d1a890e":"9442","1be78505":"9514",f0ad07a9:"9517","3666a527":"9615",c150b9b5:"9698","14eb3368":"9817",ad5627e3:"9968"}[e]||e,r.p+r.u(e)},(()=>{var e={1303:0,532:0};r.f.j=(c,a)=>{var d=r.o(e,c)?e[c]:void 0;if(0!==d)if(d)a.push(d[2]);else if(/^(1303|532)$/.test(c))e[c]=0;else{var b=new Promise(((a,b)=>d=e[c]=[a,b]));a.push(d[2]=b);var f=r.p+r.u(c),t=new Error;r.l(f,(a=>{if(r.o(e,c)&&(0!==(d=e[c])&&(e[c]=void 0),d)){var b=a&&("load"===a.type?"missing":a.type),f=a&&a.target&&a.target.src;t.message="Loading chunk "+c+" failed.\n("+b+": "+f+")",t.name="ChunkLoadError",t.type=b,t.request=f,d[1](t)}}),"chunk-"+c,c)}},r.O.j=c=>0===e[c];var c=(c,a)=>{var d,b,f=a[0],t=a[1],o=a[2],n=0;if(f.some((c=>0!==e[c]))){for(d in t)r.o(t,d)&&(r.m[d]=t[d]);if(o)var i=o(r)}for(c&&c(a);n The BSV submodule | sCrypt - + @@ -12,7 +12,7 @@

The BSV submodule

sCrypt exports a submodule named bsv which is an interface that helps you manage low-level things for the Bitcoin blockchain, such as creating key pairs, building, signing and serializing Bitcoin transactions and more.

In the context of sCrypt, it is mainly used for managing key pairs and defining custom transaction builders, as demonstrated in this section.

The goal of this section is to guide you through the basics of using the bsv submodule.

Importing

You can import the bsv submodule like so:

import { bsv } from 'scrypt-ts'

Private Keys

A private key object is essentially just a wrapper around a 256-bit integer.

You can generate a Bitcoin private key from a random value:

const privKey = bsv.PrivateKey.fromRandom()
// Same as: const privKey = bsv.PrivateKey.fromRandom(bsv.Network.mainnet)

This will generate a private key for the Bitcoin main network. To create a key for the test network (also referred to as "testnet"), do the following instead:

const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet)

The main difference between a mainnet and a testnet key is how they get serialized. Check out this page which explains this in detail.

You can also create key object from serialized keys:

const privKey = bsv.PrivateKey.fromWIF('cVDFHtcTU1wn92AkvTyDbtVqyUJ1SFQTEEanAWJ288xvA7TEPDcZ')
const privKey2 = bsv.PrivateKey.fromString('e3a9863f4c43576cdc316986ba0343826c1e0140b0156263ba6f464260456fe8')

You can see the decimal value of the private key the following way:

console.log(privKey.bn.toString())

Warning Private keys should be carefully stored and never be publicly revealed. Otherwise it may lead to loss of funds.

Public Keys

A public key is a key that is derived from a private key and can be shared publicly. Mathematically, a public key is a point on the default elliptic curve that Bitcoin uses, named SECP256K1. It is the curve's base point multiplied by the value of the private key.

You can get the public key corresponding to a private key the following way:

const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet)
const pubKey = privKey.toPublicKey()

Same as with private key you can serialize and deserialize public keys:

const pubKey = bsv.PublicKey.fromHex('03a687b08533e37d5a6ff5c8b54a9869d4def9bdc2a4bf8c3a5b3b34d8934ccd17')

console.log(pubKey.toHex())
// 03a687b08533e37d5a6ff5c8b54a9869d4def9bdc2a4bf8c3a5b3b34d8934ccd17

Addresses

You can get a Bitcoin address from either the private key or the public key:

const privKey = bsv.PrivateKey.fromRandom(bsv.Networks.testnet)
const pubKey = privKey.toPublicKey()

console.log(privKey.toAddress())
// mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF
console.log(pubKey.toAddress())
// mxRjX2uxHHmS4rdSYcmCcp2G91eseb5PpF

Read this wiki page for more information on how Bitcoin addresses get constructed.

Hash Functions

The bsv submodule offers various hash functions that are commonly used in Bitcoin. You can use them like so:

const hashString = bsv.crypto.Hash.sha256(Buffer.from('this is the data I want to hash')).toString('hex')
console.log(hashString)
// f88eec7ecabf88f9a64c4100cac1e0c0c4581100492137d1b656ea626cad63e3

The hash functions available in the bsv submodule are:

Hash FunctionOutput LengthDescription
sha25632 bytesThe SHA256 hash.
sha256sha25632 bytesThe SHA256 hash of the SHA256 hash. Used for blocks and transactions.
sha51264 bytesThe SHA512 hash. Commonly used in applications.
sha120 bytesThe SHA1 hash.
ripemd16020 bytesThe RIPEMD160 hash.
sha256ripemd16020 bytesThe RIPEMD160 hash of the SHA256 hash. Used in Bitcoin addresses.

Note however, that these bsv.js hash functions should not be confused with sCrypt's native hash functions. These functions cannot be used in a smart contract method.

Constructing Transactions

The bsv submodule offers a flexible system for constructing Bitcoin transactions. Users are able to define scripts, transaction inputs and outputs, and a whole transaction including its metadata. For a complete description of Bitcoins transaction format, please read this wiki page.

As an exercise let's construct a simple P2PKH transaction from scratch and sign it.

Note: As you will notice further in these docs, most of these steps won't be needed in a regular smart contract development workflow as sCrypt already does a lot of heavy lifting for you. This section serves more as a deeper look on what is happening under the hood.

You can create an empty transaction like this:

let tx = new bsv.Transaction()

Because the transaction will need an input that provides it with some funds, we can use the from function to add one that unlocks the specified UTXO:

tx.from({
// TXID that contains the output you want to unlock:
txId: 'f50b8c6dedea6a4371d17040a9e8d2ea73d369177737fb9f47177fbda7d4d387',
// Index of the UTXO:
outputIndex: 0,
// Script of the UTXO. In this case it's a regular P2PKH script:
script: bsv.Script.fromASM('OP_DUP OP_HASH160 fde69facc20be6eee5ebf5f0ae96444106a0053f OP_EQUALVERIFY OP_CHECKSIG').toHex(),
// Value locked in the UTXO in satoshis:
satoshis: 99904
})

Now, the transaction needs an output that will pay to the address mxXPxaRvFE3178Cr6KK7nrQ76gxjvBQ4UQ in our example:

tx.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.buildPublicKeyHashOut('mxXPxaRvFE3178Cr6KK7nrQ76gxjvBQ4UQ'),
satoshis: 99804,
})
)

Notice how the output value is 100 satoshis less than the value of the UTXO we're unlocking. This difference is the transaction fee (sometimes also called the miner fee). The transaction fees are picked up by miners when they mine a block, so adding a transaction fee basically acts as an incentive for miners to include your transaction in a block.

The amount of transaction fee you should pay depends on the fee rate and the bytes of the transaction. By adding an additional output to the transaction, we can control how much the transaction fee is actually paid. This output is called the change output. By adjusting the amount of change output, we can pay as little transaction fees as possible while meeting the needs of miners.

You can directly call the change function to add a change output to the transaction without calculating the change amount by yourself. This function is smart enough that it will only add the change output when the difference between all inputs and outputs is more than the required transaction fee.

tx.change('n4fTXc2kaKXHyaxmuH5FTKiJ8Tr4fCPHFy')

For the fee rate, you can also change it by calling feePerKb.

tx.feePerKb(50)

Signing

OK, now that we have the transaction constructed, it's time to sign it. First, we need to seal the transaction, so it will be ready to sign. Then we call the sign function, which takes the private key that can unlock the UTXO we passed to the from function. In our example, this is the private key that corresponds to the address n4fTXc2kaKXHyaxmuH5FTKiJ8Tr4fCPHFy:

tx = tx.seal().sign('cNSb8V7pRt6r5HrPTETq2Li2EWYEjA7EcQ1E8V2aGdd6UzN9EuMw')

Viola! That's it. This will add the necessary data to the transaction's input script. That being the signature along with the public key of our signing key.

Now our transaction is ready to be posted to the blockchain. You can serialize the transaction the following way:

console.log(tx.serialize())

For broadcasting, you can use any provider you like. For demo purposes you can simply paste the serialized transaction here.

OP_RETURN Scripts

In case you would like to put some arbitrary data on-chain, without any locking logic, you can use transaction outputs with an OP_RETURN script.

An example of an OP_RETURN script written in ASM format is this:

OP_FALSE OP_RETURN 734372797074

In effect, the opcodes OP_FALSE OP_RETURN will make the script unspendable. After them we can insert arbitrary chunks of data. The 734372797074 is actually the string sCrypt encoded as an utf-8 hexadecimal string.

console.log(Buffer.from('sCrypt').toString('hex'))
// 734372797074

An OP_RETURN script can also contain more than a single chunk of data:

OP_FALSE OP_RETURN 48656c6c6f 66726f6d 734372797074

The bsv submodule offers a convenient function to construct such scripts:

const opRetScript: bsv.Script = bsv.Script.buildSafeDataOut(['Hello', 'from', 'sCrypt'])

We can add the resulting bsv.Script object to an output as we showed above.

ECIES

ECIES (Elliptic Curve Integrated Encryption Scheme) is a hybrid encryption scheme that combines the strengths of public-key cryptography and symmetric encryption. It allows two parties, each having an elliptic curve key pair, to exchange encrypted messages. The bsv submodule provides the ECIES class to easily implement this encryption scheme in your sCrypt projects.

Here's how to use it:

Encryption

To encrypt a message using ECIES:

  1. First, create an instance of the ECIES class.
  2. Specify the public key of the recipient with the publicKey method.
  3. Call the encrypt method with the message you wish to encrypt.
const msg = 'Hello sCrypt!'
const encryption = new bsv.ECIES()
encryption.publicKey(recipientPublicKey)
const ciphertext = encryption.encrypt(msg)

In this example, recipientPublicKey is the recipient's public key.

Decryption

To decrypt a message:

  1. Create another instance of the ECIES class.
  2. Set the recipient's private key using the privateKey method.
  3. Call the decrypt method, passing the ciphertext you wish to decrypt.
const decryption = new bsv.ECIES()
decryption.privateKey(recipientPrivateKey)
const msg = decryption.decrypt(ciphertext)
console.log(msg)
// "Hello sCrypt!"

In this example, recipientPrivateKey is the private key of the recipient (the one corresponding to the public key used for encryption).

References

  • Take a look at the full bsv submodule reference for a full list of what functions it provides.
  • As the bsv submodule is based on MoneyButton's library implementation, take a look at their video tutorial series. Although do keep in mind that some things might be slightly different as it's an old series.
- + \ No newline at end of file diff --git a/bitcoin-basics/index.html b/bitcoin-basics/index.html index c6d05ca2a..898aaaedf 100644 --- a/bitcoin-basics/index.html +++ b/bitcoin-basics/index.html @@ -4,13 +4,13 @@ Bitcoin Basics | sCrypt - +

Bitcoin Basics

If you are new to Bitcoin development, we recommend exploring the resources listed in this section. While not an absolute prerequisite, going over these guides will provide a clearer understanding of key concepts and make it easier to begin developing sCrypt smart contracts.

If you are already familiar with the basics of Bitcoin, you can skip ahead to the How to Write a Contract section.

Useful Resources for Learning Bitcoin Fundamentals

If you want to learn more about the fundamentals of Bitcoin, consider exploring the following resources:

- + \ No newline at end of file diff --git a/category/advanced/index.html b/category/advanced/index.html index 32c393129..663b7b479 100644 --- a/category/advanced/index.html +++ b/category/advanced/index.html @@ -4,13 +4,13 @@ Advanced | sCrypt - +

Advanced

- + \ No newline at end of file diff --git a/category/tutorials/index.html b/category/tutorials/index.html index f2f7695b9..128728863 100644 --- a/category/tutorials/index.html +++ b/category/tutorials/index.html @@ -4,13 +4,13 @@ Tutorials | sCrypt - + - + \ No newline at end of file diff --git a/ethereum-devs/index.html b/ethereum-devs/index.html index 3ce3225ed..dad2efd5f 100644 --- a/ethereum-devs/index.html +++ b/ethereum-devs/index.html @@ -4,14 +4,14 @@ sCrypt for Ethereum Developers | sCrypt - +

sCrypt for Ethereum Developers

Smart contracts on Bitcoin vs Ethereum

Bitcoin and Ethereum are both layer-1 blockchains with fully programmable smart contracts. However, their designs fundamentally differ.

Ethereum is a global state machine, whose state consists of all smart contracts deployed on it. Each transaction is an input to the state machine, transitioning it to the next state according to the rules defined in the smart contract the transaction calls. The design imposes severe limitations on scalability, since transactions must be sequentially processed due to potential race conditions.

In Bitcoin, transaction processing is independent of each other since all information needed is localized. There is no shared global state. Bitcoin is maximally parallelizable by design.

Detailed side-by-side comparison can be found here, which is concisely summarized below.

EthereumBitcoin
Execution EnvironmentEthereum Virtual Machine (EVM)Bitcoin Virtual Machine (BVM)
ModelAccountUTXO
Transaction Fee$1-10$0.00001
Transactions Per Second153000+
Transaction ProcessingSequentialParallel
ScalabilityVerticalHorizontal
ParadigmImpurePure
Miner Extractable Value (MEV)YesNo

Smart contract development on Bitcoin vs Ethereum

Besides unboundedly scalable fundation, Bitcoin also offers superior smart cotnract developer experience.

The table below shows a comparison of popular Ethereum development tools and their counterparts in the Bitcoin ecosystem.

There are two noticeable differences.

  1. Bitcoin smart contract is written in TypeScript, one of the most popular programming languages tens of millions of Web2 developers are already familiar with. They do not have to learn a new niche programming language like Solidity, placing a high barrier to entry. They can reuse all of their favoriate tools, such as Visual Studio Code, WebStorm, and NPM.
  2. Ethereum's development tools are fragmented. They are developed by different entities, who are often competitors. There is disincentive to make them more interoperable, thus they don't communicate with each other well. By contrast, sCrypt takes a more holistic and systematic approach. It builds a unified full-stack platform that encompasses most tools, from programming language, to framework/libraries. Developed synergistically, they are fully compatible with each other, greatly simplifing and streamlining development process.
EthereumBitcoin
Programming LanguageSoliditysCrypt DSL
FrameworkHardhat / TruffleThe sCrypt CLI
LibrariesWeb3.js / Ethers.jsscrypt-ts
Developer PlatformAlchemy / InfurasCrypt
IDERemix1Visual Studio Code
WalletMetaMaskSensilet
Block ExplorerEtherscanWhatsOnChain

Example Code

Let's compare a counter smart contract between Solidity and sCrypt.

pragma solidity >=0.7.0 <0.9.0;

contract Counter {

int private count;

constructor(int _initialCount) {
count = _initialCount;
}

function incrementCounter() public {
count += 1;
}

function getCount() public view returns (int) {
return count;
}

}
class Counter extends SmartContract {

@prop(true)
count: bigint

constructor(count: bigint) {
super(...arguments)
this.count = count
}

@method()
public incremenCounter() {
this.count++

assert(hash256(this.buildStateOutput(this.ctx.utxo.value)) == this.ctx.hashOutputs)
}

}

  1. Visual Studio Code can also be used for Solidity with various extentions. However, its support is extremely limited compared to that of sCrypt, a TypeScript DSL, which is supported out of box without any extension. For example, VS Code debugger has first-class comprehensive support for sCrypt, but does not suppport Solidity.
- + \ No newline at end of file diff --git a/faq/index.html b/faq/index.html index 1c29f05f9..f9d1c531f 100644 --- a/faq/index.html +++ b/faq/index.html @@ -4,13 +4,13 @@ FAQ | sCrypt - +

FAQ

Smart contract call failure

If you receive a mandatory-script-verify-flag-failed error when broadcasting a transaction, it means that one or more inputs calling a contract fails.

There are several possibilities for the failure.

Script evaluated without error but finished with a false/empty top stack element is the most common one. It means one of assert fails.

Another common error is Signature must be zero for failed CHECK(MULTI)SIG operation, which means the signature is invalid in checkSig or checkMultiSig.

You need to debug the contract.

Broadcast double-spending transactions

You could get two different errors when broadcasting a double-spending transaction, depending on the status of the transaction you're trying to double-spend.

  • If the transaction you're trying to double-spend is still unconfirmed and in the mempool, the error would be txn-mempool-conflict.

  • If the transaction is already mined into a block and confirmed, the error would be Missing inputs.

1) for developers

If you encounter these errors when running code, e.g., testing on testnet, it is likely because the provider you are using fails to update your UTXO in time and return UTXOs that have already been spent when you request. Using these UTXOs that have been spent to build transactions will result in a double-spending. This situation is transitory and is caused by the provider not updating UTXO set timely due to, e.g., the provider's server overloading due to heavy blockchain traffic.

To fix this issue, you generally only need to wait a few seconds and retry. If it still persists, you can also increase the time interal between sending consecutive transactions, for example, sleep for some time after sending transactions before requesting the UTXO again, so that the provider has enough time to update the UTXO set:

// ... contract call #1

await sleep(2000) // Sleep for 2 seconds

// ... contract call #2

2) for dApp users

If you encounter these errors when using a dApp, it is likely because the state of dApp's contract has been changed by another user, who is interacting with the dApp at the same time. You are interacting with the contract instance that is stale, resulting in a double-spending.

To fix this issue, you usually only have to wait a few seconds, your local dApp will automatically obtain the latest contract state if it has subscribed to contract events; otherwise you have to manually refresh the browser and try again.

Input string too short

If you do not set the PRIVATE_KEY environment variable in .env file before deploying a contract, you would get an Input string too short error.

Please follow this guide to generate a new private key or export the private key from your Sensilet wallet, then fund the private key's address with our faucet.

No sufficient utxos

If you don't fund your private key's address before deploying a contract, you would get a No sufficient utxos error.

Please fund your address with our faucet first.

- + \ No newline at end of file diff --git a/how-to-debug-a-contract/index.html b/how-to-debug-a-contract/index.html index 703f7a57a..dd86c5e38 100644 --- a/how-to-debug-a-contract/index.html +++ b/how-to-debug-a-contract/index.html @@ -4,14 +4,14 @@ How to Debug a Contract | sCrypt - +

How to Debug a Contract

Debugging an sCrypt contract is as easy as debugging TypeScript, since it is just TypeScript.

Use console.log()

You can use console.log() to print to the console.

export class Demo extends SmartContract {

@prop()
readonly x: bigint

@prop()
readonly y: bigint

constructor(x: bigint, y: bigint) {
super(...arguments)
this.x = x
this.y = y
}

@method()
sum(a: bigint, b: bigint): bigint {
return a + b
}

@method()
public add(z: bigint) {
console.log(`z: ${z}`) // print the value of z
console.log(`sum: ${this.x + this.y}`) // print the value of this.x + this.y
assert(z == this.sum(this.x, this.y), 'incorrect sum')
}
}

Try it on Replit

After running the code, you should see the following output:

z: 3
sum: 3

Use Visual Studio Code debugger

You can use VS Code to debug sCrypt contracts, the same way as any other TypeScript programs. If you have created a project with the sCrypt CLI, you should have an auto-generated launch.json, containing everything needed for the debugger out of the box. To learn more about the VS Code TypeScript debugger, please refer to the official documentation.

You can set some breakpoints and choose Launch demo from the Run and Debug view (or press F5) to start the debugger instantly.

note

You need to change the contract file name in launch.json if needed.

Debug a test

If you want to debug a unit test written with the Mocha testing framework, choose Launch demo test from the Run and Debug view.

note

You need to change the contract test file name in launch.json if needed.

- + \ No newline at end of file diff --git a/how-to-deploy-and-call-a-contract/call-deployed/index.html b/how-to-deploy-and-call-a-contract/call-deployed/index.html index a517cf1a4..09b080a46 100644 --- a/how-to-deploy-and-call-a-contract/call-deployed/index.html +++ b/how-to-deploy-and-call-a-contract/call-deployed/index.html @@ -4,7 +4,7 @@ Interact with a Deployed Contract | sCrypt - + @@ -12,7 +12,7 @@

Interact with a Deployed Contract

Overview

In this tutorial, we will interact with a deployed smart contract by calling its public method, in a separate process or by a different party. We need to create an instance corresponding to the deployed contract on chain.

The Smart Contract

We will reuse the Counter contract.

export class Counter extends SmartContract {

@prop(true)
count: bigint

constructor(count: bigint) {
super(...arguments)
this.count = count
}

@method()
public incrementOnChain() {
// Increment counter.
this.increment()

// Ensure next output will contain this contracts code w
// the updated count property.
const amount: bigint = this.ctx.utxo.value
const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput()
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')
}

@method()
increment(): void {
this.count++
}
}

Deploy

To deploy the smart contract, we define the following function:

async function deploy(initialCount = 100n): Promise<string> {
const instance = new Counter(initialCount)
await instance.connect(getDefaultSigner())
const tx = await instance.deploy(1)
console.log(`Counter deployed: ${tx.id}, the count is: ${instance.count}`)
return tx.id
}

The function deploys the contract with a balance of 1 satoshi and returns the TXID of the deployed contract.

Interact

Next, we update our deployed smart contract by calling the following function:

async function callIncrementOnChain(
txId: string,
atOutputIndex = 0
): Promise<string> {
// Fetch TX via provider and reconstruct contract instance.
const signer = getDefaultSigner()
const tx = await signer.connectedProvider.getTransaction(txId)
const instance = Counter.fromTx(tx, atOutputIndex)

await instance.connect(signer)

const nextInstance = instance.next()
nextInstance.increment()

const { tx: callTx } = await instance.methods.incrementOnChain({
next: {
instance: nextInstance,
balance: instance.balance,
},
} as MethodCallOptions<Counter>)
console.log(`Counter incrementOnChain called: ${callTx.id}, the count now is: ${nextInstance.count}`)
return callTx.id
}

The function takes as parameters the TXID of the deployed smart contract to create an instance, along with the output index (which is usually 0). It uses the DefaultProvider to fetch the transaction data from the blockchain. Subsequently, it reconstructs the smart contract instance using the fromTx function.

Let's encapsulate the entire process within a main function, designed to deploy the contract and increment its value five times:

async function main() {
await compileContract()
let lastTxId = await deploy()
for (let i = 0; i < 5; ++i) {
lastTxId = await callIncrementOnChain(lastTxId)
}
}

(async () => {
await main()
})()

If we execute the code, we should get an output similar to the following:

Counter deployed: 1cd6eb4ff0a5bd83f06c60c5e9a5c113c6e44fd876096e4e94e04a80fee8c8ca, the count is: 100
Counter incrementOnChain called: c5b8d8f37f5d9c089a73a321d58c3ae205087ba21c1e32ed09a1b2fbd4f65330, the count now is: 101
Counter incrementOnChain called: c62bb0f187f81dfeb5b70eafe80d549d3b2c6219e16d9575639b4fbdffd1d391, the count now is: 102
Counter incrementOnChain called: 9fb217b98324b633d8a0469d6a2478f522c1f40c0b6d806430efe5ae5457ca0e, the count now is: 103
Counter incrementOnChain called: 2080ddecc7f7731fc6afd307a57c8b117227755bd7b82eb0bc7cd8b78417ad9a, the count now is: 104
Counter incrementOnChain called: de43687fd386e92cd892c18600d473bc38d5adb0cc34bbda892b94c61b5d5eb8, the count now is: 105

Conclusion

Congratulations! You've now deployed AND interacted with a Bitcoin smart contract. You can see a complete test example in our boilerplate repository.

- + \ No newline at end of file diff --git a/how-to-deploy-and-call-a-contract/deploy-cli/index.html b/how-to-deploy-and-call-a-contract/deploy-cli/index.html index 5945f4346..92e229dc5 100644 --- a/how-to-deploy-and-call-a-contract/deploy-cli/index.html +++ b/how-to-deploy-and-call-a-contract/deploy-cli/index.html @@ -4,7 +4,7 @@ Deploy Using CLI | sCrypt - + @@ -12,7 +12,7 @@

Deploy Using CLI

The deploy command allows you to deploy an instance of a smart contract to the blockchain. You can simply run the following command in the root of an sCrypt project:

npx scrypt-cli deploy

or

npx scrypt-cli d

By default, the CLI tool will run a script named deploy.ts located in the root of the project. You can also specify a different deployment script using the --file or -f option.

npx scrypt-cli d -f myCustomDeploy.ts

If the project was created using sCrypt CLI, it will already have a deploy.ts file present (except for library projects). If not, the deploy command will generate a sample deploy.ts file.

Here's an example of such a deployment file:

import { Demoproject } from './src/contracts/demoproject'
import { bsv, TestWallet, DefaultProvider, sha256, toByteString, } from 'scrypt-ts'

import * as dotenv from 'dotenv'

// Load the .env file
dotenv.config()

// Read the private key from the .env file.
// The default private key inside the .env file is meant to be used for the Bitcoin testnet.
// See https://scrypt.io/docs/bitcoin-basics/bsv/#private-keys
const privateKey = bsv.PrivateKey.fromWIF(process.env.PRIVATE_KEY)

// Prepare signer.
// See https://scrypt.io/docs/how-to-deploy-and-call-a-contract/#prepare-a-signer-and-provider
const signer = new TestWallet(privateKey, new DefaultProvider())

async function main() {
// Compile the smart contract.
await Demoproject.loadArtifact()

// The amount of satoshis locked in the smart contract:
const amount = 100

// Instantiate the smart contract and pass constructor parameters.
const instance = new Demoproject(
sha256(toByteString('hello world', true))
)

// Connect to a signer.
await instance.connect(signer)

// Contract deployment.
const deployTx = await instance.deploy(amount)
console.log('Demoproject contract deployed: ', deployTx.id)
}

main()

Upon a successful execution you should see an output like the following:

Demoproject contract deployed:  15b8055cfaf9554035f8d3b866f038a04e40b45e28109f1becfe4d0af9f743cd

You can take a look at the deployed smart contract using the WhatsOnChain block explorer. In our example, the first output contains the compiled smart contract code. It is indexed using the hash (double SHA-256) of the script: eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def

- + \ No newline at end of file diff --git a/how-to-deploy-and-call-a-contract/faucet/index.html b/how-to-deploy-and-call-a-contract/faucet/index.html index ee9c4a612..742f6b6db 100644 --- a/how-to-deploy-and-call-a-contract/faucet/index.html +++ b/how-to-deploy-and-call-a-contract/faucet/index.html @@ -4,13 +4,13 @@ Faucet | sCrypt - +

Faucet

It is highly recommended to test your contract on the testnet after passing local tests. It ensures that a contract can be successfully deployed and invoked as expected on the blockchain.

Before deploy and call a contract, you need to have a funded address:

  1. Create a new project. Skip this step if you have already created a project:
npx scrypt-cli project demo
cd demo
  1. Generate a private key with the following command executed from the root of the project:
npm install
npm run genprivkey

The command will generate a private key and store it in a .env file in our project's root directory. It also outputs the Bitcoin address corresponding to our private key.

  1. Fund the private key's address with some testnet coins. You could use this faucet to receive test coins.

faucet

Use the Sensilet Wallet

Alternatively, if you have already installed Sensilet, you can extract and use its private key on testnet as follows.

- + \ No newline at end of file diff --git a/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx/index.html b/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx/index.html index 3731073f6..88c50a4d6 100644 --- a/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx/index.html +++ b/how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx/index.html @@ -4,13 +4,13 @@ How to Customize a Contract Tx | sCrypt - +

How to Customize a Contract Tx

Deployment Tx

Default

For contract deployment, the default tx builder creates a transaction with the following structure:

  • Inputs:

    • [0…]: One or more P2PKH inputs for paying transaction fees.
  • Outputs:

    • [0]: The output containing the contract.
    • [1]: A P2PKH change output if needed.

Numbers in [] represent index, starting from 0.

Customize

You can customize a contract's deployment tx builder by overriding its buildDeployTransaction method. An example is shown below.

class DemoContract extends SmartContract {
// ...

// customize the deployment tx by overriding `SmartContract.buildDeployTransaction` method
override async buildDeployTransaction(utxos: UTXO[], amount: number,
changeAddress?: bsv.Address | string): Promise<bsv.Transaction> {

const deployTx = new bsv.Transaction()
// add p2pkh inputs for paying tx fees
.from(utxos)
// add contract output
.addOutput(new bsv.Transaction.Output({
script: this.lockingScript,
satoshis: amount,
}))
// add OP_RETURN output
.addData('Hello World')

if (changeAddress) {
deployTx.change(changeAddress);
if (this._provider) {
deployTx.feePerKb(await this.provider.getFeePerKb())
}
}

return deployTx;
}
}

You may visit the full code for more details.

Call Tx

Default

For contract calls, the default tx builder creates a transaction with the following structure:

  • Inputs

    • [0]: The input that spends the contract UTXO.
    • [1…]: Zero or more P2PKH inputs for paying transaction fees.
  • Outputs

    • [0…N-1]: One or more outputs, each containing a new contract instance (UTXO) if the contract is stateful.
    • [N]: A P2PKH change output if needed.

Customize

You can customize a tx builder for a public @method of your contract by calling bindTxBuilder. The first parameter is the public method name, and the second parameter is the customized tx builder of type MethodCallTxBuilder.

MethodCallTxBuilder takes three parameters:

  1. current: T: the actual instance of the smart contract T.
  2. options: of type MethodCallOptions<T>.
  3. ...args: any: the same list of arguments as the bound pubic @method.

Take tx builder for our auction smart contract as an example:

// bind a customized tx builder for the public method `Auction.bid`
auction.bindTxBuilder('bid', Auction.bidTxBuilder)

static bidTxBuilder(
current: Auction,
options: MethodCallOptions<Auction>,
bidder: PubKey,
bid: bigint
): Promise<ContractTransaction> {
const nextInstance = current.next()
nextInstance.bidder = bidder

const unsignedTx: Transaction = new bsv.Transaction()
// add contract input
.addInput(current.buildContractInput())
// build next instance output
.addOutput(
new bsv.Transaction.Output({
script: nextInstance.lockingScript,
satoshis: Number(bid),
})
)
// build refund output
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildPublicKeyHashScript(pubKey2Addr(current.bidder))
),
satoshis: current.balance,
})
)
// build change output
.change(options.changeAddress)

return Promise.resolve({
tx: unsignedTx,
atInputIndex: 0,
nexts: [
{
instance: nextInstance,
atOutputIndex: 0,
balance: Number(bid),
},
],
})
}

In this example, we customize the calling transaction for the publid @method bid. ...args resolves to its parameters: bidder: PubKey and bid: bigint. The first input is the one that will reference the UTXO, where our smart contract instance currently resides. We use the buildContractInput function to to build the input. Note that during the execution of the tx builder function, this input's script is empty. The script will get populated by the method arguments at a later stage of the method call.

The tx builder will return an object:

  • tx: the unsigned transaction of our method call.
  • atInputIndex: the index of the input which will reference the smart contract UTXO.
  • nexts: an array of objects that represent the contract's next instance(s).

When we are calling a stateful smart contract, we have to define the next instance of our contract. This instance will contain updated states. As we can see, first a new instance is created using the current instance's next() function. The new instance's bidder property is then updated. This new instance is then included in the 0-th output of the new transaction and goes into the nexts array of the returned object.

Implicit binding by naming convention

As a shortcut, if a static function of type MethodCallTxBuilder is named buildTxFor${camelCaseCapitalized(methodName)}, there is no need to explicitly call bindTxBuilder(), where camelCaseCapitalized() capitalizes the first letter of methodName.

In the above example, if the static function bidTxBuilder is renamed to buildTxForBid, it will be the tx builder for calling bid implicitly. There is no need to explicitly call auction.bindTxBuilder('bid', Auction.buildTxForBid).

// no need to bind explicitly
// auction.bindTxBuilder('bid', Auction.buildTxForBid)

// buildTxForBid is a customized tx builder for the public method `Auction.bid`
static buildTxForBid(
current: Auction,
options: MethodCallOptions<Auction>,
bidder: PubKey,
bid: bigint
): Promise<ContractTransaction> {
...

Notes

Please be aware that each of these tx builders should only create an unsigned transaction. If required, the transaction gets signed automatically in a later step prior to broadcasting.

Also, your customized tx must satisfy all of the called @method's assertions.

- + \ No newline at end of file diff --git a/how-to-deploy-and-call-a-contract/index.html b/how-to-deploy-and-call-a-contract/index.html index 21fc77338..84712a6ff 100644 --- a/how-to-deploy-and-call-a-contract/index.html +++ b/how-to-deploy-and-call-a-contract/index.html @@ -4,7 +4,7 @@ How to Deploy & Call a Contract | sCrypt - + @@ -14,7 +14,7 @@ We have some built-in tx builders for the most common way to interact with contracts, so usually you don't have to implement them. If the default tx builder does not meet your specific requirements, such as having extra inputs or outputs in your tx, you can customize it.

Contract Deployment Transaction

A Bitcoin transaction is required when deploying a contract to the blockchain. The transaction should have an output, whose script is compiled from the contract. This output is known as a contract UTXO and the contract instance comes from this UTXO.

An instance's from can be accessed.

// the tx that contains the instance
instance.from.tx
// the index of the tx output that contains the instance
instance.from.outputIndex

Contract Call Transaction

When you call a public method of a contract instance in a UTXO, a call transaction is needed. The transaction has an input that references to the UTXO and contains the script consisting of the method's arguments. We regard the contract instance goes to this transaction input.

An instance's to can be accessed.

// the tx that spends the instance
instance.to.tx
// the index of the tx input that spends the UTXO the instance is in
instance.to.inputIndex

This section could be summarized as the diagram below:

Prepare a Signer and Provider

A signer and a provider must be connected to a contract instance before deployment and call. When we are ready to deploy the contract to the testnet/mainnet, we need a real provider like DefaultProvider.

const network = bsv.Networks.testnet; // or bsv.Networks.mainnet
const signer = new TestWallet(privateKey, new DefaultProvider(network));

The privateKey must have enough coins. Learn how to fund it on a testnet using a faucet.

Then just connect it to your contract instance like this:

await instance.connect(signer);
note

TestWallet is just a Signer provided by sCrypt for testing. In a real production environment (Mainnet), you should use SensiletSigner, DotwalletSigner, TAALSigner. See here how to use them.

Contract Deployment

To deploy a smart contract, simply call its deploy method:

// construct a new instance of `MyContract`
let instance = new MyContract(...initArgs);

// connect the signer to the instance
await instance.connect(signer);

// the contract UTXO’s satoshis
const initBalance = 1234;

// build and send tx for deployment
const deployTx = await instance.deploy(initBalance);
console.log(`Smart contract successfully deployed with txid ${deployTx.id}`);

Contract Call

To facilitate calling a contract's public @method, we have injected a runtime object named methods in your contract class. For each public @method of your contract (e.g., contract.foo), a function with the same name and signature (including list of parameters and return type, i.e., void) is added into methods (e.g., contract.methods.foo). In addition, there is an options appended as the last paramter.

Assume you have a contract like this:

Class MyContract extends SmartContract {
...
@method()
public foo(arg1, arg2) {...}
}

You can check it like this:

let instance = new MyContract();
console.log(typeof instance.methods.foo) // output `function`

This function is designed to invoke the corresponding @method of the same name on chain, meaning calling it will spend the previous contract UTXO in a new transaction. You can call it like this:

// Note: `instance.methods.foo` should be passed in all arguments and in the same order that `instance.foo` would take.

// Additionally, it can accept an optional "options" argument to control the behavior of the function.

const { tx, atInputIndex } = await instance.methods.foo(arg1, arg2, options);

What actually happens during the call is the following.

  1. Build an unsigned transaction by calling the tx builder, which can be a default or a customized one introduced in this section, for a public @method.

  2. Use the instance's signer to sign the transaction. Note that instance.foo could be invoked during this process in order to get a valid unlocking script for the input.

  3. Use the instance's connected provider to send the transaction.

MethodCallOptions

The options argument is of type MethodCallOptions:

/**
* A option type to call a contract public `@method` function.
* Used to specify the behavior of signers and transaction builders.
* For example, specifying a transaction builder to use a specific change address or specifying a signer to use a specific public key to sign.
*/
export interface MethodCallOptions<T> {
/**
* The private key(s) associated with these address(es) or public key(s)
* must be used to sign the contract input,
* and the callback function will receive the results of the signatures as an argument named `sigResponses`
* */
readonly pubKeyOrAddrToSign?: PublicKeysOrAddressesOption | SignaturesOption;
/** The subsequent contract instance(s) produced in the outputs of the method calling tx in a stateful contract */
readonly next?: StatefulNext<T>[] | StatefulNext<T>,
/** The `lockTime` of the method calling tx */
readonly lockTime?: number;
/** The `sequence` of the input spending previous contract UTXO in the method calling tx */
readonly sequence?: number;
/** The P2PKH change output address */
readonly changeAddress?: AddressOption;
/** verify the input script before send transaction */
readonly verify?: boolean;
/** Whether to call multiple contracts at the same time in one transaction */
readonly multiContractCall?: true;
/** Pass the `ContractTransaction` of the previous call as an argument to the next call, only used if `multiContractCall = true`. */
readonly partialContractTx?: ContractTransaction;
}

The major differences between here and local tests are:

  1. the contract needs to be deployed first;
  2. the contract instance is connected to a real provider, which broadcasts transactions to the blockchain.

Create a smart contract instance from a transaction

To interact with a deployed smart contract (i.e., calling its public methods), we need its contract instance corresponding to its latest state on chain, stateful or not. When testing on testnet, we usually put a contract's deployment and its calling (note there could be multiple calls if the contract is stateful) in the same process for convenience, so that we don't need to manage the internal state of the instance manually, because it's always consistent with the transactions on chain.

In reality, a contract's deployment and its call, and its different calls in the case of a stateful contract, may well be in separate processes. For example, the deployment party is different from the calling party, or multiple parties call it. If so, we need to create a contract instance from an on-chain transaction that represents its latest state, before we can call its method.

Typically, we only know the TXID of the transaction containing the instance. We can create an instance in two steps:

  1. Using TXID, we retrieve the full transaction by calling getTransaction of the connected provider of the signer.
  2. We can create an contract instance from a transaction's by calling fromTx().
// 1) fetch a transaction from txid
const tx = await signer.connectedProvider.getTransaction(txId)
// 2) create instance from transaction
const instance = Counter.fromTx(tx, atOutputIndex)

// from now on, `instance` is in sync with the on-chain transaction
// and we can use it to interact with the contract

A complete example can be found here.

Method with Signatures

A contract public @method often needs a signature argument for authentication. Take this Pay To PubKey Hash (P2PKH) contract for example:

export class P2PKH extends SmartContract {
@prop()
readonly address: Addr

constructor(address: Addr) {
super(..arguments)
this.address = address
}

@method()
public unlock(sig: Sig, pubkey: PubKey) {
// make sure the `pubkey` is the one locked with its address in the constructor
assert(pubKey2Addr(pubkey) == this.address, 'address check failed')

// make sure the `sig` is signed by the private key corresponding to the `pubkey`
assert(this.checkSig(sig, pubkey), 'signature check failed')
}
}

We can call the unlock method like this:

// call
const { tx: callTx } = await p2pkh.methods.unlock(
// the first argument `sig` is replaced by a callback function which will return the needed signature
(sigResps) => findSig(sigResps, publicKey),

// the second argument is still the value of `pubkey`
PubKey(toHex(publicKey)),

// method call options
{
// A request for signer to sign with the private key corresponding to a public key
pubKeyOrAddrToSign: publicKey
} as MethodCallOptions<P2PKH>
);

console.log('contract called: ', callTx.id);

When p2phk.method.unlock is called, the option contains pubKeyOrAddrToSign, requesting a signature against publicKey.

The first argument is a signature, which can be obtained in a callback function. The function takes a list of signatures requested in pubKeyOrAddrToSign and find the one signature to the right public key/address.

In general, if your @method needs Sig-typed arguments, you could obtain them as follows:

  1. Ensure that the pubKeyOrAddrToSign contains all public keys/addresses corresponding to these Sigs;

  2. Replace each Sig argument with a callback function that filters to the right Sig from the full list of signature in sigResps.

Example

Here is the complete sample code for the deployment and call of a P2PKH contract.

import { privateKey } from '../../utils/privateKey';

// compile contract
await P2PKH.loadArtifact()

// public key of the `privateKey`
const publicKey = privateKey.publicKey

// setup signer
const signer = new TestWallet(privateKey, new DefaultProvider());

// initialize an instance with `pkh`
let p2pkh = new P2PKH(Addr(publicKey.toAddress().toByteString()))

// connect the signer
await p2pkh.connect(signer);

// deploy the contract, with 1 satoshi locked in
const deployTx = await p2pkh.deploy(1);
console.log('contract deployed: ', deployTx.id);

// call
const { tx: callTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, publicKey),
PubKey(toHex(publicKey)),
{
pubKeyOrAddrToSign: publicKey
} as MethodCallOptions<P2PKH>
);

console.log('contract called: ', callTx.id);

More examples can be found here.

Running the code

The deployment and call code is wrapped into a simple NPM command:

npm run testnet

Make sure you fund your address before running this command. After a successful run you should see something like the following:

P2PKH contract deployed:  f3f372aa25f159efa93db8c51a4eabbb15935358417ffbe91bfb78f4f0b1d2a3
P2PKH contract called: dc53da3e80aadcdefdedbeb6367bb8552e381e92b226ab1dc3dc9b3325d8a8ee

These are the TXIDs of the transaction which deployed the smart contract and then the one which called its method. You can see the transactions using a block explorer.

Customize Transactions

Deploying and calling a contract builds transactions with a certain format, which suffices for many cases. In cases where the tx format does not work for you and you need to customize it, please refer to this section.

- + \ No newline at end of file diff --git a/how-to-integrate-a-frontend/how-to-integrate-dotwallet/index.html b/how-to-integrate-a-frontend/how-to-integrate-dotwallet/index.html index c6612ec1c..aa48c8323 100644 --- a/how-to-integrate-a-frontend/how-to-integrate-dotwallet/index.html +++ b/how-to-integrate-a-frontend/how-to-integrate-dotwallet/index.html @@ -4,7 +4,7 @@ How to integrate DotWallet | sCrypt - + @@ -12,7 +12,7 @@

How to integrate DotWallet

DotWallet is a lightweight wallet designed to help users easily and securely manage their digital assets. We will show how to integrate it with sCrypt-powered apps.

OAuth 2.0

OAuth 2.0 is an industry-standard authorization framework that enables third-party applications to access the resources of a user on a web service —- such as Facebook, Google, and Twitter -- without requiring the user to share their credentials directly with the application. It provides a secure and standardized way for users to grant limited access to their protected resources, such as their profile information or photos, to other applications. It works by introducing an authorization layer between the user, the application, and the web service hosting the user's data. Instead of sharing their username and password with the application, the user is redirected to the web service's authentication server. The user then authenticates themselves on the service, and upon successful authentication, the service issues an access token to the application. This access token represents the user's authorization to access specific resources.

If you are new to OAuth 2.0, check out thse helpful tutorials:

DotWallet's user authorization

DotWallet uses OAuth 2.0 to allow third-party applications to safely access certain capabilities authorized by DotWallet users. More specifically, it uses Oauth2's authorization code grant type as the diagram shows. See RFC6749 for details.

Credit: Vihanga Liyanage

Follow these steps for a user authorization.

  1. Construct URI.

    Example URI: https://api.ddpurse.com/v1/oauth2/authorize?client_id=YOUR-CLIENT-ID&redirect_uri=http%3A%2F%2FYOUR-REDIRECT-URL&response_type=code&state=YOUR-STATE&scope=user.info

    URL Parameters:

    ParameterDescription
    client_idDeveloper’s dapp client_id
    redirect_uriThe redirect URL after authorization. Needs to be url_encoded
    stateIt is recommended to use a random string of more than 32 bits (such as UUID). The state is used to verify the consistency of the request and callback. This can prevent csrf attacks.
    response_typeFill in the fixed value : code
    scopeAuthorization scope. The list of permissions that the user agrees to authorize. These permissions are required for certain API endpoints. Needs to be url_encoded. Use spaces to separate multiple permissions. For a list of currently supported scope permissions, please check the scope list here
  2. Redirect the user to the URI constructed in step 1

    After clicking the link, the user will be directed to the DotWallet authorization page. DotWallet will ask the user to log in, and then ask whether they agree to authorize the application for the listed permission scopes.

  3. Receive the code through the callback uri.

    After the user agrees to authorization in step 2, DotWallet will redirect the client to the redirect_uri specified by the application. The authorization code code and the provided state will be included in the query parameters.

  4. Exchange code for access_token. The access tokens are credentials used to access protected resources, which are issued by the authorization server.

danger

To avoid security issues, any request for using or obtaining access_token must be made from the backend server. Do not disclose your access_token and client_secret1 on the client side.

DotWallet Developer Platform

  1. Before using DotWallet, you need to register and create an app on DotWallet Developer Platform.

  1. After creating the app, you will receive an email containing app_id and secret.

  1. Next, you need to set redirection URI. Redirect URLs are a critical part of the OAuth flow. After a user successfully authorizes an application, the authorization server will redirect the user back to the application. For example, in the figure below http://localhost:3000/callback/ is the redirection.

note

Callback domain in the form is the redirection URIs in OAuth.

Example Implementation

Here is an example to integration DotWallet in Nextjs, a popular React development framework.

  1. Construct URI.
export default async function Home() {
const client_id = process.env.CLIENT_ID;
const redirect_uri = encodeURIComponent(process.env.REDIRECT_URI || '');
const scope = encodeURIComponent("user.info autopay.bsv");
const state = crypto.randomUUID();
const loginUrl = `https://api.ddpurse.com/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=${state}`;

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="m-4 p-4 bg-blue-200 font-bold rounded-lg">
<a href={loginUrl}>DotWallet Login</a>
</div>
</main>
);
}
src/app/page.tsx

If the user clicks the DotWallet Login link, the page will be redirected to the wallet authorization page.

  1. After the user clicks Agree to authorize to log in, the authorization server redirects the user to the redirection URI. The following code receives the code through the callback uri, exchanges the code for access_token and save it.

Inside the app directory, folders are used to define routes in nextjs. we create src/app/callback/route.ts to handle the redirection request.

import { redirect, notFound } from 'next/navigation';

import token from "../token"

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');

if (code) {
// exchange the code for access_token
const res = await fetch(`https://api.ddpurse.com/v1/oauth2/get_access_token`, {
body: JSON.stringify({
code,
redirect_uri: process.env.REDIRECT_URI,
grant_type: "authorization_code",
client_secret: process.env.CLIENT_SECRET,
client_id: process.env.CLIENT_ID,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST'
});
const { code: apiCode, data, msg } = await res.json();

if (apiCode === 0) {
const { access_token } = data;
// save access_token
token.access_token = access_token;
// redirect to balance page.
redirect('/balance');
}

}

notFound();
}
src/app/callback/route.ts

DotWalletSigner

sCrypt SDK provides DotWalletSigner for quick integration with DotWallet.

After redirect to the /balance page, we can create a DotWalletSigner with the OAuth access token, which is passed as the first argument.

import { DotwalletSigner, DefaultProvider } from "scrypt-ts";
import token from "../token";

async function getData() {
const provider = new DefaultProvider();
const signer = new DotwalletSigner(token.access_token, provider);

const balance = await signer.getBalance();

return { balance: balance.confirmed + balance.unconfirmed };
}

export default async function Balance() {
const data = await getData();

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="m-4 p-4 bg-blue-200 font-bold rounded-lg">
<label>balance</label> {data.balance}
</div>
</main>
);
}

src/app/balance/page.tsx

After creating DotWalletSigner with access token, you can call all interfaces of DotWalletSigner as in other signers. For example, the example uses the signer to check user's balance.

Congrats! You have completed the integration of DotWallet. Full code is here.


[1] client_secret is stored in the backend. It's used to exchange authorization code for access token.

- + \ No newline at end of file diff --git a/how-to-integrate-a-frontend/index.html b/how-to-integrate-a-frontend/index.html index b7316c8c5..e17f82af2 100644 --- a/how-to-integrate-a-frontend/index.html +++ b/how-to-integrate-a-frontend/index.html @@ -4,7 +4,7 @@ How to Integrate With a Frontend | sCrypt - + @@ -12,7 +12,7 @@

How to Integrate With a Frontend

This section will show how to integrate your smart contract to a frontend, so users can interact with it. We assume that you already have the basic knowledge of frontend development, so we will not spend much time introducing this part of the code, but mostly be focusing on how to interact with the smart contract in the front end project.

Setup

Step 1

Create your frontend project as usual.

React

Run the following command to create a React project named helloworld.

npx create-react-app helloworld --template typescript

We will do most work under the src directory.

Next.js

Run the following command to create a Next.js project.

npx create-next-app helloworld --typescript --use-npm

Vue.js

Vite

Run the following command to create a Vue 3.x project bundled with Vite.

npm create vue@3

If you'd like to use Vue 2.x, run the following command to initialize the project scaffold.

npm create vue@2

Webpack

Run the following command to create a Vue project bundled with Webpack.

npx @vue/cli create helloworld
tip

Vue 3.x and 2.x bundled with Webpack are both supported.

When setting up the project, select Manually select features and enable TypeScript.

Angular

Run the following command to create an Angular project.

npx @angular/cli new helloworld

Svelte

Run the following command to create a Svelte project.

npm create svelte@latest helloworld

note

Currently, we support frontend frameworks React, Next.js, Vue, Angular, and Svelte. We anticipate to add supports for other frameworks over time.

Step 2

Run the init command of the CLI to add sCrypt support in your project.

cd helloworld
npx scrypt-cli init

This installs all the dependencies and configures the contract development environment. After this, we are ready to go!

Load Contract

Before interacting with a smart contract at the front end, we need to load the contract class in two steps.

We'll take a look at how to generate the artifact by ourselves first.

1. Compile Contract

Before you start, you need to get the contract source files, as a frontend developer.

Let's use the Helloworld contract as an example. Copy and paste helloworld.ts into the src/contracts directory.

Run the following command to compile the contract.

npx scrypt-cli compile

After the compilation, you will get an JSON artifact file at artifacts/helloworld.json.

2. Load Artifact

Now with the contract artifact file, you directly load it in the index.tsx file.

import { Helloworld } from './contracts/helloworld';
import artifact from '../artifacts/helloworld.json';
Helloworld.loadArtifact(artifact);

Now you can create an instance from the contract class as before.

const message = toByteString('hello world', true)
const instance = new Helloworld(sha256(message))
info

You cannot simply call Helloworld.compile() at the front end, since it only works in NodeJS, not in browser.

Integrate Wallet

You will integrate Sensilet, a browser extension wallet similar to MetaMask, into the project.

info

You can refer to this guide to add support for other wallets.

To request access to the wallet, you can use its requestAuth method.

const provider = new DefaultProvider({
network: bsv.Networks.testnet
});

const signer = new SensiletSigner(provider);

// request authentication
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
// something went wrong, throw an Error with `error` message
throw new Error(error);
}

// authenticated
// you can show user's default address
const userAddress = await signer.getDefaultAddress();
// ...

Now you can connect the wallet to the contract instance as before.

await instance.connect(signer);

Afterwards, you can interact with the contract from the front end by calling its method as usual.

This repo contains a counter example, integrated with all supported frameworks.

Go to the sCrypt Academy to see a step-by-step guide on how to build a Tic-Tac-Toe game on chain.

- + \ No newline at end of file diff --git a/how-to-publish-a-contract/index.html b/how-to-publish-a-contract/index.html index 146678611..8deb4d3e9 100644 --- a/how-to-publish-a-contract/index.html +++ b/how-to-publish-a-contract/index.html @@ -4,13 +4,13 @@ How to Publish a Contract to NPM | sCrypt - +

How to Publish a Contract to NPM

What is a Smart Contract Library?

A smart contract library can provide methods which can be reused in many contracts. Developers can use existing libraries to reduce the cost of developing their own contracts.

A smart contract library is different from a smart contract in these ways:

  • A smart contract library can not have any public/entry @methods, which means a library can not be deployed or called directly through a tx. They can only be called within a smart contract or another library.

  • A smart contract library can not have any stateful properties, i.e. @prop(true) properties. But a property declared as @prop() is fine.

Write a Smart Contract Library

Using sCrypt we can create a smart contract library class like this:

class MyLib extends SmartContractLib {

@prop()
readonly buf: ByteString;

constructor(buf: ByteString) {
super(...arguments);
this.buf = buf;
}

@method()
append(content: ByteString) {
this.buf += content;
}

@method()
static add(x: bigint, y: bigint): bigint {
return x + y;
}

}

A smart contract library can be declared as a class that extends SmartContractLib. It may also have @props and @methods like smart contracts which have the same rules introduced before. A smart contract library can be used within @methods like this:

class MyContract extends SmartContract {
@method()
public unlock(x: ByteString) {
let myLib = new MyLib(hexToByteString('0123'));
myLib.append(x);
assert(MyLib.add(1n, 2n) === 3n, 'incorrect sum');
}
}

Test a Smart Contract Library

You can test your smart contract library as a normal class, for example, writing some unit tests:

describe('Test SmartContractLib `MyLib`', () => {
it('should pass unit test successfully.', () => {
expect(MyLib.add(1n, 2n)).to.eq(3n)
})
})

Also you can write a smart contract using the library, then have some tests for the contract, like:

class TestLib extends SmartContract {
@method
public unlock(x: bigint) {
assert(MyLib.add(1n, 2n) == x, 'incorrect sum')
}
}

describe('Test SmartContractLib `Lib`', () => {
before(async() => {
await TestLib.loadArtifact()
})

it('should pass integration test successfully.', () => {
let testLib = new TestLib()
let result = testLib.verify(self => self.unlock(3n))
expect(result.success, result.error).to.be.true
}
})

Create and Publish a Library Project Using sCrypt CLI

The following command will create a demo sCrypt library along with tests and scaffolding:

npx scrypt-cli project --lib <your-lib-name>

Note the lib option is turned on.

You can publish the library on NPM by running the following command in the project's root directory:

npm publish

This will build the project and publish it on NPM. After the library is published, users can simply import it in any other project just like regular NPM packages.

note

Named imports are not supported yet. You should only import like the following.

import { MyLib } from “my_package”

Advanced

For the import system working properly, you should always publish the auto-generated sCrypt contracts (including scrypt.index.json file) along with the javascript outputs. The structure of the package could be like this:

node_modules
|__ my_package
|__ dist
|__ myLib.js
|__ myLib.d.ts
|__ artifacts
|__ myLib.scrypt
|__ scrypt.index.json

The scrypt.index.json file will be generated at TypeScript compile time in the same directory of your tsconfig.json which should be placed in the root folder. It shall not be moved or modified manually. The folder for auto-generated .scrypt files (artifacts in the upper file tree) can be changed by configuring the outDir option in tsconfig.json, like:

"compilerOptions": {
"plugins": [
{
"transform": "scrypt-ts/dist/transformation/transformer",
"transformProgram": "true",
"outDir": "my_scrypts_dir"
}
]
}

You should always publish the auto-generated sCrypt files along with the package.

scrypt-ts-lib

It’s a collection of smart contract libraries provided by us. You can find some useful tools here. Also you are welcome to contribute.

- + \ No newline at end of file diff --git a/how-to-test-a-contract/index.html b/how-to-test-a-contract/index.html index 33ba3cb5c..d2e34a42e 100644 --- a/how-to-test-a-contract/index.html +++ b/how-to-test-a-contract/index.html @@ -4,13 +4,13 @@ How to Test a Contract | sCrypt - +

How to Test a Contract

Before using a smart contract in production, one should always test it carefully, especially because any bug in it may cause real economic losses.

Create a sample project with the sCrypt CLI Tool:

npx scrypt-cli project demo

This will create a complete sCrypt project, which includes a sample smart contract Demo:

import {
assert,
ByteString,
method,
prop,
sha256,
Sha256,
SmartContract,
} from 'scrypt-ts'

export class Demo extends SmartContract {
@prop()
hash: Sha256

constructor(hash: Sha256) {
super(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(sha256(message) == this.hash, 'Hash does not match')
}
}

Let us now open the file tests/demo.test.ts. This file contains code for deployment of our Demo contract on the Bitcoin testnet or local and a subsequent public method call on the contract.

Load Artifact

First, call function SmartContract.loadArtifact() to load the contract artifact file in order to initialize the contract class before testing.

Demo.loadArtifact()

Instantiate the Contract

Instantiate the contract and connect a signer.

instance = new Demo(sha256(toByteString('hello world', true)))
// connect a signer
await instance.connect(getDefaultSigner())

Contract Deployment

To deploy a smart contract, simply call its deploy() method:

const deployTx = await instance.deploy(1)
console.log('Demo contract deployed: ', deployTx.id)

Call a Public Method

You can call a contract's public @method on the blockchain as follows:

// build and send tx by calling `unlock()` on `methods` object.
await instance.methods.unlock(
toByteString('hello world', true)
)

Integrate with a testing framework

You can use whatever testing framework you like to write unit tests for your contract. For example, a test using Mocha is shown below:

describe('Test SmartContract `Demo`', () => {
let instance: Demo

before(async () => {
Demo.loadArtifact()
instance = new Demo(sha256(toByteString('hello world', true)))
await instance.connect(getDefaultSigner())
})

it('should pass the public method unit test successfully.', async () => {
await instance.deploy(1)

const call = async () => instance.methods.unlock(
toByteString('hello world', true)
)

await expect(call()).not.to.be.rejected
})

it('should throw with wrong message.', async () => {
await instance.deploy(1)

const call = async () => instance.methods.unlock(toByteString('wrong message', true))
await expect(call()).to.be.rejectedWith(/Hash does not match/)
})
})

Run tests

Compared to other blockchains, smart contracts on Bitcoin are pure.

  • Given the same input, its public method always returns the same boolean output: success or failure. It has no internal state.
  • A public method call causes no side effects.

Thus, you can run tests in two different environments:

  1. Local: Running tests locally without touching the Bitcoin blockchain. Transactions are constructed with dummy UTXOs. If it passes tests off chain, we are confident it will behave the same on chain.

Run tests in the local environment using the following command:

npm run test
  1. Testnet: Running tests on the testnet of Bitcoin blockchain. Transactions are constructed with real UTXOs on the testnet.

Run tests in the testnet environment using the following command:

npm run test:testnet
note

When running tests in a testnet environment, you need to get some test coins from a faucet.

Test a Stateful Contract

Stateful contact testing is very similar to what we have described above. The only different is that you have to be aware of smart contract instance changes after method calls.

As described in the Overview, for each method call, a tx contains new contract UTXO(s) with the latest updated state, i.e., the next instance. From the perspective of the current spending tx, the public @method of a contract instance is called in one of its inputs, and the next contract instance is stored in one (or more) of its outputs.

Now, let's look at how to test the incrementOnChain method call:

// initialize the first instance, i.e., deployment
let counter = new Counter(0n);
// connect it to a signer
await counter.connect(getDefaultSigner());
// deploy the contract
await counter.deploy(1)

// set the current instance to be the first instance
let current = counter;

// create the next instance from the current
let nextInstance = current.next();

// apply the same updates on the next instance locally
nextInstance.increment();

// call the method of current instance to apply the updates on chain
const call = async () => current.methods.incrementOnChain(
{
// the `next` instance and its balance should be provided here
next: {
instance: nextInstance,
balance
}
} as MethodCallOptions<Counter>
);

await expect(call()).not.to.be.rejected

In general, we call the method of a stateful contract in 3 steps:

1. Build the current instance

The current instance refers to the contract instance containing the latest state on the blockchain. The first instance is in the deployment transaction. In the above example, we initialize the current instance to be the first instance like this:

let current = counter;

2. Create a next instance and apply updates to it off chain

The next instance is the new instance in the UTXO of the method calling tx.

To create the next of a specific contract instance, you can simply call next() on it:

let nextInstance = instance.next();

It will make a deep copy of all properties and methods of instance to create a new one.

Then, you should apply all the state updates to the next instance. Please note that these are just local/off-chain updates and are yet to be applied to the blockchain.

nextInstance.increment();

This is the SAME method we call on chain in incrementOnChain, thanks to the fact that both the on-chain smart contract and off-chain code are written in TypeScript.

3. Call the method on the current instance to apply updates on chain

As described in this section, we can build a call transaction. The only difference here is that we pass in the next instance and its balance as a method call option in a stateful contract. So the method (i.e., incrementOnChain) have all the information to verify that all updates made to the next instance follow the state transition rules in it.

const call = async () => current.methods.incrementOnChain(
{
// the `next` instance and its balance should be provided here
next: {
instance: nextInstance,
balance
}
} as MethodCallOptions<Counter>
);
await expect(call()).not.to.be.rejected

Run tests

As before, we can just use the following command:

npm run test

or

npm run test:testnet
- + \ No newline at end of file diff --git a/how-to-verify-a-contract/index.html b/how-to-verify-a-contract/index.html index 43f4f3480..97b6f570e 100644 --- a/how-to-verify-a-contract/index.html +++ b/how-to-verify-a-contract/index.html @@ -4,7 +4,7 @@ How to Verify a Contract | sCrypt - + @@ -12,7 +12,7 @@

How to Verify a Contract

You will learn how to verify smart contracts on WhatsOnChain (WoC), a blockchain explorer. By verifying your smart contract on WoC, anyone can view its source code and interact with it confidently. Let's get started!

To start with the verification process, we need to first deploy a smart contract. Let us use the "Hello World" tutorial as an example. After you complete the tutorial, you should get the ID of the deployment transaction such as a34d4e45a9108b5b9da4faf4f086e9ef36b79466383bd7a22ff2c7f6a562546c.

If you take a look at the transaction on WoC, you'll see that the first output contains a script identified by the hash eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def, which contains your contract in script format.

This hash is referred to as the scriptHash. It's essentially just a sha256 hash value of the deployed contracts locking script, encoded in a little-endian hex format. It is commonly used as an index by block explorers. You can also get this value locally, via the contract instance's scriptHash property:

console.log(instance.scriptHash)
// eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def
note

The scriptHash value can vary due to factors like the current property values and the number of times the contract has been updated, leading to inconsistencies in its value.

You can submit and verify sCrypt source code that belongs to a specific script hash.

There are two ways to verify it.

1. Using WOC sCrypt Plugin

At the deployed transaction on WOC, click on the ScriptHash of the first output. It will open a page like this:

You shall see an sCrypt tab. Click on it. You'll see a very simple form:

In the form you are able to select the version of sCrypt you've used to compile and deploy the contract, along with a text-box in which you need to paste the source code.

Now click Submit. If the code is correct, you should see something like the following in a few seconds:

Congrats, you have verified your first smart contract!

Now, every time someone opens the sCrypt tab on the script hash page, they will see the verified smart contract source code, as well as its constructor parameters when deployed.

2. Using CLI

The same process can be done using the sCrypt CLI. You can verify the deployed smart contracts script using the verify command:

npx scrypt-cli verify <scriptHash> <contractPath>

The first positional argument is the script hash of the deployed contract and the second one is the path to the file which contains the sCrypt smart contract. Note, that the file must also include all the code it depends on, i.e. third party libraries.

Using the network option, you can specify on which network the contract is deployed. This defaults to test, indicating the Bitcoin testnet:

npx scrypt-cil verify --network main <scriptHash> <contractPath>

You can also specify the version of sCrypt used during verification. By default, the command will use the version specified in package.json:

npx scrypt-cli verify -V 0.2.0-beta.9 <scriptHash> <contractPath>

For example, if we would like to verify the same deployed contract as above, we would simply run the following:

npx scrypt-cli verify eb2f10b8f1bd12527f07a5d05b40f06137cbebe4e9ecfb6a4e0fd8a3437e1def src/contracts/demoproject.ts

Upon execution, the designated contract code undergoes verification on sCrypt's servers. If successful, the outcome will be displayed on WoC, under the "sCrypt" tab, just like above.

- + \ No newline at end of file diff --git a/how-to-write-a-contract/built-ins/index.html b/how-to-write-a-contract/built-ins/index.html index 9ca3544b0..080c2867c 100644 --- a/how-to-write-a-contract/built-ins/index.html +++ b/how-to-write-a-contract/built-ins/index.html @@ -4,13 +4,13 @@ Built-ins | sCrypt - +

Built-ins

Global Functions

The following functions come with sCrypt.

Assert

  • assert(condition: boolean, errorMsg?: string) Throw an Error with the optional error message if condition is false. Otherwise, nothing happens.
assert(1n === 1n)        // nothing happens
assert(1n === 2n) // throws Error('Execution failed')
assert(false, 'hello') // throws Error('Execution failed, hello')

Fill

  • fill(value: T, length: number): T[length] Returns an FixedArray with all size elements set to value, where value can be any type.
note

length must be a compiled-time constant.

// good
fill(1n, 3) // numeric literal 3
fill(1n, M) // const M = 3
fill(1n, Demo.N) // `N` is a static readonly property of class `Demo`

Math

  • abs(a: bigint): bigint Returns the absolute value of a.
abs(1n)  // 1n
abs(0n) // 0n
abs(-1n) // 1n
  • min(a: bigint, b: bigint): bigint Returns the smallest of a and b.
min(1n, 2n) // 1n
  • max(a: bigint, b: bigint): bigint Returns the lagest of a and b.
max(1n, 2n) // 2n
  • within(x: bigint, min: bigint, max: bigint): boolean Returns true if x is within the specified range (left-inclusive and right-exclusive), false otherwise.
within(0n, 0n, 2n) // true
within(1n, 0n, 2n) // true
within(2n, 0n, 2n) // false

Hashing

  • ripemd160(a: ByteString): Ripemd160 Returns the RIPEMD160 hash result of a.
  • sha1(a: ByteString): Sha1 Returns the SHA1 hash result of a.
  • sha256(a: ByteString): Sha256 Returns the SHA256 hash result of a.
  • hash160(a: ByteString): Ripemd160 Actually returns ripemd160(sha256(a)).
  • pubKey2Addr(pk: PubKey): Addr Wrapper function of hash160.
  • hash256(a: ByteString): Sha256 Actually returns sha256(sha256(a)).

ByteString Operations

  • int2ByteString(n: bigint, size?: bigint): ByteString If size is omitted, convert n is converted to a ByteString in sign-magnitude little endian format, with as few bytes as possible (a.k.a., minimally encoded). Otherwise, converts the number n to a ByteString of the specified size, including the sign bit; fails if the number cannot be accommodated.
// as few bytes as possible
int2ByteString(128n) // '8000', little endian
int2ByteString(127n) // '7f'
int2ByteString(0n) // ''
int2ByteString(-1n) // '81'
int2ByteString(-129n) // '8180', little endian

// specified size
int2ByteString(1n, 3n) // '010000', 3 bytes
int2ByteString(-129n, 3n) // '810080', 3 bytes

// Error: -129 cannot fit in 1 byte
int2ByteString(-129n, 1n)
  • byteString2Int(a: ByteString): bigint Convert ByteString in sign-magnitude little endian format to bigint.
byteString2Int(toByteString('8000'))    // 128n
byteString2Int(toByteString('')) // 0n
byteString2Int(toByteString('00')) // 0n
byteString2Int(toByteString('81')) // -1n

byteString2Int(toByteString('010000')) // 1n
byteString2Int(toByteString('810080')) // -129n
  • len(a: ByteString): number Returns the byte length of a.
const s1 = toByteString('0011', false) // '0011', 2 bytes
len(s1) // 2

const s2 = toByteString('hello', true) // '68656c6c6f', 5 bytes
len(s2) // 5
  • reverseByteString(b: ByteString, size: number): ByteString Returns reversed bytes of b which is of size bytes. It is often useful when converting a number between little-endian and big-endian.
note

size must be a compiled-time constant.

const s1 = toByteString('793ff39de7e1dce2d853e24256099d25fa1b1598ee24069f24511d7a2deafe6c') 
reverseByteString(s1, 32) // 6cfeea2d7a1d51249f0624ee98151bfa259d095642e253d8e2dce1e79df33f79
  • slice(byteString: ByteString, start: BigInt, end?: BigInt): ByteString return a sub-byte string from start to, but not including, end. If end is not specified, the sub-byte string continues to the last byte.
const message = toByteString('001122')
slice(message, 1n) // '1122'
slice(message, 1n, 2n) // '11'

Bitwise Operator

Bigint in the Bitcoin is stored in sign–magnitude format, not two's complement format commonly used. If the operands are all nonnegative, the result of the operation is consistent with TypeScript's bitwise operator, except ~. Otherwise, the operation results may be inconsistent and thus undefined. It is strongly recommended to NEVER apply bitwise operations on negative numbers.

  • and(x: bigint, y: bigint): bigint Bitwise AND
and(13n, 5n) // 5n
and(0x0a32c845n, 0x149f72n) // 0x00108840n, 1083456n
  • or(x: bigint, y: bigint): bigint Bitwise OR
or(13n, 5n) // 13n
or(0x0a32c845n, 0x149f72n) // 0xa36df77n, 171368311n
  • xor(x: bigint, y: bigint): bigint Bitwise XOR
xor(13n, 5n) // 8n
xor(0x0a32c845n, 0x149f72n) // 0x0a265737n, 170284855n
  • invert(x: bigint): bigint Bitwise NOT
invert(13n)  // -114n
  • lshift(x: bigint, n: bigint): bigint Arithmetic left shift, returns x * 2^n.
lshift(2n, 3n)   // 16n
  • rshift(x: bigint, n: bigint): bigint Arithmetic right shift, returns x / 2^n.
rshift(21n, 3n)    // 2n
rshift(1024n, 11n) // 0n

Exit

  • exit(status: boolean): void Calling this function will terminate contract execution. If status is true then the contract succeeds; otherwise, it fails.

SmartContract Methods

The following @methods come with the SmartContract base class.

compile

Function static async compile(): Promise<TranspileError[]> compiles the contract and returns transpile errors if compiling fails.

// returns transpile errors if compiling fails
const transpileErrors = await Demo.compile()

scriptSize

Function get scriptSize(): number returns the byte length of the contract locking script.

const demo = new Demo()
const size = demo.scriptSize

loadArtifact

Function static loadArtifact(artifactFile: Artifact | string | undefined = undefined) loads the contract artifact file from the path you passed in to initialize the contract class.

If no parameter is passed when calling, the function will load the artifact file from the default directory. This is generally used during testing.

You can also pass the artifact path directly. This is usually used when the method is called when interacting with a contract at the front end.

import { TicTacToe } from './contracts/tictactoe';
import artifact from '../artifacts/tictactoe.json';
TicTacToe.loadArtifact(artifact);

checkSig

Function checkSig(signature: Sig, publicKey: PubKey): boolean verifies an ECDSA signature. It takes two inputs: an ECDSA signature and a public key.

It returns if the signature matches the public key.

caution

All signature checking functions (checkSig and checkMultiSig) follow the NULLFAIL rule: if the signature is invalid, the entire contract aborts and fails immediately, unless the signature is an empty ByteString, in which case these functions return false.

For example, Pay-to-Public-Key-Hash (P2PKH) can be implemented as below.

class P2PKH extends SmartContract {
// Address of the recipient.
@prop()
readonly address: Addr

constructor(address: Addr) {
super(...arguments)
this.address = address
}

@method()
public unlock(sig: Sig, pubkey: PubKey) {
// Check if the passed public key belongs to the specified public key hash.
assert(pubKey2Addr(pubkey) == this.pubKeyHash, 'address does not correspond to address')
// Check signature validity.
assert(this.checkSig(sig, pubkey), 'signature check failed')
}
}

checkMultiSig

Function checkMultiSig(signatures: Sig[], publickeys: PubKey[]): boolean verifies an array of ECDSA signatures. It takes two inputs: an array of ECDSA signatures and an array of public keys.

The function compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the signatures array using the same order as their corresponding public keys were placed in the publickeys array. If all signatures are valid, true is returned, false otherwise.

class MultiSigPayment extends SmartContract {
// Addresses of the 3 recipients.
@prop()
readonly addresses: FixedArray<Addr, 3>

constructor(addresses: FixedArray<Addr, 3>) {
super(...arguments)
this.addresses = addresses
}

@method()
public unlock(
signatures: FixedArray<Sig, 3>,
publicKeys: FixedArray<PubKey, 3>
) {
// Check if the passed public keys belong to the specified addresses.
for (let i = 0; i < 3; i++) {
assert(pubKey2Addr(publicKeys[i]) == this.addresses[i], 'address mismatch')
}
// Validate signatures.
assert(this.checkMultiSig(signatures, publicKeys), 'checkMultiSig failed')
}
}

buildStateOutput

Function buildStateOutput(amount: bigint): ByteString creates an output containing the latest state. It takes an input: the number of satoshis in the output.

class Counter extends SmartContract {
// ...

@method(SigHash.ANYONECANPAY_SINGLE)
public incOnChain() {
// ... update state

// construct the new state output
const output: ByteString = this.buildStateOutput(this.ctx.utxo.value)

// ... verify outputs of current tx
}
}

buildChangeOutput

Function buildChangeOutput(): ByteString creates a P2PKH change output. It will calculate the change amount (this.changeAmount) automatically, and use the signer's address by default, unless changeAddress field is explicitly set in MethodCallOptions.

class Auction extends SmartContract {

// ...

@method()
public bid(bidder: Addr, bid: bigint) {

// Addr

// Auction continues with a higher bidder.
const auctionOutput: ByteString = this.buildStateOutput(bid)

// Refund previous highest bidder.
const refundOutput: ByteString = Utils.buildPublicKeyHashOutput(
highestBidder,
highestBid
)
let outputs: ByteString = auctionOutput + refundOutput

// Add change output.
outputs += this.buildChangeOutput()

assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs check failed')
}
}

Call Auction contract with a custom change address.


const { tx: callTx, atInputIndex } = await auction.methods.bid(
Addr(addressNewBidder.toByteString()),
BigInt(balance + 1),
{
changeAddress: addressNewBidder, // specify the change address of method calling tx explicitly
} as MethodCallOptions<Auction>
)
note

this.changeAmount and this.buildChangeOutput can be accessed directly when using the default call tx builder, but if you use use a customized call tx builder, you need to explicitly set the transaction change output in the builder beforehand.

const unsignedTx: bsv.Transaction = new bsv.Transaction()
// add inputs and outputs
// ...
// add change output explicitly
// otherwise you cannot call `this.changeAmount` and `this.buildChangeOutput` in the contract
.change(options.changeAddress);

timeLock

Function timeLock(locktime: bigint): boolean returns whether the calling transaction has its nLocktime value set to a point past the passed locktime value. This value can either be a UNIX timestamp or a block height. Additionally, it ensures the value of nSequence is set to less than 0xFFFFFFFF.

If we assert the returned value to be true, we have effectively ensured that the public method of our smart contract cannot be successfully invoked until the specified time has passed.

class TimeLock extends SmartContract {

@prop()
locktime: bigint

// ...

@method()
public unlock() {
assert(this.timeLock(this.locktime), 'time lock not yet expired')
}

}
note

This mechanism can be employed solely to ensure that a method can be called after a specific point in time. In contrast, it cannot be employed to ensure that a method is called before a specific point in time.

To learn more about time locks, see the dedicated doc section.

insertCodeSeparator

Method insertCodeSeparator(): void inserts an OP_CODESEPARATOR, where it is invoked.

export class CodeSeparator extends SmartContract {

@prop()
readonly addresses: FixedArray<Addr, 3>;

constructor(addresses: FixedArray<Addr, 3>) {
super(...arguments);
this.addresses = addresses;
}

@method()
public unlock(sigs: FixedArray<Sig, 3>, pubKeys: FixedArray<PubKey, 3>) {
assert(pubKey2Addr(pubKeys[0]) == this.addresses[0]);
this.insertCodeSeparator()
assert(this.checkSig(sigs[0], pubKeys[0]));

this.insertCodeSeparator()
assert(pubKey2Addr(pubKeys[1]) == this.addresses[1]);
assert(this.checkSig(sigs[1], pubKeys[1]));

this.insertCodeSeparator()
assert(pubKey2Addr(pubKeys[2]) == this.addresses[2]);
assert(this.checkSig(sigs[2], pubKeys[2]));
}

}

fromTx

Function static fromTx(tx: bsv.Transaction, atOutputIndex: number, offchainValues?: Record<string, any>) creates an instance with its state synchronized to a given transaction output, identified by tx the transaction and atOutputIndex the output index. It is needed to create an up-to-date instance of a contract.

// create an instance from a transaction output
const instance = ContractName.fromTx(tx, atOutputIndex)

// we're good here, the `instance` is state synchronized with the on-chain transaction

If the contract contains @prop's of type HashedMap or HashedSet, the values of all these properties at this transaction must be passed in the third argument.

// e.g. the contract has two stateful properties of type `HashedMap` or `HashedSet`
// @prop(true) mySet: HashedSet<bigint>
// @prop() myMap: HashedMap<bigint, bigint>
const instance = ContractName.fromTx(tx, atOutputIndex, {
// pass the values of all these properties at the transaction moment
'mySet': currentSet,
'myMap': currentMap,
})

buildDeployTransaction

Function async buildDeployTransaction(utxos: UTXO[], amount: number, changeAddress?: bsv.Address | string): Promise<bsv.Transaction> creates a tx to deploy the contract. The first parameter utxos represents one or more P2PKH inputs for paying transaction fees. The second parameter amount is the balance of contract output. The last parameter changeAddress is optional and represents a change address. Users override it to cutomize a deployment tx as below.

override async buildDeployTransaction(utxos: UTXO[], amount: number, changeAddress?: bsv.Address | string): Promise<bsv.Transaction> {
const deployTx = new bsv.Transaction()
// add p2pkh inputs for paying tx fees
.from(utxos)
// add contract output
.addOutput(new bsv.Transaction.Output({
script: this.lockingScript,
satoshis: amount,
}))
// add the change output if passing `changeAddress`
if (changeAddress) {
deployTx.change(changeAddress);
if (this._provider) {
deployTx.feePerKb(await this.provider.getFeePerKb());
}
}

return deployTx;
}

bindTxBuilder

Function bindTxBuilder(methodName: string, txBuilder: MethodCallTxBuilder<SmartContract>):void binds the customized transaction builder MethodCallTxBuilder, which returns a ContractTransation, to a contract public @method identified by methodName.


/**
* A transaction builder.
* The default transaction builder only supports fixed-format call transactions.
* Some complex contracts require a custom transaction builder to successfully call the contract.
*/
export interface MethodCallTxBuilder<T extends SmartContract> {
(current: T, options: MethodCallOptions<T>, ...args: any): Promise<ContractTransaction>
}


// bind a customized tx builder for the public method `instance.unlock()`
instance.bindTxBuilder("unlock", (options: MethodCallOptions<T>, ...args: any) => {
// ...
})

You may visit here to see more details on how to customize tx builder.

multiContractCall

When the @methods of multiple contracts is called in a transaction, the transaction builders for each contract collectively construct the ContractTransation. Function static async multiContractCall(partialContractTx: ContractTransaction, signer: Signer): Promise<MultiContractTransaction> signs and broadcasts the final transaction.

const partialContractTx1 = await counter1.methods.incrementOnChain(
{
multiContractCall: true,
} as MethodCallOptions<Counter>
)

const partialContractTx2 = await counter2.methods.incrementOnChain(
{
multiContractCall: true,
partialContractTx: partialContractTx1
} as MethodCallOptions<Counter>
);

const {tx: callTx, nexts} = await SmartContract.multiContractCall(partialContractTx2, signer)


console.log('Counter contract counter1, counter2 called: ', callTx.id)

Standard Libraries

sCrypt comes with standard libraries that define many commonly used functions.

Utils

The Utils library provides a set of commonly used utility functions.

  • static toLEUnsigned(n: bigint, l: bigint): ByteString Convert the signed integer n to an unsigned integer of l bytes, in sign-magnitude little endian format.
Utils.toLEUnsigned(10n, 3n)   // '0a0000'
Utils.toLEUnsigned(-10n, 2n) // '0a00'
  • static fromLEUnsigned(bytes: ByteString): bigint Convert ByteString to unsigned integer.
Utils.fromLEUnsigned(toByteString('0a00'))  // 10n
Utils.fromLEUnsigned(toByteString('8a')) // 138n, actually converts 8a00 to unsigned integer
  • static readVarint(buf: ByteString): ByteString Read a VarInt field from buf.
Utils.readVarint(toByteString('0401020304')) // '01020304'
  • static writeVarint(buf: ByteString): ByteString Convert buf to a VarInt field, including the preceding length.
Utils.writeVarint(toByteString('010203')) // '03010203'
  • static buildOutput(outputScript: ByteString, outputSatoshis: bigint): ByteString Build a transaction output with the specified script and satoshi amount.
const lockingScript = toByteString('01020304')
Utils.buildOutput(lockingScript, 1n) // '01000000000000000401020304'
const address = Addr(toByteString('0011223344556677889900112233445566778899'))
Utils.buildPublicKeyHashScript(address) // '76a914001122334455667788990011223344556677889988ac'
  • static buildPublicKeyHashOutput(pubKeyHash: PubKeyHash, amount: bigint): ByteString Build a P2PKH output from the public key hash.
const address = Addr(toByteString('0011223344556677889900112233445566778899'))
Utils.buildPublicKeyHashOutput(address, 1n) // '01000000000000001976a914001122334455667788990011223344556677889988ac'
  • static buildOpreturnScript(data: ByteString): ByteString Build a data-carrying FALSE OP_RETURN script from data payload.
const data = toByteString('hello world', true)
Utils.buildOpreturnScript(data) // '006a0b68656c6c6f20776f726c64'

HashedMap

HashedMap provides a map/hashtable-like data structure. It is different to use HashedMap in on-chain and off-chain context.

On-chain

The main difference between HashedMap and other data types we’ve previously introduced is that it does NOT store raw data (i.e., keys and values) in the contract on the blockchain. It stores their hashed values instead, to minimize on-chain storage, which is expensive.

These guidelines must be followed when using HashedMap in a contract @method, i.e., on-chain context.

  • Only the following methods can be called.

    • set(key: K, val: V): HashedMap: Adds a new element with a specified key and value. If an element with the same key already exists, the element will be updated.
    • canGet(key: K, val: V): boolean: Returns true if the specified key and value pair exists, otherwise returns false.
    • has(key: K): boolean: Returns true if the specified key exists, otherwise returns false.
    • delete(key: K): boolean: Returns true if a key exists and has been removed, otherwise returns false.
    • clear(): void: Remove all key and value pairs.
    • size: number: Returns the number of elements.
note

get() is not listed, since the value itself is not stored and thus must be passed in and verified using canGet().

  • The aforementioned methods can only be used in public @methods, NOT in non-public @methods, including constructors.

  • HashedMap can be used as an @prop, either stateful or not:

@prop() map: HashedMap<KeyType, ValueType>; // valid
@prop(true) map: HashedMap<KeyType, ValueType> // also valid
  • It CANNOT be used as a @method parameter, regardless of public or not:
@method public unlock(map: HashedMap<KeyType, ValueType>) // invalid as a parameter type
@method foo(map: HashedMap<KeyType, ValueType>) // invalid as a parameter type
  • No nesting is allowed currently. That is, key and value cannot contain a HashedMap.
type Map1 = HashedMap<KeyType1, ValueType1>
HashedMap<KeyType2, Map1> // invalid
HashedMap<Map1, ValueType2> // invalid

type KeyType = {
key1: KeyType1
key2: KeyType2
}
HashedMap<KeyType, ValueType> // valid

A full example may look like this:

class MyContract extends SmartContract {
@prop(true)
myMap: HashedMap<bigint, bigint>;

// HashedMap can be a parameter in constructor
constructor(map: HashedMap<bigint, bigint>) {
// assignment is ok, but not calling method
this.myMap = map;
}

@method()
public unlock(key: bigint, val: bigint) {
this.myMap.set(key, val);
assert(this.myMap.has(key));
assert(this.myMap.canGet(key, val));
assert(this.myMap.delete(key));
assert(!this.myMap.has(key));
}
}

Off-chain

HashedMap acts just like the JavaScript/TypeScript Map when used in off-chain code (that is, not in a contract's @method). For example, you can create an instance like this:

// create an empty map
let hashedMap = new HashedMap<bigint, ByteString>();

// create from (key,value) pairs
let hashedMap1 = new HashedMap([['key1', 'value1'], ['key2', 'value2']]);

Also, you can call its functions like this:

hashedMap.set(key, value);
const v = hashedMap.get(key); // <----
hashedMap.has(key);
hashedMap.delete(key);
...
note

get() can be called since the HashedMap stores the original key and value off chain.

Only when the key is an object is HashedMap different from Map. HashedMap will treat two keys the same if they have the same values, while Map will only if they reference the same object. For instance:

interface ST {
a: bigint;
}

let map = new Map<ST, bigint>();
map.set({a: 1n}, 1n);
map.set({a: 1n}, 2n);
console.log(map.size); // output ‘2’ cause two keys {a: 1n} reference differently
console.log(map.get({a: 1n})); // output ‘undefined’


let hashedMap = new HashedMap<ST, bigint>();
hashedMap.set({a: 1n}, 1n);
hashedMap.set({a: 1n}, 2n);
console.log(hashedMap.size); // output ‘1’
console.log(hashedMap.get({a: 1n})); // output ‘2n’

HashedSet

HashedSet library provides a set-like data structure. It can be regarded as a special HashedMap where a value is the same with its key and is thus omitted. Values are hashed before being stored in contracts on the blockchain, as in HashedMap.

On-chain

When used in public @methods, HashedSet also has almost all of the same restrictions as HashedMap. Except for the methods on its own whitelist that can be called in @methods as following:

  • add(value: T): HashedSet: Inserts a new element with a specified value in to a set, if there isn't an element with the same value already in the set.

  • has(value: T): boolean: Returns true if an element with the specified value exists in the set, otherwise returns false.

  • delete(value: T): boolean: Returns true if an element in the Set existed and has been removed, or false if the element does not exist.

  • clear(): void: Delete all entries of the set.

  • size: number: Returns the size of set, i.e. the number of the entries it contains.

Off-chain

HashedSet can be used the same as a JavaScript Set in off-chain code .

let hashedSet = new HashedSet<bigint>()
hashedSet.add(1n);
hashedSet.has(1n);
hashedSet.delete(1n);
...

Similar to HashedMap, HashedSet will treat two objects as identical if their values equal, rather than requiring that they reference to the same object.

interface ST {
a: bigint;
}

let set = new Set<ST>();
set.add({a: 1n});
set.add({a: 1n});
console.log(set.size); // output ‘2’
console.log(set.has({a: 1n})); // output ‘false’


let hashedSet = new HashedSet<ST, bigint>();
hashedSet.add({a: 1n});
hashedSet.add({a: 1n});
console.log(hashedSet.size); // output ‘1’
console.log(hashedSet.has({a: 1n})); // output ‘true’

Constants

Constants defines some commonly used constant values.

class Constants {
// number of string to denote input sequence
static readonly InputSeqLen: bigint = BigInt(4);
// number of string to denote output value
static readonly OutputValueLen: bigint = BigInt(8);
// number of string to denote a public key (compressed)
static readonly PubKeyLen: bigint = BigInt(33);
// number of string to denote a public key hash
static readonly PubKeyHashLen: bigint = BigInt(20);
// number of string to denote a tx id
static readonly TxIdLen: bigint = BigInt(32);
// number of string to denote a outpoint
static readonly OutpointLen: bigint = BigInt(36);
}
- + \ No newline at end of file diff --git a/how-to-write-a-contract/index.html b/how-to-write-a-contract/index.html index f33e0ae1f..edf2b5db2 100644 --- a/how-to-write-a-contract/index.html +++ b/how-to-write-a-contract/index.html @@ -4,7 +4,7 @@ How to Write a Contract | sCrypt - + @@ -12,7 +12,7 @@

How to Write a Contract

A smart contract is a class that extends the SmartContract base class. A simple example is shown below.

import { SmartContract, method, prop, assert } from "scrypt-ts"

class Equations extends SmartContract {

@prop()
sum: bigint

@prop()
diff: bigint

constructor(sum: bigint, diff: bigint) {
super(...arguments)
this.sum = sum
this.diff = diff
}

@method()
public unlock(x: bigint, y: bigint) {
assert(x + y == this.sum, 'incorrect sum')
assert(x - y == this.diff, 'incorrect diff')
}

}

The smart contract above requires solving for two equations with unknown variables, x and y.

Class members decorated with @prop and @method will end up on the blockchain and thus must be a strict subset of TypeScript. Everywhere decorated with them can be regarded in the on-chain context. Members decorated with neither are regular TypeScript and are kept off chain. The significant benefit of sCrypt is that both on-chain and off-chain code are written in the same language: TypeScript.

note

You can use the sCrypt template Repl and play with the code in your browser!

Properties

A smart contract can have two kinds of properties:

  1. With @prop decorator: these properties are only allowed to have types specified below and they shall only be initialized in the constructor.

  2. Without @prop decorator: these properties are regular TypeScript properties without any special requirement, meaning they can use any types. Accessing these properties is prohibited in methods decorated with the @method decorator.

@prop decorator

Use this decorator to mark any property that intends to be stored on chain.

This decorator takes a boolean parameter. By default, it is set to false, meaning the property cannot be changed after the contract is deployed. If the value is true, the property is a so-called stateful property and its value can be updated in subsequent contract calls.

// good, `a` is stored on chain, and it's readonly after the contract is deployed
@prop()
readonly a: bigint

// valid, but not good enough, `a` cannot be changed after the contract is deployed
@prop()
a: bigint

// good, `b` is stored on chain, and its value can be updated in subsequent contract calls
@prop(true)
b: bigint

// invalid, `b` is a stateful property that cannot be readonly
@prop(true)
readonly b: bigint

// good
@prop()
static c: bigint = 1n

// invalid, static property must be initialized when declared
@prop()
static c: bigint

// invalid, stateful property cannot be static
@prop(true)
static c: bigint = 1n

// good, `UINT_MAX` is a compile-time constant, and no need to typed explicitly
static readonly UINT_MAX = 0xffffffffn

// valid, but not good enough, `@prop()` is not necessary for the CTC
@prop()
static readonly UINT_MAX = 0xffffffffn

// invalid
@prop(true)
static readonly UINT_MAX = 0xffffffffn

Constructor

A smart contract must have an explicit constructor if it has at least one @prop that is not static.

The super method must be called in the constructor and all the arguments of the constructor should be passed to super in the same order as they are passed into the constructor. For example,

class A extends SmartContract {
readonly p0: bigint

@prop()
readonly p1: bigint

@prop()
readonly p2: boolean

constructor(p0: bigint, p1: bigint, p2: boolean) {
super(...arguments) // same as super(p0, p1, p2)
this.p0 = p0
this.p1 = p1
this.p2 = p2
}
}

arguments is an array containing the values of the arguments passed to that function. ... is the spread syntax.

Methods

Like properties, a smart contract can also have two kinds of methods:

  1. With @method decorator: these methods can only call methods also decorated by @method or functions specified below. Also, only the properties decorated by @prop can be accessed.

  2. Without @method decorator: these methods are just regular TypeScript class methods.

@method decorator

  1. Use this decorator to mark any method that intends to run on chain.
  2. It takes a sighash flag as a parameter.

Public @methods

Each contract must have at least one public @method. It is denoted with the public modifier and does not return any value. It is visible outside the contract and acts as the main method into the contract (like main in C and Java).

A public @method can be called from an external transaction. The call succeeds if it runs to completion without violating any conditions in assert(). An example is shown below.

@method()
public unlock(x: bigint) {
// only succeeds if x is 1
assert(this.add(this.x, 1n) == x, "unequal")
}
note

In addition to the following special cases, the last statement of a public @method method must be an assert() statement.

  1. last statement is a console.log(); statement, and the console.log(); statement is preceded by an assert() statement.
  2. last statement is for statement, and the last statement in the loop body is assert() statement.
  3. last statement is if-else statement, and the last statement of each conditional branch is assert() statement.
class PublicMethodDemo extends SmartContract {


@method()
public foo() {
// valid, last statement is `assert();` statement
assert(true);
}

@method()
public foo() {
// valid, `console.log` calling will be ignored when verifying the last `assert` statement
assert(true); //
console.log();
console.log();
}

@method()
public foo() {
// valid, last statement is `for` statement
for (let index = 0; index < 3; index++) {
assert(true);
}
}

@method()
public foo(z: bigint) {
// valid, last statement is `if-else` statement
if(z > 3n) {
assert(true)
} else {
assert(true)
}
}

@method()
public foo() {
// invalid, the last statement of public method should be an `assert` function call
}

@method()
public foo() {
assert(true);
return 1n; // invalid, because a public method cannot return any value
}

@method()
public foo() {
// invalid, the last statement in the for statement body doesn't end with `assert()` statement.
for (let index = 0; index < 3; index++) {
assert(true);
z + 3n;
}
}

@method()
public foo() {
// invalid, not each conditional branch end with `assert()` statement.
if(z > 3n) {
assert(true)
} else {

}
}

@method()
public foo() {
// invalid, not each conditional branch end with `assert()` statement.
if(z > 3n) {
assert(true)
}
}
}

Non-public @methods

Without a public modifier, a @method is internal and cannot be directly called from an external transaction.

@method()
add(x0: bigint, x1:bigint) : bigint {
return x0 + x1
}
note

Recursion is disallowed. A @method, public and not, cannot call itself, either directly in its own body or indirectly calls another method that transitively calls itself.

class MethodsDemo extends SmartContract {
@prop()
readonly x: bigint;
@prop()
readonly y: bigint;

constructor(x: bigint, y: bigint) {
super(...arguments);
this.x = x;
this.y = y;
}

// good, non-public static method without access `@prop` properties
@method()
static sum(a: bigint, b: bigint): bigint {
return a + b;
}

// good, non-public method
@method()
xyDiff(): bigint {
return this.x - this.y
}

// good, public method
@method()
public add(z: bigint) {
// good, call `sum` with the class name
assert(z == MethodsDemo.sum(this.x, this.y), 'add check failed');
}

// good, another public method
@method()
public sub(z: bigint) {
// good, call `xyDiff` with the class instance
assert(z == this.xyDiff(), 'sub check failed');
}

// valid but bad, public static method
@method()
public static alwaysPass() {
assert(true)
}
}

Data Types

Types used in @prop and @method are restricted to these kinds:

Basic Types

boolean

A simple value true or false.

let isDone: boolean = false

bigint

bigint can represent arbitrarily large integers. A bigint literal is a number with suffix n:

11n
0x33FEn
const previouslyMaxSafeInteger = 9007199254740991n
const alsoHuge = BigInt(9007199254740991)
// 9007199254740991n
const hugeHex: bigint = BigInt("0x1fffffffffffff")
// 9007199254740991n

ByteString

In a smart contract context (i.e., in @methods or @props), a ByteString represents a byte array.

A literal string can be converted in to a ByteString using function toByteString(literal: string, isUtf8: boolean = false): ByteString:

  • If not passing isUtf8 or isUtf8 is false, then literal should be in the format of hex literal, which can be represented by the regular expression: /^([0-9a-fA-F]{2})*$/
  • Otherwise, literal should be in the format of utf8 literal, e.g., hello world.
note

toByteString ONLY accepts string literals for its first argument, and boolean literals for the second.

let a = toByteString('0011') // valid, `0011` is a valid hex literal
// 0011
let b = toByteString('hello world', true) // valid
// 68656c6c6f20776f726c64

toByteString('0011', false) // valid
// 30303131

toByteString(b, true) // invalid, not passing string literal to the 1st parameter

toByteString('001') // invalid, `001` is not a valid hex literal
toByteString('hello', false) // invalid, `hello` is not a valid hex literal

toByteString('hello', 1 === 1) // invalid, not passing boolean literal to the 2nd parameter

let c = true
toByteString('world', c) // invalid, not passing boolean literal to the 2nd parameter

ByteString has the following operators and methods:

  • == / ===: compare

  • +: concatenate

const str0 = toByteString('01ab23ef68')
const str1 = toByteString('656c6c6f20776f726c64')

// comparison
str0 == str1
str0 === str1
// false

// concatenation
str0 + str1
// '01ab23ef68656c6c6f20776f726c64'

number

Type number is not allowed in @props and @methods, except in the following cases. We can use Number() function to convert bigint to number.

  • Array index
let arr: FixedArray<bigint, 3> = [1n, 3n, 3n]
let idx: bigint = 2n
let item = arr[Number(idx)]
  • Loop variable
for (let i: number = 0 i < 10 i++) {
let j: bigint = BigInt(i) // convert number to bigint
}

It can also be used in defining compile-time constants.

Fixed Size Array

All arrays must be of fixed size and be declared as type of FixedArray<T, SIZE>, whose SIZE must be a CTC described later. The common TypeScript arrays declared as T[] or Array<T> are not allowed in @props and @methods, as they are of dynamic size.

let aaa: FixedArray<bigint, 3> = [1n, 3n, 3n]

// set to all 0s
const N = 20
let aab: FixedArray<bigint, N> = fill(0n, N)

// 2-dimensional array
let abb: FixedArray<FixedArray<bigint, 2>, 3> = [[1n, 3n], [1n, 3n], [1n, 3n]]
caution

A FixedArray behaves differently in an on-chain and off-chain context, when passed as a function argument. It is passed by reference off chain, as a regular TypeScript/JavaScript array, while passed by value on chain. It is thus strongly recommended to NEVER mutate a FixedArray parameter's element inside a function.

class DemoContract extends SmartContract {

@prop(true)
readonly a: FixedArray<bigint, 3>

constructor(a: FixedArray<bigint, 3>) {
super(...arguments)
this.a = a
}

@method()
onchainChange(a: FixedArray<bigint, 3>) {
a[0] = 0
}

offchainChange(a: FixedArray<bigint, 3>) {
a[0] = 0
}

@method()
public main(a: FixedArray<bigint, 3>) {
this.onchainChange(this.a)
// note: a[0] is not changed on chain
assert(this.a[0] == 1n)
}
}

const arrayA: FixedArray<bigint, 3> = [1n, 2n, 3n]
const instance = new DemoContract(arrayA);

instance.offchainChange(arrayA)
// note: arrayA[0] is changed off chain
assert(arrayA[0] = 0n)

User-defined Types

Users can best define customized types using type or interface, made of basic types.1

type ST = {
a: bigint
b: boolean
}

interface ST1 {
x: ST
y: ByteString
}

type Point = {
x: number
y: number
}

function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x)
console.log("The coordinate's y value is " + pt.y)
}

interface Point2 {
x: number
y: number
}

// Exactly the same as the earlier example
function printCoord(pt: Point2) {
console.log("The coordinate's x value is " + pt.x)
console.log("The coordinate's y value is " + pt.y)
}

Domain Types

There are several domain types, specific to the Bitcoin context, used to further improve type safety. They are all subtypes of ByteString. That is, they can be used where a ByteString is expected, but not vice versa.

  • PubKey - a public key

  • Sig - a signature type in DER format, including sighash flags at the end

  • Ripemd160 - a RIPEMD-160 hash

  • Addr - an alias for Ripemd160, usually representing a bitcoin address.

  • PubKeyHash - another alias for Ripemd160

  • Sha1 - a SHA-1 hash

  • Sha256 - a SHA-256 hash

  • SigHashType - a sighash

  • SigHashPreimage - a sighash preimage

  • OpCodeType - a Script opcode

@method()
public unlock(sig: Sig, pubkey: PubKey) {
// The pubKey2Addr() function takes a 'pubkey', which is of type PubKey.
assert(pubKey2Addr(pubkey) == this.pubKeyHash)
assert(this.checkSig(sig, pubkey), 'signature check failed')
}

Import Types

All types can be imported from scrypt-ts package:

import {
ByteString,
Pubkey,
FixedArray,
Sig,
Addr
} from 'scrypt-ts'

This may not work when isolatedModules is enabled. At this time you need to use Type-Only Imports:

import type {
ByteString,
FixedArray
} from 'scrypt-ts'

Statements

There are some constraints on these following statements within @methods, except variable declarations.

Variable declarations

Variables can be declared in @methods by keywords const / var / let, like in normal TypeScript.

let a : bigint = 1n
var b: boolean = false
const byte: ByteString = toByteString("ff")

for

Bitcoin does not allow unbounded loops for security reasons, to prevent DoS attacks. All loops must be bounded at compile time. So if you want to loop inside @method, you must strictly use the following format:

for (let $i = 0; $i < $maxLoopCount; $i++) {
...
}
note
  • the initial value must be 0 or 0n, the operator < (no <=), and increment $i++ (no pre-increment ++$i).
  • $maxLoopCount must be a CTC or a CTC expression, for example:
const N = 4

// valid, `N` is a CTC
for (let i = 0; i < N; i++) { ... }

// valid, `2 * N - 1` is a CTC expression
for (let i = 0; i < 2 * N - 1; i++) { ... }

const M = N + 1

// valid, `M` is a CTC expression
for (let i = 0; i < M; i++) { ... }
  • $i can be arbitrary name, e.g., i, j, or k. It can be both a number or a bigint type.
  • break and continue are currently not allowed, but can be emulated like
// emulate break
let x = 3n
let done = false
for (let i = 0; i < 3; i++) {
if (!done) {
x = x * 2n
if (x >= 8n) {
done = true
}
}
}

return

Due to the lack of native return semantics support in Bitcoin Script, a function currently must end with a return statement and it is the only valid place for a return statement. This requirement may be relaxed in the future.

@method() m(x: bigint): bigint {
if (x > 2n) return x // invalid
return x + 1n // valid
}

This is usually not a problem since it can be circumvented as follows:

@method()
abs(a: bigint): bigint {
if (a > 0) {
return a
} else {
return -a
}
}

can be rewritten as

@method()
abs(a: bigint): bigint {
let ret : bigint = 0

if (a > 0) {
ret = a
} else {
ret = -a
}
return ret
}

Compile-time Constant

A compile-time constant, CTC for short, is a special variable whose value can be determined at compile time. A CTC must be defined in one of the following ways.

  • A number literal like:
3
  • A const variable, whose value must be a numeric literal. Expressions cannot be used for now.
const N1 = 3 // valid
const N2: number = 3 // invalid, no explicit type `number` allowed
const N3 = 3 + 3 // invalid, no expression allowed
  • A static readonly property:
class X {
static readonly M1 = 3 // valid
static readonly M2: number = 3 // invalid
static readonly M3 = 3 + 3 // invalid
}

A CTC is required in these cases.

  • Array size
let arr1: FixedArray<bigint, 3> = [1n, 2n, 3n]
// `typeof` is needed since FixedArray takes a type as the array size, not a value
let arr1: FixedArray<bigint, typeof N1> = [1n, 2n, 3n]
let arr2: FixedArray<bigint, typeof X.M1> = [1n, 2n, 3n]
  • Loop count in for statement
for(let i=0; i< 3; i++) {}
for(let i=0; i< N1; i++) {}
for(let i=0; i< X.M1; i++) {}

Functions

Built-in Functions

You can refer to Built-ins for a full list of functions and libraries built into sCrypt.

Whitelisted Functions

By default, all Javascript/TypeScript built-in functions and global variables are not allowed in @methods, except the following kinds.

console.log

console.log can be used for debugging purposes.

@method()
add(x0: bigint, x1:bigint) : bigint {
console.log(x0)
return x0 + x1
}

Operators

sCrypt is a subset of TypeScript. Only the following operators can be used directly.

OperatorDescription
+Addition
-Subtraction
*Multiplication
/Division
%Remainder
++Increment
--Decrement
==Equal to
!=Not equal to
===Same as ==
!==Same as !=
>Greater than
>=Greater than or equal to
<Less than
<=Less than or equal to
&&Logical AND
||Logical OR
!Logical NOT
cond ? expr1 : expr2 ternary
+=Add and assign
-=Subtract and assign
*=Multiply and assign
/=Divide and assign
%=Assign remainder
note

** is not supported currently.


  1. A user-defined type is also passed by value on chain, and by reference off chain, same as a FixedArray. It is thus strongly recommended to NEVER mutate the field of a parameter, which is of a user-defined type, inside a function.
- + \ No newline at end of file diff --git a/how-to-write-a-contract/scriptcontext/index.html b/how-to-write-a-contract/scriptcontext/index.html index 16b8c1dc3..919e9105f 100644 --- a/how-to-write-a-contract/scriptcontext/index.html +++ b/how-to-write-a-contract/scriptcontext/index.html @@ -4,7 +4,7 @@ ScriptContext | sCrypt - + @@ -15,7 +15,7 @@ It defaults to SigHash.ALL, including all inputs and outputs. You can customize it by setting the argument of the @method() decorator, e.g.,

@method(SigHash.ANYONECANPAY_SINGLE)
public increment() {
...
}

There are a total of 6 sigHash types to choose from:

SigHash TypeFunctional Meaning
ALLSign all inputs and outputs
NONESign all inputs and no output
SINGLESign all inputs and the output with the same index
ANYONECANPAY_ALLSign its own input and all outputs
ANYONECANPAY_NONESign its own input and no output
ANYONECANPAY_SINGLESign its own input and the output with the same index

For more information, please refer to this detailed guide.

Serialization

You have the option to convert this.ctx into a SigHashPreimage object through serialization. This can be achieved by invoking the this.ctx.serialize() method. The output object adheres to the format utilized during the signing or verification of transactions.

Format: Source

A noteworthy application of a serialized preimage can be found in the creation of custom SigHash flags. An example is the SIGHASH_ANYPREVOUT, which showcases this process.

Debugging

See How to Debug ScriptContext Failure

- + \ No newline at end of file diff --git a/how-to-write-a-contract/stateful-contract/index.html b/how-to-write-a-contract/stateful-contract/index.html index 229321dc6..73a818d07 100644 --- a/how-to-write-a-contract/stateful-contract/index.html +++ b/how-to-write-a-contract/stateful-contract/index.html @@ -4,7 +4,7 @@ Stateful Contracts | sCrypt - + @@ -15,7 +15,7 @@ This is similar to making HTTP seem stateful by using cookies.

So far, all the contracts we’ve gone through have been stateless. But often, you may want a contract to have some concept of “memory” so that it may remember information about its previous interactions. That is, we need contracts that are stateful.

To achieve that, we divide a smart contract in the locking script of an output into two parts: code and state as shown below. The code part contains the business logic of a contract that encodes rules for state transition and must NOT change. State transition occurs when a transaction spends the output containing the old state and creates a new output containing the new state, while keeping the contract code intact. Since the new output contains the same contract code, its spending transaction must also retain the same code, otherwise it will fail. This chain of transactions can go on and on and thus a state is maintained along the chain, recursively.

Create a Stateful Contract

We can create a stateful contract using the following command:

npx scrypt-cli project --state counter

Note the state option is turned on.

This will create a project containing a sample stateful contract named Counter. This contract maintains a single state: how many times it has been called since deployment.

Let's take a look at the contract source file src/contracts/counter.ts.

Stateful properties

As shown before, a @prop(true) decorator is used to make a property part of the contract stateful, meaning it can be mutated when the contract gets called.

@prop(true)
count: bigint

Update states

The incrementOnChain() method does two things:

  1. Call increment to update the state:
@method()
increment(): void {
this.count++
}
  1. Validate the new state goes into the next UTXO containing the same contract, i.e., the state is maintained.
// make sure balance in the contract does not change
const amount: bigint = this.ctx.utxo.value
// outputs containing the latest state and an optional change output
const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput()
// verify unlocking tx has the same outputs
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')

The built-in function this.buildStateOutput() creates an output containing the latest state. It takes an input: the number of satoshis in the output. We keep the satoshis unchanged in the example. The built-in functin this.buildChangeOutput() creates a P2PKH change output when necessary. It will calculate the change amount automatically, and use the signer's address by default.

If all outputs we create in the contract hashes to hashOutputs in ScriptContext, we can be sure they are the outputs of the current transaction. Therefore, the updated state is propagated.

The complete stateful contract is as follows:

export class Counter extends SmartContract {
// stateful
@prop(true)
count: bigint

constructor(count: bigint) {
super(...arguments)
this.count = count
}

@method()
public incrementOnChain() {
this.increment()

// make sure balance in the contract does not change
const amount: bigint = this.ctx.utxo.value
// outputs containing the latest state and an optional change output
const outputs: ByteString = this.buildStateOutput(amount) + this.buildChangeOutput()
// verify unlocking tx has the same outputs
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')
}

@method()
increment(): void {
this.count++
}
}

Stateless vs Stateful Contracts

The choice between stateless and stateful smart contracts hinges on the needs of your blockchain application.

If your app needs to store persistent data on chain, a stateful smart contract is appropriate. For example, with an auction app, you want to store the highest bidder so far and how much she bids, in case you need to return the fund to her when a higher bid arrives.

If your app merely validates spending conditions without retaining data, a stateless smart contract is desirable. An example is a simple transfer using signature and public key in a P2PKH contract.

- + \ No newline at end of file diff --git a/index.html b/index.html index 54e025623..0d7f7919b 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Overview | sCrypt - + @@ -15,7 +15,7 @@ The locking script can be regarded as a boolean function f that specifies conditions to spend the bitcoins in the UTXO, acting as a lock (thus the name "locking"). The unlocking script in turns provides the function arguments that makes f evaluates to true, i.e., the "key" (also called witness) needed to unlock. Only when the “key” in an input matches previous output’s “lock”, it can spend bitcoins contained in the output.

In a regular Bitcoin payment to a Bitcoin address, the locking script is Pay To Pubkey Hash (P2PKH). It checks the spender has the right private key corresponding to the address so she can produce a valid signature in the unlocking script. The expressive Script enables the locking script to specify arbitrarily more complex spending conditions than simple P2PKH, i.e., Bitcoin smart contracts.

How does sCrypt work?

sCrypt is a high-level language to be compiled into Bitcoin Script. The resulting assembly-like scripts could be used as locking scripts when building transactions.

Learn sCrypt

Jump over to the Installation section to learn how to create an sCrypt project.

tip

You can also follow this Youtube series.

Join the #scrypt channel on Bitcoin SV Discord or Slack to ask questions.

- + \ No newline at end of file diff --git a/installation/index.html b/installation/index.html index e3a43ea87..bb69e5039 100644 --- a/installation/index.html +++ b/installation/index.html @@ -4,13 +4,13 @@ Installation | sCrypt - +

Installation

Prerequisite

  1. Install Node.js and NPM on your machine by following the instructions over here.
note

Require Node.js version >=16.

  1. Install Git.

  2. Next, install the scrypt cli

npm install -g scrypt-cli

This will install the scrypt cli globally to your machine, after which you can create scrypt projects.

The sCrypt CLI Tool

The sCrypt CLI tool is used to easily create, compile and publish sCrypt projects. The CLI provides best practice project scaffolding including dependencies such as sCrypt, a test framework (Mocha), code auto-formatting (Prettier), linting (ES Lint), & more.

We can run the CLI tool directly with npx and try it out by creating a demo project:

npx scrypt-cli project demo
tip

You can also simply fork the demo contract Repl and play with the code in your browser.

- + \ No newline at end of file diff --git a/overview/index.html b/overview/index.html index 8717b7124..1b076e8a9 100644 --- a/overview/index.html +++ b/overview/index.html @@ -4,13 +4,13 @@ sCrypt - +
- + \ No newline at end of file diff --git a/reference/classes/ActionError/index.html b/reference/classes/ActionError/index.html index f915de8bc..7bf956382 100644 --- a/reference/classes/ActionError/index.html +++ b/reference/classes/ActionError/index.html @@ -4,13 +4,13 @@ ActionError | sCrypt - +

ActionError

scrypt-ts / ActionError

Class: ActionError

Hierarchy

  • Error

    ActionError

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new ActionError(statusCode, message)

Parameters

NameType
statusCodenumber
messagestring

Overrides

Error.constructor

Defined in

dist/client/core/action-resolver.d.ts:7

Properties

message

message: string

Overrides

Error.message

Defined in

dist/client/core/action-resolver.d.ts:6


name

name: string

Inherited from

Error.name

Defined in

node_modules/typescript/lib/lib.es5.d.ts:1053


stack

Optional stack: string

Inherited from

Error.stack

Defined in

node_modules/typescript/lib/lib.es5.d.ts:1055


statusCode

statusCode: number

Defined in

dist/client/core/action-resolver.d.ts:5


prepareStackTrace

Static Optional prepareStackTrace: (err: Error, stackTraces: CallSite[]) => any

Type declaration

▸ (err, stackTraces): any

Optional override for formatting stack traces

See

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

Parameters
NameType
errError
stackTracesCallSite[]
Returns

any

Inherited from

Error.prepareStackTrace

Defined in

node_modules/@types/node/globals.d.ts:11


stackTraceLimit

Static stackTraceLimit: number

Inherited from

Error.stackTraceLimit

Defined in

node_modules/@types/node/globals.d.ts:13

Methods

captureStackTrace

Static captureStackTrace(targetObject, constructorOpt?): void

Create .stack property on a target object

Parameters

NameType
targetObjectobject
constructorOpt?Function

Returns

void

Inherited from

Error.captureStackTrace

Defined in

node_modules/@types/node/globals.d.ts:4

- + \ No newline at end of file diff --git a/reference/classes/BsvApi/index.html b/reference/classes/BsvApi/index.html index 0d5e70fde..4d5c84389 100644 --- a/reference/classes/BsvApi/index.html +++ b/reference/classes/BsvApi/index.html @@ -4,13 +4,13 @@ BsvApi | sCrypt - +

BsvApi

scrypt-ts / BsvApi

Class: BsvApi

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new BsvApi(_core)

Parameters

NameType
_coreCore

Defined in

dist/client/apis/bsv-api.d.ts:7

Properties

_core

Private Readonly _core: any

Defined in

dist/client/apis/bsv-api.d.ts:6

Methods

connect

connect(): Promise<{ error: string ; success: boolean }>

Returns

Promise<{ error: string ; success: boolean }>

Defined in

dist/client/apis/bsv-api.d.ts:14


getBalance

getBalance(address): Promise<{ confirmed: number ; unconfirmed: number }>

Parameters

NameType
addressAddress

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

Defined in

dist/client/apis/bsv-api.d.ts:9


getFeePerKB

getFeePerKB(): Promise<number>

Returns

Promise<number>

Defined in

dist/client/apis/bsv-api.d.ts:13


getTransaction

getTransaction(txId): Promise<Transaction>

Parameters

NameType
txIdstring

Returns

Promise<Transaction>

Defined in

dist/client/apis/bsv-api.d.ts:8


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Parameters

NameType
addressAddress
options?UtxoQueryOptions

Returns

Promise<IUnspentOutput[]>

Defined in

dist/client/apis/bsv-api.d.ts:19


sendRawTransaction

sendRawTransaction(txHex): Promise<string>

Parameters

NameType
txHexstring

Returns

Promise<string>

Defined in

dist/client/apis/bsv-api.d.ts:18

- + \ No newline at end of file diff --git a/reference/classes/Constants/index.html b/reference/classes/Constants/index.html index 93de78675..acab8293f 100644 --- a/reference/classes/Constants/index.html +++ b/reference/classes/Constants/index.html @@ -4,13 +4,13 @@ Constants | sCrypt - +

Constants

scrypt-ts / Constants

Class: Constants

A library than contains some commonly used constant values

Table of contents

Constructors

Properties

Constructors

constructor

new Constants()

Properties

InputSeqLen

Static Readonly InputSeqLen: bigint

length of ByteString to denote input sequence

Defined in

dist/smart-contract/builtins/functions.d.ts:1168


OutpointLen

Static Readonly OutpointLen: bigint

length of ByteString to denote a outpoint

Defined in

dist/smart-contract/builtins/functions.d.ts:1178


OutputValueLen

Static Readonly OutputValueLen: bigint

length of ByteString to denote output value

Defined in

dist/smart-contract/builtins/functions.d.ts:1170


PubKeyHashLen

Static Readonly PubKeyHashLen: bigint

length of ByteString to denote a public key hash

Defined in

dist/smart-contract/builtins/functions.d.ts:1174


PubKeyLen

Static Readonly PubKeyLen: bigint

length of ByteString to denote a public key (compressed)

Defined in

dist/smart-contract/builtins/functions.d.ts:1172


TxIdLen

Static Readonly TxIdLen: bigint

length of ByteString to denote a tx id

Defined in

dist/smart-contract/builtins/functions.d.ts:1176

- + \ No newline at end of file diff --git a/reference/classes/ContractApi/index.html b/reference/classes/ContractApi/index.html index e3ae1bdda..0d155d69f 100644 --- a/reference/classes/ContractApi/index.html +++ b/reference/classes/ContractApi/index.html @@ -4,7 +4,7 @@ ContractApi | sCrypt - + @@ -14,7 +14,7 @@ If other users call this contract instance. Then the contract instance will be invalid. At this time, calling the contract will cause a txn-mempool-conflict error (that is, UTXO double spending). If this error occurs, you need to re-acquire the contract instance

Type parameters

NameType
Textends SmartContract<T>

Parameters

NameType
clazz(...args: any) => T
contractIdContractId

Returns

Promise<T>

a contract instance contains latest state

Defined in

dist/client/apis/contract-api.d.ts:49


subscribe

subscribe<T>(options, cb): SubScription

Subscribe to notifications of contract status changes by contract ID,

Type parameters

NameType
Textends SmartContract<T>

Parameters

NameTypeDescription
optionsSubscribeOptions<T>SubscribeOptions
cb(e: ContractCalledEvent<T>) => void

Returns

SubScription

a SubScription, which can be used to unsubscribe

Defined in

dist/client/apis/contract-api.d.ts:56

- + \ No newline at end of file diff --git a/reference/classes/DefaultProvider/index.html b/reference/classes/DefaultProvider/index.html index 3409c640b..ac6552dfc 100644 --- a/reference/classes/DefaultProvider/index.html +++ b/reference/classes/DefaultProvider/index.html @@ -4,7 +4,7 @@ DefaultProvider | sCrypt - + @@ -60,7 +60,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/DotwalletSigner/index.html b/reference/classes/DotwalletSigner/index.html index d7ed3fdb9..cc7a7c084 100644 --- a/reference/classes/DotwalletSigner/index.html +++ b/reference/classes/DotwalletSigner/index.html @@ -4,7 +4,7 @@ DotwalletSigner | sCrypt - + @@ -14,7 +14,7 @@ a connection will be established for the new provider and then switched to the new provider. If no new provider is specified, a connection is established for signer's built-in provider. If neither exists, an exception is thrown.

Parameters

NameTypeDescription
provider?ProviderThe target provider.

Returns

Promise<DotwalletSigner>

Overrides

Signer.connect

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:32


getBalance

getBalance(address?): Promise<{ confirmed: number ; unconfirmed: number }>

Get the balance of BSVs in satoshis for an address.

Parameters

NameTypeDescription
address?AddressThe query address.

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

A promise which resolves to the address balance status.

Overrides

Signer.getBalance

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:35


getDefaultAddress

getDefaultAddress(): Promise<Address>

Returns

Promise<Address>

A promise which resolves to the address to the default private key of the signer.

Overrides

Signer.getDefaultAddress

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:33


getDefaultPubKey

getDefaultPubKey(): Promise<PublicKey>

Returns

Promise<PublicKey>

A promise which resolves to the public key of the default private key of the signer.

Overrides

Signer.getDefaultPubKey

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:39


getNetwork

getNetwork(): Promise<Network>

Returns

Promise<Network>

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:34


getPubKey

getPubKey(address): Promise<PublicKey>

Throws

If the private key for the address does not belong this signer.

Parameters

NameTypeDescription
addressAddressThe request address, using the default address if omitted.

Returns

Promise<PublicKey>

The public key result.

Overrides

Signer.getPubKey

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:40


getSignatures

getSignatures(rawTxHex, sigRequests): Promise<SignatureResponse[]>

Get the requested transaction signatures for the raw transaction.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to get signatures from.
sigRequestsSignatureRequest[]The signature requst informations, see details in SignatureRequest.

Returns

Promise<SignatureResponse[]>

A promise which resolves to a list of SignatureReponse corresponding to sigRequests.

Overrides

Signer.getSignatures

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:44


isAuthenticated

isAuthenticated(): Promise<boolean>

Check if the wallet has been authenticated

Returns

Promise<boolean>

true | false

Overrides

Signer.isAuthenticated

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:23


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Get a list of the P2PKH UTXOs.

Parameters

NameTypeDescription
addressAddressThe address of the returned UTXOs belongs to.
options?UtxoQueryOptionsThe optional query conditions, see details in UtxoQueryOptions.

Returns

Promise<IUnspentOutput[]>

A promise which resolves to a list of UTXO for the query options.

Overrides

Signer.listUnspent

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:51


requestAuth

requestAuth(): Promise<{ error: string ; isAuthenticated: boolean }>

Request wallet authentication

Returns

Promise<{ error: string ; isAuthenticated: boolean }>

A promise which resolves to if the wallet has been authenticated and the authenticate error message

Overrides

Signer.requestAuth

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:28


signAndsendTransaction

signAndsendTransaction(tx, options?): Promise<TransactionResponse>

Sign transaction and broadcast it

Parameters

NameTypeDescription
txTransactionA transaction is signed and broadcast
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<TransactionResponse>

A promise which resolves to the transaction id.

Inherited from

Signer.signAndsendTransaction

Defined in

dist/bsv/abstract-signer.d.ts:140


signMessage

signMessage(message, address?): Promise<string>

Sign a message string.

Parameters

NameTypeDescription
messagestringThe message to be signed.
address?AddressThe optional address whose private key will be used to sign message, using the default private key if omitted.

Returns

Promise<string>

A promise which resolves to the signautre of the message.

Overrides

Signer.signMessage

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:43


signRawTransaction

signRawTransaction(rawTxHex, options): Promise<string>

Sign a raw transaction hex string.

Throws

If any input of the transaction can not be signed properly.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to sign.
optionsSignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<string>

A promise which resolves to the signed transaction hex string.

Overrides

Signer.signRawTransaction

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:41


signTransaction

signTransaction(tx, options?): Promise<Transaction>

Sign a transaction object.

Parameters

NameTypeDescription
txTransactionThe transaction object to sign.
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<Transaction>

A promise which resolves to the signed transaction object.

Overrides

Signer.signTransaction

Defined in

dist/bsv/signers/dotwallet-signer.d.ts:42


isSigner

Static isSigner(value): value is Signer

Check if an object is a Signer

Parameters

NameTypeDescription
valueanyThe target object

Returns

value is Signer

Returns true if and only if object is a Provider.

Inherited from

Signer.isSigner

Defined in

dist/bsv/abstract-signer.d.ts:162

- + \ No newline at end of file diff --git a/reference/classes/DummyProvider/index.html b/reference/classes/DummyProvider/index.html index 11b1da1f3..952805303 100644 --- a/reference/classes/DummyProvider/index.html +++ b/reference/classes/DummyProvider/index.html @@ -4,7 +4,7 @@ DummyProvider | sCrypt - + @@ -59,7 +59,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/FunctionCall/index.html b/reference/classes/FunctionCall/index.html index 86cd2eddf..554f4acc5 100644 --- a/reference/classes/FunctionCall/index.html +++ b/reference/classes/FunctionCall/index.html @@ -4,13 +4,13 @@ FunctionCall | sCrypt - +

FunctionCall

scrypt-ts / FunctionCall

Class: FunctionCall

Table of contents

Constructors

Properties

Accessors

Methods

Constructors

constructor

new FunctionCall(methodName, binding)

Parameters

NameType
methodNamestring
bindingObject
binding.argsArguments
binding.contractAbstractContract
binding.lockingScript?Script
binding.unlockingScript?Script

Defined in

node_modules/scryptlib/dist/abi.d.ts:41

Properties

_lockingScript

Private Optional _lockingScript: any

Defined in

node_modules/scryptlib/dist/abi.d.ts:37


_unlockingScript

Private Optional _unlockingScript: any

Defined in

node_modules/scryptlib/dist/abi.d.ts:36


args

Readonly args: Arguments

Defined in

node_modules/scryptlib/dist/abi.d.ts:35


contract

Readonly contract: AbstractContract

Defined in

node_modules/scryptlib/dist/abi.d.ts:34


methodName

methodName: string

Defined in

node_modules/scryptlib/dist/abi.d.ts:33

Accessors

lockingScript

get lockingScript(): Script

Returns

Script

Defined in

node_modules/scryptlib/dist/abi.d.ts:39

set lockingScript(s): void

Parameters

NameType
sScript

Returns

void

Defined in

node_modules/scryptlib/dist/abi.d.ts:40


unlockingScript

get unlockingScript(): Script

Returns

Script

Defined in

node_modules/scryptlib/dist/abi.d.ts:38

Methods

genLaunchConfig

genLaunchConfig(txContext?): string

Parameters

NameType
txContext?TxContext

Returns

string

Defined in

node_modules/scryptlib/dist/abi.d.ts:51


toASM

toASM(): string

Returns

string

Defined in

node_modules/scryptlib/dist/abi.d.ts:47


toHex

toHex(): string

Returns

string

Defined in

node_modules/scryptlib/dist/abi.d.ts:50


toScript

toScript(): Script

Returns

Script

Defined in

node_modules/scryptlib/dist/abi.d.ts:49


toString

toString(): string

Returns

string

Defined in

node_modules/scryptlib/dist/abi.d.ts:48


verify

verify(txContext?): VerifyResult

Parameters

NameType
txContext?TxContext

Returns

VerifyResult

Defined in

node_modules/scryptlib/dist/abi.d.ts:52

- + \ No newline at end of file diff --git a/reference/classes/GorillapoolProvider/index.html b/reference/classes/GorillapoolProvider/index.html index 65b776beb..880688ab1 100644 --- a/reference/classes/GorillapoolProvider/index.html +++ b/reference/classes/GorillapoolProvider/index.html @@ -4,7 +4,7 @@ GorillapoolProvider | sCrypt - + @@ -59,7 +59,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/HashedMap/index.html b/reference/classes/HashedMap/index.html index fc6eb5239..6a9f08907 100644 --- a/reference/classes/HashedMap/index.html +++ b/reference/classes/HashedMap/index.html @@ -4,7 +4,7 @@ HashedMap | sCrypt - + @@ -20,7 +20,7 @@ Can be called in the @method function of a contract

Parameters

NameType
keyK

Returns

boolean

true if the HashedMap has the specified key in it, otherwise returns false.

Overrides

Map.has

Defined in

dist/smart-contract/builtins/hashed-map.d.ts:49


set

set(key, value): HashedMap<K, V>

Insert or update a (key, val) pair to the HashedMap. If an element with the same key already exists, the element will be updated. Can be called in the @method function of a contract

Parameters

NameTypeDescription
keyKkey
valueVvalue

Returns

HashedMap<K, V>

this

Overrides

Map.set

Defined in

dist/smart-contract/builtins/hashed-map.d.ts:36

- + \ No newline at end of file diff --git a/reference/classes/HashedSet/index.html b/reference/classes/HashedSet/index.html index c801a7b54..36f5435d5 100644 --- a/reference/classes/HashedSet/index.html +++ b/reference/classes/HashedSet/index.html @@ -4,7 +4,7 @@ HashedSet | sCrypt - + @@ -19,7 +19,7 @@ Can be called in the @method function of a contract

Returns

Bytes

Defined in

dist/smart-contract/builtins/hashed-set.d.ts:54


delete

delete(value): boolean

Remove a element with a specified value from the Set. Can be called in the @method function of a contract

Parameters

NameTypeDescription
valueTvalue of a element

Returns

boolean

true if an element in the Set existed and has been removed, or false if the element does not exist.

Overrides

Set.delete

Defined in

dist/smart-contract/builtins/hashed-set.d.ts:40


entries

entries(): IterableIterator<[T, T]>

Returns an iterable of [v,v] pairs for every value v in the set.

Returns

IterableIterator<[T, T]>

Inherited from

Set.entries

Defined in

node_modules/typescript/lib/lib.es2015.iterable.d.ts:176


forEach

forEach(callbackfn, thisArg?): void

Executes a provided function once per each value in the Set object, in insertion order.

Parameters

NameType
callbackfn(value: T, value2: T, set: Set<T>) => void
thisArg?any

Returns

void

Inherited from

Set.forEach

Defined in

node_modules/typescript/lib/lib.es2015.collection.d.ts:107


has

has(value): boolean

Check whether element exists in the set Can be called in the @method function of a contract

Parameters

NameTypeDescription
valueTvalue of a element

Returns

boolean

true if an element with the specified value exists in the Set, otherwise returns false.

Overrides

Set.has

Defined in

dist/smart-contract/builtins/hashed-set.d.ts:47


keys

keys(): IterableIterator<T>

Despite its name, returns an iterable of the values in the set.

Returns

IterableIterator<T>

Inherited from

Set.keys

Defined in

node_modules/typescript/lib/lib.es2015.iterable.d.ts:180


values

values(): IterableIterator<T>

Returns an iterable of values in the set.

Returns

IterableIterator<T>

Inherited from

Set.values

Defined in

node_modules/typescript/lib/lib.es2015.iterable.d.ts:185

- + \ No newline at end of file diff --git a/reference/classes/OpCode/index.html b/reference/classes/OpCode/index.html index 6c92c58fe..75ff365f2 100644 --- a/reference/classes/OpCode/index.html +++ b/reference/classes/OpCode/index.html @@ -4,7 +4,7 @@ OpCode | sCrypt - + @@ -17,7 +17,7 @@ If ELSE is NOT used, the script jumps to ENDIF. The top stack value is removed.

Deprecated

Name

OP_IF

Constant

OpCodeType('63')

Example

`[expression] IF
[statement 1]
ENDIF`
OR
`[expression] IF
[statement 1]
ELSE
[statement 2]
ENDIF`

Defined in

dist/smart-contract/builtins/functions.d.ts:340


OP_IFDUP

Static Readonly OP_IFDUP: OpCodeType

If the top stack value is not 0, duplicate it.

Name

OP_IFDUP

Constant

OpCodeType('73')

Defined in

dist/smart-contract/builtins/functions.d.ts:462


OP_INVALIDOPCODE

Static Readonly OP_INVALIDOPCODE: OpCodeType

Matches any opcode that is not yet assigned. The word is used internally for assisting with transaction matching. They are invalid if used in actual scripts.

Name

OP_PUBKEY

Constant

OpCodeType('ff')

Defined in

dist/smart-contract/builtins/functions.d.ts:902


OP_INVERT

Static Readonly OP_INVERT: OpCodeType

Flips all of the bits in the input.

Name

OP_INVERT

Constant

OpCodeType('83')

Defined in

dist/smart-contract/builtins/functions.d.ts:558


OP_LESSTHAN

Static Readonly OP_LESSTHAN: OpCodeType

Returns 1 if a is less than b, 0 otherwise.

Name

OP_LESSTHAN

Constant

OpCodeType('9f')

Defined in

dist/smart-contract/builtins/functions.d.ts:726


OP_LESSTHANOREQUAL

Static Readonly OP_LESSTHANOREQUAL: OpCodeType

Returns 1 if a is less than or equal to b, 0 otherwise.

Name

OP_LESSTHANOREQUAL

Constant

OpCodeType('a1')

Defined in

dist/smart-contract/builtins/functions.d.ts:738


OP_LSHIFT

Static Readonly OP_LSHIFT: OpCodeType

Logical left shift b bits. Sign data is discarded

Name

OP_LSHIFT

Constant

OpCodeType('98')

Defined in

dist/smart-contract/builtins/functions.d.ts:684


OP_MAX

Static Readonly OP_MAX: OpCodeType

Returns the larger of a and b.

Name

OP_MAX

Constant

OpCodeType('a4')

Defined in

dist/smart-contract/builtins/functions.d.ts:756


OP_MIN

Static Readonly OP_MIN: OpCodeType

Returns the smaller of a and b.

Name

OP_MIN

Constant

OpCodeType('a3')

Defined in

dist/smart-contract/builtins/functions.d.ts:750


OP_MOD

Static Readonly OP_MOD: OpCodeType

Returns the remainder after dividing a by b.

Name

OP_MOD

Constant

OpCodeType('97')

Defined in

dist/smart-contract/builtins/functions.d.ts:678


OP_MUL

Static Readonly OP_MUL: OpCodeType

a is multiplied by b.

Name

OP_MUL

Constant

OpCodeType('95')

Defined in

dist/smart-contract/builtins/functions.d.ts:666


OP_NEGATE

Static Readonly OP_NEGATE: OpCodeType

The sign of the input is flipped.

Name

OP_NEGATE

Constant

OpCodeType('8f')

Defined in

dist/smart-contract/builtins/functions.d.ts:630


OP_NIP

Static Readonly OP_NIP: OpCodeType

Removes the second-to-top stack item.

Name

OP_NIP

Constant

OpCodeType('77')

Defined in

dist/smart-contract/builtins/functions.d.ts:486


OP_NOP

Static Readonly OP_NOP: OpCodeType

Does nothing.

Name

OP_NOP

Constant

OpCodeType('61')

Defined in

dist/smart-contract/builtins/functions.d.ts:314


OP_NOP1

Static Readonly OP_NOP1: OpCodeType

No operation. The word is ignored.

Name

OP_NOP1

Constant

OpCodeType('b0')

Defined in

dist/smart-contract/builtins/functions.d.ts:830


OP_NOP10

Static Readonly OP_NOP10: OpCodeType

No operation. The word is ignored.

Name

OP_NOP10

Constant

OpCodeType('b9')

Defined in

dist/smart-contract/builtins/functions.d.ts:884


OP_NOP2

Static Readonly OP_NOP2: OpCodeType

No operation. The word is ignored. (previously OP_CHECKLOCKTIMEVERIFY)

Name

OP_NOP2

Constant

OpCodeType('b1')

Defined in

dist/smart-contract/builtins/functions.d.ts:836


OP_NOP3

Static Readonly OP_NOP3: OpCodeType

No operation. The word is ignored. (previously OP_CHECKSEQUENCEVERIFY)

Name

OP_NOP3

Constant

OpCodeType('b2')

Defined in

dist/smart-contract/builtins/functions.d.ts:842


OP_NOP4

Static Readonly OP_NOP4: OpCodeType

No operation. The word is ignored.

Name

OP_NOP4

Constant

OpCodeType('b3')

Defined in

dist/smart-contract/builtins/functions.d.ts:848


OP_NOP5

Static Readonly OP_NOP5: OpCodeType

No operation. The word is ignored.

Name

OP_NOP5

Constant

OpCodeType('b4')

Defined in

dist/smart-contract/builtins/functions.d.ts:854


OP_NOP6

Static Readonly OP_NOP6: OpCodeType

No operation. The word is ignored.

Name

OP_NOP6

Constant

OpCodeType('b5')

Defined in

dist/smart-contract/builtins/functions.d.ts:860


OP_NOP7

Static Readonly OP_NOP7: OpCodeType

No operation. The word is ignored.

Name

OP_NOP7

Constant

OpCodeType('b6')

Defined in

dist/smart-contract/builtins/functions.d.ts:866


OP_NOP8

Static Readonly OP_NOP8: OpCodeType

No operation. The word is ignored.

Name

OP_NOP8

Constant

OpCodeType('b7')

Defined in

dist/smart-contract/builtins/functions.d.ts:872


OP_NOP9

Static Readonly OP_NOP9: OpCodeType

No operation. The word is ignored.

Name

OP_NOP9

Constant

OpCodeType('b8')

Defined in

dist/smart-contract/builtins/functions.d.ts:878


OP_NOT

Static Readonly OP_NOT: OpCodeType

If the input is 0 or 1, it is flipped. Otherwise the output will be 0.

Name

OP_NOT

Constant

OpCodeType('91')

Defined in

dist/smart-contract/builtins/functions.d.ts:642


OP_NOTIF

Static Readonly OP_NOTIF: OpCodeType

If the top stack value is FALSE, statement 1 is executed. If the top stack value is TRUE and ELSE is used, statement 2 is executed. If ELSE is NOT used, the script jumps to ENDIF. The top stack value is removed.

Deprecated

Name

OP_NOTIF

Constant

OpCodeType('64')

Example

`[expression] NOTIF
[statement 1]
ENDIF`
OR
`[expression] NOTIF
[statement 1]
ELSE
[statement 2]
ENDIF`

Defined in

dist/smart-contract/builtins/functions.d.ts:359


OP_NUM2BIN

Static Readonly OP_NUM2BIN: OpCodeType

Converts numeric value a into byte sequence of length b.

Name

OP_NUM2BIN

Constant

OpCodeType('80')

Defined in

dist/smart-contract/builtins/functions.d.ts:540


OP_NUMEQUAL

Static Readonly OP_NUMEQUAL: OpCodeType

Returns 1 if the numbers are equal, 0 otherwise.

Name

OP_NUMEQUAL

Constant

OpCodeType('9c')

Defined in

dist/smart-contract/builtins/functions.d.ts:708


OP_NUMEQUALVERIFY

Static Readonly OP_NUMEQUALVERIFY: OpCodeType

Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.

Name

OP_NUMEQUALVERIFY

Constant

OpCodeType('9d')

Defined in

dist/smart-contract/builtins/functions.d.ts:714


OP_NUMNOTEQUAL

Static Readonly OP_NUMNOTEQUAL: OpCodeType

Returns 1 if the numbers are not equal, 0 otherwise.

Name

OP_NUMNOTEQUAL

Constant

OpCodeType('9e')

Defined in

dist/smart-contract/builtins/functions.d.ts:720


OP_OR

Static Readonly OP_OR: OpCodeType

Boolean or between each bit in the inputs.

Name

OP_OR

Constant

OpCodeType('85')

Defined in

dist/smart-contract/builtins/functions.d.ts:570


OP_OVER

Static Readonly OP_OVER: OpCodeType

Copies the second-to-top stack item to the top.

Name

OP_OVER

Constant

OpCodeType('78')

Defined in

dist/smart-contract/builtins/functions.d.ts:492


OP_PICK

Static Readonly OP_PICK: OpCodeType

The item n back in the stack is copied to the top.

Name

OP_PICK

Constant

OpCodeType('79')

Defined in

dist/smart-contract/builtins/functions.d.ts:498


OP_PUBKEY

Static Readonly OP_PUBKEY: OpCodeType

Represents a public key compatible with OP_CHECKSIG. The word is used internally for assisting with transaction matching. They are invalid if used in actual scripts.

Name

OP_PUBKEY

Constant

OpCodeType('fe')

Defined in

dist/smart-contract/builtins/functions.d.ts:896


OP_PUBKEYHASH

Static Readonly OP_PUBKEYHASH: OpCodeType

Represents a public key hashed with OP_HASH160. The word is used internally for assisting with transaction matching. They are invalid if used in actual scripts.

Name

OP_PUBKEYHASH

Constant

OpCodeType('fd')

Defined in

dist/smart-contract/builtins/functions.d.ts:890


OP_PUSHDATA1

Static Readonly OP_PUSHDATA1: OpCodeType

The next byte contains the number of bytes to be pushed onto the stack.

Name

OP_PUSHDATA1

Constant

OpCodeType('4c')

Defined in

dist/smart-contract/builtins/functions.d.ts:182


OP_PUSHDATA2

Static Readonly OP_PUSHDATA2: OpCodeType

The next two bytes contain the number of bytes to be pushed onto the stack in little endian order.

Name

OP_PUSHDATA2

Constant

OpCodeType('4d')

Defined in

dist/smart-contract/builtins/functions.d.ts:188


OP_PUSHDATA4

Static Readonly OP_PUSHDATA4: OpCodeType

The next four bytes contain the number of bytes to be pushed onto the stack in little endian order.

Name

OP_PUSHDATA4

Constant

OpCodeType('4e')

Defined in

dist/smart-contract/builtins/functions.d.ts:194


OP_RESERVED

Static Readonly OP_RESERVED: OpCodeType

Transaction is invalid unless occuring in an unexecuted OP_IF branch

Name

OP_RESERVED

Constant

OpCodeType('50')

Defined in

dist/smart-contract/builtins/functions.d.ts:206


OP_RESERVED1

Static Readonly OP_RESERVED1: OpCodeType

Any opcode not assigned is also reserved. Using an unassigned opcode makes the transaction invalid.

Name

OP_RESERVED1

Constant

OpCodeType('89')

Defined in

dist/smart-contract/builtins/functions.d.ts:594


OP_RESERVED2

Static Readonly OP_RESERVED2: OpCodeType

Any opcode not assigned is also reserved. Using an unassigned opcode makes the transaction invalid.

Name

OP_RESERVED2

Constant

OpCodeType('8a')

Defined in

dist/smart-contract/builtins/functions.d.ts:600


OP_RETURN

Static Readonly OP_RETURN: OpCodeType

OP_RETURN can also be used to create "False Return" outputs with a scriptPubKey consisting of OP_FALSE OP_RETURN followed by data. Such outputs are provably unspendable and should be given a value of zero Satoshis. These outputs can be pruned from storage in the UTXO set, reducing its size. Currently the BitcoinSV network supports multiple FALSE RETURN outputs in a given transaction with each one capable of holding up to 100kB of data. After the Genesis upgrade in 2020 miners will be free to mine transactions containing FALSE RETURN outputs of any size.

Name

OP_RETURN

Constant

OpCodeType('6a')

Defined in

dist/smart-contract/builtins/functions.d.ts:408


OP_RIPEMD160

Static Readonly OP_RIPEMD160: OpCodeType

The input is hashed using RIPEMD-160.

Name

OP_RIPEMD160

Constant

OpCodeType('a6')

Defined in

dist/smart-contract/builtins/functions.d.ts:768


OP_ROLL

Static Readonly OP_ROLL: OpCodeType

The item n back in the stack is moved to the top.

Name

OP_ROLL

Constant

OpCodeType('7a')

Defined in

dist/smart-contract/builtins/functions.d.ts:504


OP_ROT

Static Readonly OP_ROT: OpCodeType

The top three items on the stack are rotated to the left.

Name

OP_ROT

Constant

OpCodeType('7b')

Defined in

dist/smart-contract/builtins/functions.d.ts:510


OP_RSHIFT

Static Readonly OP_RSHIFT: OpCodeType

Logical right shift b bits. Sign data is discarded

Name

OP_RSHIFT

Constant

OpCodeType('99')

Defined in

dist/smart-contract/builtins/functions.d.ts:690


OP_SHA1

Static Readonly OP_SHA1: OpCodeType

The input is hashed using SHA-1.

Name

OP_SHA1

Constant

OpCodeType('a7')

Defined in

dist/smart-contract/builtins/functions.d.ts:774


OP_SHA256

Static Readonly OP_SHA256: OpCodeType

The input is hashed using SHA-256.

Name

OP_SHA256

Constant

OpCodeType('a8')

Defined in

dist/smart-contract/builtins/functions.d.ts:780


OP_SIZE

Static Readonly OP_SIZE: OpCodeType

Pushes the string length of the top element of the stack (without popping it).

Name

OP_SIZE

Constant

OpCodeType('82')

Defined in

dist/smart-contract/builtins/functions.d.ts:552


OP_SPLIT

Static Readonly OP_SPLIT: OpCodeType

Splits byte sequence x at position n.

Name

OP_SPLIT

Constant

OpCodeType('7f')

Defined in

dist/smart-contract/builtins/functions.d.ts:534


OP_SUB

Static Readonly OP_SUB: OpCodeType

b is subtracted from a.

Name

OP_SUB

Constant

OpCodeType('94')

Defined in

dist/smart-contract/builtins/functions.d.ts:660


OP_SWAP

Static Readonly OP_SWAP: OpCodeType

The top two items on the stack are swapped.

Name

OP_SWAP

Constant

OpCodeType('7c')

Defined in

dist/smart-contract/builtins/functions.d.ts:516


OP_TOALTSTACK

Static Readonly OP_TOALTSTACK: OpCodeType

Puts the input onto the top of the alt stack. Removes it from the main stack.

Name

OP_TOALTSTACK

Constant

OpCodeType('6b')

Defined in

dist/smart-contract/builtins/functions.d.ts:414


OP_TRUE

Static Readonly OP_TRUE: OpCodeType

The number 1 is pushed onto the stack.

Name

OP_TRUE

Constant

OpCodeType('51')

Defined in

dist/smart-contract/builtins/functions.d.ts:218


OP_TUCK

Static Readonly OP_TUCK: OpCodeType

The item at the top of the stack is copied and inserted before the second-to-top item.

Name

OP_TUCK

Constant

OpCodeType('7d')

Defined in

dist/smart-contract/builtins/functions.d.ts:522


OP_VER

Static Readonly OP_VER: OpCodeType

Puts the version of the protocol under which this transaction will be evaluated onto the stack.

Deprecated

DISABLED

Name

OP_VER

Constant

OpCodeType('62')

Defined in

dist/smart-contract/builtins/functions.d.ts:321


OP_VERIF

Static Readonly OP_VERIF: OpCodeType

Name

OP_VERIF

Constant

OpCodeType('65')

Deprecated

DISABLED

Defined in

dist/smart-contract/builtins/functions.d.ts:365


OP_VERIFY

Static Readonly OP_VERIFY: OpCodeType

Marks transaction as invalid if top stack value is not true. The top stack value is removed.

Name

OP_VERIFY

Constant

OpCodeType('69')

Defined in

dist/smart-contract/builtins/functions.d.ts:402


OP_VERNOTIF

Static Readonly OP_VERNOTIF: OpCodeType

Name

OP_VERNOTIF

Constant

OpCodeType('66')

Deprecated

DISABLED

Defined in

dist/smart-contract/builtins/functions.d.ts:371


OP_WITHIN

Static Readonly OP_WITHIN: OpCodeType

Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.

Name

OP_WITHIN

Constant

OpCodeType('a5')

Defined in

dist/smart-contract/builtins/functions.d.ts:762


OP_XOR

Static Readonly OP_XOR: OpCodeType

Boolean exclusive or between each bit in the inputs.

Name

OP_XOR

Constant

OpCodeType('86')

Defined in

dist/smart-contract/builtins/functions.d.ts:576

- + \ No newline at end of file diff --git a/reference/classes/Provider/index.html b/reference/classes/Provider/index.html index 4627c305b..481806a0c 100644 --- a/reference/classes/Provider/index.html +++ b/reference/classes/Provider/index.html @@ -4,7 +4,7 @@ Provider | sCrypt - + @@ -59,7 +59,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

EventEmitter.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

EventEmitter.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

EventEmitter.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/ScryptProvider/index.html b/reference/classes/ScryptProvider/index.html index 6e8b80caf..e3fa06aa2 100644 --- a/reference/classes/ScryptProvider/index.html +++ b/reference/classes/ScryptProvider/index.html @@ -4,7 +4,7 @@ ScryptProvider | sCrypt - + @@ -59,7 +59,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/SensibleProvider/index.html b/reference/classes/SensibleProvider/index.html index 7f707290a..99c4f2d4e 100644 --- a/reference/classes/SensibleProvider/index.html +++ b/reference/classes/SensibleProvider/index.html @@ -4,7 +4,7 @@ SensibleProvider | sCrypt - + @@ -59,7 +59,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/SensiletSigner/index.html b/reference/classes/SensiletSigner/index.html index 636b42be8..cef9df76e 100644 --- a/reference/classes/SensiletSigner/index.html +++ b/reference/classes/SensiletSigner/index.html @@ -4,7 +4,7 @@ SensiletSigner | sCrypt - + @@ -14,7 +14,7 @@ a connection will be established for the new provider and then switched to the new provider. If no new provider is specified, a connection is established for signer's built-in provider. If neither exists, an exception is thrown.

Parameters

NameTypeDescription
provider?ProviderThe target provider.

Returns

Promise<SensiletSigner>

Overrides

Signer.connect

Defined in

dist/bsv/signers/sensilet-signer.d.ts:29


getBalance

getBalance(address?): Promise<{ confirmed: number ; unconfirmed: number }>

Get the balance of BSVs in satoshis for an address.

Parameters

NameTypeDescription
address?AddressThe query address.

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

A promise which resolves to the address balance status.

Overrides

Signer.getBalance

Defined in

dist/bsv/signers/sensilet-signer.d.ts:32


getDefaultAddress

getDefaultAddress(): Promise<Address>

Returns

Promise<Address>

A promise which resolves to the address to the default private key of the signer.

Overrides

Signer.getDefaultAddress

Defined in

dist/bsv/signers/sensilet-signer.d.ts:30


getDefaultPubKey

getDefaultPubKey(): Promise<PublicKey>

Returns

Promise<PublicKey>

A promise which resolves to the public key of the default private key of the signer.

Overrides

Signer.getDefaultPubKey

Defined in

dist/bsv/signers/sensilet-signer.d.ts:36


getNetwork

getNetwork(): Promise<Network>

Returns

Promise<Network>

Defined in

dist/bsv/signers/sensilet-signer.d.ts:31


getPubKey

getPubKey(address): Promise<PublicKey>

Throws

If the private key for the address does not belong this signer.

Parameters

NameTypeDescription
addressAddressThe request address, using the default address if omitted.

Returns

Promise<PublicKey>

The public key result.

Overrides

Signer.getPubKey

Defined in

dist/bsv/signers/sensilet-signer.d.ts:37


getSignatures

getSignatures(rawTxHex, sigRequests): Promise<SignatureResponse[]>

Get the requested transaction signatures for the raw transaction.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to get signatures from.
sigRequestsSignatureRequest[]The signature requst informations, see details in SignatureRequest.

Returns

Promise<SignatureResponse[]>

A promise which resolves to a list of SignatureReponse corresponding to sigRequests.

Overrides

Signer.getSignatures

Defined in

dist/bsv/signers/sensilet-signer.d.ts:41


isAuthenticated

isAuthenticated(): Promise<boolean>

Check if the wallet has been authenticated

Returns

Promise<boolean>

true | false

Overrides

Signer.isAuthenticated

Defined in

dist/bsv/signers/sensilet-signer.d.ts:19


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Get a list of the P2PKH UTXOs.

Parameters

NameTypeDescription
addressAddressThe address of the returned UTXOs belongs to.
options?UtxoQueryOptionsThe optional query conditions, see details in UtxoQueryOptions.

Returns

Promise<IUnspentOutput[]>

A promise which resolves to a list of UTXO for the query options.

Inherited from

Signer.listUnspent

Defined in

dist/bsv/abstract-signer.d.ts:147


requestAuth

requestAuth(): Promise<{ error: string ; isAuthenticated: boolean }>

Request wallet authentication

Returns

Promise<{ error: string ; isAuthenticated: boolean }>

A promise which resolves to if the wallet has been authenticated and the authenticate error message

Overrides

Signer.requestAuth

Defined in

dist/bsv/signers/sensilet-signer.d.ts:24


signAndsendTransaction

signAndsendTransaction(tx, options?): Promise<TransactionResponse>

Sign transaction and broadcast it

Parameters

NameTypeDescription
txTransactionA transaction is signed and broadcast
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<TransactionResponse>

A promise which resolves to the transaction id.

Inherited from

Signer.signAndsendTransaction

Defined in

dist/bsv/abstract-signer.d.ts:140


signMessage

signMessage(message, address?): Promise<string>

Sign a message string.

Parameters

NameTypeDescription
messagestringThe message to be signed.
address?AddressThe optional address whose private key will be used to sign message, using the default private key if omitted.

Returns

Promise<string>

A promise which resolves to the signautre of the message.

Overrides

Signer.signMessage

Defined in

dist/bsv/signers/sensilet-signer.d.ts:40


signRawTransaction

signRawTransaction(rawTxHex, options): Promise<string>

Sign a raw transaction hex string.

Throws

If any input of the transaction can not be signed properly.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to sign.
optionsSignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<string>

A promise which resolves to the signed transaction hex string.

Overrides

Signer.signRawTransaction

Defined in

dist/bsv/signers/sensilet-signer.d.ts:38


signTransaction

signTransaction(tx, options?): Promise<Transaction>

Sign a transaction object.

Parameters

NameTypeDescription
txTransactionThe transaction object to sign.
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<Transaction>

A promise which resolves to the signed transaction object.

Overrides

Signer.signTransaction

Defined in

dist/bsv/signers/sensilet-signer.d.ts:39


isSigner

Static isSigner(value): value is Signer

Check if an object is a Signer

Parameters

NameTypeDescription
valueanyThe target object

Returns

value is Signer

Returns true if and only if object is a Provider.

Inherited from

Signer.isSigner

Defined in

dist/bsv/abstract-signer.d.ts:162

- + \ No newline at end of file diff --git a/reference/classes/SigHash/index.html b/reference/classes/SigHash/index.html index b858d4109..9452a5464 100644 --- a/reference/classes/SigHash/index.html +++ b/reference/classes/SigHash/index.html @@ -4,7 +4,7 @@ SigHash | sCrypt - + @@ -12,7 +12,7 @@

SigHash

scrypt-ts / SigHash

Class: SigHash

A library to access various fields in the [preimage][https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md](https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md). For example, we usually use SigHash.scriptCode(preimage: SigHashPreimage) to access the scriptCode of the preimage, and use SigHash.value(preimage: SigHashPreimage) to access the value field of the preimage, which is the value of the number of bitcoins spent in this contract.

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new SigHash()

Properties

ALL

Static Readonly ALL: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:985


ANYONECANPAY_ALL

Static Readonly ANYONECANPAY_ALL: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:988


ANYONECANPAY_NONE

Static Readonly ANYONECANPAY_NONE: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:989


ANYONECANPAY_SINGLE

Static Readonly ANYONECANPAY_SINGLE: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:990


NONE

Static Readonly NONE: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:986


SINGLE

Static Readonly SINGLE: SigHashType

Defined in

dist/smart-contract/builtins/functions.d.ts:987

Methods

hashOutputs

Static hashOutputs(preimage): ByteString

get hashOutputs of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return hashOutputs ByteString in 32-byte hash

Defined in

dist/smart-contract/builtins/functions.d.ts:1050


hashPrevouts

Static hashPrevouts(preimage): ByteString

get hashPrevouts of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return hashPrevouts ByteString in 32-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1002


hashSequence

Static hashSequence(preimage): ByteString

get hashSequence of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return hashSequence ByteString in 32-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1008


nLocktime

Static nLocktime(preimage): bigint

get nLocktime of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

bigint

return nLocktime

Defined in

dist/smart-contract/builtins/functions.d.ts:1062


nLocktimeRaw

Static nLocktimeRaw(preimage): ByteString

get nLocktime of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return nLocktime ByteString in 4-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1056


nSequence

Static nSequence(preimage): bigint

nSequence of the input from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

bigint

return nSequence

Defined in

dist/smart-contract/builtins/functions.d.ts:1044


nSequenceRaw

Static nSequenceRaw(preimage): ByteString

nSequence of the input from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return nSequence ByteString in 4-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1038


nVersion

Static nVersion(preimage): ByteString

get version of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return version ByteString in 4-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:996


outpoint

Static outpoint(preimage): ByteString

get outpoint of the transaction from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return outpoint ByteString in 32-byte hash + 4-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1014


scriptCode

Static scriptCode(preimage): ByteString

get scriptCode of the transaction from the preimage. scriptCode is just scriptPubKey if there is no CODESEPARATOR in the latter

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return scriptCode ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1020


sigHashType

Static sigHashType(preimage): SigHashType

sighash type of the signature from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

SigHashType

return sighash type

Defined in

dist/smart-contract/builtins/functions.d.ts:1068


value

Static value(preimage): bigint

get value of the output spent by this input from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

bigint

return value

Defined in

dist/smart-contract/builtins/functions.d.ts:1032


valueRaw

Static valueRaw(preimage): ByteString

get value of the output spent by this input from the preimage

Parameters

NameTypeDescription
preimageSigHashPreimagethe preimage

Returns

ByteString

return value ByteString in 8-byte little endian

Defined in

dist/smart-contract/builtins/functions.d.ts:1026

- + \ No newline at end of file diff --git a/reference/classes/Signer/index.html b/reference/classes/Signer/index.html index 3256b93ba..27da357b3 100644 --- a/reference/classes/Signer/index.html +++ b/reference/classes/Signer/index.html @@ -4,7 +4,7 @@ Signer | sCrypt - + @@ -13,7 +13,7 @@ a connection will be established for the new provider and then switched to the new provider. If no new provider is specified, a connection is established for signer's built-in provider. If neither exists, an exception is thrown.

Parameters

NameTypeDescription
newProvider?ProviderThe target provider.

Returns

Promise<Signer>

Defined in

dist/bsv/abstract-signer.d.ts:80


getBalance

getBalance(address?): Promise<{ confirmed: number ; unconfirmed: number }>

Get the balance of BSVs in satoshis for an address.

Parameters

NameTypeDescription
address?AddressThe query address.

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

A promise which resolves to the address balance status.

Defined in

dist/bsv/abstract-signer.d.ts:153


getDefaultAddress

Abstract getDefaultAddress(): Promise<Address>

Returns

Promise<Address>

A promise which resolves to the address to the default private key of the signer.

Defined in

dist/bsv/abstract-signer.d.ts:90


getDefaultPubKey

Abstract getDefaultPubKey(): Promise<PublicKey>

Returns

Promise<PublicKey>

A promise which resolves to the public key of the default private key of the signer.

Defined in

dist/bsv/abstract-signer.d.ts:85


getPubKey

Abstract getPubKey(address?): Promise<PublicKey>

Throws

If the private key for the address does not belong this signer.

Parameters

NameTypeDescription
address?AddressThe request address, using the default address if omitted.

Returns

Promise<PublicKey>

The public key result.

Defined in

dist/bsv/abstract-signer.d.ts:97


getSignatures

Abstract getSignatures(rawTxHex, sigRequests): Promise<SignatureResponse[]>

Get the requested transaction signatures for the raw transaction.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to get signatures from.
sigRequestsSignatureRequest[]The signature requst informations, see details in SignatureRequest.

Returns

Promise<SignatureResponse[]>

A promise which resolves to a list of SignatureReponse corresponding to sigRequests.

Defined in

dist/bsv/abstract-signer.d.ts:127


isAuthenticated

Abstract isAuthenticated(): Promise<boolean>

Check if the wallet has been authenticated

Returns

Promise<boolean>

true | false

Defined in

dist/bsv/abstract-signer.d.ts:63


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Get a list of the P2PKH UTXOs.

Parameters

NameTypeDescription
addressAddressThe address of the returned UTXOs belongs to.
options?UtxoQueryOptionsThe optional query conditions, see details in UtxoQueryOptions.

Returns

Promise<IUnspentOutput[]>

A promise which resolves to a list of UTXO for the query options.

Defined in

dist/bsv/abstract-signer.d.ts:147


requestAuth

Abstract requestAuth(): Promise<{ error: string ; isAuthenticated: boolean }>

Request wallet authentication

Returns

Promise<{ error: string ; isAuthenticated: boolean }>

A promise which resolves to if the wallet has been authenticated and the authenticate error message

Defined in

dist/bsv/abstract-signer.d.ts:68


signAndsendTransaction

signAndsendTransaction(tx, options?): Promise<TransactionResponse>

Sign transaction and broadcast it

Parameters

NameTypeDescription
txTransactionA transaction is signed and broadcast
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<TransactionResponse>

A promise which resolves to the transaction id.

Defined in

dist/bsv/abstract-signer.d.ts:140


signMessage

Abstract signMessage(message, address?): Promise<string>

Sign a message string.

Parameters

NameTypeDescription
messagestringThe message to be signed.
address?AddressThe optional address whose private key will be used to sign message, using the default private key if omitted.

Returns

Promise<string>

A promise which resolves to the signautre of the message.

Defined in

dist/bsv/abstract-signer.d.ts:120


signRawTransaction

Abstract signRawTransaction(rawTxHex, options): Promise<string>

Sign a raw transaction hex string.

Throws

If any input of the transaction can not be signed properly.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to sign.
optionsSignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<string>

A promise which resolves to the signed transaction hex string.

Defined in

dist/bsv/abstract-signer.d.ts:106


signTransaction

Abstract signTransaction(tx, options?): Promise<Transaction>

Sign a transaction object.

Parameters

NameTypeDescription
txTransactionThe transaction object to sign.
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<Transaction>

A promise which resolves to the signed transaction object.

Defined in

dist/bsv/abstract-signer.d.ts:113


isSigner

Static isSigner(value): value is Signer

Check if an object is a Signer

Parameters

NameTypeDescription
valueanyThe target object

Returns

value is Signer

Returns true if and only if object is a Provider.

Defined in

dist/bsv/abstract-signer.d.ts:162

- + \ No newline at end of file diff --git a/reference/classes/SmartContract/index.html b/reference/classes/SmartContract/index.html index 034eb0dff..4cf0f83b6 100644 --- a/reference/classes/SmartContract/index.html +++ b/reference/classes/SmartContract/index.html @@ -4,7 +4,7 @@ SmartContract | sCrypt - + @@ -20,7 +20,7 @@ if the contract contains onchain properties of type HashedMap or HashedSet it's required to pass all their offchain raw data at this transaction moment

Type parameters

NameType
Textends SmartContract<T>

Parameters

NameTypeDescription
this(...args: any[]) => T-
txTransactiontransaction
atOutputIndexnumberoutput index of tx
offchainValues?Record<string, any>the value of offchain properties, the raw data of onchain HashedMap and HashedSet properties, at this transaction moment

Returns

T

Defined in

dist/smart-contract/contract.d.ts:499


getArtifact

Static getArtifact(): Artifact

The contract class needs to call this function before instantiating.

Returns

Artifact

Defined in

dist/smart-contract/contract.d.ts:138


loadArtifact

Static loadArtifact(artifactFile?): void

This function is usually called on the frontend. The contract class needs to call this function before instantiating.

Parameters

NameTypeDescription
artifactFile?string | Artifacta merged contract artifact object, or its file path

Returns

void

Defined in

dist/smart-contract/contract.d.ts:132


multiContractCall

Static multiContractCall(partialContractTx, signer, options?): Promise<MultiContractTransaction>

When the @methods of multiple contracts is called in a transaction, this function signs and broadcasts the final transaction.

Parameters

NameTypeDescription
partialContractTxContractTransactiona ContractTransation with a unsigned transation.
signerSignera signer to sign the transation.
options?MultiContractCallOptions-

Returns

Promise<MultiContractTransaction>

a MultiContractTransation with a signed transation.

Defined in

dist/smart-contract/contract.d.ts:510


parseCallData

Static parseCallData(tx, inputIndex): CallData

parse call data when a contract public method called in a transation.

Parameters

NameType
txTransaction
inputIndexnumber

Returns

CallData

Defined in

dist/smart-contract/contract.d.ts:517


Signature Verification Methods

checkMultiSig

checkMultiSig(signatures, publickeys): boolean

Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack.

Onchain

See

https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script

Parameters

NameType
signaturesSig[]
publickeysPubKey[]

Returns

boolean

Defined in

dist/smart-contract/contract.d.ts:286


checkSig

checkSig(signature, publickey, errorMsg?): boolean

A built-in function verifies an ECDSA signature. It takes two inputs from the stack, a public key (on top of the stack) and an ECDSA signature in its DER_CANONISED format concatenated with sighash flags. It outputs true or false on the stack based on whether the signature check passes or fails.

Onchain

See

https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script

Parameters

NameType
signatureSig
publickeyPubKey
errorMsg?string

Returns

boolean

Defined in

dist/smart-contract/contract.d.ts:246

- + \ No newline at end of file diff --git a/reference/classes/SmartContractLib/index.html b/reference/classes/SmartContractLib/index.html index ca808b5b0..86a198f3d 100644 --- a/reference/classes/SmartContractLib/index.html +++ b/reference/classes/SmartContractLib/index.html @@ -4,13 +4,13 @@ SmartContractLib | sCrypt - +

SmartContractLib

scrypt-ts / SmartContractLib

Class: SmartContractLib

The contract library class. To write a contract library, extend this class as such:

Example

class YourSmartContractLib extends SmartContractLib {
// your library functions code here
}

Table of contents

Constructors

Properties

Constructors

constructor

new SmartContractLib(...args)

Parameters

NameType
...argsany[]

Defined in

dist/smart-contract/library.d.ts:15

Properties

args

args: any[]

Defined in

dist/smart-contract/library.d.ts:14

- + \ No newline at end of file diff --git a/reference/classes/TAALSigner/index.html b/reference/classes/TAALSigner/index.html index 6dcc24014..882cc949d 100644 --- a/reference/classes/TAALSigner/index.html +++ b/reference/classes/TAALSigner/index.html @@ -4,7 +4,7 @@ TAALSigner | sCrypt - + @@ -13,7 +13,7 @@ a connection will be established for the new provider and then switched to the new provider. If no new provider is specified, a connection is established for signer's built-in provider. If neither exists, an exception is thrown.

Parameters

NameTypeDescription
provider?ProviderThe target provider.

Returns

Promise<TAALSigner>

Overrides

Signer.connect

Defined in

dist/bsv/signers/taal-signer/index.d.ts:21


getBalance

getBalance(address?): Promise<{ confirmed: number ; unconfirmed: number }>

Get the balance of BSVs in satoshis for an address.

Parameters

NameTypeDescription
address?AddressThe query address.

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

A promise which resolves to the address balance status.

Overrides

Signer.getBalance

Defined in

dist/bsv/signers/taal-signer/index.d.ts:37


getDefaultAddress

getDefaultAddress(): Promise<Address>

Returns

Promise<Address>

A promise which resolves to the address to the default private key of the signer.

Overrides

Signer.getDefaultAddress

Defined in

dist/bsv/signers/taal-signer/index.d.ts:22


getDefaultPubKey

getDefaultPubKey(): Promise<PublicKey>

Returns

Promise<PublicKey>

A promise which resolves to the public key of the default private key of the signer.

Overrides

Signer.getDefaultPubKey

Defined in

dist/bsv/signers/taal-signer/index.d.ts:23


getNetwork

getNetwork(): Promise<any>

Returns

Promise<any>

Defined in

dist/bsv/signers/taal-signer/index.d.ts:26


getPubKey

getPubKey(address): Promise<PublicKey>

Throws

If the private key for the address does not belong this signer.

Parameters

NameTypeDescription
addressAddressThe request address, using the default address if omitted.

Returns

Promise<PublicKey>

The public key result.

Overrides

Signer.getPubKey

Defined in

dist/bsv/signers/taal-signer/index.d.ts:24


getSignatures

getSignatures(rawTxHex, sigRequests): Promise<SignatureResponse[]>

Get signatures with api

Parameters

NameTypeDescription
rawTxHexstringa transation raw hex
sigRequestsSignatureRequest[]a SignatureRequest array for the some inputs of the transaction.

Returns

Promise<SignatureResponse[]>

a SignatureResponse array

Overrides

Signer.getSignatures

Defined in

dist/bsv/signers/taal-signer/index.d.ts:35


isAuthenticated

isAuthenticated(): Promise<boolean>

Check if the wallet has been authenticated

Returns

Promise<boolean>

true | false

Overrides

Signer.isAuthenticated

Defined in

dist/bsv/signers/taal-signer/index.d.ts:15


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Get a list of the P2PKH UTXOs.

Parameters

NameTypeDescription
addressAddressThe address of the returned UTXOs belongs to.
options?UtxoQueryOptionsThe optional query conditions, see details in UtxoQueryOptions.

Returns

Promise<IUnspentOutput[]>

A promise which resolves to a list of UTXO for the query options.

Inherited from

Signer.listUnspent

Defined in

dist/bsv/abstract-signer.d.ts:147


requestAuth

requestAuth(): Promise<{ error: string ; isAuthenticated: boolean }>

Request wallet authentication

Returns

Promise<{ error: string ; isAuthenticated: boolean }>

A promise which resolves to if the wallet has been authenticated and the authenticate error message

Overrides

Signer.requestAuth

Defined in

dist/bsv/signers/taal-signer/index.d.ts:16


signAndsendTransaction

signAndsendTransaction(tx, options?): Promise<TransactionResponse>

Sign transaction and broadcast it

Parameters

NameTypeDescription
txTransactionA transaction is signed and broadcast
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<TransactionResponse>

A promise which resolves to the transaction id.

Inherited from

Signer.signAndsendTransaction

Defined in

dist/bsv/abstract-signer.d.ts:140


signMessage

signMessage(message, address?): Promise<string>

Sign a message string.

Parameters

NameTypeDescription
messagestringThe message to be signed.
address?AddressThe optional address whose private key will be used to sign message, using the default private key if omitted.

Returns

Promise<string>

A promise which resolves to the signautre of the message.

Overrides

Signer.signMessage

Defined in

dist/bsv/signers/taal-signer/index.d.ts:36


signRawTransaction

signRawTransaction(rawTxHex, options): Promise<string>

Sign a raw transaction hex string.

Throws

If any input of the transaction can not be signed properly.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to sign.
optionsSignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<string>

A promise which resolves to the signed transaction hex string.

Overrides

Signer.signRawTransaction

Defined in

dist/bsv/signers/taal-signer/index.d.ts:25


signTransaction

signTransaction(tx, options?): Promise<Transaction>

Sign a transaction object.

Parameters

NameTypeDescription
txTransactionThe transaction object to sign.
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<Transaction>

A promise which resolves to the signed transaction object.

Overrides

Signer.signTransaction

Defined in

dist/bsv/signers/taal-signer/index.d.ts:28


updateInputsWithInfo

updateInputsWithInfo(tx, inputInfos): Transaction

Parameters

NameType
txTransaction
inputInfosInputInfo[]

Returns

Transaction

Defined in

dist/bsv/signers/taal-signer/index.d.ts:27


isSigner

Static isSigner(value): value is Signer

Check if an object is a Signer

Parameters

NameTypeDescription
valueanyThe target object

Returns

value is Signer

Returns true if and only if object is a Provider.

Inherited from

Signer.isSigner

Defined in

dist/bsv/abstract-signer.d.ts:162

- + \ No newline at end of file diff --git a/reference/classes/TaalProvider/index.html b/reference/classes/TaalProvider/index.html index d24ba4530..7878d7f5b 100644 --- a/reference/classes/TaalProvider/index.html +++ b/reference/classes/TaalProvider/index.html @@ -4,7 +4,7 @@ TaalProvider | sCrypt - + @@ -60,7 +60,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/TestWallet/index.html b/reference/classes/TestWallet/index.html index e555e3016..3b1e930ec 100644 --- a/reference/classes/TestWallet/index.html +++ b/reference/classes/TestWallet/index.html @@ -4,7 +4,7 @@ TestWallet | sCrypt - + @@ -14,7 +14,7 @@ a connection will be established for the new provider and then switched to the new provider. If no new provider is specified, a connection is established for signer's built-in provider. If neither exists, an exception is thrown.

Parameters

NameTypeDescription
provider?ProviderThe target provider.

Returns

Promise<TestWallet>

Overrides

Signer.connect

Defined in

dist/bsv/wallets/test-wallet.d.ts:33


enableSplitFeeTx

enableSplitFeeTx(on): void

Parameters

NameType
onboolean

Returns

void

Defined in

dist/bsv/wallets/test-wallet.d.ts:16


getBalance

getBalance(address?): Promise<{ confirmed: number ; unconfirmed: number }>

Get the balance of BSVs in satoshis for an address.

Parameters

NameTypeDescription
address?AddressThe query address.

Returns

Promise<{ confirmed: number ; unconfirmed: number }>

A promise which resolves to the address balance status.

Inherited from

Signer.getBalance

Defined in

dist/bsv/abstract-signer.d.ts:153


getDefaultAddress

getDefaultAddress(): Promise<Address>

Returns

Promise<Address>

A promise which resolves to the address to the default private key of the signer.

Overrides

Signer.getDefaultAddress

Defined in

dist/bsv/wallets/test-wallet.d.ts:26


getDefaultPubKey

getDefaultPubKey(): Promise<PublicKey>

Returns

Promise<PublicKey>

A promise which resolves to the public key of the default private key of the signer.

Overrides

Signer.getDefaultPubKey

Defined in

dist/bsv/wallets/test-wallet.d.ts:27


getPubKey

getPubKey(address): Promise<PublicKey>

Throws

If the private key for the address does not belong this signer.

Parameters

NameTypeDescription
addressAddressThe request address, using the default address if omitted.

Returns

Promise<PublicKey>

The public key result.

Overrides

Signer.getPubKey

Defined in

dist/bsv/wallets/test-wallet.d.ts:28


getSignatures

getSignatures(rawTxHex, sigRequests): Promise<SignatureResponse[]>

Get the requested transaction signatures for the raw transaction.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to get signatures from.
sigRequestsSignatureRequest[]The signature requst informations, see details in SignatureRequest.

Returns

Promise<SignatureResponse[]>

A promise which resolves to a list of SignatureReponse corresponding to sigRequests.

Overrides

Signer.getSignatures

Defined in

dist/bsv/wallets/test-wallet.d.ts:32


isAuthenticated

isAuthenticated(): Promise<boolean>

Check if the wallet has been authenticated

Returns

Promise<boolean>

true | false

Overrides

Signer.isAuthenticated

Defined in

dist/bsv/wallets/test-wallet.d.ts:17


listUnspent

listUnspent(address, options?): Promise<IUnspentOutput[]>

Get a list of the P2PKH UTXOs.

Parameters

NameTypeDescription
addressAddressThe address of the returned UTXOs belongs to.
options?UtxoQueryOptionsThe optional query conditions, see details in UtxoQueryOptions.

Returns

Promise<IUnspentOutput[]>

A promise which resolves to a list of UTXO for the query options.

Overrides

Signer.listUnspent

Defined in

dist/bsv/wallets/test-wallet.d.ts:34


requestAuth

requestAuth(): Promise<{ error: string ; isAuthenticated: boolean }>

Request wallet authentication

Returns

Promise<{ error: string ; isAuthenticated: boolean }>

A promise which resolves to if the wallet has been authenticated and the authenticate error message

Overrides

Signer.requestAuth

Defined in

dist/bsv/wallets/test-wallet.d.ts:18


signAndsendTransaction

signAndsendTransaction(tx, options?): Promise<TransactionResponse>

Sign transaction and broadcast it

Parameters

NameTypeDescription
txTransactionA transaction is signed and broadcast
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<TransactionResponse>

A promise which resolves to the transaction id.

Overrides

Signer.signAndsendTransaction

Defined in

dist/bsv/wallets/test-wallet.d.ts:39


signMessage

signMessage(message, address?): Promise<string>

Sign a message string.

Parameters

NameTypeDescription
messagestringThe message to be signed.
address?AddressThe optional address whose private key will be used to sign message, using the default private key if omitted.

Returns

Promise<string>

A promise which resolves to the signautre of the message.

Overrides

Signer.signMessage

Defined in

dist/bsv/wallets/test-wallet.d.ts:31


signRawTransaction

signRawTransaction(rawTxHex, options): Promise<string>

Sign a raw transaction hex string.

Throws

If any input of the transaction can not be signed properly.

Parameters

NameTypeDescription
rawTxHexstringThe raw transaction hex to sign.
optionsSignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<string>

A promise which resolves to the signed transaction hex string.

Overrides

Signer.signRawTransaction

Defined in

dist/bsv/wallets/test-wallet.d.ts:29


signTransaction

signTransaction(tx, options?): Promise<Transaction>

Sign a transaction object.

Parameters

NameTypeDescription
txTransactionThe transaction object to sign.
options?SignTransactionOptionsThe options for signing, see the details of SignTransactionOptions.

Returns

Promise<Transaction>

A promise which resolves to the signed transaction object.

Overrides

Signer.signTransaction

Defined in

dist/bsv/wallets/test-wallet.d.ts:30


isSigner

Static isSigner(value): value is Signer

Check if an object is a Signer

Parameters

NameTypeDescription
valueanyThe target object

Returns

value is Signer

Returns true if and only if object is a Provider.

Inherited from

Signer.isSigner

Defined in

dist/bsv/abstract-signer.d.ts:162

- + \ No newline at end of file diff --git a/reference/classes/Utils/index.html b/reference/classes/Utils/index.html index 3e6e12ed2..4fddd5f14 100644 --- a/reference/classes/Utils/index.html +++ b/reference/classes/Utils/index.html @@ -4,13 +4,13 @@ Utils | sCrypt - +

Utils

scrypt-ts / Utils

Class: Utils

The Utils library provides a set of commonly used utility functions.

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Utils()

Properties

OutputValueLen

Static Readonly OutputValueLen: bigint

number of string to denote output value

Defined in

dist/smart-contract/builtins/functions.d.ts:910


PubKeyHashLen

Static Readonly PubKeyHashLen: bigint

number of string to denote a public key hash

Defined in

dist/smart-contract/builtins/functions.d.ts:912

Methods

buildAddressOutput

Static buildAddressOutput(addr, amount): ByteString

constructs a standard payment (P2PKH) output from a given address and satoshi amount

Parameters

NameTypeDescription
addrRipemd160the recipient's address
amountbigintthe satoshi amount

Returns

ByteString

a ByteString representing the P2PKH output

Defined in

dist/smart-contract/builtins/functions.d.ts:970


buildAddressScript

Static buildAddressScript(addr): ByteString

constructs a standard payment (P2PKH) script from a given address

Parameters

NameTypeDescription
addrRipemd160the recipient's address

Returns

ByteString

a ByteString representing the P2PKH script

Defined in

dist/smart-contract/builtins/functions.d.ts:963


buildOpreturnScript

Static buildOpreturnScript(data): ByteString

build OP_FALSE OP_RETURN script from data payload

Parameters

NameTypeDescription
dataByteStringthe data payload

Returns

ByteString

a ByteString contains the data payload

Defined in

dist/smart-contract/builtins/functions.d.ts:976


buildOutput

Static buildOutput(outputScript, outputSatoshis): ByteString

build a tx output from its script and satoshi amount

Parameters

NameTypeDescription
outputScriptByteStringthe locking script
outputSatoshisbigintthe satoshi amount

Returns

ByteString

a ByteString that represents an output

Defined in

dist/smart-contract/builtins/functions.d.ts:944


buildPublicKeyHashOutput

Static buildPublicKeyHashOutput(pubKeyHash, amount): ByteString

constructs a P2PKH output from a given PubKeyHash and satoshi amount

Parameters

NameTypeDescription
pubKeyHashRipemd160the recipient's public key hash
amountbigintthe satoshi amount

Returns

ByteString

a ByteString representing the P2PKH output

Defined in

dist/smart-contract/builtins/functions.d.ts:957


buildPublicKeyHashScript

Static buildPublicKeyHashScript(pubKeyHash): ByteString

constructs a P2PKH script from a given PubKeyHash

Parameters

NameTypeDescription
pubKeyHashRipemd160the recipient's public key hash

Returns

ByteString

a ByteString representing the P2PKH script

Defined in

dist/smart-contract/builtins/functions.d.ts:950


fromLEUnsigned

Static fromLEUnsigned(bytes): bigint

convert ByteString to unsigned integer, in sign-magnitude little endian

Parameters

NameTypeDescription
bytesByteStringthe ByteString to be converted

Returns

bigint

returns a number

Defined in

dist/smart-contract/builtins/functions.d.ts:925


readVarint

Static readVarint(buf): ByteString

read a [VarInt (variable integer)][https://learnmeabitcoin.com/technical/varint](https://learnmeabitcoin.com/technical/varint) field from the beginning of 'buf'

Parameters

NameTypeDescription
bufByteStringa buffer ByteString

Returns

ByteString

return a ByteString of the VarInt field

Defined in

dist/smart-contract/builtins/functions.d.ts:931


toLEUnsigned

Static toLEUnsigned(n, l): ByteString

convert signed integer n to unsigned integer of l string, in little endian

Parameters

NameTypeDescription
nbigintthe number to be converted
lbigintexpected length

Returns

ByteString

returns a ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:919


writeVarint

Static writeVarint(buf): ByteString

convert 'b' to a [VarInt (variable integer)][https://learnmeabitcoin.com/technical/varint](https://learnmeabitcoin.com/technical/varint) field, including the preceding length

Parameters

NameTypeDescription
bufByteStringa buffer ByteString

Returns

ByteString

return a ByteString appended the VarInt

Defined in

dist/smart-contract/builtins/functions.d.ts:937

- + \ No newline at end of file diff --git a/reference/classes/VarIntReader/index.html b/reference/classes/VarIntReader/index.html index 9915200dd..98c98bb21 100644 --- a/reference/classes/VarIntReader/index.html +++ b/reference/classes/VarIntReader/index.html @@ -4,13 +4,13 @@ VarIntReader | sCrypt - +

VarIntReader

scrypt-ts / VarIntReader

Class: VarIntReader

A reader to parse a ByteString buffer

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new VarIntReader(buf)

Parameters

NameType
bufByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1067

Properties

buf

buf: ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1065


pos

pos: bigint

Defined in

dist/smart-contract/builtins/functions.d.ts:1066


StateLen

Static Readonly StateLen: bigint

Defined in

dist/smart-contract/builtins/functions.d.ts:1062


Version

Static Readonly Version: bigint

Defined in

dist/smart-contract/builtins/functions.d.ts:1064


VersionLen

Static Readonly VersionLen: bigint

Defined in

dist/smart-contract/builtins/functions.d.ts:1063

Methods

eof

eof(): boolean

Check if all have been read

Returns

boolean

true if all have been read

Defined in

dist/smart-contract/builtins/functions.d.ts:1072


readBool

readBool(): boolean

read a byte as boolean

Returns

boolean

true if the read byte not equal to '00'

Defined in

dist/smart-contract/builtins/functions.d.ts:1082


readBytes

readBytes(): ByteString

read bytes which encoded with bitcoin [value-pushing words][https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script](https://wiki.bitcoinsv.io/index.php/Opcodes_used_in_Bitcoin_Script)

Returns

ByteString

true if all have been read

Defined in

dist/smart-contract/builtins/functions.d.ts:1077


readInt

readInt(): bigint

read bytes as readBytes and convert it to a number with byteString2Int

Returns

bigint

a number

Defined in

dist/smart-contract/builtins/functions.d.ts:1087

- + \ No newline at end of file diff --git a/reference/classes/VarIntWriter/index.html b/reference/classes/VarIntWriter/index.html index 0077ff17b..e6b3167a5 100644 --- a/reference/classes/VarIntWriter/index.html +++ b/reference/classes/VarIntWriter/index.html @@ -4,13 +4,13 @@ VarIntWriter | sCrypt - +

VarIntWriter

scrypt-ts / VarIntWriter

Class: VarIntWriter

A writer that serializes ByteString, boolean, bigint

Table of contents

Constructors

Methods

Constructors

constructor

new VarIntWriter()

Methods

writeBool

Static writeBool(x): ByteString

serializes boolean with fixed 1 byte

Parameters

NameTypeDescription
xbooleana boolean

Returns

ByteString

serialized ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1124


writeBytes

Static writeBytes(buf): ByteString

serializes ByteString with VarInt encoding

Parameters

NameTypeDescription
bufByteStringa ByteString

Returns

ByteString

serialized ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1118


writeInt

Static writeInt(x): ByteString

serializes bigint with VarInt encoding

Parameters

NameTypeDescription
xbiginta boolean

Returns

ByteString

serialized ByteString

Defined in

dist/smart-contract/builtins/functions.d.ts:1130

- + \ No newline at end of file diff --git a/reference/classes/WhatsonchainProvider/index.html b/reference/classes/WhatsonchainProvider/index.html index ffc85e3ff..4591fc023 100644 --- a/reference/classes/WhatsonchainProvider/index.html +++ b/reference/classes/WhatsonchainProvider/index.html @@ -4,7 +4,7 @@ WhatsonchainProvider | sCrypt - + @@ -60,7 +60,7 @@ semantics and does not listen to the 'error' event.

const { once, EventEmitter } = require('events');

async function run() {
const ee = new EventEmitter();

process.nextTick(() => {
ee.emit('myevent', 42);
});

const [value] = await once(ee, 'myevent');
console.log(value);

const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});

try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}

run();

The special handling of the 'error' event is only used when events.once()is used to wait for another event. If events.once() is used to wait for the 'error' event itself, then it is treated as any other kind of event without special handling:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();

once(ee, 'error')
.then(([err]) => console.log('ok', err.message))
.catch((err) => console.log('error', err.message));

ee.emit('error', new Error('boom'));

// Prints: ok boom

An AbortSignal can be used to cancel waiting for the event:

const { EventEmitter, once } = require('events');

const ee = new EventEmitter();
const ac = new AbortController();

async function foo(emitter, event, signal) {
try {
await once(emitter, event, { signal });
console.log('event emitted!');
} catch (error) {
if (error.name === 'AbortError') {
console.error('Waiting for the event was canceled!');
} else {
console.error('There was an error', error.message);
}
}
}

foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Since

v11.13.0, v10.16.0

Parameters

NameType
emitter_NodeEventTarget
eventNamestring | symbol
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:194

Static once(emitter, eventName, options?): Promise<any[]>

Parameters

NameType
emitter_DOMEventTarget
eventNamestring
options?StaticEventEmitterOptions

Returns

Promise<any[]>

Inherited from

Provider.once

Defined in

node_modules/@types/node/events.d.ts:195


setMaxListeners

Static setMaxListeners(n?, ...eventTargets): void

const {
setMaxListeners,
EventEmitter
} = require('events');

const target = new EventTarget();
const emitter = new EventEmitter();

setMaxListeners(5, target, emitter);

Since

v15.4.0

Parameters

NameTypeDescription
n?numberA non-negative number. The maximum number of listeners per EventTarget event.
...eventTargets(EventEmitter | _DOMEventTarget)[]-

Returns

void

Inherited from

Provider.setMaxListeners

Defined in

node_modules/@types/node/events.d.ts:346

- + \ No newline at end of file diff --git a/reference/classes/bsv.Address/index.html b/reference/classes/bsv.Address/index.html index a8d239b87..fba45fb0e 100644 --- a/reference/classes/bsv.Address/index.html +++ b/reference/classes/bsv.Address/index.html @@ -4,13 +4,13 @@ bsv.Address | sCrypt - +

bsv.Address

scrypt-ts / bsv / Address

Class: Address

bsv.Address

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Address(data, network?, type?)

Parameters

NameType
datastring | object | Uint8Array | Buffer
network?string | Network
type?string

Defined in

node_modules/bsv/index.d.ts:1401

Properties

hashBuffer

Readonly hashBuffer: Buffer

Defined in

node_modules/bsv/index.d.ts:1397


network

Readonly network: Network

Defined in

node_modules/bsv/index.d.ts:1398


type

Readonly type: string

Defined in

node_modules/bsv/index.d.ts:1399

Methods

isValid

isValid(data, network?, type?): boolean

Parameters

NameType
datastring | object | Uint8Array | Buffer
network?string | Network
type?string

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1421


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1426


toByteString

toByteString(): ByteString

Returns

ByteString

Defined in

dist/smart-contract/bsv/address.d.ts:5


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1427


toObject

toObject(): Object

Returns

Object

NameType
hashstring
networkstring
typestring

Defined in

node_modules/bsv/index.d.ts:1429


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1428


fromHex

Static fromHex(hex, network?): Address

Parameters

NameType
hexstring
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1407


fromPrivateKey

Static fromPrivateKey(privateKey, network?): Address

Parameters

NameType
privateKeyPrivateKey
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1409


fromPublicKey

Static fromPublicKey(data, network?): Address

Parameters

NameType
dataPublicKey
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1408


fromPublicKeyHash

Static fromPublicKeyHash(hash, network?): Address

Parameters

NameType
hashUint8Array | Buffer
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1413


fromScriptHash

Static fromScriptHash(hash, network?): Address

Parameters

NameType
hashUint8Array | Buffer
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1417


fromString

Static fromString(address, network?): Address

Parameters

NameType
addressstring
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1406

- + \ No newline at end of file diff --git a/reference/classes/bsv.Block/index.html b/reference/classes/bsv.Block/index.html index 25636d14a..58c003360 100644 --- a/reference/classes/bsv.Block/index.html +++ b/reference/classes/bsv.Block/index.html @@ -4,13 +4,13 @@ bsv.Block | sCrypt - +

bsv.Block

scrypt-ts / bsv / Block

Class: Block

bsv.Block

Table of contents

Constructors

Properties

Constructors

constructor

new Block(data)

Parameters

NameType
dataobject | Buffer

Defined in

node_modules/bsv/index.d.ts:1024

Properties

hash

hash: string

Defined in

node_modules/bsv/index.d.ts:1016


header: Object

Type declaration

NameType
prevHashstring
timenumber

Defined in

node_modules/bsv/index.d.ts:1019


height

height: number

Defined in

node_modules/bsv/index.d.ts:1017


transactions

transactions: Transaction[]

Defined in

node_modules/bsv/index.d.ts:1018

- + \ No newline at end of file diff --git a/reference/classes/bsv.BlockHeader/index.html b/reference/classes/bsv.BlockHeader/index.html index ab092f051..98aaddb1b 100644 --- a/reference/classes/bsv.BlockHeader/index.html +++ b/reference/classes/bsv.BlockHeader/index.html @@ -4,13 +4,13 @@ bsv.BlockHeader | sCrypt - +

bsv.BlockHeader

scrypt-ts / bsv / BlockHeader

Class: BlockHeader

bsv.BlockHeader

Table of contents

Constructors

Methods

Constructors

constructor

new BlockHeader(arg)

Parameters

NameType
argobject | JSON | Buffer

Defined in

node_modules/bsv/index.d.ts:1451

Methods

toObject

toObject(): Object

Returns

Object

NameType
bitsnumber
hashstring
merkleRootstring
noncenumber
prevHashstring
timenumber
versionnumber

Defined in

node_modules/bsv/index.d.ts:1453

- + \ No newline at end of file diff --git a/reference/classes/bsv.ECIES/index.html b/reference/classes/bsv.ECIES/index.html index 069269f3b..1233b768c 100644 --- a/reference/classes/bsv.ECIES/index.html +++ b/reference/classes/bsv.ECIES/index.html @@ -4,13 +4,13 @@ bsv.ECIES | sCrypt - +

bsv.ECIES

scrypt-ts / bsv / ECIES

Class: ECIES

bsv.ECIES

Table of contents

Constructors

Methods

Constructors

constructor

new ECIES(opts?, algorithm?)

Parameters

NameType
opts?any
algorithm?string

Defined in

node_modules/bsv/index.d.ts:1008

Methods

decrypt

decrypt(encbuf): Buffer

Parameters

NameType
encbufBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1013


encrypt

encrypt(message): Buffer

Parameters

NameType
messagestring | Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1012


privateKey

privateKey(privateKey): ECIES

Parameters

NameType
privateKeyPrivateKey

Returns

ECIES

Defined in

node_modules/bsv/index.d.ts:1010


publicKey

publicKey(publicKey): ECIES

Parameters

NameType
publicKeyPublicKey

Returns

ECIES

Defined in

node_modules/bsv/index.d.ts:1011

- + \ No newline at end of file diff --git a/reference/classes/bsv.HDPrivateKey/index.html b/reference/classes/bsv.HDPrivateKey/index.html index 7dc097c96..0c2e21d0e 100644 --- a/reference/classes/bsv.HDPrivateKey/index.html +++ b/reference/classes/bsv.HDPrivateKey/index.html @@ -4,13 +4,13 @@ bsv.HDPrivateKey | sCrypt - +

bsv.HDPrivateKey

scrypt-ts / bsv / HDPrivateKey

Class: HDPrivateKey

bsv.HDPrivateKey

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new HDPrivateKey(data?)

Parameters

NameType
data?string | object | Buffer

Defined in

node_modules/bsv/index.d.ts:1125

Properties

depth

Readonly depth: number

Defined in

node_modules/bsv/index.d.ts:1132


fingerPrint

Readonly fingerPrint: Buffer

Defined in

node_modules/bsv/index.d.ts:1135


hdPublicKey

Readonly hdPublicKey: HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1127


network

Readonly network: Network

Defined in

node_modules/bsv/index.d.ts:1131


privateKey

Readonly privateKey: PrivateKey

Defined in

node_modules/bsv/index.d.ts:1133


publicKey

Readonly publicKey: PublicKey

Defined in

node_modules/bsv/index.d.ts:1134


xprivkey

Readonly xprivkey: Buffer

Defined in

node_modules/bsv/index.d.ts:1129


xpubkey

Readonly xpubkey: Buffer

Defined in

node_modules/bsv/index.d.ts:1130

Methods

derive

derive(arg, hardened?): HDPrivateKey

Parameters

NameType
argstring | number
hardened?boolean

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1137


deriveChild

deriveChild(arg, hardened?): HDPrivateKey

Parameters

NameType
argstring | number
hardened?boolean

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1138


deriveNonCompliantChild

deriveNonCompliantChild(arg, hardened?): HDPrivateKey

Parameters

NameType
argstring | number
hardened?boolean

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1139


inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1149


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1147


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1148


toJSON

toJSON(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1146


toObject

toObject(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1145


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1144


fromBuffer

Static fromBuffer(buf): HDPrivateKey

Parameters

NameType
bufBuffer

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1158


fromHex

Static fromHex(hex): HDPrivateKey

Parameters

NameType
hexstring

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1159


fromObject

Static fromObject(obj): HDPrivateKey

Parameters

NameType
objobject

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1153


fromRandom

Static fromRandom(): HDPrivateKey

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1151


fromSeed

Static fromSeed(hexa, network): HDPrivateKey

Parameters

NameType
hexastring | Buffer
networkstring | Network

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1154


fromString

Static fromString(str): HDPrivateKey

Parameters

NameType
strstring

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1152


getSerializedError

Static getSerializedError(data, network?): any

Parameters

NameType
datastring | Buffer
network?string | Network

Returns

any

Defined in

node_modules/bsv/index.d.ts:1165


isValidPath

Static isValidPath(arg, hardened): boolean

Parameters

NameType
argstring | number
hardenedboolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1160


isValidSerialized

Static isValidSerialized(data, network?): boolean

Parameters

NameType
datastring | Buffer
network?string | Network

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1161

- + \ No newline at end of file diff --git a/reference/classes/bsv.HDPublicKey/index.html b/reference/classes/bsv.HDPublicKey/index.html index a95d817ea..598602eb2 100644 --- a/reference/classes/bsv.HDPublicKey/index.html +++ b/reference/classes/bsv.HDPublicKey/index.html @@ -4,13 +4,13 @@ bsv.HDPublicKey | sCrypt - +

bsv.HDPublicKey

scrypt-ts / bsv / HDPublicKey

Class: HDPublicKey

bsv.HDPublicKey

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new HDPublicKey(arg)

Parameters

NameType
argstring | object | Buffer

Defined in

node_modules/bsv/index.d.ts:1172

Properties

depth

Readonly depth: number

Defined in

node_modules/bsv/index.d.ts:1176


fingerPrint

Readonly fingerPrint: Buffer

Defined in

node_modules/bsv/index.d.ts:1178


network

Readonly network: Network

Defined in

node_modules/bsv/index.d.ts:1175


publicKey

Readonly publicKey: PublicKey

Defined in

node_modules/bsv/index.d.ts:1177


xpubkey

Readonly xpubkey: Buffer

Defined in

node_modules/bsv/index.d.ts:1174

Methods

derive

derive(arg, hardened?): HDPublicKey

Parameters

NameType
argstring | number
hardened?boolean

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1180


deriveChild

deriveChild(arg, hardened?): HDPublicKey

Parameters

NameType
argstring | number
hardened?boolean

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1181


inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1188


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1186


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1187


toJSON

toJSON(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1185


toObject

toObject(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1184


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1183


fromBuffer

Static fromBuffer(buf): HDPublicKey

Parameters

NameType
bufBuffer

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1192


fromHDPrivateKey

Static fromHDPrivateKey(hdPrivateKey): HDPublicKey

Parameters

NameType
hdPrivateKeyHDPrivateKey

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1195


fromHex

Static fromHex(hex): HDPublicKey

Parameters

NameType
hexstring

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1193


fromObject

Static fromObject(obj): HDPublicKey

Parameters

NameType
objobject

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1191


fromString

Static fromString(str): HDPublicKey

Parameters

NameType
strstring

Returns

HDPublicKey

Defined in

node_modules/bsv/index.d.ts:1190


getSerializedError

Static getSerializedError(data, network?): any

Parameters

NameType
datastring | Buffer
network?string | Network

Returns

any

Defined in

node_modules/bsv/index.d.ts:1201


isValidPath

Static isValidPath(arg): boolean

Parameters

NameType
argstring | number

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1196


isValidSerialized

Static isValidSerialized(data, network?): boolean

Parameters

NameType
datastring | Buffer
network?string | Network

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1197

- + \ No newline at end of file diff --git a/reference/classes/bsv.MerkleBlock/index.html b/reference/classes/bsv.MerkleBlock/index.html index 98affb1a7..54b947b34 100644 --- a/reference/classes/bsv.MerkleBlock/index.html +++ b/reference/classes/bsv.MerkleBlock/index.html @@ -4,13 +4,13 @@ bsv.MerkleBlock | sCrypt - +

bsv.MerkleBlock

scrypt-ts / bsv / MerkleBlock

Class: MerkleBlock

bsv.MerkleBlock

Table of contents

Constructors

Methods

Constructors

constructor

new MerkleBlock(arg)

Parameters

NameType
argobject | JSON | Buffer

Defined in

node_modules/bsv/index.d.ts:1464

Methods

toObject

toObject(): Object

Returns

Object

NameType
flagsnumber[]
hashesstring[]
header{ bits: number ; hash: string ; merkleRoot: string ; nonce: number ; prevHash: string ; time: number ; version: number }
header.bitsnumber
header.hashstring
header.merkleRootstring
header.noncenumber
header.prevHashstring
header.timenumber
header.versionnumber
numTransactionsnumber

Defined in

node_modules/bsv/index.d.ts:1466

- + \ No newline at end of file diff --git a/reference/classes/bsv.Message/index.html b/reference/classes/bsv.Message/index.html index 8cc2a6c87..8128835b9 100644 --- a/reference/classes/bsv.Message/index.html +++ b/reference/classes/bsv.Message/index.html @@ -4,13 +4,13 @@ bsv.Message | sCrypt - +

bsv.Message

scrypt-ts / bsv / Message

Class: Message

bsv.Message

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Message(message)

Parameters

NameType
messagestring | Buffer

Defined in

node_modules/bsv/index.d.ts:1083

Properties

messageBuffer

Readonly messageBuffer: Buffer

Defined in

node_modules/bsv/index.d.ts:1085


MAGIC_BYTES

Static MAGIC_BYTES: Buffer

Defined in

node_modules/bsv/index.d.ts:1100

Methods

inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1092


sign

sign(privateKey): string

Parameters

NameType
privateKeyPrivateKey

Returns

string

Defined in

node_modules/bsv/index.d.ts:1087


toJSON

toJSON(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1090


toObject

toObject(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1089


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1091


verify

verify(address, signature): boolean

Parameters

NameType
addressstring | Address
signaturestring

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1088


fromJSON

Static fromJSON(json): Message

Parameters

NameType
jsonstring

Returns

Message

Defined in

node_modules/bsv/index.d.ts:1103


fromObject

Static fromObject(obj): Message

Parameters

NameType
objobject

Returns

Message

Defined in

node_modules/bsv/index.d.ts:1104


fromString

Static fromString(str): Message

Parameters

NameType
strstring

Returns

Message

Defined in

node_modules/bsv/index.d.ts:1102


magicHash

Static magicHash(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1101


sign

Static sign(message, privateKey): string

Parameters

NameType
messagestring | Buffer
privateKeyPrivateKey

Returns

string

Defined in

node_modules/bsv/index.d.ts:1094


verify

Static verify(message, address, signature): boolean

Parameters

NameType
messagestring | Buffer
addressstring | Address
signaturestring

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1095

- + \ No newline at end of file diff --git a/reference/classes/bsv.Mnemonic/index.html b/reference/classes/bsv.Mnemonic/index.html index 165c5251e..14edc956a 100644 --- a/reference/classes/bsv.Mnemonic/index.html +++ b/reference/classes/bsv.Mnemonic/index.html @@ -4,13 +4,13 @@ bsv.Mnemonic | sCrypt - +

bsv.Mnemonic

scrypt-ts / bsv / Mnemonic

Class: Mnemonic

bsv.Mnemonic

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Mnemonic(data, wordList?)

Parameters

NameType
datastring | string[]
wordList?string[]

Defined in

node_modules/bsv/index.d.ts:1108

Properties

phrase

Readonly phrase: string

Defined in

node_modules/bsv/index.d.ts:1111


wordList

Readonly wordList: string[]

Defined in

node_modules/bsv/index.d.ts:1110

Methods

inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1116


toHDPrivateKey

toHDPrivateKey(passphrase, network): HDPrivateKey

Parameters

NameType
passphrasestring
networkType

Returns

HDPrivateKey

Defined in

node_modules/bsv/index.d.ts:1114


toSeed

toSeed(passphrase?): Buffer

Parameters

NameType
passphrase?string

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1113


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1115


fromRandom

Static fromRandom(wordlist?): Mnemonic

Parameters

NameType
wordlist?string[]

Returns

Mnemonic

Defined in

node_modules/bsv/index.d.ts:1118


fromSeed

Static fromSeed(seed, wordlist): Mnemonic

Parameters

NameType
seedBuffer
wordliststring[]

Returns

Mnemonic

Defined in

node_modules/bsv/index.d.ts:1121


fromString

Static fromString(mnemonic, wordList?): Mnemonic

Parameters

NameType
mnemonicstring
wordList?string[]

Returns

Mnemonic

Defined in

node_modules/bsv/index.d.ts:1119


isValid

Static isValid(mnemonic, wordList?): boolean

Parameters

NameType
mnemonicstring
wordList?string[]

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1120

- + \ No newline at end of file diff --git a/reference/classes/bsv.Opcode/index.html b/reference/classes/bsv.Opcode/index.html index 496bff80e..5d41df002 100644 --- a/reference/classes/bsv.Opcode/index.html +++ b/reference/classes/bsv.Opcode/index.html @@ -4,7 +4,7 @@ bsv.Opcode | sCrypt - + @@ -17,7 +17,7 @@ Such outputs are provably unspendable and should be given a value of zero Satoshis. These outputs can be pruned from storage in the UTXO set, reducing its size. After the Genesis upgrade in 2020 miners will be free to mine transactions containing FALSE RETURN outputs of any size.

Opcode

Hex

Input

Nothing

Output

Ends script with top value on stack as final result

Static

Defined in

node_modules/bsv/index.d.ts:346


OP_RIPEMD160

Static OP_RIPEMD160: number

Defined in

node_modules/bsv/index.d.ts:572


OP_ROLL

Static OP_ROLL: number

The item n back in the stack is copied to the top.

Opcode

Hex

Input

xn ... x2 x1 x0 {n}

Output

... x2 x1 x0 xn

Static

Defined in

node_modules/bsv/index.d.ts:493


OP_ROT

Static OP_ROT: number

The top three items on the stack are rotated to the left.

Opcode

Hex

Input

x1 x2 x3

Output

x2 x3 x1

Static

Defined in

node_modules/bsv/index.d.ts:502


OP_RSHIFT

Static OP_RSHIFT: number

Defined in

node_modules/bsv/index.d.ts:555


OP_SHA1

Static OP_SHA1: number

Defined in

node_modules/bsv/index.d.ts:573


OP_SHA256

Static OP_SHA256: number

Defined in

node_modules/bsv/index.d.ts:574


OP_SIZE

Static OP_SIZE: number

Defined in

node_modules/bsv/index.d.ts:527


OP_SPLIT

Static OP_SPLIT: number

Defined in

node_modules/bsv/index.d.ts:524


OP_SUB

Static OP_SUB: number

Defined in

node_modules/bsv/index.d.ts:550


OP_SWAP

Static OP_SWAP: number

The top two items on the stack are swapped.

Opcode

Hex

Input

x1 x2

Output

x2 x1

Static

Defined in

node_modules/bsv/index.d.ts:511


OP_TOALTSTACK

Static OP_TOALTSTACK: number

Puts the input onto the top of the alt stack. Removes it from the main stack.

Opcode

Hex

Input

x1

Output

(alt)x1

Static

Defined in

node_modules/bsv/index.d.ts:358


OP_TRUE

Static OP_TRUE: number

The number 1 is pushed onto the stack.

Opcode

Hex

Input

Nothing

Output

1

Static

Defined in

node_modules/bsv/index.d.ts:83


OP_TUCK

Static OP_TUCK: number

The item at the top of the stack is copied and inserted before the second-to-top item.

Opcode

Hex

Input

x1 x2

Output

x2 x1 x2

Static

Defined in

node_modules/bsv/index.d.ts:520


OP_VER

Static OP_VER: number

DISABLED.Puts the version of the protocol under which this transaction will be evaluated onto the stack.

Opcode

Hex

Input

Nothing

Output

Protocol version

Static

Defined in

node_modules/bsv/index.d.ts:247


OP_VERIF

Static OP_VERIF: number

DISABLED

Opcode

Hex

Static

Defined in

node_modules/bsv/index.d.ts:292


OP_VERIFY

Static OP_VERIFY: number

Marks transaction as invalid if top stack value is not true. The top stack value is removed.

Opcode

Hex

Input

True / false

Output

Nothing / fail

Static

Defined in

node_modules/bsv/index.d.ts:334


OP_VERNOTIF

Static OP_VERNOTIF: number

DISABLED

Opcode

Hex

Static

Defined in

node_modules/bsv/index.d.ts:299


OP_WITHIN

Static OP_WITHIN: number

Defined in

node_modules/bsv/index.d.ts:569


OP_XOR

Static OP_XOR: number

Defined in

node_modules/bsv/index.d.ts:533

- + \ No newline at end of file diff --git a/reference/classes/bsv.PrivateKey/index.html b/reference/classes/bsv.PrivateKey/index.html index 76cb50e45..799b93ff4 100644 --- a/reference/classes/bsv.PrivateKey/index.html +++ b/reference/classes/bsv.PrivateKey/index.html @@ -4,13 +4,13 @@ bsv.PrivateKey | sCrypt - +

bsv.PrivateKey

scrypt-ts / bsv / PrivateKey

Class: PrivateKey

bsv.PrivateKey

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new PrivateKey(key?, network?)

Parameters

NameType
key?string | PrivateKey
network?Type

Defined in

node_modules/bsv/index.d.ts:1028

Properties

bn

Readonly bn: BN

Defined in

node_modules/bsv/index.d.ts:1030


compressed

Readonly compressed: boolean

Defined in

node_modules/bsv/index.d.ts:1033


network

Readonly network: Network

Defined in

node_modules/bsv/index.d.ts:1034


publicKey

Readonly publicKey: PublicKey

Defined in

node_modules/bsv/index.d.ts:1032

Methods

inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1045


toAddress

toAddress(network?): Address

Parameters

NameType
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1036


toBigNumber

toBigNumber(): any

Returns

any

Defined in

node_modules/bsv/index.d.ts:1043


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1044


toByteString

toByteString(): ByteString

Returns

ByteString

Defined in

dist/smart-contract/bsv/privateKey.d.ts:5


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1042


toJSON

toJSON(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1040


toObject

toObject(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1039


toPublicKey

toPublicKey(): PublicKey

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1037


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1038


toWIF

toWIF(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1041


fromBuffer

Static fromBuffer(buf, network): PrivateKey

Parameters

NameType
bufBuffer
networkType

Returns

PrivateKey

Defined in

node_modules/bsv/index.d.ts:1050


fromHex

Static fromHex(hex, network): PrivateKey

Parameters

NameType
hexstring
networkType

Returns

PrivateKey

Defined in

node_modules/bsv/index.d.ts:1051


fromRandom

Static fromRandom(netowrk?): PrivateKey

Parameters

NameType
netowrk?string | Network

Returns

PrivateKey

Defined in

node_modules/bsv/index.d.ts:1049


fromString

Static fromString(str): PrivateKey

Parameters

NameType
strstring

Returns

PrivateKey

Defined in

node_modules/bsv/index.d.ts:1047


fromWIF

Static fromWIF(str): PrivateKey

Parameters

NameType
strstring

Returns

PrivateKey

Defined in

node_modules/bsv/index.d.ts:1048


getValidationError

Static getValidationError(data): any

Parameters

NameType
datastring

Returns

any

Defined in

node_modules/bsv/index.d.ts:1052


isValid

Static isValid(data): boolean

Parameters

NameType
datastring

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1053

- + \ No newline at end of file diff --git a/reference/classes/bsv.PublicKey/index.html b/reference/classes/bsv.PublicKey/index.html index 1f72b1de4..9c60dc5fd 100644 --- a/reference/classes/bsv.PublicKey/index.html +++ b/reference/classes/bsv.PublicKey/index.html @@ -4,13 +4,13 @@ bsv.PublicKey | sCrypt - +

bsv.PublicKey

scrypt-ts / bsv / PublicKey

Class: PublicKey

bsv.PublicKey

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new PublicKey(source, extra?)

Parameters

NameType
sourcestring | PublicKey | Point
extra?object

Defined in

node_modules/bsv/index.d.ts:1057

Properties

compressed

Readonly compressed: boolean

Defined in

node_modules/bsv/index.d.ts:1060


network

Readonly network: Network

Defined in

node_modules/bsv/index.d.ts:1061


point

Readonly point: Point

Defined in

node_modules/bsv/index.d.ts:1059

Methods

inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1069


toAddress

toAddress(network?): Address

Parameters

NameType
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1066


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1065


toByteString

toByteString(): ByteString

Returns

ByteString

Defined in

dist/smart-contract/bsv/publicKey.d.ts:5


toDER

toDER(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1063


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1068


toObject

toObject(): object

Returns

object

Defined in

node_modules/bsv/index.d.ts:1064


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1067


fromBuffer

Static fromBuffer(buf, strict?): PublicKey

Parameters

NameType
bufBuffer
strict?boolean

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1072


fromDER

Static fromDER(buf, strict?): PublicKey

Parameters

NameType
bufBuffer
strict?boolean

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1073


fromHex

Static fromHex(hex): PublicKey

Parameters

NameType
hexstring

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1077


fromPrivateKey

Static fromPrivateKey(privateKey): PublicKey

Parameters

NameType
privateKeyPrivateKey

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1071


fromString

Static fromString(str): PublicKey

Parameters

NameType
strstring

Returns

PublicKey

Defined in

node_modules/bsv/index.d.ts:1076


getValidationError

Static getValidationError(data): any

Parameters

NameType
datastring

Returns

any

Defined in

node_modules/bsv/index.d.ts:1078


isValid

Static isValid(data): boolean

Parameters

NameType
datastring

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1079

- + \ No newline at end of file diff --git a/reference/classes/bsv.Script-1/index.html b/reference/classes/bsv.Script-1/index.html index 3f652c809..32d7af8b6 100644 --- a/reference/classes/bsv.Script-1/index.html +++ b/reference/classes/bsv.Script-1/index.html @@ -4,13 +4,13 @@ bsv.Script-1 | sCrypt - +

bsv.Script-1

scrypt-ts / bsv / Script

Class: Script

bsv.Script

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Script(data)

Parameters

NameType
datastring | object

Defined in

node_modules/bsv/index.d.ts:1306

Properties

chunks

chunks: IOpChunk[]

Defined in

node_modules/bsv/index.d.ts:1308


length

length: number

Defined in

node_modules/bsv/index.d.ts:1309

Methods

add

add(obj): Script

Parameters

NameType
objany

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1347


checkMinimalPush

checkMinimalPush(i): boolean

Parameters

NameType
inumber

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1356


classify

classify(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1340


classifyInput

classifyInput(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1341


classifyOutput

classifyOutput(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1342


clone

clone(): Script

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1361


equals

equals(script): boolean

Parameters

NameType
scriptScript

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1352


findAndDelete

findAndDelete(script): Script

Parameters

NameType
scriptScript

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1355


getAddressInfo

getAddressInfo(): boolean | Address

Returns

boolean | Address

Defined in

node_modules/bsv/index.d.ts:1354


getData

getData(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1337


getPublicKey

getPublicKey(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1321


getPublicKeyHash

getPublicKeyHash(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1322


getSignatureOperationsCount

getSignatureOperationsCount(accurate): number

Parameters

NameType
accurateboolean

Returns

number

Defined in

node_modules/bsv/index.d.ts:1357


hasCodeseparators

hasCodeseparators(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1349


isDataOut

isDataOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1334


isMultisigIn

isMultisigIn(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1333


isMultisigOut

isMultisigOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1332


isPublicKeyHashIn

isPublicKeyHashIn(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1319


isPublicKeyHashOut

isPublicKeyHashOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1318


isPublicKeyIn

isPublicKeyIn(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1325


isPublicKeyOut

isPublicKeyOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1324


isPushOnly

isPushOnly(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1338


isSafeDataOut

isSafeDataOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1335


isScriptHashIn

isScriptHashIn(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1331


isScriptHashOut

isScriptHashOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1327


isStandard

isStandard(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1344


isWitnessProgram

isWitnessProgram(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1330


isWitnessPublicKeyHashOut

isWitnessPublicKeyHashOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1329


isWitnessScriptHashOut

isWitnessScriptHashOut(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1328


prepend

prepend(obj): Script

Parameters

NameType
objany

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1346


removeCodeseparators

removeCodeseparators(): Script

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1350


set

set(obj): Script

Parameters

NameType
objobject

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1311


subScript

subScript(n): Script

Parameters

NameType
nnumber

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1363


toASM

toASM(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1314


toAddress

toAddress(network?): Address

Parameters

NameType
network?Type

Returns

Address

Defined in

node_modules/bsv/index.d.ts:1359


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1313


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1316


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1315


fromChunks

Static fromChunks(chunks): Script

Parameters

NameType
chunksIOpChunk[]

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1365

- + \ No newline at end of file diff --git a/reference/classes/bsv.Script.Interpreter-1/index.html b/reference/classes/bsv.Script.Interpreter-1/index.html index 602e81e45..98250ab0b 100644 --- a/reference/classes/bsv.Script.Interpreter-1/index.html +++ b/reference/classes/bsv.Script.Interpreter-1/index.html @@ -4,13 +4,13 @@ bsv.Script.Interpreter-1 | sCrypt - +

bsv.Script.Interpreter-1

scrypt-ts / bsv / Script / Interpreter

Class: Interpreter

bsv.Script.Interpreter

Table of contents

Constructors

Properties

Constructors

constructor

new Interpreter()

Properties

errstr

Optional errstr: string

Defined in

node_modules/bsv/index.d.ts:1293


stepListener

Optional stepListener: StepListenerFunction

Defined in

node_modules/bsv/index.d.ts:1292


verify

verify: (inputScript: Script, outputScript: Script, txn: Transaction, nin: number, flags: any, satoshisBN: BN) => boolean

Type declaration

▸ (inputScript, outputScript, txn, nin, flags, satoshisBN): boolean

Parameters
NameType
inputScriptScript
outputScriptScript
txnTransaction
ninnumber
flagsany
satoshisBNBN
Returns

boolean

Defined in

node_modules/bsv/index.d.ts:1294


DEFAULT_FLAGS

Static DEFAULT_FLAGS: number

Defined in

node_modules/bsv/index.d.ts:1291


MAXIMUM_ELEMENT_SIZE

Static MAXIMUM_ELEMENT_SIZE: number

Defined in

node_modules/bsv/index.d.ts:1289


MAX_SCRIPT_ELEMENT_SIZE

Static MAX_SCRIPT_ELEMENT_SIZE: number

Defined in

node_modules/bsv/index.d.ts:1288


SCRIPT_ENABLE_MAGNETIC_OPCODES

Static SCRIPT_ENABLE_MAGNETIC_OPCODES: number

Defined in

node_modules/bsv/index.d.ts:1276


SCRIPT_ENABLE_MONOLITH_OPCODES

Static SCRIPT_ENABLE_MONOLITH_OPCODES: number

Defined in

node_modules/bsv/index.d.ts:1277


SCRIPT_ENABLE_SIGHASH_FORKID

Static SCRIPT_ENABLE_SIGHASH_FORKID: number

Defined in

node_modules/bsv/index.d.ts:1279


SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY

Static SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY: number

Defined in

node_modules/bsv/index.d.ts:1286


SCRIPT_VERIFY_CHECKSEQUENCEVERIFY

Static SCRIPT_VERIFY_CHECKSEQUENCEVERIFY: number

Defined in

node_modules/bsv/index.d.ts:1287


SCRIPT_VERIFY_CLEANSTACK

Static SCRIPT_VERIFY_CLEANSTACK: number

Defined in

node_modules/bsv/index.d.ts:1290


SCRIPT_VERIFY_DERSIG

Static SCRIPT_VERIFY_DERSIG: number

Defined in

node_modules/bsv/index.d.ts:1282


SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS

Static SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS: number

Defined in

node_modules/bsv/index.d.ts:1285


SCRIPT_VERIFY_LOW_S

Static SCRIPT_VERIFY_LOW_S: number

Defined in

node_modules/bsv/index.d.ts:1280


SCRIPT_VERIFY_MINIMALDATA

Static SCRIPT_VERIFY_MINIMALDATA: number

Defined in

node_modules/bsv/index.d.ts:1283


SCRIPT_VERIFY_NULLDUMMY

Static SCRIPT_VERIFY_NULLDUMMY: number

Defined in

node_modules/bsv/index.d.ts:1284


SCRIPT_VERIFY_NULLFAIL

Static SCRIPT_VERIFY_NULLFAIL: number

Defined in

node_modules/bsv/index.d.ts:1281


SCRIPT_VERIFY_STRICTENC

Static SCRIPT_VERIFY_STRICTENC: number

Defined in

node_modules/bsv/index.d.ts:1278

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction-1/index.html b/reference/classes/bsv.Transaction-1/index.html index ee7a98234..9eb5aad91 100644 --- a/reference/classes/bsv.Transaction-1/index.html +++ b/reference/classes/bsv.Transaction-1/index.html @@ -4,13 +4,13 @@ bsv.Transaction-1 | sCrypt - +

bsv.Transaction-1

scrypt-ts / bsv / Transaction

Class: Transaction

bsv.Transaction

Hierarchy

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Transaction(raw?)

Parameters

NameType
raw?string

Defined in

node_modules/bsv/index.d.ts:910

Properties

_estimateSize

_estimateSize: number

Defined in

node_modules/bsv/index.d.ts:963


hash

Readonly hash: string

Defined in

node_modules/bsv/index.d.ts:904


id

Readonly id: string

Defined in

node_modules/bsv/index.d.ts:903


inputAmount

Readonly inputAmount: number

Defined in

node_modules/bsv/index.d.ts:905


inputs

inputs: Input[]

Defined in

node_modules/bsv/index.d.ts:901


nLockTime

nLockTime: number

Defined in

node_modules/bsv/index.d.ts:908


nid

nid: string

Defined in

node_modules/bsv/index.d.ts:907


outputAmount

Readonly outputAmount: number

Defined in

node_modules/bsv/index.d.ts:906


outputs

outputs: Output[]

Defined in

node_modules/bsv/index.d.ts:902


DUMMY_PRIVATEKEY

Static DUMMY_PRIVATEKEY: PrivateKey

Defined in

node_modules/bsv/index.d.ts:900

Methods

_estimateFee

_estimateFee(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:962


_getUnspentValue

_getUnspentValue(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:961


addData

addData(value): Transaction

Parameters

NameType
valuestring | Buffer

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:933


addDummyInput

addDummyInput(script, satoshis): Transaction

Parameters

NameType
scriptScript
satoshisnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:987


addInput

addInput(input, outputScript?, satoshis?): Transaction

Parameters

NameType
inputInput
outputScript?string | Script
satoshis?number

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:927


addInputFromPrevTx

addInputFromPrevTx(prevTx, outputIndex?): Transaction

Parameters

NameType
prevTxTransaction
outputIndex?number

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:986


addOutput

addOutput(output): Transaction

Parameters

NameType
outputOutput

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:932


applySignature

applySignature(sig): Transaction

Parameters

NameType
sigObject
sig.inputIndexnumber
sig.publicKeyPublicKey
sig.signatureSignature
sig.sigtypenumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:925


change

change(address): Transaction

Parameters

NameType
addressstring | Address

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:918


checkFeeRate

checkFeeRate(feePerKb?): boolean

Parameters

NameType
feePerKb?number

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:982


dummyChange

dummyChange(): Transaction

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:988


enableRBF

enableRBF(): Transaction

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:947


fee

fee(amount): Transaction

Parameters

NameType
amountnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:919


feePerKb

feePerKb(amount): Transaction

Parameters

NameType
amountnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:920


from

from(utxos): Transaction

Parameters

NameType
utxosIUnspentOutput | IUnspentOutput[]

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:912


fromBuffer

fromBuffer(buffer): Transaction

Parameters

NameType
bufferBuffer

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:916


fromString

fromString(rawTxHex): Transaction

Parameters

NameType
rawTxHexstring

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:915


getChangeAddress

getChangeAddress(): Address

Returns

Address

Defined in

node_modules/bsv/index.d.ts:940


getChangeAmount

getChangeAmount(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:980


getChangeOutput

getChangeOutput(): Output

Returns

Output

Defined in

node_modules/bsv/index.d.ts:939


getEstimateFee

getEstimateFee(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:981


getFee

getFee(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:938


getInputAmount

getInputAmount(inputIndex): number

Parameters

NameType
inputIndexnumber

Returns

number

Defined in

node_modules/bsv/index.d.ts:1003


getLockTime

getLockTime(): number | Date

Returns

number | Date

Defined in

node_modules/bsv/index.d.ts:941


getOutputAmount

getOutputAmount(outputIndex): number

Parameters

NameType
outputIndexnumber

Returns

number

Defined in

node_modules/bsv/index.d.ts:1004


getPreimage

getPreimage(inputIndex, sigtype?, isLowS?): string

Parameters

NameType
inputIndexnumber
sigtype?number
isLowS?boolean

Returns

string

Defined in

node_modules/bsv/index.d.ts:985


getSerializationError

getSerializationError(opts?): any

Parameters

NameType
opts?object

Returns

any

Defined in

node_modules/bsv/index.d.ts:959


getSignature

getSignature(inputIndex, privateKey?, sigtype?): string | string[]

Parameters

NameType
inputIndexnumber
privateKey?PrivateKey | PrivateKey[]
sigtype?number

Returns

string | string[]

Defined in

node_modules/bsv/index.d.ts:984


hasWitnesses

hasWitnesses(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:937


inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:950


isCoinbase

isCoinbase(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:945


isFullySigned

isFullySigned(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:957


isRBF

isRBF(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:948


isSealed

isSealed(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:979


lockUntilBlockHeight

lockUntilBlockHeight(height): Transaction

Parameters

NameType
heightnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:935


lockUntilDate

lockUntilDate(time): Transaction

Parameters

NameType
timenumber | Date

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:934


prevouts

prevouts(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:983


seal

seal(): Transaction

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:977


sealAsync

sealAsync(): Promise<Transaction>

Returns

Promise<Transaction>

Defined in

node_modules/bsv/index.d.ts:978


serialize

serialize(opts?): string

Parameters

NameType
opts?object

Returns

string

Defined in

node_modules/bsv/index.d.ts:951


setInputScript

setInputScript(inputIndex, unlockingScript): Transaction

Parameters

NameType
inputIndexnumber | { inputIndex: number ; isLowS?: boolean ; privateKey?: PrivateKey | PrivateKey[] ; sigtype?: number }
unlockingScriptScript | (tx: Transaction, outputInPrevTx: Output) => Script

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:964


setInputScriptAsync

setInputScriptAsync(inputIndex, callback): Promise<Transaction>

Parameters

NameType
inputIndexnumber | { inputIndex: number ; isLowS?: boolean ; sigtype?: number }
callback(tx: Transaction, outputInPrevTx: Output) => Promise<Script>

Returns

Promise<Transaction>

Defined in

node_modules/bsv/index.d.ts:970


setInputSequence

setInputSequence(inputIndex, sequence): Transaction

Parameters

NameType
inputIndexnumber
sequencenumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:975


setLockTime

setLockTime(t): Transaction

Parameters

NameType
tnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:942


setOutput

setOutput(outputIndex, output): Transaction

Parameters

NameType
outputIndexnumber
outputOutput | (tx: Transaction) => Output

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:976


sign

sign(privateKey, sigtype?): Transaction

Parameters

NameType
privateKeystring | string[] | PrivateKey | PrivateKey[]
sigtype?number

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:921


to

to(address, amount): Transaction

Parameters

NameType
addressstring | Address | Address[]
amountnumber

Returns

Transaction

Defined in

node_modules/bsv/index.d.ts:917


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:955


toObject

toObject(): any

Returns

any

Defined in

node_modules/bsv/index.d.ts:954


uncheckedSerialize

uncheckedSerialize(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:952


verify

verify(): string | true

Returns

string | true

Defined in

node_modules/bsv/index.d.ts:944


verifyInputScript

verifyInputScript(inputIndex): Object

Deprecated

please use verifyScript instead

Parameters

NameType
inputIndexnumber

Returns

Object

NameType
errorstring
failedAtany
successboolean

Defined in

node_modules/bsv/index.d.ts:993


verifyScript

verifyScript(inputIndex): Object

Parameters

NameType
inputIndexnumber

Returns

Object

NameType
errorstring
failedAtany
successboolean

Defined in

node_modules/bsv/index.d.ts:998


verifySignature

verifySignature(sig, pubkey, nin, subscript, satoshisBN, flags): boolean

Parameters

NameType
sigSignature
pubkeyPublicKey
ninnumber
subscriptScript
satoshisBNBN
flagsnumber

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:926

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction.Input-1/index.html b/reference/classes/bsv.Transaction.Input-1/index.html index b635039c6..75d090843 100644 --- a/reference/classes/bsv.Transaction.Input-1/index.html +++ b/reference/classes/bsv.Transaction.Input-1/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Input-1 | sCrypt - +

bsv.Transaction.Input-1

scrypt-ts / bsv / Transaction / Input

Class: Input

bsv.Transaction.Input

Hierarchy

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Input(params)

Parameters

NameType
paramsobject

Defined in

node_modules/bsv/index.d.ts:833

Properties

output

Optional output: Output

Defined in

node_modules/bsv/index.d.ts:832


outputIndex

Readonly outputIndex: number

Defined in

node_modules/bsv/index.d.ts:829


prevTxId

Readonly prevTxId: Buffer

Defined in

node_modules/bsv/index.d.ts:828


script

Readonly script: Script

Defined in

node_modules/bsv/index.d.ts:831


sequenceNumber

sequenceNumber: number

Defined in

node_modules/bsv/index.d.ts:830

Methods

getPreimage

getPreimage(tx, inputIndex, sigtype?, isLowS?): any

Parameters

NameType
txTransaction
inputIndexnumber
sigtype?number
isLowS?boolean

Returns

any

Defined in

node_modules/bsv/index.d.ts:837


getSignatures

getSignatures(tx, privateKey, inputIndex, sigtype?): any

Parameters

NameType
txTransaction
privateKeyPrivateKey
inputIndexnumber
sigtype?number

Returns

any

Defined in

node_modules/bsv/index.d.ts:836


isValidSignature

isValidSignature(tx, sig): boolean

Parameters

NameType
txTransaction
sigany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:834


setScript

setScript(script): Input

Parameters

NameType
scriptScript

Returns

Input

Defined in

node_modules/bsv/index.d.ts:835

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction.Input.PublicKeyHash/index.html b/reference/classes/bsv.Transaction.Input.PublicKeyHash/index.html index ee1a96d4b..d93a86399 100644 --- a/reference/classes/bsv.Transaction.Input.PublicKeyHash/index.html +++ b/reference/classes/bsv.Transaction.Input.PublicKeyHash/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Input.PublicKeyHash | sCrypt - +

bsv.Transaction.Input.PublicKeyHash

scrypt-ts / bsv / Transaction / Input / PublicKeyHash

Class: PublicKeyHash

Transaction.Input.PublicKeyHash

Hierarchy

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new PublicKeyHash(params)

Parameters

NameType
paramsobject

Inherited from

Input.constructor

Defined in

node_modules/bsv/index.d.ts:833

Properties

output

Optional output: Output

Inherited from

Input.output

Defined in

node_modules/bsv/index.d.ts:832


outputIndex

Readonly outputIndex: number

Inherited from

Input.outputIndex

Defined in

node_modules/bsv/index.d.ts:829


prevTxId

Readonly prevTxId: Buffer

Inherited from

Input.prevTxId

Defined in

node_modules/bsv/index.d.ts:828


script

Readonly script: Script

Inherited from

Input.script

Defined in

node_modules/bsv/index.d.ts:831


sequenceNumber

sequenceNumber: number

Inherited from

Input.sequenceNumber

Defined in

node_modules/bsv/index.d.ts:830

Methods

getPreimage

getPreimage(tx, inputIndex, sigtype?, isLowS?): any

Parameters

NameType
txTransaction
inputIndexnumber
sigtype?number
isLowS?boolean

Returns

any

Inherited from

Input.getPreimage

Defined in

node_modules/bsv/index.d.ts:837


getSignatures

getSignatures(tx, privateKey, inputIndex, sigtype?): any

Parameters

NameType
txTransaction
privateKeyPrivateKey
inputIndexnumber
sigtype?number

Returns

any

Inherited from

Input.getSignatures

Defined in

node_modules/bsv/index.d.ts:836


isValidSignature

isValidSignature(tx, sig): boolean

Parameters

NameType
txTransaction
sigany

Returns

boolean

Inherited from

Input.isValidSignature

Defined in

node_modules/bsv/index.d.ts:834


setScript

setScript(script): PublicKeyHash

Parameters

NameType
scriptScript

Returns

PublicKeyHash

Inherited from

Input.setScript

Defined in

node_modules/bsv/index.d.ts:835

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction.Output/index.html b/reference/classes/bsv.Transaction.Output/index.html index 2f4d884ea..ffd470f53 100644 --- a/reference/classes/bsv.Transaction.Output/index.html +++ b/reference/classes/bsv.Transaction.Output/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Output | sCrypt - +

bsv.Transaction.Output

scrypt-ts / bsv / Transaction / Output

Class: Output

bsv.Transaction.Output

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Output(data)

Parameters

NameType
dataObject
data.satoshisnumber
data.scriptScript

Defined in

node_modules/bsv/index.d.ts:814

Properties

satoshis

Readonly satoshis: number

Defined in

node_modules/bsv/index.d.ts:811


satoshisBN

Readonly satoshisBN: BN

Defined in

node_modules/bsv/index.d.ts:812


script

Readonly script: Script

Defined in

node_modules/bsv/index.d.ts:810


spentTxId

spentTxId: string

Defined in

node_modules/bsv/index.d.ts:813

Methods

getSize

getSize(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:822


inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:820


setScript

setScript(script): Output

Parameters

NameType
scriptstring | Buffer | Script

Returns

Output

Defined in

node_modules/bsv/index.d.ts:819


toBufferWriter

toBufferWriter(writer?): BufferWriter

Parameters

NameType
writer?BufferWriter

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:823


toObject

toObject(): Object

Returns

Object

NameType
satoshisnumber
scriptstring

Defined in

node_modules/bsv/index.d.ts:821


fromBufferReader

Static fromBufferReader(reader): Output

Parameters

NameType
readerBufferReader

Returns

Output

Defined in

node_modules/bsv/index.d.ts:824

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction.Signature/index.html b/reference/classes/bsv.Transaction.Signature/index.html index 77b9f2d48..90c3e77d2 100644 --- a/reference/classes/bsv.Transaction.Signature/index.html +++ b/reference/classes/bsv.Transaction.Signature/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Signature | sCrypt - +

bsv.Transaction.Signature

scrypt-ts / bsv / Transaction / Signature

Class: Signature

bsv.Transaction.Signature

Table of contents

Constructors

Properties

Constructors

constructor

new Signature(arg)

Parameters

NameType
argstring | object | Signature

Defined in

node_modules/bsv/index.d.ts:851

Properties

inputIndex

inputIndex: number

Defined in

node_modules/bsv/index.d.ts:857


outputIndex

outputIndex: number

Defined in

node_modules/bsv/index.d.ts:856


prevTxId

prevTxId: Buffer

Defined in

node_modules/bsv/index.d.ts:855


publicKey

publicKey: PublicKey

Defined in

node_modules/bsv/index.d.ts:854


signature

signature: Signature

Defined in

node_modules/bsv/index.d.ts:853


sigtype

sigtype: number

Defined in

node_modules/bsv/index.d.ts:858

- + \ No newline at end of file diff --git a/reference/classes/bsv.Transaction.UnspentOutput/index.html b/reference/classes/bsv.Transaction.UnspentOutput/index.html index 33619a7be..9165da241 100644 --- a/reference/classes/bsv.Transaction.UnspentOutput/index.html +++ b/reference/classes/bsv.Transaction.UnspentOutput/index.html @@ -4,13 +4,13 @@ bsv.Transaction.UnspentOutput | sCrypt - +

bsv.Transaction.UnspentOutput

scrypt-ts / bsv / Transaction / UnspentOutput

Class: UnspentOutput

bsv.Transaction.UnspentOutput

Table of contents

Constructors

Methods

Constructors

constructor

new UnspentOutput(data)

Parameters

NameType
dataIUnspentOutput

Defined in

node_modules/bsv/index.d.ts:803

Methods

inspect

inspect(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:804


toObject

toObject(): IUnspentOutput

Returns

IUnspentOutput

Defined in

node_modules/bsv/index.d.ts:805


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:806


fromObject

Static fromObject(o): UnspentOutput

Parameters

NameType
oIUnspentOutput

Returns

UnspentOutput

Defined in

node_modules/bsv/index.d.ts:802

- + \ No newline at end of file diff --git a/reference/classes/bsv.Unit/index.html b/reference/classes/bsv.Unit/index.html index b75884b5e..f5156af1a 100644 --- a/reference/classes/bsv.Unit/index.html +++ b/reference/classes/bsv.Unit/index.html @@ -4,13 +4,13 @@ bsv.Unit | sCrypt - +

bsv.Unit

scrypt-ts / bsv / Unit

Class: Unit

bsv.Unit

Table of contents

Constructors

Methods

Constructors

constructor

new Unit(amount, unitPreference)

Parameters

NameType
amountnumber
unitPreferencestring

Defined in

node_modules/bsv/index.d.ts:1442

Methods

toBTC

toBTC(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:1444


toBits

toBits(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:1446


toMilis

toMilis(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:1445


toSatoshis

toSatoshis(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:1447


fromBTC

Static fromBTC(amount): Unit

Parameters

NameType
amountnumber

Returns

Unit

Defined in

node_modules/bsv/index.d.ts:1437


fromBits

Static fromBits(amount): Unit

Parameters

NameType
amountnumber

Returns

Unit

Defined in

node_modules/bsv/index.d.ts:1439


fromMilis

Static fromMilis(amount): Unit

Parameters

NameType
amountnumber

Returns

Unit

Defined in

node_modules/bsv/index.d.ts:1438


fromSatoshis

Static fromSatoshis(amount): Unit

Parameters

NameType
amountnumber

Returns

Unit

Defined in

node_modules/bsv/index.d.ts:1440

- + \ No newline at end of file diff --git a/reference/classes/bsv.crypto.BN/index.html b/reference/classes/bsv.crypto.BN/index.html index 1798b0296..33358e28e 100644 --- a/reference/classes/bsv.crypto.BN/index.html +++ b/reference/classes/bsv.crypto.BN/index.html @@ -4,13 +4,13 @@ bsv.crypto.BN | sCrypt - +

bsv.crypto.BN

scrypt-ts / bsv / crypto / BN

Class: BN

bsv.crypto.BN

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new BN(number, base?, endian?)

Parameters

NameType
numberstring | number | bigint | number[] | readonly number[] | Buffer | BN
base?number
endian?Endianness

Defined in

node_modules/bsv/index.d.ts:658

Properties

Minus1

Static Minus1: BN

Defined in

node_modules/bsv/index.d.ts:666


One

Static One: BN

Defined in

node_modules/bsv/index.d.ts:665


Zero

Static Zero: BN

Defined in

node_modules/bsv/index.d.ts:664

Methods

abs

abs(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:694


add

add(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:695

add(one): BN

Parameters

NameType
oneBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:720


and

and(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:705


bincn

bincn(b): BN

Parameters

NameType
bnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:712


bitLength

bitLength(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:674


byteLength

byteLength(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:676


clone

clone(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:668


cmp

cmp(b): number

Parameters

NameType
bany

Returns

number

Defined in

node_modules/bsv/index.d.ts:682


div

div(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:700


divRound

divRound(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:702


egcd

egcd(b): Object

Parameters

NameType
bBN

Returns

Object

NameType
aBN
bBN
gcdBN

Defined in

node_modules/bsv/index.d.ts:716


eq

eq(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:687


eqn

eqn(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:688


gcd

gcd(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:715


gt

gt(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:685


gte

gte(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:686


gten

gten(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:689


invm

invm(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:717


isBN

isBN(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:681

isBN(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:691


isEven

isEven(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:678


isNeg

isNeg(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:677


isOdd

isOdd(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:679


isZero

isZero(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:680


lt

lt(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:683


lte

lte(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:684


lten

lten(b): boolean

Parameters

NameType
bany

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:690


maskn

maskn(b): BN

Parameters

NameType
bnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:711


mod

mod(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:701


mul

mul(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:697


neg

neg(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:693

neg(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:719


notn

notn(w): BN

Parameters

NameType
wnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:713


or

or(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:704


pow

pow(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:699


setn

setn(b): BN

Parameters

NameType
bnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:707


shln

shln(b): BN

Parameters

NameType
bnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:708


shrn

shrn(b): BN

Parameters

NameType
bnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:709


sqr

sqr(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:698


sub

sub(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:696


testn

testn(b): boolean

Parameters

NameType
bnumber

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:710


toArray

toArray(endian?, length?): number[]

Parameters

NameType
endian?Endianness
length?number

Returns

number[]

Defined in

node_modules/bsv/index.d.ts:672


toBuffer

toBuffer(opts?): Buffer

Parameters

NameType
opts?IOpts

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:673


toJSON

toJSON(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:671


toNumber

toNumber(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:670

toNumber(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:722


toSM

toSM(opts?): Buffer

Parameters

NameType
opts?IOpts

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:721


toString

toString(base?, length?): string

Parameters

NameType
base?number | "hex"
length?number

Returns

string

Defined in

node_modules/bsv/index.d.ts:669


xor

xor(b): BN

Parameters

NameType
bBN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:706


zeroBits

zeroBits(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:675


fromBuffer

Static fromBuffer(buf, opts?): BN

Parameters

NameType
bufBuffer
opts?IOpts

Returns

BN

Defined in

node_modules/bsv/index.d.ts:723


fromHex

Static fromHex(hex, opts?): BN

Parameters

NameType
hexstring
opts?IOpts

Returns

BN

Defined in

node_modules/bsv/index.d.ts:725


fromNumber

Static fromNumber(n): BN

Parameters

NameType
nnumber

Returns

BN

Defined in

node_modules/bsv/index.d.ts:724


fromSM

Static fromSM(buf, opts?): BN

Parameters

NameType
bufBuffer
opts?IOpts

Returns

BN

Defined in

node_modules/bsv/index.d.ts:718


fromString

Static fromString(hex, base?): BN

Parameters

NameType
hexstring
base?number

Returns

BN

Defined in

node_modules/bsv/index.d.ts:726

- + \ No newline at end of file diff --git a/reference/classes/bsv.crypto.Point/index.html b/reference/classes/bsv.crypto.Point/index.html index c086aef95..5e76046f2 100644 --- a/reference/classes/bsv.crypto.Point/index.html +++ b/reference/classes/bsv.crypto.Point/index.html @@ -4,13 +4,13 @@ bsv.crypto.Point | sCrypt - +

bsv.crypto.Point

scrypt-ts / bsv / crypto / Point

Class: Point

bsv.crypto.Point

Table of contents

Constructors

Methods

Constructors

constructor

new Point()

Methods

getX

getX(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:759


getY

getY(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:760


mul

mul(n): Point

Parameters

NameType
nBN

Returns

Point

Defined in

node_modules/bsv/index.d.ts:762


validate

validate(): Point

Returns

Point

Defined in

node_modules/bsv/index.d.ts:761


fromX

Static fromX(odd, x): Point

Parameters

NameType
oddboolean
xstring | BN

Returns

Point

Defined in

node_modules/bsv/index.d.ts:756


getG

Static getG(): any

Returns

any

Defined in

node_modules/bsv/index.d.ts:757


getN

Static getN(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:758

- + \ No newline at end of file diff --git a/reference/classes/bsv.crypto.Signature/index.html b/reference/classes/bsv.crypto.Signature/index.html index 0acc3dab5..c4943415a 100644 --- a/reference/classes/bsv.crypto.Signature/index.html +++ b/reference/classes/bsv.crypto.Signature/index.html @@ -4,13 +4,13 @@ bsv.crypto.Signature | sCrypt - +

bsv.crypto.Signature

scrypt-ts / bsv / crypto / Signature

Class: Signature

bsv.crypto.Signature

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new Signature()

Properties

nhashtype

nhashtype: number

Defined in

node_modules/bsv/index.d.ts:782


ALL

Static ALL: number

Defined in

node_modules/bsv/index.d.ts:775


ANYONECANPAY_ALL

Static ANYONECANPAY_ALL: number

Defined in

node_modules/bsv/index.d.ts:778


ANYONECANPAY_NONE

Static ANYONECANPAY_NONE: number

Defined in

node_modules/bsv/index.d.ts:779


ANYONECANPAY_SINGLE

Static ANYONECANPAY_SINGLE: number

Defined in

node_modules/bsv/index.d.ts:780


NONE

Static NONE: number

Defined in

node_modules/bsv/index.d.ts:776


SIGHASH_ALL

Static SIGHASH_ALL: number

Defined in

node_modules/bsv/index.d.ts:769


SIGHASH_ANYONECANPAY

Static SIGHASH_ANYONECANPAY: number

Defined in

node_modules/bsv/index.d.ts:773


SIGHASH_FORKID

Static SIGHASH_FORKID: number

Defined in

node_modules/bsv/index.d.ts:772


SIGHASH_NONE

Static SIGHASH_NONE: number

Defined in

node_modules/bsv/index.d.ts:770


SIGHASH_SINGLE

Static SIGHASH_SINGLE: number

Defined in

node_modules/bsv/index.d.ts:771


SINGLE

Static SINGLE: number

Defined in

node_modules/bsv/index.d.ts:777

Methods

hasDefinedHashtype

hasDefinedHashtype(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:786


hasLowS

hasLowS(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:788


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:784


toDER

toDER(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:785


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:783


toTxFormat

toTxFormat(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:789


fromDER

Static fromDER(sig): Signature

Parameters

NameType
sigBuffer

Returns

Signature

Defined in

node_modules/bsv/index.d.ts:766


fromString

Static fromString(data): Signature

Parameters

NameType
datastring

Returns

Signature

Defined in

node_modules/bsv/index.d.ts:768


fromTxFormat

Static fromTxFormat(buf): Signature

Parameters

NameType
bufBuffer

Returns

Signature

Defined in

node_modules/bsv/index.d.ts:767


isTxDER

Static isTxDER(buf): boolean

Parameters

NameType
bufBuffer

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:787

- + \ No newline at end of file diff --git a/reference/classes/bsv.encoding.Base58/index.html b/reference/classes/bsv.encoding.Base58/index.html index 8ef7c25aa..4af69ef10 100644 --- a/reference/classes/bsv.encoding.Base58/index.html +++ b/reference/classes/bsv.encoding.Base58/index.html @@ -4,13 +4,13 @@ bsv.encoding.Base58 | sCrypt - +
- + \ No newline at end of file diff --git a/reference/classes/bsv.encoding.Base58Check/index.html b/reference/classes/bsv.encoding.Base58Check/index.html index 0f92a52e6..750851e0a 100644 --- a/reference/classes/bsv.encoding.Base58Check/index.html +++ b/reference/classes/bsv.encoding.Base58Check/index.html @@ -4,13 +4,13 @@ bsv.encoding.Base58Check | sCrypt - +
- + \ No newline at end of file diff --git a/reference/classes/bsv.encoding.BufferReader/index.html b/reference/classes/bsv.encoding.BufferReader/index.html index 56edf6b27..08c1d3331 100644 --- a/reference/classes/bsv.encoding.BufferReader/index.html +++ b/reference/classes/bsv.encoding.BufferReader/index.html @@ -4,13 +4,13 @@ bsv.encoding.BufferReader | sCrypt - +

bsv.encoding.BufferReader

scrypt-ts / bsv / encoding / BufferReader

Class: BufferReader

bsv.encoding.BufferReader

Table of contents

Constructors

Properties

Methods

Constructors

constructor

new BufferReader(buf)

Parameters

NameType
bufBuffer

Defined in

node_modules/bsv/index.d.ts:610

Properties

pos

pos: number

Defined in

node_modules/bsv/index.d.ts:629

Methods

eof

eof(): boolean

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:627


read

read(len): Buffer

Parameters

NameType
lennumber

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:611


readAll

readAll(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:626


readInt32LE

readInt32LE(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:617


readReverse

readReverse(len?): Buffer

Parameters

NameType
len?number

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:625


readUInt16BE

readUInt16BE(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:613


readUInt16LE

readUInt16LE(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:614


readUInt32BE

readUInt32BE(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:615


readUInt32LE

readUInt32LE(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:616


readUInt64BEBN

readUInt64BEBN(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:618


readUInt64LEBN

readUInt64LEBN(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:619


readUInt8

readUInt8(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:612


readVarLengthBuffer

readVarLengthBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:621


readVarintBN

readVarintBN(): BN

Returns

BN

Defined in

node_modules/bsv/index.d.ts:623


readVarintBuf

readVarintBuf(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:622


readVarintNum

readVarintNum(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:620


remaining

remaining(): number

Returns

number

Defined in

node_modules/bsv/index.d.ts:628


reverse

reverse(): BufferReader

Returns

BufferReader

Defined in

node_modules/bsv/index.d.ts:624

- + \ No newline at end of file diff --git a/reference/classes/bsv.encoding.BufferWriter/index.html b/reference/classes/bsv.encoding.BufferWriter/index.html index c39772890..d5bf7da28 100644 --- a/reference/classes/bsv.encoding.BufferWriter/index.html +++ b/reference/classes/bsv.encoding.BufferWriter/index.html @@ -4,13 +4,13 @@ bsv.encoding.BufferWriter | sCrypt - +

bsv.encoding.BufferWriter

scrypt-ts / bsv / encoding / BufferWriter

Class: BufferWriter

bsv.encoding.BufferWriter

Table of contents

Constructors

Methods

Constructors

constructor

new BufferWriter()

Methods

toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:644


write

write(buf): BufferWriter

Parameters

NameType
bufBuffer

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:632


writeInt32LE

writeInt32LE(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:638


writeReverse

writeReverse(buf): BufferWriter

Parameters

NameType
bufBuffer

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:643


writeUInt16BE

writeUInt16BE(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:634


writeUInt16LE

writeUInt16LE(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:635


writeUInt32BE

writeUInt32BE(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:636


writeUInt32LE

writeUInt32LE(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:637


writeUInt64BEBN

writeUInt64BEBN(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:639


writeUInt64LEBN

writeUInt64LEBN(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:640


writeUInt8

writeUInt8(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:633


writeVarintBN

writeVarintBN(n): BufferWriter

Parameters

NameType
nBN

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:642


writeVarintNum

writeVarintNum(n): BufferWriter

Parameters

NameType
nnumber

Returns

BufferWriter

Defined in

node_modules/bsv/index.d.ts:641

- + \ No newline at end of file diff --git a/reference/classes/bsv.encoding.Varint/index.html b/reference/classes/bsv.encoding.Varint/index.html index eed8af951..ed275d097 100644 --- a/reference/classes/bsv.encoding.Varint/index.html +++ b/reference/classes/bsv.encoding.Varint/index.html @@ -4,13 +4,13 @@ bsv.encoding.Varint | sCrypt - +
- + \ No newline at end of file diff --git a/reference/enums/ProviderEvent/index.html b/reference/enums/ProviderEvent/index.html index 8f7a426eb..e636ee276 100644 --- a/reference/enums/ProviderEvent/index.html +++ b/reference/enums/ProviderEvent/index.html @@ -4,14 +4,14 @@ ProviderEvent | sCrypt - +

ProviderEvent

scrypt-ts / ProviderEvent

Enumeration: ProviderEvent

The provider is an EventEmitter, and the following are all the events it can emit. https://stackoverflow.com/a/50511773

Table of contents

Enumeration Members

Enumeration Members

Connected

Connected = "connected"

The provider will send a 'Connected' event after the connection is successful.

Defined in

dist/bsv/abstract-provider.d.ts:24


NetworkChange

NetworkChange = "networkChange"

After the network connected to the provider changes, it will issue the 'NetworkChange' event, such as switching from the testnet to the mainnet.

Defined in

dist/bsv/abstract-provider.d.ts:26

- + \ No newline at end of file diff --git a/reference/enums/SignatureHashType/index.html b/reference/enums/SignatureHashType/index.html index 1f1cc90e7..79d144d7b 100644 --- a/reference/enums/SignatureHashType/index.html +++ b/reference/enums/SignatureHashType/index.html @@ -4,13 +4,13 @@ SignatureHashType | sCrypt - +

SignatureHashType

scrypt-ts / SignatureHashType

Enumeration: SignatureHashType

Table of contents

Enumeration Members

Enumeration Members

ALL

ALL = 65

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:79


ANYONECANPAY_ALL

ANYONECANPAY_ALL = 193

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:82


ANYONECANPAY_NONE

ANYONECANPAY_NONE = 194

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:83


ANYONECANPAY_SINGLE

ANYONECANPAY_SINGLE = 195

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:84


NONE

NONE = 66

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:80


SINGLE

SINGLE = 67

Defined in

node_modules/scryptlib/dist/scryptTypes.d.ts:81

- + \ No newline at end of file diff --git a/reference/index.html b/reference/index.html index 5daac6364..e7c652949 100644 --- a/reference/index.html +++ b/reference/index.html @@ -4,7 +4,7 @@ README | sCrypt - + @@ -32,7 +32,7 @@ If not passing isUtf8 or isUtf8 is false, then literal should be in the format of hex literal, i.e. /^([0-9a-fA-F]{2})*$/ Otherwise, literal should be in the format of utf8 literal, i.e. hello world

Parameters

NameTypeDescription
literalstringliteral string, can be hex literal or utf8 literal, depends on the isUtf8 marker
isUtf8?booleanmarker indicating whether literal is utf8 or hex

Returns

ByteString

Defined in

dist/smart-contract/builtins/types.d.ts:97


toHex

toHex(x): string

Parameters

NameType
xObject
x.toString(format: "hex") => string

Returns

string

Defined in

node_modules/scryptlib/dist/utils.d.ts:18


toNumber

toNumber(sighashType): number

Parameters

NameType
sighashTypeSigHashType

Returns

number

Defined in

dist/smart-contract/utils/index.d.ts:6


utxoFromOutput

utxoFromOutput(tx, outputIndex): UTXO

Parameters

NameType
txTransaction
outputIndexnumber

Returns

UTXO

Defined in

dist/bsv/utils.d.ts:6


xor

xor(a, b): Int

Parameters

NameType
aInt
bInt

Returns

Int

Defined in

node_modules/scryptlib/dist/builtins.d.ts:17


assert Functions

assert

assert(condition, msg?): asserts condition

assert(condition: boolean, errorMsg?: string) Throw an Error with the optional error message if condition is false. Otherwise, nothing happens.

Parameters

NameType
conditionboolean
msg?string

Returns

asserts condition

Defined in

dist/smart-contract/builtins/functions.d.ts:143


decorator Functions

method

method(sigHashType?): (target: any, methodName: string, descriptor: PropertyDescriptor) => PropertyDescriptor

Indicates whether the method is a contract method, and ordinary methods do not affect the execution of the contract

Parameters

NameType
sigHashType?SigHashType

Returns

fn

▸ (target, methodName, descriptor): PropertyDescriptor

Parameters
NameType
targetany
methodNamestring
descriptorPropertyDescriptor
Returns

PropertyDescriptor

Defined in

dist/smart-contract/decorators.d.ts:17


prop

prop(state?): (target: any, propertyName: string) => void

Indicates whether the property is an property of a contract, and ordinary class properties cannot be accessed in contract methods

Parameters

NameTypeDescription
state?booleanWhether the property is a property of a stateful contract

Returns

fn

▸ (target, propertyName): void

Parameters
NameType
targetany
propertyNamestring
Returns

void

Defined in

dist/smart-contract/decorators.d.ts:31

- + \ No newline at end of file diff --git a/reference/interfaces/Artifact/index.html b/reference/interfaces/Artifact/index.html index 6afa44206..56a816163 100644 --- a/reference/interfaces/Artifact/index.html +++ b/reference/interfaces/Artifact/index.html @@ -4,13 +4,13 @@ Artifact | sCrypt - +

Artifact

scrypt-ts / Artifact

Interface: Artifact

Table of contents

Properties

Properties

abi

abi: ABIEntity[]

ABI of the contract: interfaces of its public functions and constructor

Defined in

node_modules/scryptlib/dist/contract.d.ts:50


alias

alias: AliasEntity[]

all typealias defined in the contracts, including dependent contracts

Defined in

node_modules/scryptlib/dist/contract.d.ts:48


asm

Optional asm: string

Deprecated

locking script of the contract in ASM format, including placeholders for constructor parameters

Defined in

node_modules/scryptlib/dist/contract.d.ts:52


buildType

buildType: string

build type, can be debug or release

Defined in

node_modules/scryptlib/dist/contract.d.ts:36


compilerVersion

compilerVersion: string

version of compiler used to produce this file

Defined in

node_modules/scryptlib/dist/contract.d.ts:34


contract

contract: string

name of the contract

Defined in

node_modules/scryptlib/dist/contract.d.ts:38


file

file: string

file uri of the main contract source code file

Defined in

node_modules/scryptlib/dist/contract.d.ts:56


hex

hex: string

locking script of the contract in hex format, including placeholders for constructor parameters

Defined in

node_modules/scryptlib/dist/contract.d.ts:54


library

library: LibraryEntity[]

all library defined in the contracts, including dependent contracts

Defined in

node_modules/scryptlib/dist/contract.d.ts:46


md5

md5: string

md5 of the contract source code

Defined in

node_modules/scryptlib/dist/contract.d.ts:40


sourceMap

Optional sourceMap: string[]

Deprecated

Defined in

node_modules/scryptlib/dist/contract.d.ts:60


sourceMapFile

sourceMapFile: string

file uri of source map file *

Defined in

node_modules/scryptlib/dist/contract.d.ts:62


sources

Optional sources: string[]

Deprecated

Defined in

node_modules/scryptlib/dist/contract.d.ts:58


stateProps

stateProps: ParamEntity[]

all stateful properties defined in the contracts

Defined in

node_modules/scryptlib/dist/contract.d.ts:42


structs

structs: StructEntity[]

all structures defined in the contracts, including dependent contracts

Defined in

node_modules/scryptlib/dist/contract.d.ts:44


version

version: number

version of artifact file

Defined in

node_modules/scryptlib/dist/contract.d.ts:32

- + \ No newline at end of file diff --git a/reference/interfaces/ContractCalledEvent/index.html b/reference/interfaces/ContractCalledEvent/index.html index 180c3cc97..30610cd3c 100644 --- a/reference/interfaces/ContractCalledEvent/index.html +++ b/reference/interfaces/ContractCalledEvent/index.html @@ -4,14 +4,14 @@ ContractCalledEvent | sCrypt - +

ContractCalledEvent

scrypt-ts / ContractCalledEvent

Interface: ContractCalledEvent<T>

ContractCalledEvent is the relevant information when the contract is called, such as the public function name and function arguments when the call occurs.

Type parameters

Name
T

Table of contents

Properties

Properties

args

args: SupportedParamType[]

public function arguments

Defined in

dist/client/apis/contract-api.d.ts:30


methodName

methodName: string

name of public function

Defined in

dist/client/apis/contract-api.d.ts:28


nexts

nexts: T[]

If a stateful contract is called, nexts contains the contract instance containing the new state generated by this call. If a stateless contract is called, nexts is empty.

Defined in

dist/client/apis/contract-api.d.ts:26


tx

tx: Transaction

transaction where contract is called

Defined in

dist/client/apis/contract-api.d.ts:32

- + \ No newline at end of file diff --git a/reference/interfaces/ContractTransaction/index.html b/reference/interfaces/ContractTransaction/index.html index e5160ec94..5fff903c1 100644 --- a/reference/interfaces/ContractTransaction/index.html +++ b/reference/interfaces/ContractTransaction/index.html @@ -4,13 +4,13 @@ ContractTransaction | sCrypt - +

ContractTransaction

scrypt-ts / ContractTransaction

Interface: ContractTransaction

A structure describing a transaction that invokes a single contract.

Table of contents

Properties

Properties

atInputIndex

atInputIndex: number

The input index of previous contract UTXO to spend in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:63


next

Optional next: StatefulNext<any>

The first element of nexts, this value should be set for user convenience.

Defined in

dist/smart-contract/types/contract-call.d.ts:67


nexts

nexts: StatefulNext<any>[]

The subsequent contract instance(s) produced in the outputs of the method calling tx in a stateful contract

Defined in

dist/smart-contract/types/contract-call.d.ts:65


tx

tx: Transaction

The method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:61

- + \ No newline at end of file diff --git a/reference/interfaces/DefaultProviderOption/index.html b/reference/interfaces/DefaultProviderOption/index.html index 5ab62b878..8faefc803 100644 --- a/reference/interfaces/DefaultProviderOption/index.html +++ b/reference/interfaces/DefaultProviderOption/index.html @@ -4,13 +4,13 @@ DefaultProviderOption | sCrypt - +

DefaultProviderOption

scrypt-ts / DefaultProviderOption

Interface: DefaultProviderOption

Table of contents

Properties

Properties

gorillapool

Optional gorillapool: string

api key of gorillapool

Defined in

dist/providers/default-provider.d.ts:9


network

Optional network: Network

Defined in

dist/providers/default-provider.d.ts:14


scrypt

Optional scrypt: ScryptConfig

api key of scrypt

Defined in

dist/providers/default-provider.d.ts:13


sensible

Optional sensible: string

api key of sensible

Defined in

dist/providers/default-provider.d.ts:11


taal

Optional taal: string

api key of taal

Defined in

dist/providers/default-provider.d.ts:7

- + \ No newline at end of file diff --git a/reference/interfaces/LogConfig/index.html b/reference/interfaces/LogConfig/index.html index 601333586..b5aa5a7d4 100644 --- a/reference/interfaces/LogConfig/index.html +++ b/reference/interfaces/LogConfig/index.html @@ -4,13 +4,13 @@ LogConfig | sCrypt - +
- + \ No newline at end of file diff --git a/reference/interfaces/MethodCallOptions/index.html b/reference/interfaces/MethodCallOptions/index.html index 8a30f2c74..ddc60cd54 100644 --- a/reference/interfaces/MethodCallOptions/index.html +++ b/reference/interfaces/MethodCallOptions/index.html @@ -4,7 +4,7 @@ MethodCallOptions | sCrypt - + @@ -14,7 +14,7 @@ For example, specifying a transaction builder to use a specific change address or specifying a signer to use a specific public key to sign.

Type parameters

Name
T

Table of contents

Properties

Properties

autoPayFee

Optional Readonly autoPayFee: boolean

auto add utxo to pay transaction fee, default is true

Defined in

dist/smart-contract/types/contract-call.d.ts:48


changeAddress

Optional Readonly changeAddress: Address

The P2PKH change output address

Defined in

dist/smart-contract/types/contract-call.d.ts:36


exec

Optional Readonly exec: boolean

execute a contract's public method to to check if arguments are valid, default is true

Defined in

dist/smart-contract/types/contract-call.d.ts:46


fromUTXO

Optional Readonly fromUTXO: IUnspentOutput

The previous contract UTXO to spend in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:34


lockTime

Optional Readonly lockTime: number

The lockTime of the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:30


multiContractCall

Optional Readonly multiContractCall: boolean

Whether to call multiple contracts at the same time in one transaction

Defined in

dist/smart-contract/types/contract-call.d.ts:40


next

Optional Readonly next: StatefulNext<T> | StatefulNext<T>[]

The subsequent contract instance(s) produced in the outputs of the method calling tx in a stateful contract

Defined in

dist/smart-contract/types/contract-call.d.ts:28


partialContractTx

Optional Readonly partialContractTx: ContractTransaction

Pass the ContractTransaction of the previous call as an argument to the next call, only used if multiContractCall = true.

Defined in

dist/smart-contract/types/contract-call.d.ts:42


partiallySigned

Optional Readonly partiallySigned: boolean

signer does not contain all private keys, it is used when multiple parties are required to perform signature authorization, default is false, only work in single call

Defined in

dist/smart-contract/types/contract-call.d.ts:44


pubKeyOrAddrToSign

Optional Readonly pubKeyOrAddrToSign: PublicKeysOrAddressesOption | SignaturesOption

The private key(s) associated with these address(es) or public key(s) must be used to sign the contract input, and the callback function will receive the results of the signatures as an argument named sigResponses

Defined in

dist/smart-contract/types/contract-call.d.ts:26


sequence

Optional Readonly sequence: number

The sequence of the input spending previous contract UTXO in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:32


verify

Optional Readonly verify: boolean

verify transaction before send transaction

Defined in

dist/smart-contract/types/contract-call.d.ts:38

- + \ No newline at end of file diff --git a/reference/interfaces/MethodCallTxBuilder/index.html b/reference/interfaces/MethodCallTxBuilder/index.html index dba821edc..1a4c5d736 100644 --- a/reference/interfaces/MethodCallTxBuilder/index.html +++ b/reference/interfaces/MethodCallTxBuilder/index.html @@ -4,7 +4,7 @@ MethodCallTxBuilder | sCrypt - + @@ -12,7 +12,7 @@

MethodCallTxBuilder

scrypt-ts / MethodCallTxBuilder

Interface: MethodCallTxBuilder<T>

A transaction builder. The default transaction builder only supports fixed-format call transactions. Some complex contracts require a custom transaction builder to successfully call the contract.

Type parameters

NameType
Textends SmartContract

Callable

MethodCallTxBuilder

MethodCallTxBuilder(current, options, ...args): Promise<ContractTransaction>

Parameters

NameType
currentT
optionsMethodCallOptions<T>
...argsany

Returns

Promise<ContractTransaction>

Defined in

dist/smart-contract/types/contract-call.d.ts:84

- + \ No newline at end of file diff --git a/reference/interfaces/MultiContractCallOptions/index.html b/reference/interfaces/MultiContractCallOptions/index.html index 331c70d60..ca3bc887f 100644 --- a/reference/interfaces/MultiContractCallOptions/index.html +++ b/reference/interfaces/MultiContractCallOptions/index.html @@ -4,13 +4,13 @@ MultiContractCallOptions | sCrypt - +

MultiContractCallOptions

scrypt-ts / MultiContractCallOptions

Interface: MultiContractCallOptions

Table of contents

Properties

Properties

autoPayFee

Optional Readonly autoPayFee: boolean

auto add utxo to pay transaction fee, default is true

Defined in

dist/smart-contract/types/contract-call.d.ts:56


partiallySigned

Optional Readonly partiallySigned: boolean

signer does not contain all private keys, it is used when multiple parties are required to perform signature authorization, default is false, only work in single call

Defined in

dist/smart-contract/types/contract-call.d.ts:54


verify

Optional Readonly verify: boolean

verify transaction before send transaction

Defined in

dist/smart-contract/types/contract-call.d.ts:52

- + \ No newline at end of file diff --git a/reference/interfaces/MultiContractTransaction/index.html b/reference/interfaces/MultiContractTransaction/index.html index 77a999cb9..de8fb1a85 100644 --- a/reference/interfaces/MultiContractTransaction/index.html +++ b/reference/interfaces/MultiContractTransaction/index.html @@ -4,13 +4,13 @@ MultiContractTransaction | sCrypt - +

MultiContractTransaction

scrypt-ts / MultiContractTransaction

Interface: MultiContractTransaction

A structure describing a transaction that invokes multiple contracts.

Table of contents

Properties

Properties

atInputIndices

atInputIndices: number[]

The input indices of previous contract UTXOs to spend in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:74


nexts

nexts: StatefulNext<any>[]

The subsequent contract instance(s) produced in the outputs of the method calling tx in a stateful contract

Defined in

dist/smart-contract/types/contract-call.d.ts:76


tx

tx: Transaction

The method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:72

- + \ No newline at end of file diff --git a/reference/interfaces/Outpoint/index.html b/reference/interfaces/Outpoint/index.html index fdeb90f88..288cd527d 100644 --- a/reference/interfaces/Outpoint/index.html +++ b/reference/interfaces/Outpoint/index.html @@ -4,13 +4,13 @@ Outpoint | sCrypt - +

Outpoint

scrypt-ts / Outpoint

Interface: Outpoint

The structure used to refer to a particular transaction output

Table of contents

Properties

Properties

outputIndex

outputIndex: bigint

index of the specific output

Defined in

dist/smart-contract/contract.d.ts:35


txid

txid: ByteString

txid of the transaction holding the output

Defined in

dist/smart-contract/contract.d.ts:33

- + \ No newline at end of file diff --git a/reference/interfaces/RequestConfig/index.html b/reference/interfaces/RequestConfig/index.html index 74b799c2b..4411036ea 100644 --- a/reference/interfaces/RequestConfig/index.html +++ b/reference/interfaces/RequestConfig/index.html @@ -4,13 +4,13 @@ RequestConfig | sCrypt - +

RequestConfig

scrypt-ts / RequestConfig

Interface: RequestConfig

Hierarchy

Table of contents

Properties

Properties

apiKey

apiKey: string

Defined in

dist/client/core/request-controller.d.ts:12


maxRetries

Optional maxRetries: number

Defined in

dist/client/core/request-controller.d.ts:15


network

Optional network: Network

Defined in

dist/client/core/request-controller.d.ts:13


timeout

Optional timeout: number

Defined in

dist/client/core/request-controller.d.ts:14

- + \ No newline at end of file diff --git a/reference/interfaces/ScriptContext/index.html b/reference/interfaces/ScriptContext/index.html index 295e9361d..58a48c9ac 100644 --- a/reference/interfaces/ScriptContext/index.html +++ b/reference/interfaces/ScriptContext/index.html @@ -4,7 +4,7 @@ ScriptContext | sCrypt - + @@ -12,7 +12,7 @@

ScriptContext

scrypt-ts / ScriptContext

Interface: ScriptContext

ScriptContext contains all the information in the transaction's [preimage][https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#digest-algorithm](https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#digest-algorithm). The preimage is automatically generated during the user's construction of the transaction, and the user does not need to calculate it explicitly

Table of contents

Properties

Methods

Properties

hashOutputs

hashOutputs: ByteString

double-SHA256 hash of the serialization of some/all output amount with its locking script, see [hashOutputs][https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashoutputs](https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashoutputs)

Defined in

dist/smart-contract/contract.d.ts:61


hashPrevouts

hashPrevouts: ByteString

double-SHA256 hash of the serialization of some/all input outpoints, see [hashPrevouts][https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashprevouts](https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashprevouts)

Defined in

dist/smart-contract/contract.d.ts:55


hashSequence

hashSequence: ByteString

double-SHA256 hash of the serialization of some/all input sequence values, see [hashSequence][https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashsequence](https://github.com/bitcoin-sv/bitcoin-sv/blob/master/doc/abc/replay-protected-sighash.md#hashsequence)

Defined in

dist/smart-contract/contract.d.ts:57


locktime

locktime: bigint

locktime of [transaction][https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction)

Defined in

dist/smart-contract/contract.d.ts:63


sequence

sequence: bigint

sequence number of [transaction input][https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#Format_of_a_Transaction_Input](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#Format_of_a_Transaction_Input)

Defined in

dist/smart-contract/contract.d.ts:59


sigHashType

sigHashType: SigHashType

[SIGHASH flag][https://wiki.bitcoinsv.io/index.php/SIGHASH_flags](https://wiki.bitcoinsv.io/index.php/SIGHASH_flags) used by this input

Defined in

dist/smart-contract/contract.d.ts:65


utxo

utxo: Object

the specific UTXO spent by this transaction input

Type declaration

NameTypeDescription
outpointOutpointoutpoint referenced by this UTXO
scriptByteStringlocking script
valuebigintamount in satoshis

Defined in

dist/smart-contract/contract.d.ts:46


version

version: ByteString

version number of [transaction][https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction](https://wiki.bitcoinsv.io/index.php/Bitcoin_Transactions#General_format_of_a_Bitcoin_transaction)

Defined in

dist/smart-contract/contract.d.ts:44

Methods

serialize

serialize(): SigHashPreimage

get the whole serialized sighash preimage

Returns

SigHashPreimage

Defined in

dist/smart-contract/contract.d.ts:67

- + \ No newline at end of file diff --git a/reference/interfaces/ScryptConfig/index.html b/reference/interfaces/ScryptConfig/index.html index 47eca2d77..b27b193bc 100644 --- a/reference/interfaces/ScryptConfig/index.html +++ b/reference/interfaces/ScryptConfig/index.html @@ -4,13 +4,13 @@ ScryptConfig | sCrypt - +

ScryptConfig

scrypt-ts / ScryptConfig

Interface: ScryptConfig

Hierarchy

Table of contents

Properties

Properties

apiKey

apiKey: string

Inherited from

RequestConfig.apiKey

Defined in

dist/client/core/request-controller.d.ts:12


logLevel

Optional logLevel: LogLevel

Inherited from

LogConfig.logLevel

Defined in

dist/client/core/logger-controller.d.ts:4


maxRetries

Optional maxRetries: number

Inherited from

RequestConfig.maxRetries

Defined in

dist/client/core/request-controller.d.ts:15


network

Optional network: Network

Inherited from

RequestConfig.network

Defined in

dist/client/core/request-controller.d.ts:13


timeout

Optional timeout: number

Inherited from

RequestConfig.timeout

Defined in

dist/client/core/request-controller.d.ts:14

- + \ No newline at end of file diff --git a/reference/interfaces/SignTransactionOptions/index.html b/reference/interfaces/SignTransactionOptions/index.html index 94933a16f..5b0555703 100644 --- a/reference/interfaces/SignTransactionOptions/index.html +++ b/reference/interfaces/SignTransactionOptions/index.html @@ -4,13 +4,13 @@ SignTransactionOptions | sCrypt - +

SignTransactionOptions

scrypt-ts / SignTransactionOptions

Interface: SignTransactionOptions

SignTransactionOptions is the options can be provided when signing a transaction.

Table of contents

Properties

Properties

address

Optional address: AddressesOption

The address(es) whose corresponding private key(s) should be used to sign the tx.

Defined in

dist/bsv/abstract-signer.d.ts:50


sigRequests

Optional sigRequests: SignatureRequest[]

The SignatureRequest for the some inputs of the transaction.

Defined in

dist/bsv/abstract-signer.d.ts:48

- + \ No newline at end of file diff --git a/reference/interfaces/SignatureRequest/index.html b/reference/interfaces/SignatureRequest/index.html index c7c068844..98c784be9 100644 --- a/reference/interfaces/SignatureRequest/index.html +++ b/reference/interfaces/SignatureRequest/index.html @@ -4,14 +4,14 @@ SignatureRequest | sCrypt - +

SignatureRequest

scrypt-ts / SignatureRequest

Interface: SignatureRequest

SignatureRequest contains required informations for a signer to sign a certain input of a transaction.

Table of contents

Properties

Properties

address

address: AddressesOption

The address(es) of corresponding private key(s) required to sign the input.

Defined in

dist/bsv/abstract-signer.d.ts:15


csIdx

Optional csIdx: number

Index of the OP_CODESEPARATOR to split the previous output script at during verification. If undefined, the whole script is used.

Defined in

dist/bsv/abstract-signer.d.ts:24


data

Optional data: unknown

The extra information for signing.

Defined in

dist/bsv/abstract-signer.d.ts:26


inputIndex

inputIndex: number

The index of input to sign.

Defined in

dist/bsv/abstract-signer.d.ts:11


outputIndex

outputIndex: number

Defined in

dist/bsv/abstract-signer.d.ts:9


prevTxId

prevTxId: string

Defined in

dist/bsv/abstract-signer.d.ts:8


satoshis

satoshis: number

The previous output satoshis value of the input to spend.

Defined in

dist/bsv/abstract-signer.d.ts:13


scriptHex

Optional scriptHex: string

The previous output script of input, default value is a P2PKH locking script for the address if omitted.

Defined in

dist/bsv/abstract-signer.d.ts:17


sigHashType

Optional sigHashType: number

The sighash type, default value is SIGHASH_ALL | SIGHASH_FORKID if omitted.

Defined in

dist/bsv/abstract-signer.d.ts:19

- + \ No newline at end of file diff --git a/reference/interfaces/SignatureResponse/index.html b/reference/interfaces/SignatureResponse/index.html index 7893001fc..c5792289c 100644 --- a/reference/interfaces/SignatureResponse/index.html +++ b/reference/interfaces/SignatureResponse/index.html @@ -4,13 +4,13 @@ SignatureResponse | sCrypt - +

SignatureResponse

scrypt-ts / SignatureResponse

Interface: SignatureResponse

SignatureResponse contains the signing result corresponding to a SignatureRequest.

Table of contents

Properties

Properties

csIdx

Optional csIdx: number

The index of the OP_CODESEPARATOR to split the previous output script at.

Defined in

dist/bsv/abstract-signer.d.ts:41


inputIndex

inputIndex: number

The index of input.

Defined in

dist/bsv/abstract-signer.d.ts:33


publicKey

publicKey: string

The public key bound with the sig.

Defined in

dist/bsv/abstract-signer.d.ts:37


sig

sig: string

The signature.

Defined in

dist/bsv/abstract-signer.d.ts:35


sigHashType

sigHashType: number

The sighash type, default value is SIGHASH_ALL | SIGHASH_FORKID if omitted.

Defined in

dist/bsv/abstract-signer.d.ts:39

- + \ No newline at end of file diff --git a/reference/interfaces/StatefulNext/index.html b/reference/interfaces/StatefulNext/index.html index 7857a7868..b432e3bda 100644 --- a/reference/interfaces/StatefulNext/index.html +++ b/reference/interfaces/StatefulNext/index.html @@ -4,13 +4,13 @@ StatefulNext | sCrypt - +

StatefulNext

scrypt-ts / StatefulNext

Interface: StatefulNext<T>

Contains information about the new state of a set of stateful contracts, used to construct transactions in the transaction builder.

Type parameters

Name
T

Table of contents

Properties

Properties

atOutputIndex

atOutputIndex: number

The index of the subsequent contract output in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:13


balance

balance: number

Satoshis of the subsequent contract output in the method calling tx

Defined in

dist/smart-contract/types/contract-call.d.ts:11


instance

instance: T

The subsequent stateful contract instance

Defined in

dist/smart-contract/types/contract-call.d.ts:9

- + \ No newline at end of file diff --git a/reference/interfaces/SubScription/index.html b/reference/interfaces/SubScription/index.html index 27bec1ab3..f6929f33e 100644 --- a/reference/interfaces/SubScription/index.html +++ b/reference/interfaces/SubScription/index.html @@ -4,13 +4,13 @@ SubScription | sCrypt - +

SubScription

scrypt-ts / SubScription

Interface: SubScription

SubScription can be used to unsubscribe

Table of contents

Properties

Properties

unsubscribe

unsubscribe: () => void

Type declaration

▸ (): void

Returns

void

Defined in

dist/client/apis/contract-api.d.ts:16

- + \ No newline at end of file diff --git a/reference/interfaces/SubscribeOptions/index.html b/reference/interfaces/SubscribeOptions/index.html index 205feea07..d8a9520a8 100644 --- a/reference/interfaces/SubscribeOptions/index.html +++ b/reference/interfaces/SubscribeOptions/index.html @@ -4,13 +4,13 @@ SubscribeOptions | sCrypt - +

SubscribeOptions

scrypt-ts / SubscribeOptions

Interface: SubscribeOptions<T>

A options can be used to subscribe

Type parameters

Name
T

Table of contents

Properties

Properties

clazz

clazz: (...args: any) => T

Type declaration

new clazz(...args)

Contract typescript class

Parameters
NameType
...argsany

Defined in

dist/client/apis/contract-api.d.ts:10


id

id: ContractId

Contract id

Defined in

dist/client/apis/contract-api.d.ts:8


methodNames

Optional methodNames: string[]

Use methodNames to specify that only receive events when specific methods are called. The default is to notify when all methods are called

Defined in

dist/client/apis/contract-api.d.ts:12

- + \ No newline at end of file diff --git a/reference/interfaces/TransactionResponse/index.html b/reference/interfaces/TransactionResponse/index.html index d3245e06b..d25071a8d 100644 --- a/reference/interfaces/TransactionResponse/index.html +++ b/reference/interfaces/TransactionResponse/index.html @@ -4,13 +4,13 @@ TransactionResponse | sCrypt - +

TransactionResponse

scrypt-ts / TransactionResponse

Interface: TransactionResponse

Hierarchy

Table of contents

Properties

Methods

Properties

_estimateSize

_estimateSize: number

Inherited from

Transaction._estimateSize

Defined in

node_modules/bsv/index.d.ts:963


hash

Readonly hash: string

Inherited from

Transaction.hash

Defined in

node_modules/bsv/index.d.ts:904


id

Readonly id: string

Inherited from

Transaction.id

Defined in

node_modules/bsv/index.d.ts:903


inputAmount

Readonly inputAmount: number

Inherited from

Transaction.inputAmount

Defined in

node_modules/bsv/index.d.ts:905


inputs

inputs: Input[]

Inherited from

Transaction.inputs

Defined in

node_modules/bsv/index.d.ts:901


nLockTime

nLockTime: number

Inherited from

Transaction.nLockTime

Defined in

node_modules/bsv/index.d.ts:908


nid

nid: string

Inherited from

Transaction.nid

Defined in

node_modules/bsv/index.d.ts:907


outputAmount

Readonly outputAmount: number

Inherited from

Transaction.outputAmount

Defined in

node_modules/bsv/index.d.ts:906


outputs

outputs: Output[]

Inherited from

Transaction.outputs

Defined in

node_modules/bsv/index.d.ts:902

Methods

_estimateFee

_estimateFee(): number

Returns

number

Inherited from

Transaction._estimateFee

Defined in

node_modules/bsv/index.d.ts:962


_getUnspentValue

_getUnspentValue(): number

Returns

number

Inherited from

Transaction._getUnspentValue

Defined in

node_modules/bsv/index.d.ts:961


addData

addData(value): TransactionResponse

Parameters

NameType
valuestring | Buffer

Returns

TransactionResponse

Inherited from

Transaction.addData

Defined in

node_modules/bsv/index.d.ts:933


addDummyInput

addDummyInput(script, satoshis): TransactionResponse

Parameters

NameType
scriptScript
satoshisnumber

Returns

TransactionResponse

Inherited from

Transaction.addDummyInput

Defined in

node_modules/bsv/index.d.ts:987


addInput

addInput(input, outputScript?, satoshis?): TransactionResponse

Parameters

NameType
inputInput
outputScript?string | Script
satoshis?number

Returns

TransactionResponse

Inherited from

Transaction.addInput

Defined in

node_modules/bsv/index.d.ts:927


addInputFromPrevTx

addInputFromPrevTx(prevTx, outputIndex?): TransactionResponse

Parameters

NameType
prevTxTransaction
outputIndex?number

Returns

TransactionResponse

Inherited from

Transaction.addInputFromPrevTx

Defined in

node_modules/bsv/index.d.ts:986


addOutput

addOutput(output): TransactionResponse

Parameters

NameType
outputOutput

Returns

TransactionResponse

Inherited from

Transaction.addOutput

Defined in

node_modules/bsv/index.d.ts:932


applySignature

applySignature(sig): TransactionResponse

Parameters

NameType
sigObject
sig.inputIndexnumber
sig.publicKeyPublicKey
sig.signatureSignature
sig.sigtypenumber

Returns

TransactionResponse

Inherited from

Transaction.applySignature

Defined in

node_modules/bsv/index.d.ts:925


change

change(address): TransactionResponse

Parameters

NameType
addressstring | Address

Returns

TransactionResponse

Inherited from

Transaction.change

Defined in

node_modules/bsv/index.d.ts:918


checkFeeRate

checkFeeRate(feePerKb?): boolean

Parameters

NameType
feePerKb?number

Returns

boolean

Inherited from

Transaction.checkFeeRate

Defined in

node_modules/bsv/index.d.ts:982


dummyChange

dummyChange(): TransactionResponse

Returns

TransactionResponse

Inherited from

Transaction.dummyChange

Defined in

node_modules/bsv/index.d.ts:988


enableRBF

enableRBF(): TransactionResponse

Returns

TransactionResponse

Inherited from

Transaction.enableRBF

Defined in

node_modules/bsv/index.d.ts:947


fee

fee(amount): TransactionResponse

Parameters

NameType
amountnumber

Returns

TransactionResponse

Inherited from

Transaction.fee

Defined in

node_modules/bsv/index.d.ts:919


feePerKb

feePerKb(amount): TransactionResponse

Parameters

NameType
amountnumber

Returns

TransactionResponse

Inherited from

Transaction.feePerKb

Defined in

node_modules/bsv/index.d.ts:920


from

from(utxos): TransactionResponse

Parameters

NameType
utxosIUnspentOutput | IUnspentOutput[]

Returns

TransactionResponse

Inherited from

Transaction.from

Defined in

node_modules/bsv/index.d.ts:912


fromBuffer

fromBuffer(buffer): TransactionResponse

Parameters

NameType
bufferBuffer

Returns

TransactionResponse

Inherited from

Transaction.fromBuffer

Defined in

node_modules/bsv/index.d.ts:916


fromString

fromString(rawTxHex): TransactionResponse

Parameters

NameType
rawTxHexstring

Returns

TransactionResponse

Inherited from

Transaction.fromString

Defined in

node_modules/bsv/index.d.ts:915


getChangeAddress

getChangeAddress(): Address

Returns

Address

Inherited from

Transaction.getChangeAddress

Defined in

node_modules/bsv/index.d.ts:940


getChangeAmount

getChangeAmount(): number

Returns

number

Inherited from

Transaction.getChangeAmount

Defined in

node_modules/bsv/index.d.ts:980


getChangeOutput

getChangeOutput(): Output

Returns

Output

Inherited from

Transaction.getChangeOutput

Defined in

node_modules/bsv/index.d.ts:939


getEstimateFee

getEstimateFee(): number

Returns

number

Inherited from

Transaction.getEstimateFee

Defined in

node_modules/bsv/index.d.ts:981


getFee

getFee(): number

Returns

number

Inherited from

Transaction.getFee

Defined in

node_modules/bsv/index.d.ts:938


getInputAmount

getInputAmount(inputIndex): number

Parameters

NameType
inputIndexnumber

Returns

number

Inherited from

Transaction.getInputAmount

Defined in

node_modules/bsv/index.d.ts:1003


getLockTime

getLockTime(): number | Date

Returns

number | Date

Inherited from

Transaction.getLockTime

Defined in

node_modules/bsv/index.d.ts:941


getOutputAmount

getOutputAmount(outputIndex): number

Parameters

NameType
outputIndexnumber

Returns

number

Inherited from

Transaction.getOutputAmount

Defined in

node_modules/bsv/index.d.ts:1004


getPreimage

getPreimage(inputIndex, sigtype?, isLowS?): string

Parameters

NameType
inputIndexnumber
sigtype?number
isLowS?boolean

Returns

string

Inherited from

Transaction.getPreimage

Defined in

node_modules/bsv/index.d.ts:985


getSerializationError

getSerializationError(opts?): any

Parameters

NameType
opts?object

Returns

any

Inherited from

Transaction.getSerializationError

Defined in

node_modules/bsv/index.d.ts:959


getSignature

getSignature(inputIndex, privateKey?, sigtype?): string | string[]

Parameters

NameType
inputIndexnumber
privateKey?PrivateKey | PrivateKey[]
sigtype?number

Returns

string | string[]

Inherited from

Transaction.getSignature

Defined in

node_modules/bsv/index.d.ts:984


hasWitnesses

hasWitnesses(): boolean

Returns

boolean

Inherited from

Transaction.hasWitnesses

Defined in

node_modules/bsv/index.d.ts:937


inspect

inspect(): string

Returns

string

Inherited from

Transaction.inspect

Defined in

node_modules/bsv/index.d.ts:950


isCoinbase

isCoinbase(): boolean

Returns

boolean

Inherited from

Transaction.isCoinbase

Defined in

node_modules/bsv/index.d.ts:945


isFullySigned

isFullySigned(): boolean

Returns

boolean

Inherited from

Transaction.isFullySigned

Defined in

node_modules/bsv/index.d.ts:957


isRBF

isRBF(): boolean

Returns

boolean

Inherited from

Transaction.isRBF

Defined in

node_modules/bsv/index.d.ts:948


isSealed

isSealed(): boolean

Returns

boolean

Inherited from

Transaction.isSealed

Defined in

node_modules/bsv/index.d.ts:979


lockUntilBlockHeight

lockUntilBlockHeight(height): TransactionResponse

Parameters

NameType
heightnumber

Returns

TransactionResponse

Inherited from

Transaction.lockUntilBlockHeight

Defined in

node_modules/bsv/index.d.ts:935


lockUntilDate

lockUntilDate(time): TransactionResponse

Parameters

NameType
timenumber | Date

Returns

TransactionResponse

Inherited from

Transaction.lockUntilDate

Defined in

node_modules/bsv/index.d.ts:934


prevouts

prevouts(): string

Returns

string

Inherited from

Transaction.prevouts

Defined in

node_modules/bsv/index.d.ts:983


seal

seal(): TransactionResponse

Returns

TransactionResponse

Inherited from

Transaction.seal

Defined in

node_modules/bsv/index.d.ts:977


sealAsync

sealAsync(): Promise<TransactionResponse>

Returns

Promise<TransactionResponse>

Inherited from

Transaction.sealAsync

Defined in

node_modules/bsv/index.d.ts:978


serialize

serialize(opts?): string

Parameters

NameType
opts?object

Returns

string

Inherited from

Transaction.serialize

Defined in

node_modules/bsv/index.d.ts:951


setInputScript

setInputScript(inputIndex, unlockingScript): TransactionResponse

Parameters

NameType
inputIndexnumber | { inputIndex: number ; isLowS?: boolean ; privateKey?: PrivateKey | PrivateKey[] ; sigtype?: number }
unlockingScriptScript | (tx: Transaction, outputInPrevTx: Output) => Script

Returns

TransactionResponse

Inherited from

Transaction.setInputScript

Defined in

node_modules/bsv/index.d.ts:964


setInputScriptAsync

setInputScriptAsync(inputIndex, callback): Promise<TransactionResponse>

Parameters

NameType
inputIndexnumber | { inputIndex: number ; isLowS?: boolean ; sigtype?: number }
callback(tx: Transaction, outputInPrevTx: Output) => Promise<Script>

Returns

Promise<TransactionResponse>

Inherited from

Transaction.setInputScriptAsync

Defined in

node_modules/bsv/index.d.ts:970


setInputSequence

setInputSequence(inputIndex, sequence): TransactionResponse

Parameters

NameType
inputIndexnumber
sequencenumber

Returns

TransactionResponse

Inherited from

Transaction.setInputSequence

Defined in

node_modules/bsv/index.d.ts:975


setLockTime

setLockTime(t): TransactionResponse

Parameters

NameType
tnumber

Returns

TransactionResponse

Inherited from

Transaction.setLockTime

Defined in

node_modules/bsv/index.d.ts:942


setOutput

setOutput(outputIndex, output): TransactionResponse

Parameters

NameType
outputIndexnumber
outputOutput | (tx: Transaction) => Output

Returns

TransactionResponse

Inherited from

Transaction.setOutput

Defined in

node_modules/bsv/index.d.ts:976


sign

sign(privateKey, sigtype?): TransactionResponse

Parameters

NameType
privateKeystring | string[] | PrivateKey | PrivateKey[]
sigtype?number

Returns

TransactionResponse

Inherited from

Transaction.sign

Defined in

node_modules/bsv/index.d.ts:921


to

to(address, amount): TransactionResponse

Parameters

NameType
addressstring | Address | Address[]
amountnumber

Returns

TransactionResponse

Inherited from

Transaction.to

Defined in

node_modules/bsv/index.d.ts:917


toBuffer

toBuffer(): Buffer

Returns

Buffer

Inherited from

Transaction.toBuffer

Defined in

node_modules/bsv/index.d.ts:955


toObject

toObject(): any

Returns

any

Inherited from

Transaction.toObject

Defined in

node_modules/bsv/index.d.ts:954


uncheckedSerialize

uncheckedSerialize(): string

Returns

string

Inherited from

Transaction.uncheckedSerialize

Defined in

node_modules/bsv/index.d.ts:952


verify

verify(): string | true

Returns

string | true

Inherited from

Transaction.verify

Defined in

node_modules/bsv/index.d.ts:944


verifyInputScript

verifyInputScript(inputIndex): Object

Deprecated

please use verifyScript instead

Parameters

NameType
inputIndexnumber

Returns

Object

NameType
errorstring
failedAtany
successboolean

Inherited from

Transaction.verifyInputScript

Defined in

node_modules/bsv/index.d.ts:993


verifyScript

verifyScript(inputIndex): Object

Parameters

NameType
inputIndexnumber

Returns

Object

NameType
errorstring
failedAtany
successboolean

Inherited from

Transaction.verifyScript

Defined in

node_modules/bsv/index.d.ts:998


verifySignature

verifySignature(sig, pubkey, nin, subscript, satoshisBN, flags): boolean

Parameters

NameType
sigSignature
pubkeyPublicKey
ninnumber
subscriptScript
satoshisBNBN
flagsnumber

Returns

boolean

Inherited from

Transaction.verifySignature

Defined in

node_modules/bsv/index.d.ts:926

- + \ No newline at end of file diff --git a/reference/interfaces/TxContext/index.html b/reference/interfaces/TxContext/index.html index 0b1f4d2de..c5529f90a 100644 --- a/reference/interfaces/TxContext/index.html +++ b/reference/interfaces/TxContext/index.html @@ -4,14 +4,14 @@ TxContext | sCrypt - +

TxContext

scrypt-ts / TxContext

Interface: TxContext

TxContext provides some context information of the current transaction, needed only if signature is checked inside the contract.

Table of contents

Properties

Properties

inputIndex

inputIndex: number

input index

Defined in

node_modules/scryptlib/dist/contract.d.ts:13


inputSatoshis

inputSatoshis: number

input amount in satoshis

Defined in

node_modules/scryptlib/dist/contract.d.ts:15


opReturn

Optional opReturn: string

contract state in ASM format

Defined in

node_modules/scryptlib/dist/contract.d.ts:17


opReturnHex

Optional opReturnHex: string

contract state in hex format

Defined in

node_modules/scryptlib/dist/contract.d.ts:19


tx

tx: string | Transaction

current transaction represented in bsv.Transaction object or hex string

Defined in

node_modules/scryptlib/dist/contract.d.ts:11

- + \ No newline at end of file diff --git a/reference/interfaces/TxInputRef/index.html b/reference/interfaces/TxInputRef/index.html index 6665f04ed..b8e1c6d0a 100644 --- a/reference/interfaces/TxInputRef/index.html +++ b/reference/interfaces/TxInputRef/index.html @@ -4,13 +4,13 @@ TxInputRef | sCrypt - +

TxInputRef

scrypt-ts / TxInputRef

Interface: TxInputRef

A reference to an input of a transaction

Table of contents

Properties

Properties

inputIndex

inputIndex: number

an index indicating the position of inputs in this transaction

Defined in

dist/smart-contract/contract.d.ts:17


tx

tx: Transaction

transaction object

Defined in

dist/smart-contract/contract.d.ts:15

- + \ No newline at end of file diff --git a/reference/interfaces/TxOutputRef/index.html b/reference/interfaces/TxOutputRef/index.html index 7729cd20a..ae0be9779 100644 --- a/reference/interfaces/TxOutputRef/index.html +++ b/reference/interfaces/TxOutputRef/index.html @@ -4,13 +4,13 @@ TxOutputRef | sCrypt - +

TxOutputRef

scrypt-ts / TxOutputRef

Interface: TxOutputRef

A reference to an output of a transaction

Table of contents

Properties

Properties

outputIndex

outputIndex: number

index of an output in this transaction

Defined in

dist/smart-contract/contract.d.ts:25


tx

tx: Transaction

transaction object

Defined in

dist/smart-contract/contract.d.ts:23

- + \ No newline at end of file diff --git a/reference/interfaces/UtxoQueryOptions/index.html b/reference/interfaces/UtxoQueryOptions/index.html index 4a9404c82..1e25d6fc4 100644 --- a/reference/interfaces/UtxoQueryOptions/index.html +++ b/reference/interfaces/UtxoQueryOptions/index.html @@ -4,13 +4,13 @@ UtxoQueryOptions | sCrypt - +

UtxoQueryOptions

scrypt-ts / UtxoQueryOptions

Interface: UtxoQueryOptions

The optional conditions for querying P2PKH UTXO.

Table of contents

Properties

Properties

additional

Optional additional: number

Defined in

dist/bsv/abstract-provider.d.ts:16


estimateSize

estimateSize: number

Defined in

dist/bsv/abstract-provider.d.ts:14


feePerKb

feePerKb: number

Defined in

dist/bsv/abstract-provider.d.ts:15


unspentValue

unspentValue: number

Defined in

dist/bsv/abstract-provider.d.ts:13

- + \ No newline at end of file diff --git a/reference/interfaces/VerifyResult/index.html b/reference/interfaces/VerifyResult/index.html index 4d4c93562..23c0adbed 100644 --- a/reference/interfaces/VerifyResult/index.html +++ b/reference/interfaces/VerifyResult/index.html @@ -4,13 +4,13 @@ VerifyResult | sCrypt - +

VerifyResult

scrypt-ts / VerifyResult

Interface: VerifyResult

Table of contents

Properties

Properties

error

Optional error: string

Defined in

node_modules/scryptlib/dist/contract.d.ts:26


success

success: boolean

Defined in

node_modules/scryptlib/dist/contract.d.ts:25

- + \ No newline at end of file diff --git a/reference/interfaces/bsv.Networks.Network/index.html b/reference/interfaces/bsv.Networks.Network/index.html index 5ca40218a..17c1b7f4c 100644 --- a/reference/interfaces/bsv.Networks.Network/index.html +++ b/reference/interfaces/bsv.Networks.Network/index.html @@ -4,13 +4,13 @@ bsv.Networks.Network | sCrypt - +

bsv.Networks.Network

scrypt-ts / bsv / Networks / Network

Interface: Network

bsv.Networks.Network

Table of contents

Properties

Properties

alias

Readonly alias: string

Defined in

node_modules/bsv/index.d.ts:1378


name

Readonly name: string

Defined in

node_modules/bsv/index.d.ts:1377

- + \ No newline at end of file diff --git a/reference/interfaces/bsv.Script.IOpChunk/index.html b/reference/interfaces/bsv.Script.IOpChunk/index.html index e7b08a32a..501a44ccb 100644 --- a/reference/interfaces/bsv.Script.IOpChunk/index.html +++ b/reference/interfaces/bsv.Script.IOpChunk/index.html @@ -4,13 +4,13 @@ bsv.Script.IOpChunk | sCrypt - +

bsv.Script.IOpChunk

scrypt-ts / bsv / Script / IOpChunk

Interface: IOpChunk

bsv.Script.IOpChunk

Table of contents

Properties

Properties

buf

buf: Buffer

Defined in

node_modules/bsv/index.d.ts:1213


len

len: number

Defined in

node_modules/bsv/index.d.ts:1214


opcodenum

opcodenum: number

Defined in

node_modules/bsv/index.d.ts:1215

- + \ No newline at end of file diff --git a/reference/interfaces/bsv.Script.Interpreter.InterpretState/index.html b/reference/interfaces/bsv.Script.Interpreter.InterpretState/index.html index e8f8e8307..3761183dc 100644 --- a/reference/interfaces/bsv.Script.Interpreter.InterpretState/index.html +++ b/reference/interfaces/bsv.Script.Interpreter.InterpretState/index.html @@ -4,13 +4,13 @@ bsv.Script.Interpreter.InterpretState | sCrypt - +
- + \ No newline at end of file diff --git a/reference/interfaces/bsv.Transaction.IUnspentOutput/index.html b/reference/interfaces/bsv.Transaction.IUnspentOutput/index.html index b7cedad20..350c0fef5 100644 --- a/reference/interfaces/bsv.Transaction.IUnspentOutput/index.html +++ b/reference/interfaces/bsv.Transaction.IUnspentOutput/index.html @@ -4,13 +4,13 @@ bsv.Transaction.IUnspentOutput | sCrypt - +

bsv.Transaction.IUnspentOutput

scrypt-ts / bsv / Transaction / IUnspentOutput

Interface: IUnspentOutput

bsv.Transaction.IUnspentOutput

Table of contents

Properties

Properties

address

Optional address: string

Defined in

node_modules/bsv/index.d.ts:795


outputIndex

outputIndex: number

Defined in

node_modules/bsv/index.d.ts:797


satoshis

satoshis: number

Defined in

node_modules/bsv/index.d.ts:799


script

script: string

Defined in

node_modules/bsv/index.d.ts:798


txId

txId: string

Defined in

node_modules/bsv/index.d.ts:796

- + \ No newline at end of file diff --git a/reference/interfaces/bsv.Util/index.html b/reference/interfaces/bsv.Util/index.html index 352aab146..da5d725f1 100644 --- a/reference/interfaces/bsv.Util/index.html +++ b/reference/interfaces/bsv.Util/index.html @@ -4,13 +4,13 @@ bsv.Util | sCrypt - +
- + \ No newline at end of file diff --git a/reference/interfaces/bsv.crypto.IOpts/index.html b/reference/interfaces/bsv.crypto.IOpts/index.html index 59e41b447..c2ec27d9c 100644 --- a/reference/interfaces/bsv.crypto.IOpts/index.html +++ b/reference/interfaces/bsv.crypto.IOpts/index.html @@ -4,13 +4,13 @@ bsv.crypto.IOpts | sCrypt - +

bsv.crypto.IOpts

scrypt-ts / bsv / crypto / IOpts

Interface: IOpts

bsv.crypto.IOpts

Table of contents

Properties

Properties

endian

endian: "big" | "little"

Defined in

node_modules/bsv/index.d.ts:651


size

Optional size: number

Defined in

node_modules/bsv/index.d.ts:652

- + \ No newline at end of file diff --git a/reference/modules/bsv.Networks/index.html b/reference/modules/bsv.Networks/index.html index bb00a2d51..9e2a542d9 100644 --- a/reference/modules/bsv.Networks/index.html +++ b/reference/modules/bsv.Networks/index.html @@ -4,13 +4,13 @@ bsv.Networks | sCrypt - +

bsv.Networks

scrypt-ts / bsv / Networks

Namespace: Networks

bsv.Networks

Table of contents

Interfaces

Type Aliases

Variables

Functions

Type Aliases

Type

Ƭ Type: "livenet" | "testnet" | Network

Defined in

node_modules/bsv/index.d.ts:1381

Variables

defaultNetwork

Const defaultNetwork: Network

Defined in

node_modules/bsv/index.d.ts:1386


livenet

Const livenet: Network

Defined in

node_modules/bsv/index.d.ts:1383


mainnet

Const mainnet: Network

Defined in

node_modules/bsv/index.d.ts:1384


testnet

Const testnet: Network

Defined in

node_modules/bsv/index.d.ts:1385

Functions

add

add(data): Network

Parameters

NameType
dataany

Returns

Network

Defined in

node_modules/bsv/index.d.ts:1388


get

get(args, keys?): Network

Parameters

NameType
argsstring | number | Network
keys?string | string[]

Returns

Network

Defined in

node_modules/bsv/index.d.ts:1390


remove

remove(network): void

Parameters

NameType
networkType

Returns

void

Defined in

node_modules/bsv/index.d.ts:1389

- + \ No newline at end of file diff --git a/reference/modules/bsv.Script.Interpreter/index.html b/reference/modules/bsv.Script.Interpreter/index.html index e764b93e0..673ab531f 100644 --- a/reference/modules/bsv.Script.Interpreter/index.html +++ b/reference/modules/bsv.Script.Interpreter/index.html @@ -4,13 +4,13 @@ bsv.Script.Interpreter | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.Script/index.html b/reference/modules/bsv.Script/index.html index 21faab980..61b3f1be9 100644 --- a/reference/modules/bsv.Script/index.html +++ b/reference/modules/bsv.Script/index.html @@ -4,13 +4,13 @@ bsv.Script | sCrypt - +

bsv.Script

scrypt-ts / bsv / Script

Namespace: Script

bsv.Script

Table of contents

Namespaces

Classes

Interfaces

Variables

Functions

Variables

types

Const types: Object

Type declaration

NameType
DATA_OUTstring

Defined in

node_modules/bsv/index.d.ts:1208

Functions

buildMultisigIn

buildMultisigIn(pubkeys, threshold, signatures, opts): Script

Parameters

NameType
pubkeysPublicKey[]
thresholdnumber
signaturesBuffer[]
optsobject

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1223


buildMultisigOut

buildMultisigOut(publicKeys, threshold, opts): Script

Parameters

NameType
publicKeysPublicKey[]
thresholdnumber
optsobject

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1218


buildPublicKeyHashIn

buildPublicKeyHashIn(publicKey, signature, sigtype): Script

Parameters

NameType
publicKeyPublicKey
signatureBuffer | Signature
sigtypenumber

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1243


buildPublicKeyHashOut

buildPublicKeyHashOut(address): Script

Parameters

NameType
addressstring | PublicKey | Address

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1230


buildPublicKeyIn

buildPublicKeyIn(signature, sigtype): Script

Parameters

NameType
signatureBuffer | Signature
sigtypenumber

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1239


buildPublicKeyOut

buildPublicKeyOut(pubkey): Script

Parameters

NameType
pubkeyPublicKey

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1233


buildSafeDataOut

buildSafeDataOut(data, encoding?): Script

Parameters

NameType
datastring | Buffer | (string | Buffer)[]
encoding?string

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1234


buildScriptHashOut

buildScriptHashOut(script): Script

Parameters

NameType
scriptScript

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1238


empty

empty(): Script

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1260


fromASM

fromASM(str): Script

Parameters

NameType
strstring

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1250


fromAddress

fromAddress(address): Script

Parameters

NameType
addressstring | Address

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1249


fromBuffer

fromBuffer(buf): Script

Parameters

NameType
bufBuffer

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1253


fromHex

fromHex(hex): Script

Parameters

NameType
hexstring

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1251


fromString

fromString(str): Script

Parameters

NameType
strstring

Returns

Script

Defined in

node_modules/bsv/index.d.ts:1252


toASM

toASM(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1255


toBuffer

toBuffer(): Buffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:1256


toHex

toHex(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1257


toString

toString(): string

Returns

string

Defined in

node_modules/bsv/index.d.ts:1258

- + \ No newline at end of file diff --git a/reference/modules/bsv.Transaction.Input/index.html b/reference/modules/bsv.Transaction.Input/index.html index b368c4f9e..1951798db 100644 --- a/reference/modules/bsv.Transaction.Input/index.html +++ b/reference/modules/bsv.Transaction.Input/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Input | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.Transaction.Sighash/index.html b/reference/modules/bsv.Transaction.Sighash/index.html index 62cfaaeb3..6f3b37481 100644 --- a/reference/modules/bsv.Transaction.Sighash/index.html +++ b/reference/modules/bsv.Transaction.Sighash/index.html @@ -4,13 +4,13 @@ bsv.Transaction.Sighash | sCrypt - +

bsv.Transaction.Sighash

scrypt-ts / bsv / Transaction / Sighash

Namespace: Sighash

bsv.Transaction.Sighash

Table of contents

Functions

Functions

sighash

sighash(transaction, sighashType, inputNumber, subscript, satoshisBN, flags?): Buffer

Parameters

NameType
transactionTransaction
sighashTypenumber
inputNumbernumber
subscriptScript
satoshisBNBN
flags?number

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:870


sighashPreimage

sighashPreimage(transaction, sighashType, inputNumber, subscript, satoshisBN, flags?): Buffer

Parameters

NameType
transactionTransaction
sighashTypenumber
inputNumbernumber
subscriptScript
satoshisBNBN
flags?number

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:862


sign

sign(transaction, privateKey, sighashType, inputIndex, subscript, satoshisBN, flags?): Signature

Parameters

NameType
transactionTransaction
privateKeyPrivateKey
sighashTypenumber
inputIndexnumber
subscriptScript
satoshisBNBN
flags?number

Returns

Signature

Defined in

node_modules/bsv/index.d.ts:878


verify

verify(transaction, signature, publicKey, inputIndex, subscript, satoshisBN, flags?): boolean

Parameters

NameType
transactionTransaction
signatureSignature
publicKeyPublicKey
inputIndexnumber
subscriptScript
satoshisBNBN
flags?number

Returns

boolean

Defined in

node_modules/bsv/index.d.ts:887

- + \ No newline at end of file diff --git a/reference/modules/bsv.Transaction/index.html b/reference/modules/bsv.Transaction/index.html index 911085a6e..005f9b420 100644 --- a/reference/modules/bsv.Transaction/index.html +++ b/reference/modules/bsv.Transaction/index.html @@ -4,13 +4,13 @@ bsv.Transaction | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.crypto.ECDSA/index.html b/reference/modules/bsv.crypto.ECDSA/index.html index 686a36471..fedab6226 100644 --- a/reference/modules/bsv.crypto.ECDSA/index.html +++ b/reference/modules/bsv.crypto.ECDSA/index.html @@ -4,13 +4,13 @@ bsv.crypto.ECDSA | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.crypto.Hash/index.html b/reference/modules/bsv.crypto.Hash/index.html index 8da691cdd..495e4264f 100644 --- a/reference/modules/bsv.crypto.Hash/index.html +++ b/reference/modules/bsv.crypto.Hash/index.html @@ -4,13 +4,13 @@ bsv.crypto.Hash | sCrypt - +

bsv.crypto.Hash

scrypt-ts / bsv / crypto / Hash

Namespace: Hash

bsv.crypto.Hash

Table of contents

Functions

Functions

ripemd160

ripemd160(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:745


sha1

sha1(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:740


sha256

sha256(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:741


sha256hmac

sha256hmac(data, key): Buffer

Parameters

NameType
dataBuffer
keyBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:747


sha256ripemd160

sha256ripemd160(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:743


sha256sha256

sha256sha256(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:742


sha512

sha512(buffer): Buffer

Parameters

NameType
bufferBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:744


sha512hmac

sha512hmac(data, key): Buffer

Parameters

NameType
dataBuffer
keyBuffer

Returns

Buffer

Defined in

node_modules/bsv/index.d.ts:748

- + \ No newline at end of file diff --git a/reference/modules/bsv.crypto.Random/index.html b/reference/modules/bsv.crypto.Random/index.html index dcf5d269d..6634204ad 100644 --- a/reference/modules/bsv.crypto.Random/index.html +++ b/reference/modules/bsv.crypto.Random/index.html @@ -4,13 +4,13 @@ bsv.crypto.Random | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.crypto/index.html b/reference/modules/bsv.crypto/index.html index d923079a4..6b634ff82 100644 --- a/reference/modules/bsv.crypto/index.html +++ b/reference/modules/bsv.crypto/index.html @@ -4,13 +4,13 @@ bsv.crypto | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv.encoding/index.html b/reference/modules/bsv.encoding/index.html index 506533ae8..b37482029 100644 --- a/reference/modules/bsv.encoding/index.html +++ b/reference/modules/bsv.encoding/index.html @@ -4,13 +4,13 @@ bsv.encoding | sCrypt - + - + \ No newline at end of file diff --git a/reference/modules/bsv/index.html b/reference/modules/bsv/index.html index e7d4b8a9a..dea056a47 100644 --- a/reference/modules/bsv/index.html +++ b/reference/modules/bsv/index.html @@ -4,13 +4,13 @@ bsv | sCrypt - + - + \ No newline at end of file diff --git a/search/index.html b/search/index.html index ac1263fbf..2a69c1c77 100644 --- a/search/index.html +++ b/search/index.html @@ -4,13 +4,13 @@ Search the documentation | sCrypt - +

Search the documentation

- + \ No newline at end of file diff --git a/tokens/ft/buildstateoutputft/index.html b/tokens/ft/buildstateoutputft/index.html index c7cc6bbf3..b68c9f9ec 100644 --- a/tokens/ft/buildstateoutputft/index.html +++ b/tokens/ft/buildstateoutputft/index.html @@ -4,13 +4,13 @@ buildStateOutputFT | sCrypt - +

buildStateOutputFT

Any instance of BSV20V1 or BSV20V2 contains the buildStateOutputFT method. Unlike the regular buildStateOutput method, this method inscribes the subsequent amount with an appropriate BSV-20 transfer inscription. The method takes the number of tokens to be transferred to the subsequent output as an argument.

Below is an example of an FT counter contract:

class CounterFTV2 extends BSV20V2 {

@prop(true)
counter: bigint

constructor(id: ByteString, max: bigint, dec: bigint, counter: bigint) {
super(id, max, dec)
this.init(...arguments)
this.counter = counter
}

@method(SigHash.ANYONECANPAY_SINGLE)
public inc(tokenAmt: bigint) {
this.counter++

const outputs = this.buildStateOutputFT(tokenAmt)

assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

}

Each iteration will contain the number of tokens that was passed via tokenAmt. Note that this amount cannot be larger than the amount in the previous iteration. If the amount is less than in the previous iteration, the remaining tokens will be returned as change.

- + \ No newline at end of file diff --git a/tokens/ft/existing/index.html b/tokens/ft/existing/index.html index 7557696a5..14c192eba 100644 --- a/tokens/ft/existing/index.html +++ b/tokens/ft/existing/index.html @@ -4,13 +4,13 @@ Transfer Existing FT to a Smart Contract | sCrypt - +
-

Transfer Existing FT to a Smart Contract

Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract.

If you would like to unlock a single specific UTXO, you can do the following:

HashPuzzleFT.loadArtifact();

// Initialize recipient smart contract.
const message = toByteString('super secret', true);
const hash = sha256(message);
const recipient = new HashPuzzleFT(hash);
await recipient.connect(getDefaultSigner());

// Create P2PKH object from an UTXO
// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.
const utxo: UTXO = ...
const p2pkh = BSV20V2P2PKH.fromUTXO(utxo);
await p2pkh.connect(getDefaultSigner());

// Unlock it and transfer the FTs carried by it.
const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
} as MethodCallOptions<BSV20V2P2PKH>
);

console.log("Transferred FT: ", transferTx.id);

Alternatively, you can unlock multiple FT UTXOs and send them to a smart contract. Using the getBSV20 function you can simply fetch FT UTXOs for a given Ordinal wallet address.

// ... initialize recipient smart contract

// Fetch FT UTXOs for given Ordinal address.
// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.
const bsv20P2PKHs = await BSV20V2P2PKH.getBSV20(tokenId, `your ordinal address`);

await Promise.all(bsv20P2PKHs.map((p) => p.connect(signer)));

// Construct recipient smart contract(s)
const recipients: Array<FTReceiver> = [
{
instance: new HashPuzzleFTV2(tokenId, amount, dec, sha256(message)),
amt: 6n,
},
];

// Transfer
const { tx, nexts } = await BSV20V2P2PKH.transfer(
bsv20P2PKHs,
signer,
recipients
);

console.log("Transferred FT: ", transferTx.id);

Handling Change

Note, how the mechanism above is very similar to a regular Bitcoin transfer. If the FT amount from the inputs exceeds the recipients amount, the leftover will be transferred back to the Ordinal address as change.

You can customize the address using the method call option tokenChangeAddress:

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
tokenChangeAddress: yourOrdinalAddress
} as MethodCallOptions<BSV20V2P2PKH>
)

You can also skip change altogether by using the skipTokenChange option. But be careful! Any leftover tokens will be considered burned in this case:

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
skipTokenChange: true
} as MethodCallOptions<BSV20V2P2PKH>
)
- +

Transfer Existing FT to a Smart Contract

Suppose you'd like to unlock existing UTXOs that carry a FT to a smart contract.

If you would like to unlock a single specific UTXO, you can do the following:

HashLockFT.loadArtifact();
...
// Initialize recipient smart contract.
const message = toByteString('super secret', true);
const hash = sha256(message);
const recipient = new HashLockFT(tick, max, lim, dec, hash);
await recipient.connect(getDefaultSigner());

// Create P2PKH object from an UTXO
// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.
const utxo: UTXO = ...
const p2pkh = BSV20V2P2PKH.fromUTXO(utxo);
await p2pkh.connect(getDefaultSigner());

// Unlock it and transfer the FTs carried by it.
const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
} as OrdiMethodCallOptions<BSV20V2P2PKH>
);

console.log("Transferred FT: ", transferTx.id);

Alternatively, you can unlock multiple FT UTXOs and send them to a smart contract. Using the getBSV20 function you can simply fetch FT UTXOs for a given Ordinal wallet address.

// ... initialize recipient smart contract

// Fetch FT UTXOs for given Ordinal address.
// NOTE: You can not use BSV20V2P2PKH.getLatestInstance for BSV-20, it only works for NFTs.
const bsv20P2PKHs = await BSV20V2P2PKH.getBSV20(tokenId, `your ordinal address`);

await Promise.all(bsv20P2PKHs.map((p) => p.connect(signer)));

// Construct recipient smart contract(s)
const recipients: Array<FTReceiver> = [
{
instance: new HashLockFTV2(tokenId, amount, dec, sha256(message)),
amt: 6n,
},
];

// Transfer
const { tx, nexts } = await BSV20V2P2PKH.transfer(
bsv20P2PKHs,
signer,
recipients
);

console.log("Transferred FT: ", transferTx.id);

Handling Change

Note, how the mechanism above is very similar to a regular Bitcoin transfer. If the FT amount from the inputs exceeds the recipients amount, the leftover will be transferred back to the Ordinal address as change.

You can customize the address using the method call option tokenChangeAddress:

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
tokenChangeAddress: yourOrdinalAddress
} as OrdiMethodCallOptions<BSV20V2P2PKH>
)

You can also skip change altogether by using the skipTokenChange option. But be careful! Any leftover tokens will be considered burned in this case:

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
skipTokenChange: true
} as OrdiMethodCallOptions<BSV20V2P2PKH>
)
+ \ No newline at end of file diff --git a/tokens/ft/index.html b/tokens/ft/index.html index 7a8f51012..8d44b9a2e 100644 --- a/tokens/ft/index.html +++ b/tokens/ft/index.html @@ -4,14 +4,14 @@ Funglible Tokens - FTs | sCrypt - +
-

Funglible Tokens - FTs

Just like NFTs, scrypt-ord also supports fungible tokens. Under the hood it utilizes the bsv-20 protocol.

BSV-20 is a protocol for creating fungible tokens on the Bitcoin SV blockchain. Fungible tokens are interchangeable with each other, and can be used to represent a variety of assets, such as currencies, securities, and in-game items.

scrypt-ord supports both v1 and v2 of the BSV-20 FT protocol.

v1

Tokens utilizing the first version of the bsv-20 must be initialized by a deployment inscription, which specifies the token's ticker symbol, amount and mint limit. For more information, refer to the 1Sat docs.

To create a v1 token smart contract, have it extend the BSV20V1 class:

class HashPuzzleFT extends BSV20V1 {
@prop()
hash: Sha256

constructor(tick: ByteString, max: bigint, lim: bigint, hash: Sha256) {
super(tick, max, lim)
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

As you can see above, the constructor of contract extending the BSV20V1 takes as parameters all of the needed information for the token deployment, succeeded by other parameters needed you use for your contract (hash in this particular example). -Each constructor extending the BSV20V1 class must also call the instances init method and pass the constructors arguments. It is important to call this function after the call to super.

Deployment

Here's an example of how you would deploy the new FT:

HashPuzzleFT.loadArtifact();

const tick = toByteString("DOGE", true);
const max = 100000n;
const lim = max / 10n;

const hashPuzzle = new HashPuzzleFT(
tick,
max,
lim,
sha256(toByteString("secret0", true))
);
await hashPuzzle.connect(getDefaultSigner());
await hashPuzzle.deployToken();

Mint and Transfer

Once the deployment transaction was successfully broadcast, the token is ready for minting.

Here's how you can mint some amount:

// Minting
const amt = 1000n;
const mintTx = await hashPuzzle.mint(amt);
console.log("Minted tx: ", mintTx.id);

Note, that if the amount exceeds the limit set above, or the token was already wholely minted, the transaction won't be considered valid by 1Sat indexers.

The minted amount can then be transferred by calling the contract, as in regular sCrypt contracts:

// Transfer
for (let i = 0; i < 3; i++) {
// The recipient contract.
// Because this particular contract doesn't enforce subsequent outputs,
// it could be any other contract or just a P2PKH.
const receiver = new HashPuzzleFT(
tick,
max,
lim,
sha256(toByteString(`secret${i + 1}`, true))
);
const recipients: Array<FTReceiver> = [
{
instance: receiver,
amt: 10n,
},
];

// Unlock and transfer.
const { tx } = await hashPuzzle.methods.unlock(
toByteString(`secret:${i}`, true),
{
transfer: recipients,
}
);
console.log("Transfer tx: ", tx.id);

// Update instance for next iteration.
hashPuzzle = recipients[0].instance as HashPuzzleFT;
}

Note that the new recipient smart contract instance is passed as a parameter named transfer during the call to the deployed instance. The transfer parameter is an array of contract instances that extend BSV20V1.

v2

Version 2 of the BSV-20 token protocol simplifies the process of minting a new fungible token. In this version, the deployment and minting are done within a single transaction. Unlike v1, v2 lacks a token ticker field. The token is identified by an id field, which is the transaction id and output index where the token was minted, in the form of <txid>_<vout>.

Please refer to the official 1Sat documentation for more info.

To create a v2 token smart contract, have it extend the BSV20V2 class:

class HashPuzzleFTV2 extends BSV20V2 {
@prop()
hash: Sha256

constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {
super(id, max, dec)
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

Mint

In v1, tokens are deployed and minted in separate transactions, but in v2, all tokens are deployed and minted in one transactions. Here's an example of how you would deploy the new v2 FT:

HashPuzzleFTV2.loadArtifact()

const max = 10000n // Whole token amount.
const dec = 0n // Decimal precision.

hashPuzzle = new HashPuzzleFTV2(
toByteString(''),
max,
dec,
sha256(toByteString('super secret', true))
)
await hashPuzzle.connect(getDefaultSigner())

tokenId = await hashPuzzle.deployToken()
console.log('token id: ', tokenId)
note

Since we cannot know the id of the token deployment transaction at the time of deployment, the id is empty.

The whole token supply is minted within the first transaction, and whoever can unlock the deployment UTXO will gain full control of the whole supply. Additionally, the smart contract itself can enforce rules for the distribution of the tokens.

Transfer

The minted amount can be transferred by invoking the contract, similar to standard sCrypt contracts:

// Transfer
for (let i = 0; i < 3; i++) {
// The recipient contract.
// Because this particular contract doesn't enforce subsequent outputs,
// it could be any other contract or just a P2PKH.
const receiver = new HashPuzzleFT(
toByteString(tokenId, true),
max,
dec,
sha256(toByteString(`secret${i + 1}`, true))
);
const recipients: Array<FTReceiver> = [
{
instance: receiver,
amt: 10n,
},
];

// Unlock and transfer.
const { tx } = await hashPuzzle.methods.unlock(
toByteString(`secret:${i}`, true),
{
transfer: recipients,
}
);
console.log("Transfer tx: ", tx.id);

// Update instance for next iteration.
hashPuzzle = recipients[0].instance as HashPuzzleFTV2;
}

The new recipient smart contract instance is provided as a transfer parameter when calling the deployed instance. The transfer parameter consists of an array of contract instances derived from BSV20V2.


- +

Funglible Tokens - FTs

Just like NFTs, scrypt-ord also supports fungible tokens. Under the hood it utilizes the bsv-20 protocol.

BSV-20 is a protocol for creating fungible tokens on the Bitcoin SV blockchain. Fungible tokens are interchangeable with each other, and can be used to represent a variety of assets, such as currencies, securities, and in-game items.

scrypt-ord supports both v1 and v2 of the BSV-20 FT protocol.

v1

Tokens utilizing the first version of the bsv-20 must be initialized by a deployment inscription, which specifies the token's ticker symbol, amount and mint limit. For more information, refer to the 1Sat docs.

To create a v1 token smart contract, have it extend the BSV20V1 class:

class HashLockFT extends BSV20V1 {
@prop()
hash: Sha256

constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {
super(tick, max, lim, dec)
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

As you can see above, the constructor of contract extending the BSV20V1 takes as parameters all of the needed information for the token deployment, succeeded by other parameters needed you use for your contract (hash in this particular example). +Each constructor extending the BSV20V1 class must also call the instances init method and pass the constructors arguments. It is important to call this function after the call to super.

Deployment

Here's an example of how you would deploy the new FT:

HashLockFT.loadArtifact();

const tick = toByteString("DOGE", true);
const max = 100000n;
const lim = max / 10n;
const dec = 0n

const hashLock = new HashLockFT(
tick,
max,
lim,
dec,
sha256(toByteString("secret0", true))
);
await hashLock.connect(getDefaultSigner());
await hashLock.deployToken();

Mint and Transfer

Once the deployment transaction was successfully broadcast, the token is ready for minting.

Here's how you can mint some amount:

// Minting
const amt = 1000n;
const mintTx = await hashLock.mint(amt);
console.log("Minted tx: ", mintTx.id);

Note, that if the amount exceeds the limit set above, or the token was already wholely minted, the transaction won't be considered valid by 1Sat indexers.

The minted amount can then be transferred by calling the contract, as in regular sCrypt contracts:

// Transfer
for (let i = 0; i < 3; i++) {
// The recipient contract.
// Because this particular contract doesn't enforce subsequent outputs,
// it could be any other contract or just a P2PKH.
const receiver = new HashLockFT(
tick,
max,
lim,
dec,
sha256(toByteString(`secret${i + 1}`, true))
);
const recipients: Array<FTReceiver> = [
{
instance: receiver,
amt: 10n,
},
];

// Unlock and transfer.
const { tx } = await hashLock.methods.unlock(
toByteString(`secret:${i}`, true),
{
transfer: recipients,
}
);
console.log("Transfer tx: ", tx.id);

// Update instance for next iteration.
hashLock = recipients[0].instance as HashLockFT;
}

Note that the new recipient smart contract instance is passed as a parameter named transfer during the call to the deployed instance. The transfer parameter is an array of contract instances that extend BSV20V1.

v2

Version 2 of the BSV-20 token protocol simplifies the process of minting a new fungible token. In this version, the deployment and minting are done within a single transaction. Unlike v1, v2 lacks a token ticker field. The token is identified by an id field, which is the transaction id and output index where the token was minted, in the form of <txid>_<vout>.

Please refer to the official 1Sat documentation for more info.

To create a v2 token smart contract, have it extend the BSV20V2 class:

class HashLockFTV2 extends BSV20V2 {
@prop()
hash: Sha256

constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {
super(id, max, dec)
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

Mint

In v1, tokens are deployed and minted in separate transactions, but in v2, all tokens are deployed and minted in one transactions. Here's an example of how you would deploy the new v2 FT:

HashLockFTV2.loadArtifact()

const max = 10000n // Whole token amount.
const dec = 0n // Decimal precision.

hashLock = new HashLockFTV2(
toByteString(''),
max,
dec,
sha256(toByteString('super secret', true))
)
await hashLock.connect(getDefaultSigner())

tokenId = await hashLock.deployToken()
console.log('token id: ', tokenId)
note

Since we cannot know the id of the token deployment transaction at the time of deployment, the id is empty.

The whole token supply is minted within the first transaction, and whoever can unlock the deployment UTXO will gain full control of the whole supply. Additionally, the smart contract itself can enforce rules for the distribution of the tokens.

Transfer

The minted amount can be transferred by invoking the contract, similar to standard sCrypt contracts:

// Transfer
for (let i = 0; i < 3; i++) {
// The recipient contract.
// Because this particular contract doesn't enforce subsequent outputs,
// it could be any other contract or just a P2PKH.
const receiver = new HashLockFTV2(
toByteString(tokenId, true),
max,
dec,
sha256(toByteString(`secret${i + 1}`, true))
);
const recipients: Array<FTReceiver> = [
{
instance: receiver,
amt: 10n,
},
];

// Unlock and transfer.
const { tx } = await hashLock.methods.unlock(
toByteString(`secret:${i}`, true),
{
transfer: recipients,
}
);
console.log("Transfer tx: ", tx.id);

// Update instance for next iteration.
hashLock = recipients[0].instance as HashLockFTV2;
}

The new recipient smart contract instance is provided as a transfer parameter when calling the deployed instance. The transfer parameter consists of an array of contract instances derived from BSV20V2.


+ \ No newline at end of file diff --git a/tokens/ft/multiple/index.html b/tokens/ft/multiple/index.html index 4aa81f5c7..d468ae109 100644 --- a/tokens/ft/multiple/index.html +++ b/tokens/ft/multiple/index.html @@ -4,13 +4,13 @@ Multiple Inputs with Different Contracts | sCrypt - +
-

Multiple Inputs with Different Contracts

Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the section for calling multiple contract instances.

// One sender is regular bsv-20 P2PKH.
const sender0 = BSV20V2P2PKH.fromUTXO(utxo)
await sender0.connect(signer)

// Second sender is a hash puzzle contract.
const sender1 = HashPuzzleFTV2.fromUTXO(utxo)
await sender1.connect(signer)

// Recipient will be a single hash puzzle contract.
const recipientAmt = 6n
const recipients: Array<FTReceiver> = [
{
instance: new HashPuzzleFT(
tokenId,
amount,
dec,
sha256(toByteString('next super secret', true))
),
amt: recipientAmt,
},
];

const totalTokenAmt = sender0.getAmt() + sender1.getAmt()
const tokenChangeAmt = totalTokenAmt - recipientAmt

const ordPubKey = await signer.getDefaultPubKey()

sender0.bindTxBuilder(
'unlock',
async (
current: BSV20V2P2PKH,
options: MethodCallOptions<BSV20V2P2PKH>
): Promise<ContractTransaction> => {
const tx = new bsv.Transaction()
const nexts: StatefulNext<SmartContract>[] = []

for (let i = 0; i < recipients.length; i++) {
const receiver = recipients[i]

if (receiver.instance instanceof BSV20V2) {
receiver.instance.setAmt(receiver.amt)
} else {
throw new Error('Unsupported receiver, only BSV-20!')
}

tx.addOutput(
new bsv.Transaction.Output({
script: receiver.instance.lockingScript,
satoshis: 1,
})
)

nexts.push({
instance: receiver.instance,
balance: 1,
atOutputIndex: i,
})
}

if (tokenChangeAmt > 0n) {
const p2pkh = new BSV20V2P2PKH(
tokenId,
amount,
dec,
Addr(ordPubKey.toAddress().toByteString())
)

p2pkh.setAmt(tokenChangeAmt)

tx.addOutput(
new bsv.Transaction.Output({
script: p2pkh.lockingScript,
satoshis: 1,
})
)

nexts.push({
instance: p2pkh,
balance: 1,
atOutputIndex: nexts.length,
})
}

tx.change(ordPubKey.toAddress())

tx.addInput(current.buildContractInput())

return Promise.resolve({
tx: tx,
atInputIndex: 0,
nexts,
})
}
)

let partialContractTx = await sender0.methods.unlock(
(sigResps) => findSig(sigResps, ordPubKey),
PubKey(ordPubKey.toByteString()),
{
pubKeyOrAddrToSign: ordPubKey,
multiContractCall: true,
} as MethodCallOptions<BSV20V2P2PKH>
)

sender1.bindTxBuilder(
'unlock',
async (
current: HashPuzzleFTV2,
options: MethodCallOptions<HashPuzzleFTV2>
): Promise<ContractTransaction> => {
if (options.partialContractTx) {
const tx = options.partialContractTx.tx
tx.addInput(current.buildContractInput())

return Promise.resolve({
tx: tx,
atInputIndex: 1,
nexts: partialContractTx.nexts,
})
}

throw new Error('no partialContractTx')
}
)

partialContractTx = await sender1.methods.unlock(message1, {
partialContractTx,
transfer: recipients,
pubKeyOrAddrToSign: ordPubKey,
multiContractCall: true,
} as MethodCallOptions<BSV20V2P2PKH>)

const { tx } = await SmartContract.multiContractCall(
partialContractTx,
signer
)

console.log('Transfer tx:', tx.id)

In the above code, a partial transaction is constructed, which unlocks the first UTXO containing a BSV20V2P2PKH instance. The actual contract call doesn't execute yet, as we set the multiContractCall flag within the method call parameters.

We then feed that partially constructed transaction via the second contract call, which will unlock the HashPuzzleFT instance. Just like the first call, this call also has the multiContractCall flag set.

Once the transaction is fully built, we can sign and broadcast it using the SmartContract.multiContractCall function.

The above code is an example based on v2, but the same can be achieved using v1.

- +

Multiple Inputs with Different Contracts

Suppose we would like to unlock FTs within a single transaction that are located in different smart contracts. We can utilize the same technique demonstrated in the section for calling multiple contract instances.

// One sender is regular bsv-20 P2PKH.
const sender0 = BSV20V2P2PKH.fromUTXO(utxo)
await sender0.connect(signer)

// Second sender is a hash lock contract.
const sender1 = HashLockFTV2.fromUTXO(utxo)
await sender1.connect(signer)

// Recipient will be a single hash lock contract.
const recipientAmt = 6n
const recipients: Array<FTReceiver> = [
{
instance: new HashLockFTV2(
tokenId,
amount,
dec,
sha256(toByteString('next super secret', true))
),
amt: recipientAmt,
},
];

const totalTokenAmt = sender0.getAmt() + sender1.getAmt()
const tokenChangeAmt = totalTokenAmt - recipientAmt

const ordPubKey = await signer.getDefaultPubKey()

sender0.bindTxBuilder(
'unlock',
async (
current: BSV20V2P2PKH,
options: OrdiMethodCallOptions<BSV20V2P2PKH>
): Promise<ContractTransaction> => {
const tx = new bsv.Transaction()
const nexts: StatefulNext<SmartContract>[] = []

for (let i = 0; i < recipients.length; i++) {
const receiver = recipients[i]

if (receiver.instance instanceof BSV20V2) {
receiver.instance.setAmt(receiver.amt)
} else {
throw new Error('Unsupported receiver, only BSV-20!')
}

tx.addOutput(
new bsv.Transaction.Output({
script: receiver.instance.lockingScript,
satoshis: 1,
})
)

nexts.push({
instance: receiver.instance,
balance: 1,
atOutputIndex: i,
})
}

if (tokenChangeAmt > 0n) {
const p2pkh = new BSV20V2P2PKH(
tokenId,
amount,
dec,
Addr(ordPubKey.toAddress().toByteString())
)

p2pkh.setAmt(tokenChangeAmt)

tx.addOutput(
new bsv.Transaction.Output({
script: p2pkh.lockingScript,
satoshis: 1,
})
)

nexts.push({
instance: p2pkh,
balance: 1,
atOutputIndex: nexts.length,
})
}

tx.change(ordPubKey.toAddress())

tx.addInput(current.buildContractInput())

return Promise.resolve({
tx: tx,
atInputIndex: 0,
nexts,
})
}
)

let partialContractTx = await sender0.methods.unlock(
(sigResps) => findSig(sigResps, ordPubKey),
PubKey(ordPubKey.toByteString()),
{
pubKeyOrAddrToSign: ordPubKey,
multiContractCall: true,
} as OrdiMethodCallOptions<BSV20V2P2PKH>
)

sender1.bindTxBuilder(
'unlock',
async (
current: HashLockFTV2,
options: MethodCallOptions<HashLockFTV2>
): Promise<ContractTransaction> => {
if (options.partialContractTx) {
const tx = options.partialContractTx.tx
tx.addInput(current.buildContractInput())

return Promise.resolve({
tx: tx,
atInputIndex: 1,
nexts: partialContractTx.nexts,
})
}

throw new Error('no partialContractTx')
}
)

partialContractTx = await sender1.methods.unlock(message1, {
partialContractTx,
transfer: recipients,
pubKeyOrAddrToSign: ordPubKey,
multiContractCall: true,
} as OrdiMethodCallOptions<BSV20V2P2PKH>)

const { tx } = await SmartContract.multiContractCall(
partialContractTx,
signer
)

console.log('Transfer tx:', tx.id)

In the above code, a partial transaction is constructed, which unlocks the first UTXO containing a BSV20V2P2PKH instance. The actual contract call doesn't execute yet, as we set the multiContractCall flag within the method call parameters.

We then feed that partially constructed transaction via the second contract call, which will unlock the HashLockFTV2 instance. Just like the first call, this call also has the multiContractCall flag set.

Once the transaction is fully built, we can sign and broadcast it using the SmartContract.multiContractCall function.

The above code is an example based on v2, but the same can be achieved using v1.

+ \ No newline at end of file diff --git a/tokens/index.html b/tokens/index.html index 8c977afc7..97049ec8f 100644 --- a/tokens/index.html +++ b/tokens/index.html @@ -4,13 +4,13 @@ The Official sCrypt 1Sat Ordinals SDK | sCrypt - +
-

The Official sCrypt 1Sat Ordinals SDK

sCrypt offers its official 1Sat Ordinals SDK named scrypt-ord.

The SDK offers an easy to use interface for deploying and transferring 1Sat Ordinals and augmenting them with the power of sCrypt smart contracts.

It facilitates the development of smart contracts for both non-fungible tokens (NFTs) and fungible tokens (FTs).

Installation

It is recommended that you create an sCrypt project using our CLI tool. Once you have the project set up, simply run the following command:

npm i scrypt-ord

Base Classes

scrypt-ord provides base classes that can be extended with custom smart contract functionality. Unlike the SmartContract class, which you would typically extend for a regular sCrypt smart contract, here you should extend these base classes to integrate your smart contract with 1Sat ordinals functionality.

Non-fungible tokens (NFTs):

  • OrdinalNFT

Fungible tokens (FTs):

  • BSV20V1
  • BSV20V2

There are also pre-made classes that represent standard 1Sat transfers using the widely employed P2PKH mechanism:

  • OrdNFTP2PKH
  • BSV20V1P2PKH
  • BSV20V2P2PKH

Suppose you wish to lock an ordinal token using a custom hash puzzle contract, you would define the smart contract class as shown below:

class HashPuzzleNFT extends OrdinalNFT {
@prop()
hash: Sha256

constructor(hash: Sha256) {
super()
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

For a deeper exploration, please refer to the respective subsections:

OrdProvider

When you use sCrypt 1Sat Ordinals SDK, we recommend that you use OrdProvider as the provider. This allows your transaction to be indexed instantly, instead of waiting for it to be mined into a block.

export function getDefaultSigner(): TestWallet {
return new TestWallet(
myPrivateKey,
new OrdProvider(bsv.Networks.mainnet)
)
}
- +

The Official sCrypt 1Sat Ordinals SDK

sCrypt offers its official 1Sat Ordinals SDK named scrypt-ord.

The SDK offers an easy to use interface for deploying and transferring 1Sat Ordinals and augmenting them with the power of sCrypt smart contracts.

It facilitates the development of smart contracts for both non-fungible tokens (NFTs) and fungible tokens (FTs).

Installation

It is recommended that you create an sCrypt project using our CLI tool. Once you have the project set up, simply run the following command:

npm i scrypt-ord

Base Classes

scrypt-ord provides base classes that can be extended with custom smart contract functionality. Unlike the SmartContract class, which you would typically extend for a regular sCrypt smart contract, here you should extend these base classes to integrate your smart contract with 1Sat ordinals functionality.

Non-fungible tokens (NFTs):

  • OrdinalNFT

Fungible tokens (FTs):

  • BSV20V1
  • BSV20V2

There are also pre-made classes that represent standard 1Sat transfers using the widely employed P2PKH mechanism:

  • OrdiNFTP2PKH
  • BSV20V1P2PKH
  • BSV20V2P2PKH

Suppose you wish to lock an ordinal token using a custom hash puzzle contract, you would define the smart contract class as shown below:

class HashLockNFT extends OrdinalNFT {
@prop()
hash: Sha256

constructor(hash: Sha256) {
super()
this.init(...arguments)
this.hash = hash
}

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

For a deeper exploration, please refer to the respective subsections:

OrdProvider

When you use sCrypt 1Sat Ordinals SDK, we recommend that you use OrdiProvider as the provider. This allows your transaction to be indexed instantly, instead of waiting for it to be mined into a block.

export function getDefaultSigner(): TestWallet {
return new TestWallet(
myPrivateKey,
new OrdiProvider(bsv.Networks.mainnet)
)
}
+ \ No newline at end of file diff --git a/tokens/nft/buildstateoutputnft/index.html b/tokens/nft/buildstateoutputnft/index.html index 175beb7ba..6b2125227 100644 --- a/tokens/nft/buildstateoutputnft/index.html +++ b/tokens/nft/buildstateoutputnft/index.html @@ -4,13 +4,13 @@ buildStateOutputNFT | sCrypt - +

buildStateOutputNFT

Any instance of an OrdinalNFT contains the buildStateOutputNFT method. In contrast to the regular buildStateOutput method, this method also removes any inscription data that might be included in the smart contract's locking script. This is necessary because, within a stateful smart contract, we don't want the next iteration to re-inscribe the ordinal. Additionally, the buildStateOutputNFT method doesn't require a satoshi amount argument, as the amount is always 1 satoshi.

Below is an example of an ordinal counter contract:

class CounterNFT extends OrdinalNFT {

@prop(true)
counter: bigint

constructor(counter: bigint) {
super()
this.init(counter)
this.counter = counter
}

@method()
public incOnchain() {
this.counter++

...

let outputs = this.buildStateOutputNFT() // Does not include inscription in the next iteration.
outputs += this.buildChangeOutput()
assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

}

See the complete code on GitHub.

- + \ No newline at end of file diff --git a/tokens/nft/existing/index.html b/tokens/nft/existing/index.html index b51f2bc6b..75e867955 100644 --- a/tokens/nft/existing/index.html +++ b/tokens/nft/existing/index.html @@ -4,14 +4,14 @@ Transfer Existing NFT to a Smart Contract | sCrypt - +

Transfer Existing NFT to a Smart Contract

Suppose you would like to transfer an existing NFT that was already inscribed in the past, which is typically locked using a P2PKH lock. -You can fetch all the needed data for the transfer by either using fromUTXO or getLatestInstance. The former takes the deployed NFT's current UTXO, while the latter takes the NFT's origin.

If the deployed NFT is locked using a regular P2PKH you may unlock it like the following:

const outpoint = '036718e5c603169b9981a55f276adfa7b5d024616ac95e048b05a81258ea2388_0';

// Create a P2PKH object from a UTXO
const utxo: UTXO = OneSatApis.fetchUTXOByOutpoint(outpoint);
const p2pkh = OrdNFTP2PKH.fromUTXO(utxo);
// Alternatively, create a P2PKH from an origin
const p2pkh = await OrdNFTP2PKH.getLatestInstance(outpoint);

// Construct recipient smart contract
const message = toByteString('super secret', true);
const hash = sha256(message);
const recipient = new HashPuzzleNFT(hash);
await recipient.connect(getDefaultSigner());

// Unlock deployed NFT and send it to the recipient hash puzzle contract
await p2pkh.connect(getDefaultSigner());

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
} as MethodCallOptions<OrdNFTP2PKH>
);

console.log("Transferred NFT: ", transferTx.id);

Alternatively, if the NFT is locked using a smart contract, i.e. HashPuzzleNFT:

HashPuzzleNFT.loadArtifact();

// Retrieve `HashPuzzleNFT` instance holding the NFT
const nft = await HashPuzzleNFT.getLatestInstance(outpoint);
await nft.connect(getDefaultSigner());

const hash = sha256(toByteString('next super secret', true));
const recipient = new HashPuzzleNFT(hash);
await recipient.connect(getDefaultSigner());

// Send NFT to recipient
const { tx: transferTx } = await nft.methods.unlock(
toByteString('super secret', true),
{
transfer: recipient,
}
);

console.log("Transferred NFT: ", transferTx.id);
- +You can fetch all the needed data for the transfer by either using fromUTXO or getLatestInstance. The former takes the deployed NFT's current UTXO, while the latter takes the NFT's origin.

If the deployed NFT is locked using a regular P2PKH you may unlock it like the following:

const outpoint = '036718e5c603169b9981a55f276adfa7b5d024616ac95e048b05a81258ea2388_0';

// Create a P2PKH object from a UTXO
const utxo: UTXO = OneSatApis.fetchUTXOByOutpoint(outpoint);
const p2pkh = OrdiNFTP2PKH.fromUTXO(utxo);
// Alternatively, create a P2PKH from an origin
const p2pkh = await OrdiNFTP2PKH.getLatestInstance(outpoint);

// Construct recipient smart contract
const message = toByteString('super secret', true);
const hash = sha256(message);
const recipient = new HashLockNFT(hash);
await recipient.connect(getDefaultSigner());

// Unlock deployed NFT and send it to the recipient hash lock contract
await p2pkh.connect(getDefaultSigner());

const { tx: transferTx } = await p2pkh.methods.unlock(
(sigResps) => findSig(sigResps, `yourPubKey`),
PubKey(`yourPubKey`.toByteString()),
{
transfer: recipient,
pubKeyOrAddrToSign: `yourPubKey`,
} as OrdiMethodCallOptions<OrdiNFTP2PKH>
);

console.log("Transferred NFT: ", transferTx.id);

Alternatively, if the NFT is locked using a smart contract, i.e. HashLockNFT:

HashLockNFT.loadArtifact();

// Retrieve `HashLockNFT` instance holding the NFT
const nft = await HashLockNFT.getLatestInstance(outpoint);
await nft.connect(getDefaultSigner());

const hash = sha256(toByteString('next super secret', true));
const recipient = new HashLockNFT(hash);
await recipient.connect(getDefaultSigner());

// Send NFT to recipient
const { tx: transferTx } = await nft.methods.unlock(
toByteString('super secret', true),
{
transfer: recipient,
}
);

console.log("Transferred NFT: ", transferTx.id);
+ \ No newline at end of file diff --git a/tokens/nft/index.html b/tokens/nft/index.html index f8f5bae5a..4b6976bdc 100644 --- a/tokens/nft/index.html +++ b/tokens/nft/index.html @@ -4,14 +4,14 @@ Non Funglible Tokens - NFTs | sCrypt - +
-

Non Funglible Tokens - NFTs

To create a smart contract that will carry an NFT, have your smart contract extend the OrdinalNFT class:

import { method, prop, assert, ByteString, sha256, Sha256 } from "scrypt-ts";
import { OrdinalNFT } from "scrypt-ord";

export class HashPuzzleNFT extends OrdinalNFT {
@prop()
hash: Sha256;

constructor(hash: Sha256) {
super();
// Important: Call `init` after the `super()` statement.
this.init(...arguments);
this.hash = hash;
}

@method()
public unlock(message: ByteString) {
assert(this.hash === sha256(message), "hashes are not equal");
}
}

The contract above represents an NFT that can be unlocked / transferred by providing the secret pre-image of a hash value. -Each constructor extending the OrdinalNFT class must also call the instances init method and pass the constructors arguments. It is important to call this function after the call to super.

Inscribe

The following code demonstrates how deploy / inscribe the NFT contract:

HashPuzzleNFT.loadArtifact();

const text = "Hello sCrypt and 1Sat Ordinals";

const message = toByteString('secret string', true);
const hash = sha256(message);

const instance = new HashPuzzleNFT(hash);

const signer = getDefaultSigner();
await instance.connect(signer);

const inscriptionTx = await instance.inscribeTextNft(text);
console.log("Inscribed NFT: ", inscriptionTx.id);

The inscribeTextNft first inscribes the locking script with the specified text and then deploys the contract.

Among text the inscription can contain many other types of data. Here's how you can conveniently inscribe an image:

// ...

const bb = readFileSync(join(__dirname, "..", "..", "logo.png")).toString("base64");

const tx = await instance.inscribeImageNft(bb, ContentType.PNG);
console.log("Inscribed NFT: ", tx.id);

In fact the data type can be arbitrary. It only depends on the Ordinals wallet you're using to support that data type.

const tx = await instance.inscribe({
content: `your content in hex`,
contentType: `your contentType`,
});
console.log("Inscribed NFT: ", tx.id);

The value contentType must be a MIME-type string. The ContentType object contains common MIME-types.

Transfer

You can easily transfer a deployed NFT to an Ordinals address by passing a transfer value via the method call parameters.

OrdNFTP2PKH is a P2PKH contract for holding ordinals NFTs. Like a normal P2PKH contract, you need an address to instantiate it.

// ... deploy code from above

const { tx: transferTx } = await instance.methods.unlock(
message,
{
transfer: new OrdNFTP2PKH(
Addr(recipientAddress.toByteString())
),
}
);

console.log("Transferred NFT: ", transferTx.id);

The transfer parameter can be any single instance of a contract that extends OrdinalNFT.

- +

Non Funglible Tokens - NFTs

To create a smart contract that will carry an NFT, have your smart contract extend the OrdinalNFT class:

import { method, prop, assert, ByteString, sha256, Sha256 } from "scrypt-ts";
import { OrdinalNFT } from "scrypt-ord";

export class HashLockNFT extends OrdinalNFT {
@prop()
hash: Sha256;

constructor(hash: Sha256) {
super();
// Important: Call `init` after the `super()` statement.
this.init(...arguments);
this.hash = hash;
}

@method()
public unlock(message: ByteString) {
assert(this.hash === sha256(message), "hashes are not equal");
}
}

The contract above represents an NFT that can be unlocked / transferred by providing the secret pre-image of a hash value. +Each constructor extending the OrdinalNFT class must also call the instances init method and pass the constructors arguments. It is important to call this function after the call to super.

Inscribe

The following code demonstrates how deploy / inscribe the NFT contract:

HashLockNFT.loadArtifact();

const text = "Hello sCrypt and 1Sat Ordinals";

const message = toByteString('secret string', true);
const hash = sha256(message);

const instance = new HashLockNFT(hash);

const signer = getDefaultSigner();
await instance.connect(signer);

const inscriptionTx = await instance.inscribeText(text);
console.log("Inscribed NFT: ", inscriptionTx.id);

The inscribeText first inscribes the locking script with the specified text and then deploys the contract.

Among text the inscription can contain many other types of data. Here's how you can conveniently inscribe an image:

// ...

const bb = readFileSync(join(__dirname, "..", "..", "logo.png")).toString("base64");

const tx = await instance.inscribeImage(bb, ContentType.PNG);
console.log("Inscribed NFT: ", tx.id);

In fact the data type can be arbitrary. It only depends on the Ordinals wallet you're using to support that data type.

const tx = await instance.inscribe({
content: `your content in hex`,
contentType: `your contentType`,
});
console.log("Inscribed NFT: ", tx.id);

The value contentType must be a MIME-type string. The ContentType object contains common MIME-types.

Transfer

You can easily transfer a deployed NFT to an Ordinals address by passing a transfer value via the method call parameters.

OrdiNFTP2PKH is a P2PKH contract for holding ordinals NFTs. Like a normal P2PKH contract, you need an address to instantiate it.

// ... deploy code from above

const { tx: transferTx } = await instance.methods.unlock(
message,
{
transfer: new OrdiNFTP2PKH(
Addr(recipientAddress.toByteString())
),
}
);

console.log("Transferred NFT: ", transferTx.id);

The transfer parameter can be any single instance of a contract that extends OrdinalNFT.

+ \ No newline at end of file diff --git a/tutorials/auction/index.html b/tutorials/auction/index.html index 50e51246a..a8a1087da 100644 --- a/tutorials/auction/index.html +++ b/tutorials/auction/index.html @@ -4,13 +4,13 @@ Tutorial 2: Auction | sCrypt - +

Tutorial 2: Auction

Overview

In this tutorial, we will go over how to build an auction contract. It is open and transparent, where everyone can participate and the highest bidder wins when the bidding is over.

There are two ways to interact with the contract:

  1. Bid: if a higher bid is found, the current highest bidder is updated, and the previous highest bidder is refunded.
  2. Close: the auctioneer can close the auction after it expires and take the offer.

Contract Properties

According to the interactions above, this contract needs to store three properties:

  • The auctioneer, who starts the auction
  • The deadline for the auction
  • The highest bidder until now
// The bidder's public key.
@prop(true)
bidder: PubKey

// The auctioneer's public key.
@prop()
readonly auctioneer: PubKey

// Deadline of the auction. Can be block height or timestamp.
@prop()
readonly auctionDeadline: bigint

Constructor

Initialize all the @prop properties in the constructor. Note that we don't need to pass a bidder parameter.

constructor(auctioneer: PubKey, auctionDeadline: bigint) {
super(...arguments)
// the initial bidder is the auctioneer himeself
this.bidder = auctioneer
this.auctioneer = auctioneer
this.auctionDeadline = auctionDeadline
}

When deploying the contract, the auctioneer locked the minimal bid into the contract, and at this time, the highest bidder would be himself.

const auction = new Auction(publicKeyAuctioneer, auctionDeadline)
const deployTx = await auction.deploy(minBid)

Public Methods

Bid

In method public bid(bidder: Addr, bid: bigint), we need to check if the bidder has a higher bid than the previous one. If so, we update the highest bidder in the contract state and refund the previous bidder.

We can read the previous highest bid from the balance of the contract UTXO.

const highestBid: bigint = this.ctx.utxo.value

Then it's easy to demand a higher bid.

assert(bid > highestBid, 'the auction bid is lower than the current highest bid')

The spending/redeeming tx has these outputs.

  • Contract's new state output: records the new bidder and locks the new bid into contract UTXO.
// Log the previous highest bidder
const highestBidder: PubKey = this.bidder
// Change the public key of the highest bidder.
this.bidder = bidder

// Auction continues with a higher bidder.
const auctionOutput: ByteString = this.buildStateOutput(bid)
  • A refund P2PKH output: pay back the previous bidder.
// Refund previous highest bidder.
const refundOutput: ByteString = Utils.buildPublicKeyHashOutput(highestBidder, highestBid)
  • An optional change P2PKH output.
let outputs: ByteString = auctionOutput + refundOutput
// Add change output.
outputs += this.buildChangeOutput()

At last, we require the transaction to have these outputs using ScriptContext.

assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs check failed')

As bid is called continuously, the state of the contract is constantly updated. The highest bidder, and the highest bid as well, is recorded in the latest contract UTXO until the auctioneer closes the auction.

// Call this public method to bid with a higher offer.
@method()
public bid(bidder: Addr, bid: bigint) {
const highestBid: bigint = this.ctx.utxo.value
assert(bid > highestBid, 'the auction bid is lower than the current highest bid')

// Change the public key of the highest bidder.
const highestBidder: PubKey = this.bidder
this.bidder = bidder

// Auction continues with a higher bidder.
const auctionOutput: ByteString = this.buildStateOutput(bid)

// Refund previous highest bidder.
const refundOutput: ByteString = Utils.buildPublicKeyHashOutput(highestBidder, highestBid)

let outputs: ByteString = auctionOutput + refundOutput
// Add change output.
outputs += this.buildChangeOutput()

assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs check failed')
}

Close

Method public close(sig: Sig) is simple, we require:

  • It can only be called by the auctioneer. That is why we need to pass in the caller's signature.
// Check signature of the auctioneer.
assert(this.checkSig(sig, this.auctioneer), 'signature check failed')
  • Now the auction deadline has passed
assert(this.ctx.locktime >= this.auctionDeadline, 'auction is not over yet')
note

We don't place any constraint on transaction outputs here, because the auctioneer can send the highest bid to any address he controls, which is what we actually want.

// Close the auction if deadline is reached.
@method()
public close(sig: Sig) {
...
// Check deadline
assert(this.ctx.locktime >= this.auctionDeadline, 'auction is not over yet')
// Check signature of the auctioneer.
assert(this.checkSig(sig, this.auctioneer), 'signature check failed')
}

Customize tx builder for bid

Using default tx builder cannot meet our demand when calling bid, since the second output - the refund P2PKH output - is not a new contract instance.

In Function static bidTxBuilder(current: Auction, options: MethodCallOptions<Auction>, bidder: PubKey, bid: bigint): Promise<ContractTransaction>, we add all three outputs as designed.

const unsignedTx: Transaction = new bsv.Transaction()
// add contract input
.addInput(current.buildContractInput())
// build next instance output
.addOutput(new bsv.Transaction.Output({script: nextInstance.lockingScript, satoshis: Number(bid),}))
// build refund output
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(Utils.buildPublicKeyHashScript(current.bidder)),
satoshis: current.balance,
})
)
// build change output
.change(options.changeAddress)

Conclusion

Congratulations, you have completed the Auction contract! To use it in practice, you can refer to this example of an NFT auction.

The final complete code is as follows:

export class Auction extends SmartContract {
static readonly LOCKTIME_BLOCK_HEIGHT_MARKER = 500000000
static readonly UINT_MAX = 0xffffffffn

// The bidder's public key.
@prop(true)
bidder: PubKey

// The auctioneer's public key.
@prop()
readonly auctioneer: PubKey

// Deadline of the auction. Can be block height or timestamp.
@prop()
readonly auctionDeadline: bigint

constructor(auctioneer: PubKey, auctionDeadline: bigint) {
super(...arguments)
this.bidder = auctioneer
this.auctioneer = auctioneer
this.auctionDeadline = auctionDeadline
}

// Call this public method to bid with a higher offer.
@method()
public bid(bidder: PubKey, bid: bigint) {
const highestBid: bigint = this.ctx.utxo.value
assert(bid > highestBid, 'the auction bid is lower than the current highest bid')

// Change the public key of the highest bidder.
const highestBidder: PubKey = this.bidder
this.bidder = bidder

// Auction continues with a higher bidder.
const auctionOutput: ByteString = this.buildStateOutput(bid)

// Refund previous highest bidder.
const refundOutput: ByteString = Utils.buildPublicKeyHashOutput(pubKey2Addr(highestBidder), highestBid)
let outputs: ByteString = auctionOutput + refundOutput

// Add change output.
outputs += this.buildChangeOutput()

assert(hash256(outputs) == this.ctx.hashOutputs, 'hashOutputs check failed')
}

// Close the auction if deadline is reached.
@method()
public close(sig: Sig) {
// Check if using block height.
if (this.auctionDeadline < Auction.LOCKTIME_BLOCK_HEIGHT_MARKER) {
// Enforce nLocktime field to also use block height.
assert(this.ctx.locktime < Auction.LOCKTIME_BLOCK_HEIGHT_MARKER)
}

// Check nSequence is less than UINT_MAX.
assert(
this.ctx.sequence < Auction.UINT_MAX,
'input sequence should less than UINT_MAX'
)

// Check deadline
assert(
this.ctx.locktime >= this.auctionDeadline,
'auction is not over yet'
)


// Check signature of the auctioneer.
assert(this.checkSig(sig, this.auctioneer), 'signature check failed')
}

// User defined transaction builder for calling function `bid`
static bidTxBuilder(
current: Auction,
options: MethodCallOptions<Auction>,
bidder: PubKey,
bid: bigint
): Promise<ContractTransaction> {
const nextInstance = current.next()
nextInstance.bidder = bidder

const unsignedTx: Transaction = new bsv.Transaction()
// add contract input
.addInput(current.buildContractInput())
// build next instance output
.addOutput(
new bsv.Transaction.Output({
script: nextInstance.lockingScript,
satoshis: Number(bid),
})
)
// build refund output
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildPublicKeyHashScript(pubKey2Addr(current.bidder))
),
satoshis: current.balance,
})
)
// build change output
.change(options.changeAddress)

return Promise.resolve({
tx: unsignedTx,
atInputIndex: 0,
nexts: [
{
instance: nextInstance,
atOutputIndex: 0,
balance: Number(bid),
},
],
})
}
}
- + \ No newline at end of file diff --git a/tutorials/escrow/index.html b/tutorials/escrow/index.html index 92d1bba5e..56b1a4037 100644 --- a/tutorials/escrow/index.html +++ b/tutorials/escrow/index.html @@ -4,13 +4,13 @@ Tutorial 7: Escrow | sCrypt - +

Tutorial 7: Escrow

Overview

In this tutorial, we will go over how to create and escrow smart contract with some advanced features, such as a requirement for multiple arbiters and a deadline, after which the buyer can get a refund.

What is an escrow smart contract?

An escrow smart contract is a type of digital agreement that Bitcoin to facilitate transactions between parties in a secure, trustless manner.

In traditional escrow services, a trusted third party holds assets—like money, property, or goods—on behalf of the transacting parties. The assets are released only when specific conditions are met.

In the case of an escrow smart contract, the "third party" is the smart contract itself, programmed on the blockchain. The contract is written with the conditions of the transaction, and if they are met, the contract can be unlocked and the recipient(s) get payed.

Our implementation

We will implement a specific type of escrow, called a multi-sig escrow. The participants of this contract are a buyer (Alice), a seller (Bob) and one or more arbiters.

Suppose Alice want's to buy a specific item from Bob. They don't trust each other, so they decide to use an escrow smart contract. They pick one or more arbiters, which they both trust. The job of the chosen arbiters is to verify, that the item really gets delivered in the right condition. If the conditions are met, the contract will pay the seller, Bob. In the opposite case, Alice gets a refund. Additionally, Alice is also eligible for a refund after a set period of time in the case the arbiters are not responsive.

Contract properties

Let's declare the properties of our smart contract:

// Number of arbiters chosen.
static readonly N_ARBITERS = 3

// Buyer (Alice) address.
@prop()
readonly buyerAddr: Addr

// Seller (Bob) address.
@prop()
readonly sellerAddr: Addr

// Arbiter public keys.
@prop()
readonly arbiters: FixedArray<PubKey, typeof MultiSigEscrow.N_ARBITERS>

// Contract deadline nLocktime value.
// Either timestamp or block height.
@prop()
readonly deadline: bigint

Public method - confirmPayment

The first method of our contract will be confirmPayment. This public method will be called if the item was successfully delivered in the right condition.

The method takes as inputs the buyers signature, along with her public key and the signatures of the arbiters.

// Buyer and arbiters confirm, that the item was delivered.
// Seller gets paid.
@method(SigHash.ANYONECANPAY_SINGLE)
public confirmPayment(
buyerSig: Sig,
buyerPubKey: PubKey,
arbiterSigs: FixedArray<Sig, typeof MultiSigEscrow.N_ARBITERS>
) {
// Validate buyer sig.
assert(
pubKey2Addr(buyerPubKey) == this.buyerAddr,
'invalid public key for buyer'
)
assert(
this.checkSig(buyerSig, buyerPubKey),
'buyer signature check failed'
)

// Validate arbiter sigs.
assert(
this.checkMultiSig(arbiterSigs, this.arbiters),
'arbiters checkMultiSig failed'
)

// Ensure seller gets payed.
const amount = this.ctx.utxo.value
const out = Utils.buildPublicKeyHashOutput(this.sellerAddr, amount)
assert(hash256(out) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

The method validates all signatures are correct and ensures the seller receives the funds.

Public method - refund

Next, we implement the public method refund. If the delivery wasn't successful or there is something wrong with the item and needs to be sent back, the buyer is eligible for a refund.

The method again takes as inputs the buyers signature, along with her public key and the signatures of the arbiters.

// Regular refund. Needs arbiters agreement.
@method()
public refund(
buyerSig: Sig,
buyerPubKey: PubKey,
arbiterSigs: FixedArray<Sig, typeof MultiSigEscrow.N_ARBITERS>
) {
// Validate buyer sig.
assert(
pubKey2Addr(buyerPubKey) == this.buyerAddr,
'invalid public key for buyer'
)
assert(
this.checkSig(buyerSig, buyerPubKey),
'buyer signature check failed'
)

// Validate arbiter sigs.
assert(
this.checkMultiSig(arbiterSigs, this.arbiters),
'arbiters checkMultiSig failed'
)

// Ensure buyer gets refund.
const amount = this.ctx.utxo.value
const out = Utils.buildPublicKeyHashOutput(this.buyerAddr, amount)
assert(hash256(out) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

The method validates all signatures are correct and ensures the buyer receives the refund.

Public method - refundDeadline

Lastly, we implement the refundDeadline method. This method can be called, after the specified contract deadline has been reached. After the deadline, the buyer can receive the refund, even without the arbiters agreement.

The method takes as inputs in the buyers signature, along with her public key.

// Deadline for delivery. If reached, the  buyer gets refunded.
@method()
public refundDeadline(buyerSig: Sig, buyerPubKey: PubKey) {
assert(
pubKey2Addr(buyerPubKey) == this.buyerAddr,
'invalid public key for buyer'
)
assert(
this.checkSig(buyerSig, buyerPubKey),
'buyer signature check failed'
)

// Require nLocktime enabled https://wiki.bitcoinsv.io/index.php/NLocktime_and_nSequence
assert(
this.ctx.sequence < UINT_MAX,
'require nLocktime enabled'
)

// Check if using block height.
if (this.deadline < LOCKTIME_BLOCK_HEIGHT_MARKER) {
// Enforce nLocktime field to also use block height.
assert(
this.ctx.locktime < LOCKTIME_BLOCK_HEIGHT_MARKER
)
}
assert(this.ctx.locktime >= this.deadline, 'deadline not yet reached')

// Ensure buyer gets refund.
const amount = this.ctx.utxo.value
const out = Utils.buildPublicKeyHashOutput(this.buyerAddr, amount)
assert(hash256(out) == this.ctx.hashOutputs, 'hashOutputs mismatch')
}

The method checks the buyers signature validity. It also checks the transaction nLocktime value, to ensure it can be accepted by miners only after the deadline.

Conclusion

Congratulations! You have completed the escrow tutorial!

The full code can be found in our boilerplate repository.

- + \ No newline at end of file diff --git a/tutorials/hello-world/index.html b/tutorials/hello-world/index.html index ca23f8ade..aa7e29985 100644 --- a/tutorials/hello-world/index.html +++ b/tutorials/hello-world/index.html @@ -4,13 +4,13 @@ Tutorial 1: Hello World | sCrypt - +

Tutorial 1: Hello World

Overview

In this tutorial, we will go over how to quickly create a “Hello World” smart contract, deploy and call it.

Create a new project

Make sure all prerequisite tools are installed.

Run the following commands to create a new project:

npx scrypt-cli project helloworld
cd helloworld
npm install

The resulting project will contain a sample smart contract src/contracts/helloworld.ts, along with all the scaffolding. Let's modify it to the following code:

import { assert, ByteString, method, prop, sha256, Sha256, SmartContract } from 'scrypt-ts'

export class Helloworld extends SmartContract {

@prop()
hash: Sha256;

constructor(hash: Sha256){
super(...arguments);
this.hash = hash;
}

@method()
public unlock(message: ByteString) {
assert(sha256(message) == this.hash, 'Hash does not match')
}
}

The Helloworld contract stores the sha256 hash of a message in the contract property hash. Only a message which hashes to the value set in this.hash will unlock the contract.

Now let’s look at what is in the smart contract.

  • SmartContract: all smart contracts must extend the SmartContract base class.

  • @prop: the @prop decorator marks a contract property.

  • @method: the @method decorator marks a contract method. A public method is an entry point to a contract.

  • assert: throws an error and makes the method call fail if its first argument is false. Here it ensures the passed message hashed to the expected digest.

Compile Contract

  1. Run following command to compile the Helloworld contract:
npx scrypt-cli compile

This command will generate a contract artifact file at artifacts\helloworld.json.

  1. Or call the loadArtifact() function in the code:
await Helloworld.loadArtifact()

Contract Deployment & Call

Before we deploy the contract, follow the instruction to fund a Bitcoin key.

  1. To deploy a smart contract, simply call its deploy method.

  2. To call a smart contract, call one of its public method.

Overwrite deploy.ts in the root of the project with the following code to deploy and call the Helloworld contract:

import { Helloworld } from './src/contracts/helloworld'
import { getDefaultSigner } from './tests/utils/txHelper'
import { toByteString, sha256 } from 'scrypt-ts'

(async () => {

const message = toByteString('hello world', true)

await Helloworld.loadArtifact()
const instance = new Helloworld(sha256(message))

// connect to a signer
await instance.connect(getDefaultSigner())

// deploy the contract and lock up 42 satoshis in it
const deployTx = await instance.deploy(42)
console.log('Helloworld contract deployed: ', deployTx.id)

// contract call
const { tx: callTx } = await instance.methods.unlock(message)
console.log('Helloworld contract `unlock` called: ', callTx.id)

})()

Run the following command:

npx ts-node deploy.ts

You will see some output like:

You can view the deployment transaction using the WhatsOnChain blockchain explorer:

You can also view the calling transaction:

Congrats! You have deployed and called your first Bitcoin smart contract.

- + \ No newline at end of file diff --git a/tutorials/inscribe-image/index.html b/tutorials/inscribe-image/index.html index cda91133b..d75d2c34d 100644 --- a/tutorials/inscribe-image/index.html +++ b/tutorials/inscribe-image/index.html @@ -4,13 +4,13 @@ Tutorial 8: Inscribe Image | sCrypt - +
-

Tutorial 8: Inscribe Image

Overview

In this tutorial, we will use contract HashLock as an example to introduce how to inscribe an image on an ordinal, which is locked in a smart contract. It can be transferred by calling the contract.

note

Your wallet must be funded before inscribing the image.

First, you install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockNFT is almost the same as the previous implementation, except it must be derived from OrdinalNFT instead of SmartContract, which comes with package scrypt-ord.

class HashLockNFT extends OrdinalNFT {
...
}

It also stores a hash value in the contract. It will be unlocked successfully when calling the public method unlock with the correct hash preimage.

class HashLockNFT extends OrdinalNFT {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class OrdinalNFT encapsulates helper functions to handle ordinals. If you want to create your own contract that control Ordinal NFTs, you must derive from it.

Inscribe Image

We first create an instance of contract HashLockNFT. Next we call inscribeImage on the instance to inscribe an image.

// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockNFT(hash)
...
// inscribe image into contract
const image = readImage()
const mintTx = await hashLock.inscribeImage(image, 'image/png')

Execute command npx ts-node tests/examples/inscribeImage.ts to run this example.

Then you can check your inscription on the explorer.

Now that the inscription is locked to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

This is different from using a P2PKH address to receive the inscription, where the inscription is controlled by a private key.

Transfer the Inscription

The contract instance holds the inscription and we transfer it to a bitcoin address.

Step 1. Create Receiver Instance

Class OrdiNFTP2PKH represents an address that can hold inscriptions. Its constructor takes one parameter which is the receiving address.

const receiver = new OrdiNFTP2PKH(Addr(address.toByteString()))

Step 2. Call the Contract

Similar to contract calling before, we call the unlock of HashLockNFT as follows.

const { tx: transferTx } = await hashLock.methods.unlock(
message,
{
transfer: receiver, // <-----
} as MethodCallOptions<HashLockNFT>
)

We pass the receiver instance to transfer of struct MethodCallOptions.

Conclusion

Great! You have finished the tutorial on how to inscribe and transfer a 1Sat Ordinal with a smart contract.

The full complete contract and example can be found in sCrypt's repository.

- +

Tutorial 8: Inscribe Image

Overview

In this tutorial, we will use contract HashLock as an example to introduce how to inscribe an image on an ordinal, which is locked in a smart contract. It can be transferred by calling the contract.

note

Your wallet must be funded before inscribing the image.

First, you install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockNFT is almost the same as the previous implementation, except it must be derived from OrdinalNFT instead of SmartContract, which comes with package scrypt-ord.

class HashLockNFT extends OrdinalNFT {
...
}

It also stores a hash value in the contract. It will be unlocked successfully when calling the public method unlock with the correct hash preimage.

class HashLockNFT extends OrdinalNFT {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class OrdinalNFT encapsulates helper functions to handle ordinals. If you want to create your own contract that control Ordinal NFTs, you must derive from it.

Inscribe Image

We first create an instance of contract HashLockNFT. Next we call inscribeImage on the instance to inscribe an image.

// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockNFT(hash)
...
// inscribe image into contract
const image = readImage()
const mintTx = await hashLock.inscribeImage(image, 'image/png')

Execute command npx ts-node tests/examples/inscribeImage.ts to run this example.

Then you can check your inscription on the explorer.

Now that the inscription is locked to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

This is different from using a P2PKH address to receive the inscription, where the inscription is controlled by a private key.

Transfer the Inscription

The contract instance holds the inscription and we transfer it to a bitcoin address.

Step 1. Create Receiver Instance

Class OrdiNFTP2PKH represents an address that can hold inscriptions. Its constructor takes one parameter which is the receiving address.

const receiver = new OrdiNFTP2PKH(Addr(address.toByteString()))

Step 2. Call the Contract

Similar to contract calling before, we call the unlock of HashLockNFT as follows.

const { tx: transferTx } = await hashLock.methods.unlock(
message,
{
transfer: receiver, // <-----
} as OrdiMethodCallOptions<HashLockNFT>
)

We pass the receiver instance to transfer of struct OrdiMethodCallOptions.

Conclusion

Great! You have finished the tutorial on how to inscribe and transfer a 1Sat Ordinal with a smart contract.

The full complete contract and example can be found in sCrypt's repository.

+ \ No newline at end of file diff --git a/tutorials/mint-bsv20-v1/index.html b/tutorials/mint-bsv20-v1/index.html index 7ad6d2740..e8b58145f 100644 --- a/tutorials/mint-bsv20-v1/index.html +++ b/tutorials/mint-bsv20-v1/index.html @@ -4,13 +4,13 @@ Tutorial 10: Mint BSV20 V1 Token | sCrypt - +
-

Tutorial 10: Mint BSV20 V1 Token

Overview

In this tutorial, we will use contract HashLock as an example, to introduce how to mint a BSV20 Token (version 1) with sCrypt and transfer it with a Smart Contract.

To enable all these features, you should install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockFT is almost the same as the previous implementation, except two differences.

  1. It must be derived from BSV20V1 instead of SmartContract.
class HashLockFT extends BSV20V1 {
...
}
  1. The constructor has extra parameters - tick, max, lim, and dec - representing BSV20 fields.
constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {
super(tick, max, lim, dec)
this.init(...arguments)
this.hash = hash
}

The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method unlock with the correct message.

class HashLockFT extends BSV20V1 {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class BSV20V1 encapsulated helper functions to handle BSV20 (version 1) tokens. If you want to create your own contract that can interact with BSV20 protocol, derive from it.

Deploy and Mint

For BSV20 version 1, tokens must be deployed before mint. We first create an instance of contract HashLockFT, then call function deployToken to deploy the new token, and call mint at last to mint tokens into the contract instance.

// BSV20 fields
const tick = toByteString('HELLO', true)
const max = 100n
const lim = 10n
const dec = 0n
// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockFT(tick, max, lim, dec, hash)
...
// deploy the new BSV20 token $HELLO
await hashLock.deployToken()
// mint 10 $HELLO into contract instance
const mintTx = await hashLock.mint(10n)

Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH.

In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

Transfer Token

For now, the contract instance holds the token and we try to transfer it to a P2PKH address.

Step 1. Create Receiver Instance

Class BSV20V1P2PKH represents a P2PKH address that can hold BSV20 version 1 tokens. Its constructor takes BSV20 fields and an receiving address as parameters.

const alice = new BSV20V1P2PKH(tick, max, lim, dec, addressAlice)
const bob = new BSV20V1P2PKH(tick, max, lim, dec, addressBob)

Step 2. Call the Contract

Just as other contract calling methods we introduced before, we call the public method unlock of HashLockFT as follows.

// Call the contract
const { tx: transferTx } = await hashLock.methods.unlock(message, {
transfer: [
{
instance: alice,
amt: 2n,
},
{
instance: bob,
amt: 5n,
},
],
} as MethodCallOptions<HashLockFT>)

This code will create a transaction that transfers 2 tokens to alice and 5 to bob.

The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key tokenChangeAddress of struct MethodCallOptions.

Execute command npx ts-node tests/examples/mintBSV20.ts to run this example.

Then you can check your token transfer details on the explorer.

The UTXO model is a powerful feature of BSV20, we can send tokens to multiple receivers in a single transaction, allowing us to create complex and efficient transactions.

Conclusion

Great! You have finished the tutorial on how to mint and transfer the BSV20 Token with a Smart Contract.

The full complete contract and example can be found in sCrypt's repository.

- +

Tutorial 10: Mint BSV20 V1 Token

Overview

In this tutorial, we will use contract HashLock as an example, to introduce how to mint a BSV20 Token (version 1) with sCrypt and transfer it with a Smart Contract.

To enable all these features, you should install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockFT is almost the same as the previous implementation, except two differences.

  1. It must be derived from BSV20V1 instead of SmartContract.
class HashLockFT extends BSV20V1 {
...
}
  1. The constructor has extra parameters - tick, max, lim, and dec - representing BSV20 fields.
constructor(tick: ByteString, max: bigint, lim: bigint, dec: bigint, hash: Sha256) {
super(tick, max, lim, dec)
this.init(...arguments)
this.hash = hash
}

The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method unlock with the correct message.

class HashLockFT extends BSV20V1 {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class BSV20V1 encapsulated helper functions to handle BSV20 (version 1) tokens. If you want to create your own contract that can interact with BSV20 protocol, derive from it.

Deploy and Mint

For BSV20 version 1, tokens must be deployed before mint. We first create an instance of contract HashLockFT, then call function deployToken to deploy the new token, and call mint at last to mint tokens into the contract instance.

// BSV20 fields
const tick = toByteString('HELLO', true)
const max = 100n
const lim = 10n
const dec = 0n
// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockFT(tick, max, lim, dec, hash)
...
// deploy the new BSV20 token $HELLO
await hashLock.deployToken()
// mint 10 $HELLO into contract instance
const mintTx = await hashLock.mint(10n)

Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH.

In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

Transfer Token

For now, the contract instance holds the token and we try to transfer it to a P2PKH address.

Step 1. Create Receiver Instance

Class BSV20V1P2PKH represents a P2PKH address that can hold BSV20 version 1 tokens. Its constructor takes BSV20 fields and an receiving address as parameters.

const alice = new BSV20V1P2PKH(tick, max, lim, dec, addressAlice)
const bob = new BSV20V1P2PKH(tick, max, lim, dec, addressBob)

Step 2. Call the Contract

Just as other contract calling methods we introduced before, we call the public method unlock of HashLockFT as follows.

// Call the contract
const { tx: transferTx } = await hashLock.methods.unlock(message, {
transfer: [
{
instance: alice,
amt: 2n,
},
{
instance: bob,
amt: 5n,
},
],
} as OrdiMethodCallOptions<HashLockFT>)

This code will create a transaction that transfers 2 tokens to alice and 5 to bob.

The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key tokenChangeAddress of struct OrdiMethodCallOptions.

Execute command npx ts-node tests/examples/mintBSV20.ts to run this example.

Then you can check your token transfer details on the explorer.

The UTXO model is a powerful feature of BSV20, we can send tokens to multiple receivers in a single transaction, allowing us to create complex and efficient transactions.

Conclusion

Great! You have finished the tutorial on how to mint and transfer the BSV20 Token with a Smart Contract.

The full complete contract and example can be found in sCrypt's repository.

+ \ No newline at end of file diff --git a/tutorials/mint-bsv20-v2/index.html b/tutorials/mint-bsv20-v2/index.html index 79172f1a4..8c6b61969 100644 --- a/tutorials/mint-bsv20-v2/index.html +++ b/tutorials/mint-bsv20-v2/index.html @@ -4,13 +4,13 @@ Tutorial 9: Mint BSV20 V2 Token | sCrypt - +
-

Tutorial 9: Mint BSV20 V2 Token

Overview

In this tutorial, we will use contract HashLock as an example, to introduce how to mint a BSV20 Token (version 2) with sCrypt and transfer it with a Smart Contract.

To enable all these features, you should install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockFTV2 is almost the same as the previous implementation, except two differences.

  1. It must be derived from BSV20V2 instead of SmartContract.
class HashLockFTV2 extends BSV20V2 {
...
}
  1. The constructor has extra parameters - id, max, and dec - representing BSV20 V2 fields.
constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {
super(id, max, dec)
this.init(...arguments)
this.hash = hash
}

The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method unlock with the correct message.

export class HashLockFTV2 extends BSV20V2 {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class BSV20V2 encapsulated helper functions to handle BSV20 V2 tokens. If you want to create your own contract that can interact with BSV20 V2 protocol, derive from it.

Deploy Token

We first create an instance of contract HashLockFTV2, then call function deployToken to deploy the new token.

// BSV20 V2 fields
const max = 10n
const dec = 0n
// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockFTV2(toByteString(''), max, dec, hash)
...
// deploy the new BSV20V2 token
const tokenId = await hashLock.deployToken()

Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH.

In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

Transfer Token

For now, the contract instance holds the token and we try to transfer it to a P2PKH address.

Step 1. Create Receiver Instance

Class BSV20V2P2PKH represents a P2PKH address that can hold BSV20 V2 tokens. Its constructor takes BSV20 V2 fields and an receiving address as parameters.

const alice = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressAlice )
const bob = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressBob)

Step 2. Call the Contract

Just as other contract calling methods we introduced before, we call the public method unlock of HashLockFTV2 as follows.

// Call the contract
const { tx: transferTx } = await hashLock.methods.unlock(message, {
transfer: [
{
instance: alice,
amt: 2n,
},
{
instance: bob,
amt: 5n,
},
],
} as MethodCallOptions<HashLockFTV2>)

This code will create a transaction that transfers 2 tokens to alice and 5 to bob.

The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key tokenChangeAddress of struct MethodCallOptions.

Execute command npx ts-node tests/examples/mintBSV20V2.ts to run this example.

Then you can check your token transfer details on the explorer.

Conclusion

Great! You have finished the tutorial on how to mint and transfer the BSV20 V2 Token with a Smart Contract.

The full complete contract and example can be found in sCrypt's repository.

- +

Tutorial 9: Mint BSV20 V2 Token

Overview

In this tutorial, we will use contract HashLock as an example, to introduce how to mint a BSV20 Token (version 2) with sCrypt and transfer it with a Smart Contract.

To enable all these features, you should install scrypt-ord as an dependency in your project.

npm install scrypt-ord

Contract

The new contract HashLockFTV2 is almost the same as the previous implementation, except two differences.

  1. It must be derived from BSV20V2 instead of SmartContract.
class HashLockFTV2 extends BSV20V2 {
...
}
  1. The constructor has extra parameters - id, max, and dec - representing BSV20 V2 fields.
constructor(id: ByteString, max: bigint, dec: bigint, hash: Sha256) {
super(id, max, dec)
this.init(...arguments)
this.hash = hash
}

The contract also stores a hash value in the contract, and it will be unlocked successfully when calling the public method unlock with the correct message.

export class HashLockFTV2 extends BSV20V2 {
@prop()
hash: Sha256

...

@method()
public unlock(message: ByteString) {
assert(this.hash == sha256(message), 'hashes are not equal')
}
}

The base class BSV20V2 encapsulated helper functions to handle BSV20 V2 tokens. If you want to create your own contract that can interact with BSV20 V2 protocol, derive from it.

Deploy Token

We first create an instance of contract HashLockFTV2, then call function deployToken to deploy the new token.

// BSV20 V2 fields
const max = 10n
const dec = 0n
// create contract instance
const message = toByteString('Hello sCrypt', true)
const hash = sha256(message)
const hashLock = new HashLockFTV2(toByteString(''), max, dec, hash)
...
// deploy the new BSV20V2 token
const tokenId = await hashLock.deployToken()

Normally, we use a P2PKH address to receive the token, then the token is controlled by a private key the same as the general P2PKH.

In this example, the token is mint to a contract instance, it is controlled by the smart contract, which means it can only be transferred when the hash lock is unlocked.

Transfer Token

For now, the contract instance holds the token and we try to transfer it to a P2PKH address.

Step 1. Create Receiver Instance

Class BSV20V2P2PKH represents a P2PKH address that can hold BSV20 V2 tokens. Its constructor takes BSV20 V2 fields and an receiving address as parameters.

const alice = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressAlice )
const bob = new BSV20V2P2PKH(toByteString(tokenId, true), max, dec, addressBob)

Step 2. Call the Contract

Just as other contract calling methods we introduced before, we call the public method unlock of HashLockFTV2 as follows.

// Call the contract
const { tx: transferTx } = await hashLock.methods.unlock(message, {
transfer: [
{
instance: alice,
amt: 2n,
},
{
instance: bob,
amt: 5n,
},
],
} as OrdiMethodCallOptions<HashLockFTV2>)

This code will create a transaction that transfers 2 tokens to alice and 5 to bob.

The default transaction builder will automatically add a token change output on the transaction. In this example, it will automatically add a token change output with 3 tokens, paying to the default address of the instance connected signer. You can also specify the token change address by passing the value to the key tokenChangeAddress of struct OrdiMethodCallOptions.

Execute command npx ts-node tests/examples/mintBSV20V2.ts to run this example.

Then you can check your token transfer details on the explorer.

Conclusion

Great! You have finished the tutorial on how to mint and transfer the BSV20 V2 Token with a Smart Contract.

The full complete contract and example can be found in sCrypt's repository.

+ \ No newline at end of file diff --git a/tutorials/oracle/index.html b/tutorials/oracle/index.html index cf14cb31d..439732a8d 100644 --- a/tutorials/oracle/index.html +++ b/tutorials/oracle/index.html @@ -4,14 +4,14 @@ Tutorial 3: Oracle | sCrypt - +

Tutorial 3: Oracle

Overview

In this tutorial, we will go over how to build a smart contract that consumes off-chain data from an oracle. Specifically, we will implement a smart contract that lets two players bet on the price of BSV at some point in the future. It retrieves prices from an oracle.

What is an Oracle?

A blockchain oracle is a third-party service or agent that provides external data to a blockchain network. It is a bridge between the blockchain and the external world, enabling smart contracts to access, verify, and incorporate data from outside the blockchain. This allows smart contracts to execute based on real-world events and conditions, enhancing their utility and functionality.

Credit: bitnovo

The data supplied by oracles can include various types of information, such as stock prices, weather data, election results, and sports scores.

Rabin Signatures

A digital signature is required to verify the authenticity and integrity of arbitrary data provided by known oracles in a smart contract. Instead of ECDSA used in Bitcoin, we use an alternative digital signature algorithm called Rabin signatures. This is because Rabin signature verification is orders of magnitude cheaper than ECDSA. We have implemented Rabin signature as part of the standard libraries scrypt-ts-lib, which can be imported and used directly.

Contract Properties

Our contract will take signed pricing data from the WitnessOnChain oracle. Depending if the price target is reached or not, it will pay out a reward to one of the two players.

There are quite a few properties which our price betting smart contract will require:

// Price target that needs to be reached.
@prop()
targetPrice: bigint

// Symbol of the pair, e.g. "BSV_USDC"
@prop()
symbol: ByteString

// Timestamp window in which the price target needs to be reached.
@prop()
timestampFrom: bigint
@prop()
timestampTo: bigint

// Oracles Rabin public key.
@prop()
oraclePubKey: RabinPubKey

// Addresses of both players.
@prop()
aliceAddr: Addr
@prop()
bobAddr: Addr

Notice that the type RabinPubKey, which represents a Rabin public key, is not a standard type. You can import it the following way:

import { RabinPubKey } from 'scrypt-ts-lib'

Public Method - unlock

The contract will have only a single public method, namely unlock. As parameters, it will take the oracles signature, the signed message from the oracle, and a signature of the winner, who can unlock the funds:

@method()
public unlock(msg: ByteString, sig: RabinSig, winnerSig: Sig) {
// Verify oracle signature.
assert(
RabinVerifierWOC.verifySig(msg, sig, this.oraclePubKey),
'Oracle sig verify failed.'
)

// Decode data.
const exchangeRate = PriceBet.parseExchangeRate(msg)

// Validate data.
assert(
exchangeRate.timestamp >= this.timestampFrom,
'Timestamp too early.'
)
assert(
exchangeRate.timestamp <= this.timestampTo,
'Timestamp too late.'
)
assert(exchangeRate.symbol == this.symbol, 'Wrong symbol.')

// Decide winner and check their signature.
const winner =
exchangeRate.price >= this.targetPrice
? this.alicePubKey
: this.bobPubKey
assert(this.checkSig(winnerSig, winner))
}

Let's walk through each part.

First, we verify that the passed signature is correct. For that we use the RabinVerifierWOC library from the scrypt-ts-lib package

import { RabinPubKey, RabinSig, RabinVerifierWoc } from 'scrypt-ts-lib'

Now, we can call the verifySig method of the verification library:

// Verify oracle signature.
assert(
RabinVerifierWOC.verifySig(msg, sig, this.oraclePubKey),
'Oracle sig verify failed.'
)

The verification method requires the message signed by the oracle, the oracles signature for the message, and the oracle's public key, which we already set via the constructor.

Next, we need to parse information from the chunk of data that is the signed message and assert on it. For a granular description of the message format check out the "Exchange Rate" section in the WitnessOnChain API docs.

We need to implement the static method parseExchangeRate as follows:

// Parses signed message from the oracle.
@method()
static parseExchangeRate(msg: ByteString): ExchangeRate {
// 4 bytes timestamp (LE) + 8 bytes rate (LE) + 1 byte decimal + 16 bytes symbol
return {
timestamp: Utils.fromLEUnsigned(slice(msg, 0n, 4n)),
price: Utils.fromLEUnsigned(slice(msg, 4n, 12n)),
symbol: slice(msg, 13n, 29n),
}
}

We parse out the following data:

  • timestamp - The time at which this exchange rate is present.
  • price - The exchange rate encoded as an integer -> (priceFloat * (10^decimal)).
  • symbol - The symbol of the token pair, e.g. BSV_USDC.

Finally, we wrap the parsed values in a custom type, named ExchangeRate and return it. Here's the definition of the type:

type ExchangeRate = {
timestamp: bigint
price: bigint
symbol: ByteString
}

Now we can validate the data. First, we check if the timestamp of the exchange rate is within our specified range that we bet on:

assert(
exchangeRate.timestamp >= this.timestampFrom,
'Timestamp too early.'
)
assert(
exchangeRate.timestamp <= this.timestampTo,
'Timestamp too late.'
)

Additionally, we check if the exchange rate is actually for the correct token pair:

assert(exchangeRate.symbol == this.symbol, 'Wrong symbol.')

Lastly, upon having all the necessary information, we can choose the winner and check their signature:

const winner =
exchangeRate.price >= this.targetPrice
? this.alicePubKey
: this.bobPubKey
assert(this.checkSig(winnerSig, winner))

As we can see, if the target price is reached, only Alice is able to unlock the funds, and if not, then only Bob is able to do so.

Conclusion

Congratulations! You have completed the oracle tutorial!

The full code along with tests can be found in sCrypt's boilerplate repository.

- + \ No newline at end of file diff --git a/tutorials/ordinal-lock/index.html b/tutorials/ordinal-lock/index.html index 7ed33e21e..ca254026b 100644 --- a/tutorials/ordinal-lock/index.html +++ b/tutorials/ordinal-lock/index.html @@ -4,13 +4,13 @@ Tutorial 11: Ordinal Lock | sCrypt - +
-

Tutorial 11: Ordinal Lock

Overview

In this tutorial, we will go over how to use sCrypt to build a full-stack dApp on Bitcoin to sell 1Sat Ordinals, including the smart contract and an interactive front-end.

Contract

The contract OrdinalLock allows an ordinal to be offered up for sale on a decentralized marketplace. These listings can be purchased by anyone who is able to pay the requested price. Listings can also be cancelled by the person who listed them.

To record the seller and price, we need to add two properties to the contract.

export class OrdinalLock extends OrdinalNFT {
@prop()
seller: PubKey

@prop()
amount: bigint

...
}

Constructor

Initialize all the @prop properties in the constructor.

constructor(seller: PubKey, amount: bigint) {
super()
this.init(...arguments)
this.seller = seller
this.amount = amount
}

Methods

The public method purchase only needs to confine the transaction's outputs to contain:

  • transfer ordinal to the buyer
  • payment to the seller
@method()
public purchase(receiver: Addr) {
const outputs =
Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer
Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller
this.buildChangeOutput()
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs check failed')
}

The final complete code is as follows:

import { Addr, prop, method, Utils, hash256, assert, MethodCallOptions, ContractTransaction, bsv, PubKey, hash160 } from 'scrypt-ts'
import { OrdinalNFT } from 'scrypt-ord'

export class OrdinalLock extends OrdinalNFT {
@prop()
seller: PubKey

@prop()
amount: bigint

constructor(seller: PubKey, amount: bigint) {
super()
this.init(...arguments)
this.seller = seller
this.amount = amount
}

@method()
public purchase(receiver: Addr) {
const outputs =
Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer
Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller
this.buildChangeOutput()
assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

@method(SigHash.ANYONECANPAY_SINGLE)
public cancel(sig: Sig) {
assert(this.checkSig(sig, this.seller), 'seller signature check failed')
const outputs = Utils.buildAddressOutput(hash160(this.seller), 1n) // ordinal back to the seller
assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

static async buildTxForPurchase(
current: OrdinalLock,
options: MethodCallOptions<OrdinalLock>,
receiver: Addr
): Promise<ContractTransaction> {
const defaultAddress = await current.signer.getDefaultAddress()
const tx = new bsv.Transaction()
.addInput(current.buildContractInput())
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(receiver)
),
satoshis: 1,
})
)
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(hash160(current.seller))
),
satoshis: Number(current.amount),
})
)
.change(options.changeAddress || defaultAddress)
return {
tx,
atInputIndex: 0,
nexts: [],
}
}

static async buildTxForCancel(
current: OrdinalLock,
options: MethodCallOptions<OrdinalLock>
): Promise<ContractTransaction> {
const defaultAddress = await current.signer.getDefaultAddress()
const tx = new bsv.Transaction()
.addInput(current.buildContractInput())
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(hash160(current.seller))
),
satoshis: 1,
})
)
.change(options.changeAddress || defaultAddress)
return {
tx,
atInputIndex: 0,
nexts: [],
}
}
}

Note the customized calling method buildTxForPurchase and buildTxForCancel ensure the ordinal is in the first input and goes to the first output, which is also a 1sat output.

Frontend

We will add a frontend to the OrdinalLock smart contract accroding to this guide.

Setup Project

The front-end will be created using Create React App.

npx create-react-app ordinal-lock-demo --template typescript

Install the sCrypt SDK

The sCrypt SDK enables you to easily compile, test, deploy, and call contracts.

Use the scrypt-cli command line to install the SDK.

cd ordinal-lock-demo
npm i scrypt-ord
npx scrypt-cli init

This command will create a contract under src/contracts. Replace the file with the contract written above.

Compile Contract

Compile the contract with the following command:

npx scrypt-cli compile

This command will generate a contract artifact file under artifacts.

Load Contract Artifact

Before writing the front-end code, we need to load the contract artifact in src/index.tsx.

import { OrdinalLock } from './contracts/ordinalLock'
import artifact from '../artifacts/ordinalLock.json'
OrdinalLock.loadArtifact(artifact)

Connect Signer to OrdiProvider

const provider = new OrdiProvider();
const signer = new SensiletSigner(provider);

Integrate Wallet

Use requestAuth method of signer to request access to the wallet.

// request authentication
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
// something went wrong, throw an Error with `error` message
throw new Error(error);
}

// authenticated
// ...

Load Ordinals

After a user connect wallet, we can get the his address. Call the 1Sat Ordinals API to retrieve ordinals on this address.

useEffect(() => {
loadCollections()
}, [connectedAddress])

function loadCollections() {
if (connectedAddress) {
const url = `https://v3.ordinals.gorillapool.io/api/txos/address/${connectedAddress.toString()}/unspent?bsv20=false`
fetch(url).then(r => r.json()).then(r => r.filter(e => e.origin.data.insc.file.type !== 'application/bsv-20')).then(r => setCollections(r)) }
}

List an Ordinal

For each ordinal in the collection list, we can click the Sell button to list it after filling in the selling price, in satoshis. Sell an ordinal means we need to create a contract instance, and then transfer the ordinal into it. Afterwards, the ordinal is under the control of the contract, meaning it can be bought by anyone paying the price to the seller.

async function sell() {
const signer = new SensiletSigner(new OrdiProvider())
const publicKey = await signer.getDefaultPubKey()

const instance = new OrdinalLock(PubKey(toHex(publicKey)), amount)
await instance.connect(signer)

const inscriptionUtxo = await parseUtxo(txid, vout)
const inscriptionP2PKH = OrdiNFTP2PKH.fromUTXO(inscriptionUtxo)
await inscriptionP2PKH.connect(signer)

const { tx } = await inscriptionP2PKH.methods.unlock(
(sigResps) => findSig(sigResps, publicKey),
PubKey(toHex(publicKey)),
{
transfer: instance, // <----
pubKeyOrAddrToSign: publicKey,
} as MethodCallOptions<OrdiNFTP2PKH>
)
}

Buy an Ordinal

To buy an ordinal that is on sale, we only need to call the contract public method purchase.

async function buy() {
const signer = new SensiletSigner(new OrdiProvider())
const address = await signer.getDefaultAddress()
const { tx } = await instance.methods.purchase(Addr(address.toByteString()))
}

Conclusion

Congratulations! You have successfully completed a full-stack dApp that can sell 1Sat Ordinals on Bitcoin.

The full example repo can be found here.

- +

Tutorial 11: Ordinal Lock

Overview

In this tutorial, we will go over how to use sCrypt to build a full-stack dApp on Bitcoin to sell 1Sat Ordinals, including the smart contract and an interactive front-end.

Contract

The contract OrdinalLock allows an ordinal to be offered up for sale on a decentralized marketplace. These listings can be purchased by anyone who is able to pay the requested price. Listings can also be cancelled by the person who listed them.

To record the seller and price, we need to add two properties to the contract.

export class OrdinalLock extends OrdinalNFT {
@prop()
seller: PubKey

@prop()
amount: bigint

...
}

Constructor

Initialize all the @prop properties in the constructor.

constructor(seller: PubKey, amount: bigint) {
super()
this.init(...arguments)
this.seller = seller
this.amount = amount
}

Methods

The public method purchase only needs to confine the transaction's outputs to contain:

  • transfer ordinal to the buyer
  • payment to the seller
@method()
public purchase(receiver: Addr) {
const outputs =
Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer
Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller
this.buildChangeOutput()
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs check failed')
}

The final complete code is as follows:

import { Addr, prop, method, Utils, hash256, assert, ContractTransaction, bsv, PubKey, hash160, Sig, SigHash } from 'scrypt-ts'
import { OrdiMethodCallOptions, OrdinalNFT } from '../scrypt-ord'

export class OrdinalLock extends OrdinalNFT {
@prop()
seller: PubKey

@prop()
amount: bigint

constructor(seller: PubKey, amount: bigint) {
super()
this.init(...arguments)
this.seller = seller
this.amount = amount
}

@method()
public purchase(receiver: Addr) {
const outputs =
Utils.buildAddressOutput(receiver, 1n) + // ordinal to the buyer
Utils.buildAddressOutput(hash160(this.seller), this.amount) + // fund to the seller
this.buildChangeOutput()
assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

@method(SigHash.ANYONECANPAY_SINGLE)
public cancel(sig: Sig) {
assert(this.checkSig(sig, this.seller), 'seller signature check failed')
const outputs = Utils.buildAddressOutput(hash160(this.seller), 1n) // ordinal back to the seller
assert(
this.ctx.hashOutputs == hash256(outputs),
'hashOutputs check failed'
)
}

static async buildTxForPurchase(
current: OrdinalLock,
options: OrdiMethodCallOptions<OrdinalLock>,
receiver: Addr
): Promise<ContractTransaction> {
const defaultAddress = await current.signer.getDefaultAddress()
const tx = new bsv.Transaction()
.addInput(current.buildContractInput())
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(receiver)
),
satoshis: 1,
})
)
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(hash160(current.seller))
),
satoshis: Number(current.amount),
})
)
.change(options.changeAddress || defaultAddress)
return {
tx,
atInputIndex: 0,
nexts: [],
}
}

static async buildTxForCancel(
current: OrdinalLock,
options: OrdiMethodCallOptions<OrdinalLock>
): Promise<ContractTransaction> {
const defaultAddress = await current.signer.getDefaultAddress()
const tx = new bsv.Transaction()
.addInput(current.buildContractInput())
.addOutput(
new bsv.Transaction.Output({
script: bsv.Script.fromHex(
Utils.buildAddressScript(hash160(current.seller))
),
satoshis: 1,
})
)
.change(options.changeAddress || defaultAddress)
return {
tx,
atInputIndex: 0,
nexts: [],
}
}
}

Note the customized calling method buildTxForPurchase and buildTxForCancel ensure the ordinal is in the first input and goes to the first output, which is also a 1sat output.

Frontend

We will add a frontend to the OrdinalLock smart contract accroding to this guide.

Setup Project

The front-end will be created using Create React App.

npx create-react-app ordinal-lock-demo --template typescript

Install the sCrypt SDK

The sCrypt SDK enables you to easily compile, test, deploy, and call contracts.

Use the scrypt-cli command line to install the SDK.

cd ordinal-lock-demo
npm i scrypt-ord
npx scrypt-cli init

This command will create a contract under src/contracts. Replace the file with the contract written above.

Compile Contract

Compile the contract with the following command:

npx scrypt-cli compile

This command will generate a contract artifact file under artifacts.

Load Contract Artifact

Before writing the front-end code, we need to load the contract artifact in src/index.tsx.

import { OrdinalLock } from './contracts/ordinalLock'
import artifact from '../artifacts/ordinalLock.json'
OrdinalLock.loadArtifact(artifact)

Connect Signer to OrdiProvider

const provider = new OrdiProvider();
const signer = new SensiletSigner(provider);

Integrate Wallet

Use requestAuth method of signer to request access to the wallet.

// request authentication
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
// something went wrong, throw an Error with `error` message
throw new Error(error);
}

// authenticated
// ...

Load Ordinals

After a user connect wallet, we can get the his address. Call the 1Sat Ordinals API to retrieve ordinals on this address.

useEffect(() => {
loadCollections()
}, [connectedAddress])

function loadCollections() {
if (connectedAddress) {
const url = `https://v3.ordinals.gorillapool.io/api/txos/address/${connectedAddress.toString()}/unspent?bsv20=false`
fetch(url).then(r => r.json()).then(r => r.filter(e => e.origin.data.insc.file.type !== 'application/bsv-20')).then(r => setCollections(r)) }
}

List an Ordinal

For each ordinal in the collection list, we can click the Sell button to list it after filling in the selling price, in satoshis. Sell an ordinal means we need to create a contract instance, and then transfer the ordinal into it. Afterwards, the ordinal is under the control of the contract, meaning it can be bought by anyone paying the price to the seller.

async function sell() {
const signer = new SensiletSigner(new OrdiProvider())
const publicKey = await signer.getDefaultPubKey()

const instance = new OrdinalLock(PubKey(toHex(publicKey)), amount)
await instance.connect(signer)

const inscriptionUtxo = await parseUtxo(txid, vout)
const inscriptionP2PKH = OrdiNFTP2PKH.fromUTXO(inscriptionUtxo)
await inscriptionP2PKH.connect(signer)

const { tx } = await inscriptionP2PKH.methods.unlock(
(sigResps) => findSig(sigResps, publicKey),
PubKey(toHex(publicKey)),
{
transfer: instance, // <----
pubKeyOrAddrToSign: publicKey,
} as OrdiMethodCallOptions<OrdiNFTP2PKH>
)
}

Buy an Ordinal

To buy an ordinal that is on sale, we only need to call the contract public method purchase.

async function buy() {
const signer = new SensiletSigner(new OrdiProvider())
const address = await signer.getDefaultAddress()
const { tx } = await instance.methods.purchase(Addr(address.toByteString()))
}

Conclusion

Congratulations! You have successfully completed a full-stack dApp that can sell 1Sat Ordinals on Bitcoin.

The full example repo can be found here.

+ \ No newline at end of file diff --git a/tutorials/tic-tac-toe/index.html b/tutorials/tic-tac-toe/index.html index 8cbf2521a..505c139e9 100644 --- a/tutorials/tic-tac-toe/index.html +++ b/tutorials/tic-tac-toe/index.html @@ -4,13 +4,13 @@ Tutorial 4: Tic Tac Toe | sCrypt - +

Tutorial 4: Tic Tac Toe

Overview

In this tutorial, we will go over how to use sCrypt to build a Tic-Tac-Toe Contract on Bitcoin.

It is initialized with the Bitcoin public key of two players (Alice and Bob respectively). They each bet the same amount and lock it into the contract. The winner takes all bitcoins locked in the contract. If no one wins and there is a draw, the two players can each withdraw half of the money.

Contract Properties

Use @prop decorator to mark any property that intends to be stored on chain. This decorator accepts a boolean parameter. By default, it is set to false, meaning the property cannot be changed after the contract is deployed. If it is true, the property is a so-called stateful property and its value can be updated in subsequent contract calls.

The tic-tac-toe contract supports two players and their public keys need to be saved. It contains the following contract properties:

  • Two stateless properties alice and bob, both of which are PubKey type.
  • Two stateful properties:
    • is_alice_turn: a boolean. It represents whether it is alice's turn to play.
    • board: a fixed-size array FixedArray<bigint, 9> with a size of 9. It represents the state of every square in the board.
  • Three constants:
    • EMPTY, type bigint, value 0n. It means that a square in the board is empty
    • ALICE, type bigint, value 1n. Alice places symbol X in a square.
    • BOB, type bigint, value 2n. Bob places symbol O in a square.
@prop()
alice: PubKey; // alice's public Key
@prop()
bob: PubKey; // bob's public Key

@prop(true)
is_alice_turn: boolean; // stateful property, it represents whether it is `alice`'s turn to play.

@prop(true)
board: FixedArray<bigint, 9>; // stateful property, a fixed-size array, it represents the state of every square in the board.

@prop()
static readonly EMPTY: bigint = 0n; // static property, it means that the a square in the board is empty
@prop()
static readonly ALICE: bigint = 1n; // static property, it means that alice places symbol `X` in a square.
@prop()
static readonly BOB: bigint = 2n; // static property, it means that bob places symbol `O` in a square.

Constructor

Initialize all non-static properties in the constructor. Specifically, the entire board is empty at first.

constructor(alice: PubKey, bob: PubKey) {
super(...arguments);
this.alice = alice;
this.bob = bob;
this.is_alice_turn = true;
this.board = fill(TicTacToe.EMPTY, 9);
}

Public Methods

A public @method can be called from an external transaction. The call succeeds if it runs to completion without violating any conditions in assert().

The TicTacToe contract have a public @method called move() with 2 parameters:

/**
* play the game by calling move()
* @param n which square to place the symbol
* @param sig a player's signature
*/
@method()
public move(n: bigint, sig: Sig) {
assert(n >= 0n && n < 9n);
}

Alice and Bob each locks X bitcoins in a UTXO containing contract TicTacToe. Next, they alternately play the game by calling move().

Signature Verification

Once the game contract is deployed, anyone can view and potentially interact with it. We need a authentication mechanism to ensure only the desired player can update the contract if it's their turn. This is achieved using ditigal signatures.

this.checkSig() is used to verify a signature against a public key. Use it to verify the sig parameter against the desired player in move(), identified by their public key stored in the contract's properties.

// check signature `sig`
let player: PubKey = this.is_alice_turn ? this.alice : this.bob;
assert(this.checkSig(sig, player), `checkSig failed, pubkey: ${player}`);

Non-Public Methods

Without a public modifier, a @method is internal and cannot be directly called from an external transaction.

The TicTacToe contract have two Non-Public methods:

  • won() : iterate over the lines array to check if a player has won the game. returns boolean type.
  • full() : traverse all the squares of the board to check if all squares of the board have symbols. returns boolean type.
@method()
won(play: bigint) : boolean {
let lines: FixedArray<FixedArray<bigint, 3>, 8> = [
[0n, 1n, 2n],
[3n, 4n, 5n],
[6n, 7n, 8n],
[0n, 3n, 6n],
[1n, 4n, 7n],
[2n, 5n, 8n],
[0n, 4n, 8n],
[2n, 4n, 6n]
];

let anyLine = false;

for (let i = 0; i < 8; i++) {
let line = true;
for (let j = 0; j < 3; j++) {
line = line && this.board[Number(lines[i][j])] === play;
}

anyLine = anyLine || line;
}

return anyLine;
}

@method()
full() : boolean {
let full = true;
for (let i = 0; i < 9; i++) {
full = full && this.board[i] !== TicTacToe.EMPTY;
}
return full;
}

Maintain Game State

We can directly access the ScriptContext through this.ctx in the public @method move() to maintain game state. It can be considered additional information a public method gets when called, besides its function parameters.

Contract maintenance state consists of the following three steps:

Step 1

Update the stateful properties in public @method.

A player call move() to places the symbol in the board. We should update the stateful properties board and is_alice_turn in the move() @method:

assert(this.board[Number(n)] === TicTacToe.EMPTY, `board at position ${n} is not empty: ${this.board[Number(n)]}`);
let play = this.is_alice_turn ? TicTacToe.ALICE : TicTacToe.BOB;
// update stateful properties to make the move
this.board[Number(n)] = play; // Number() converts a bigint to a number
this.is_alice_turn = !this.is_alice_turn;

Step 2

When you are ready to pass the new state onto the output[s] in the current spending transaction, simply call a built-in function this.buildStateOutput() to create an output containing the new state. It takes an input: the number of satoshis in the output. We keep the satoshis unchanged in the example.

let output = this.buildStateOutput(this.ctx.utxo.value);

Build outputs in public @method

TicTacToe can contain the following three types of output during execution:

  1. The game is not over: a output containing the new state and a change output
  2. A player wins the game: a P2PKH output that pays the winner, and a change output.
  3. A draw: two P2PKH outputs that split the contract-locked bets equally between the players and a change output.

The P2PKH output can be built using Utils.buildPublicKeyHashOutput(pkh: PubKeyHash, amount: bigint). The change output can be built using this.buildChangeOutput().

// build the transation outputs
let outputs = toByteString('');
if (this.won(play)) {
outputs = Utils.buildPublicKeyHashOutput(pubKey2Addr(player), this.ctx.utxo.value);
}
else if (this.full()) {
const halfAmount = this.ctx.utxo.value / 2n;
const aliceOutput = Utils.buildPublicKeyHashOutput(pubKey2Addr(this.alice), halfAmount);
const bobOutput = Utils.buildPublicKeyHashOutput(pubKey2Addr(this.bob), halfAmount);
outputs = aliceOutput + bobOutput;
}
else {
// build a output that contains latest contract state.
outputs = this.buildStateOutput(this.ctx.utxo.value);
}

outputs += this.buildChangeOutput();

Step 3

Make sure that the output of the current transaction must contain this incremented new state. If all outputs (only a single output here) we create in the contract hashes to hashOutputs in ScriptContext, we can be sure they are the outputs of the current transaction. Therefore, the updated state is propagated.

// verify current tx has this single output
assert(this.ctx.hashOutputs == hash256(outputs), 'hashOutputs mismatch')

Conclusion

Congratulations, you have completed the TicTacToe contract!

The final complete code is as follows:

export class TicTacToe extends SmartContract {
@prop()
alice: PubKey;
@prop()
bob: PubKey;

@prop(true)
is_alice_turn: boolean;

@prop(true)
board: FixedArray<bigint, 9>;

@prop()
static readonly EMPTY: bigint = 0n;
@prop()
static readonly ALICE: bigint = 1n;
@prop()
static readonly BOB: bigint = 2n;

constructor(alice: PubKey, bob: PubKey) {
super(...arguments)
this.alice = alice;
this.bob = bob;
this.is_alice_turn = true;
this.board = fill(TicTacToe.EMPTY, 9);
}

@method()
public move(n: bigint, sig: Sig) {
// check position `n`
assert(n >= 0n && n < 9n);
// check signature `sig`
let player: PubKey = this.is_alice_turn ? this.alice : this.bob;
assert(this.checkSig(sig, player), `checkSig failed, pubkey: ${player}`);
// update stateful properties to make the move
assert(this.board[Number(n)] === TicTacToe.EMPTY, `board at position ${n} is not empty: ${this.board[Number(n)]}`);
let play = this.is_alice_turn ? TicTacToe.ALICE : TicTacToe.BOB;
this.board[Number(n)] = play;
this.is_alice_turn = !this.is_alice_turn;

// build the transation outputs
let outputs = toByteString('');
if (this.won(play)) {
outputs = Utils.buildPublicKeyHashOutput(pubKey2Addr(player), this.ctx.utxo.value);
}
else if (this.full()) {
const halfAmount = this.ctx.utxo.value / 2n;
const aliceOutput = Utils.buildPublicKeyHashOutput(pubKey2Addr(this.alice), halfAmount);
const bobOutput = Utils.buildPublicKeyHashOutput(pubKey2Addr(this.bob), halfAmount);
outputs = aliceOutput + bobOutput;
}
else {
// build a output that contains latest contract state.
outputs = this.buildStateOutput(this.ctx.utxo.value);
}

outputs += this.buildChangeOutput();

// make sure the transaction contains the expected outputs built above
assert(this.ctx.hashOutputs === hash256(outputs), "check hashOutputs failed");
}

@method()
won(play: bigint): boolean {
let lines: FixedArray<FixedArray<bigint, 3>, 8> = [
[0n, 1n, 2n],
[3n, 4n, 5n],
[6n, 7n, 8n],
[0n, 3n, 6n],
[1n, 4n, 7n],
[2n, 5n, 8n],
[0n, 4n, 8n],
[2n, 4n, 6n]
];

let anyLine = false;

for (let i = 0; i < 8; i++) {
let line = true;
for (let j = 0; j < 3; j++) {
line = line && this.board[Number(lines[i][j])] === play;
}

anyLine = anyLine || line;
}

return anyLine;
}

@method()
full(): boolean {
let full = true;
for (let i = 0; i < 9; i++) {
full = full && this.board[i] !== TicTacToe.EMPTY;
}
return full;
}

}

But no dApp is complete if users cannot interact with it. Go here to see how to add a front end to it.

- + \ No newline at end of file diff --git a/tutorials/voting/index.html b/tutorials/voting/index.html index f12a5e851..1929320ec 100644 --- a/tutorials/voting/index.html +++ b/tutorials/voting/index.html @@ -4,7 +4,7 @@ Tutorial 6: Voting | sCrypt - + @@ -12,7 +12,7 @@

Tutorial 6: Voting

Overview

In this tutorial, we will go over how to use sCrypt to build a full-stack voting dApp on Bitcoin, including the smart contract and an interactive front-end.

On the web page, you can see the candidate list. Clicking the like button will cast one vote for the corresponding candidate. This will prompt the wallet to ask for a user's approval. A transaction calling the contract will be sent after her approval.

First, we will write and deploy the smart contract step by step. Afterward, we will build a front-end with React that allows users to cast votes and thus interact with the contract.

Contract

Properties

For each candidate, there are two properties we need to store in the contract: her name and her votes received so far.

We define a type alias of ByteString to represent a candidate name.

export type Name = ByteString

We define a struct to represent a candidate.

export type Candidate = {
name: Name
votesReceived: bigint
}

We use a FixedArray to store the list of candidates, which we alias as type Candidates. Since candidates' vote counts can be updated, we mark it stateful by setting @prop(true).

export const N = 2
export type Candidates = FixedArray<Candidate, typeof N>

export class Voting extends SmartContract {
@prop(true)
candidates: Candidates
// ...
}

Constructor

Initialize all the @prop properties in the constructor. Note that we only need to pass the candidate names in the argument, because the votes they received would be all 0 at the beginning.

constructor(names: FixedArray<Name, typeof N>) {
super(...arguments)
// initialize fixed array
this.candidates = fill({
name: toByteString(''),
votesReceived: 0n
}, N)
// set names and set votes they received to 0
for (let i = 0; i < N; i++) {
this.candidates[i] = { name: names[i], votesReceived: 0n }
}
}

Methods

The only way to interact with this contract is to vote for one candidate in the list, so we will have only 1 public method vote. It takes only 1 parameter: the name of the candidate you want to vote for.

@method()
public vote(name: Name) {
// 1) change contract state: add one vote to `candidate` in the list
// 2) propogate the state
}

We can simply use a for loop to implement this: find the corresponding candidate in the list by name, then increment its vote by one. We implement this in a helper method increaseVotesReceived.

// cast one vote to a candidate
@method()
increaseVotesReceived(name: Name): void {
for (let i = 0; i < N; i++) {
if (this.candidates[i].name === name) {
this.candidates[i].votesReceived++
}
}
}

After we increment the candidate's votes and update the contract state, we make sure the new state is maintained in the spending transaction's output as usual. Another output is added if change is needed.

let outputs: ByteString = this.buildStateOutput(this.ctx.utxo.value)
outputs += this.buildChangeOutput()
assert(this.ctx.hashOutputs === hash256(outputs), 'hashOutputs mismatch')

The public function vote is now finished.

@method()
public vote(name: Name) {
// change contract state: add one vote to `candidate` in the list
this.increaseVotesReceived(name)

// restrict tx outputs
// to contain the latest state with the same balance
let outputs: ByteString = this.buildStateOutput(this.ctx.utxo.value)
// to contain the change output when necessary
outputs += this.buildChangeOutput()

assert(this.ctx.hashOutputs === hash256(outputs), 'hashOutputs mismatch')
}

Final Code

You have completed the Voting contract! The final complete code is as follows:

import { assert, ByteString, hash256, method, prop, SmartContract, FixedArray, fill, toByteString } from 'scrypt-ts'

export type Name = ByteString

export type Candidate = {
name: Name
votesReceived: bigint
}

export const N = 2

export type Candidates = FixedArray<Candidate, typeof N>

export class Voting extends SmartContract {
@prop(true)
candidates: Candidates

constructor(names: FixedArray<Name, typeof N>) {
super(...arguments)
// initialize fixed array
this.candidates = fill({
name: toByteString(''),
votesReceived: 0n,
}, N)
// set names and set votes they received to 0
for (let i = 0; i < N; i++) {
this.candidates[i] = {
name: names[i],
votesReceived: 0n,
}
}
}

/**
* vote for a candidate
* @param name candidate's name
*/
@method()
public vote(name: Name) {
// change contract state: add one vote to `candidate` in the list
this.increaseVotesReceived(name)
// output containing the latest state and the same balance
let outputs: ByteString = this.buildStateOutput(this.ctx.utxo.value)
outputs += this.buildChangeOutput()
assert(this.ctx.hashOutputs === hash256(outputs), 'hashOutputs mismatch')
}

@method()
increaseVotesReceived(name: Name): void {
for (let i = 0; i < N; i++) {
if (this.candidates[i].name === name) {
this.candidates[i].votesReceived++
}
}
}
}

Frontend

We will add a frontend to the voting smart contract according to this guide.

Setup Project

The front-end will be created using Create React App.

npx create-react-app voting --template typescript

Install the sCrypt SDK

The sCrypt SDK enables you to easily compile, test, deploy, and call contracts.

Use the scrypt-cli command line to install the SDK.

cd voting
npx scrypt-cli init

This command will create a contract file at src\contracts\voting.ts, replace the content of the file with the contract written above.

Compile Contract

Compile the contract with the following command:

npx scrypt-cli compile

This command will generate a contract artifact file at artifacts\voting.json.

Contract Deployment

After installing the sCrypt SDK, you will have a script deploy.ts in the project directory, which can be used to deploy our Voting contract after some minor modifications.

import { Name, Voting, N } from './src/contracts/voting'
import { bsv, TestWallet, DefaultProvider, toByteString, FixedArray } from 'scrypt-ts'

import * as dotenv from 'dotenv'

// Load the .env file
dotenv.config()

// Read the private key from the .env file.
// The default private key inside the .env file is meant to be used for the Bitcoin testnet.
// See https://scrypt.io/docs/bitcoin-basics/bsv/#private-keys
const privateKey = bsv.PrivateKey.fromWIF(process.env.PRIVATE_KEY || '')

// Prepare signer.
// See https://scrypt.io/docs/how-to-deploy-and-call-a-contract/#prepare-a-signer-and-provider
const signer = new TestWallet(privateKey, new DefaultProvider({
network: bsv.Networks.testnet
}))

async function main() {
await Voting.loadArtifact()

const candidateNames: FixedArray<Name, typeof N> = [
toByteString('iPhone', true),
toByteString('Android', true)
]

const instance = new Voting(
candidateNames
)

// Connect to a signer.
await instance.connect(signer)

// Contract deployment.
const amount = 1
const deployTx = await instance.deploy(amount)
console.log('Voting contract deployed: ', deployTx.id)
}

main()

Before deploying the contract, we need to create a .env file and save your private key in the PRIVATE_KEY environment variable.

PRIVATE_KEY=xxxxx

If you don't have a private key, you can follow this guide to generate one using Sensilet wallet, then fund the private key's address with our faucet.

Run the following command to deploy the contract.

npm run deploy:contract

After success, you will see an output similar to the following:

Contract ID

Your can get the deployed contract's ID: the TXID and the output index where the contract is located.

const contract_id = {
/** the deployment transaction id */
txId: "6751b645e1579e8e6201e3c59b900ad58e59868aa5e4ee89359d3f8ca1d66c8a",
/** the output index */
outputIndex: 0,
};

Verify

After a successful deployment of a smart contract, you can verify the deployed contract script:

npm run verify:contract

Upon execution, the designated contract code undergoes verification on sCrypt's servers. If successful, the outcome will be displayed on WoC, under the "sCrypt" tab. See the "How to Verify a Contract" page for more details.

Load Contract Artifact

Before writing the front-end code, we need to load the contract artifact in src\index.tsx.

import { Voting } from './contracts/voting';
import artifact from '../artifacts/voting.json';
Voting.loadArtifact(artifact);

Integrate Wallet

Use requestAuth method of signer to request access to the wallet.

// request authentication
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
// something went wrong, throw an Error with `error` message
throw new Error(error);
}

// authenticated
// ...

Integrate sCrypt Service

To interacte with the voting contract, we need to create a contract instance representing the latest state of the contract on chain. When both Alice and Bob vote on the webpage, we need to ensure that their contract instances are always up to date. After Alice votes, we have to notify Bob that the state of the contract has changed and synchronize his local contract instance to the latest state on chain.

Fortunately,sCrypt provides such infrastructure service, which abstracts away all the common complexities of communicating with the blockchain, so we do not have to track the contract state, which could be computationally demanding as blockchain grows. We can instead focus on our application's business logic.

To use it, we first have to initialize it according to this guide.

Scrypt.init({
apiKey: 'YOUR_API_KEY',
network: bsv.Networks.testnet
})

Connect Signer to ScryptProvider

It's required to connect your signer to ScryptProvider when using sCrypt service.

const provider = new ScryptProvider();
const signer = new SensiletSigner(provider);

signerRef.current = signer;

Fetch Latest Contract Instance

We can fetch a contract's latest instance by calling the Scrypt.contractApi.getLatestInstance() using its contract ID. With this instance, we can easily read a contract's properties to display to the user on the webpage, or update the contract state by calling its public method as before when the user votes for a candidate.

function App() {
const [votingContract, setContract] = useState<Voting>();
const [error, setError] = React.useState("");

// ...

async function fetchContract() {
try {
const instance = await Scrypt.contractApi.getLatestInstance(
Voting,
contract_id
);
setContract(instance);
} catch (error: any) {
console.error("fetchContract error: ", error);
setError(error.message);
}
}

// ...
}

Read contract state

With the contract instance, we can read its lastest state and render it.

function byteString2utf8(b: ByteString) {
return Buffer.from(b, "hex").toString("utf8");
}

function App() {
// ...

return (
<div className="App">
<header className="App-header">
<h2>What's your favorite phone?</h2>
</header>
<TableContainer
component={Paper}
variant="outlined"
style={{ width: 1200, height: "80vh", margin: "auto" }}
>
<Table>
<TableHead>
<TableRow>
<TableCell align="center">Iphone</TableCell>
<TableCell align="center">Android</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell align="center">
<Box>
<Box
sx={{
height: 200,
}}
component="img"
alt={"iphone"}
src={`${process.env.PUBLIC_URL}/${"iphone"}.png`}
/>
</Box>
</TableCell>
<TableCell align="center">
<Box>
<Box
sx={{
height: 200,
}}
component="img"
alt={"android"}
src={`${process.env.PUBLIC_URL}/${"android"}.png`}
/>
</Box>
</TableCell>
</TableRow>
<TableRow>
<TableCell align="center">
<Box>
<Typography variant={"h1"} >
{votingContract?.candidates[0].votesReceived.toString()}
</Typography>
<Button
variant="text"
onClick={voting}
name={votingContract?.candidates[0].name}
>
👍
</Button>
</Box>
</TableCell>

<TableCell align="center">
<Divider orientation="vertical" flexItem />
<Box>
<Typography variant={"h1"}>
{votingContract?.candidates[1].votesReceived.toString()}
</Typography>
<Button
variant="text"
onClick={voting}
name={votingContract?.candidates[1].name}
>
👍
</Button>
</Box>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<Footer />
<Snackbar
open={error !== ""}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert severity="error">{error}</Alert>
</Snackbar>

<Snackbar
open={success.candidate !== "" && success.txId !== ""}
autoHideDuration={6000}
onClose={handleSuccessClose}
>
<Alert severity="success">
{" "}
<Link
href={`https://test.whatsonchain.com/tx/${success.txId}`}
target="_blank"
rel="noreferrer"
>
{`"${byteString2utf8(success.candidate)}" got one vote, tx: ${
success.txId
}`}
</Link>
</Alert>
</Snackbar>
</div>
);
}

Update Contract State

To update the contract's state, we need to call its public method. We create a function voting() to handle the voting event triggered by a user.

Calling a contract public method is the same as before.

async function voting(e: any) {
// ...

const signer = signerRef.current as SensiletSigner;

if (votingContract && signer) {
const { isAuthenticated, error } = await signer.requestAuth();
if (!isAuthenticated) {
throw new Error(error);
}

await votingContract.connect(signer);

// create the next instance from the current
const nextInstance = votingContract.next();

const candidateName = e.target.name;

// update state
nextInstance.increaseVotesReceived(candidateName);

// call the method of current instance to apply the updates on chain
votingContract.methods
.vote(candidateName, {
next: {
instance: nextInstance,
balance: votingContract.balance,
},
})
.then((result) => {
console.log(`Voting call tx: ${result.tx.id}`);
})
.catch((e) => {
setError(e.message);
fetchContract();
console.error("call error: ", e);
});
}
}

If successful, you will see the following log in the console:

Voting call tx: fc8b3d03b8fa7469d66a165b017fe941fa8ab59c0979457cef2b6415d659e3f7

Subscribe to Contract Event

So far, we have a fully working app. However, there is a slight problem. When Alice clicks on the like button for a candadate in her browser, the candidate's vote count in Bob's browser does not increase, unless he manually refreshes. We need a way to listen to contract event.

We call Scrypt.contractApi.subscribe(options: SubscribeOptions<T>, cb: (e: ContractCalledEvent<T>) => void): SubScription to subscribe to events that the contract has been called. When a contract gets called and updated, we refresh the UI in real time, re-render all the content on the page and show the updated vote count.

The subscribe function takes 2 parameters:

  1. options: SubscribeOptions<T>: it includes a contract class, a contract ID, and a optional list of method names monitored.
interface SubscribeOptions<T> {
clazz: new (...args: any) => T;
id: ContractId;
methodNames?: Array<string>;
}

If methodNames is set, you will be notified only when public functions in the list are called. Otherwise, you will be notified when ANY public function is called.

  1. callback: (event: ContractCalledEvent<T>) => void: a callback funciton upon receiving notifications.

ContractCalledEvent<T> contains the relevant information when the contract is called, such as the public function name and function arguments when the call occurs.

export interface ContractCalledEvent<T> {
/** name of public function */
methodName: string;
/** public function arguments */
args: SupportedParamType[];
/** transaction where contract is called from */
tx: bsv.Transaction;
/**
* If a stateful contract is called, `nexts` contains the contract instance containing the new state generated by this call.
* If a stateless contract is called, `nexts` is empty.
*/
nexts: Array<T>;
}

The code to subscribe to contract events is as follows.

useEffect(() => {
const provider = new ScryptProvider();
const signer = new SensiletSigner(provider);

signerRef.current = signer;

fetchContract();

// subscribe by contract_id
const subscription = Scrypt.contractApi.subscribe({
clazz: Voting,
id: contract_id
}, (event: ContractCalledEvent<Voting>) => {
// update the contract instance
setSuccess({
txId: event.tx.id,
candidate: event.args[0] as ByteString,
});
setContract(event.nexts[0]);
});

return () => {
// unsubscribe
subscription.unsubscribe();
};
}, []);

Deploy to GitHub Pages

After pushing the frontend project to your GitHub account, it's easy to publish a website with GitHub Pages, so that users can interact with your dApp with the browser.

Step 1. Add homepage to package.json

Open your package.json and add a homepage field for your project.

{
"name": "voting",
"homepage": "https://YOUR-GITHUB-USERNAME.github.io/YOUR-REPO-NAME"
...
}

For example, our demo repo is at https://github.com/sCrypt-Inc/voting, so we set

https://sCrypt-Inc.github.io/voting

as the homepage, where sCrypt-Inc is our GitHub username, and voting is the repo name.

Step 2. Install gh-pages and add scripts in package.json

Run the following command to install the dependency.

npm install --save gh-pages

Then add two scripts in package.json.

"scripts": {
"predeploy": "npm run build",
"deploy": "gh-pages -d build",
...
},

note

The predeploy script will run automatically before deploy is run.

Step 3. Deploy the site

Run the following command to deploy the website.

npm run deploy

Step 4. Update GitHub project settings

After running the deploy script, don't forget to update your GitHub project settings to use gh-pages branch. Go to Settings --> Code and automation/Pages, and select gh-pages as the branch used by the GitHub Pages site.

Conclusion

Congratulations! You have successfully completed a fullstack voting dapp fully on Bitcoin.

The repo is here. And an online example is here.

- + \ No newline at end of file diff --git a/tutorials/zkp/index.html b/tutorials/zkp/index.html index 2b2846190..dffe2f9e0 100644 --- a/tutorials/zkp/index.html +++ b/tutorials/zkp/index.html @@ -4,14 +4,14 @@ Tutorial 5: Zero Knowledge Proofs | sCrypt - +

Tutorial 5: Zero Knowledge Proofs

Overview

In this tutorial we will go over how to create a zero-knowledge proof (ZKP) and verify it on Bitcoin using sCrypt.

What are zk-SNARKS?

SNARK (zero-knowledge Succinct Non-interactive ARguments of Knowledge) is a type of ZKP that is amenable for blockchains. The generated proof is “succinct” and “non-interactive”: a proof is only a few hundred bytes and can be verified in constant time and within a few milliseconds, without needing to ask additional questions of the prover. Together, these properties make zk-SNARK especially suitable for blockchains, where on-chain storage and computation can be expensive and senders often go offline after sending a transaction.

A proof is constructed off-chain by a prover who generates the proof using a secret input (often referred to as the "witness") and a public input. The prover can then use this proof as an input for an sCrypt smart contract, which can verify the validity of the proof using a verification key and the public input.

Credit: altoros

There are many tools for creating such proofs, ZoKrates and SnarkJS are among the most popular.

In this example we will use ZoKrates. It provides a python-like higher-level language for developers to code the computational problem they want to prove.

For a more comprehensive explanation of zk-SNARKS and how they work, we recommend reading this blog post.

Install ZoKrates

Run the following command to install released binaries:

curl -Ls https://scrypt.io/scripts/setup-zokrates.sh | sh -s -

or build from source:

git clone https://github.com/sCrypt-Inc/zokrates
cd ZoKrates
cargo +nightly build -p zokrates_cli --release
cd target/release

ZoKrates Workflow

1. Design a circuit

Create a new ZoKrates file named factor.zok with the following content:

// p, q are the factors of n
def main(private field p, private field q, field n) {
assert(p * q == n);
assert(p > 1);
assert(q > 1);
return;
}

This simple circuit/program proves one knows a factorization of an integer n into two integers, without revealing the factors. The circuit has two private inputs named p and q and one public input named n.

2. Compile the circuit

Compile the circuit with the following command:

zokrates compile -i factor.zok

This generates two files that encode the circuit in binary and human-readable format.

3. Setup

This generates a proving key and a verification key for this circuit.

zokrates setup

4. Calculate a witness

A proof attests that a prover knows some secret/private information that satisfies the original program. This secret information is called witness. In the following example, 7 and 13 are the witnesses, as they are factors of 91.

zokrates compute-witness -a 7 13 91

A file named witness is generated.

5. Creating a proof

The following command produces a proof, using both the proving key and the witness:

zokrates generate-proof

The resulting file proof.json looks like the following:

{
"scheme": "g16",
"curve": "bn128",
"proof": {
"a": [
"0x0a7ea3ca37865347396645d017c7623431d13103e9107c937d722e5da15f352b",
"0x040c202ba8fa153f84af8dabc2ca40ff534f54efeb3271acc04a70c41afd079b"
],
"b": [
[
"0x0ec1e4faea792762de35dcfd0da0e6859ce491cafad455c334d2c72cb8b24550",
"0x0985ef1d036b41d44376c1d42ff803b7cab9f9d4cf5bd75298e0fab2d109f096"
],
[
"0x265151afd8626b4c72dfefb86bac2b63489423d6cf895ed9fa186548b0b9e3f3",
"0x301f2b356621408e037649d0f5b4ad5f4b2333f58453791cc24f07d5673349bf"
]
],
"c": [
"0x2b75a257d68763100ca11afb3beae511732c1cd1d3f1ce1804cbc0c26043cb6b",
"0x2f80c706b58482eec9e759fce805585595a76c27e37b67af3463414246fbabbd"
]
},
"inputs": [
"0x000000000000000000000000000000000000000000000000000000000000005b"
]
}

6. Export an sCrypt verifier

Using our version of ZoKrates, we can export a project template, which will contain a verifier for our circuit. Simply run the following command:

zokrates export-verifier-scrypt

This will create a directory named verifier, containing the project. Let's set it up. Run the following:

cd verifier && git init && npm i

Now the verifier is ready to be used. In the following section we will go over the code and show how to use it.

7. Run the sCrypt Verifier

In the generated project, let's open the file src/contracts/verifier.ts. This file contains an sCrypt smart contract, named Verifier, which can be unlocked by providing a valid ZK proof.

Under the hood it uses the SNARK library from src/contracts/snark.ts. This file includes an elliptic curve implementation along with a library that implements pairings over that elliptic curve and lastly the implementation of the proof verification algorithm. In our example the BN-256 elliptic curve is being used along with the Groth-16 proof system..

Let's take a look at the implementation of Verifier:

export class Verifier extends SmartContract {

@prop()
vk: VerifyingKey

@prop()
publicInputs: FixedArray<bigint, typeof N_PUB_INPUTS>,

constructor(
vk: VerifyingKey,
publicInputs: FixedArray<bigint, typeof N_PUB_INPUTS>,
) {
super(...arguments)
this.vk = vk
this.publicInputs = publicInputs
}

@method()
public verifyProof(
proof: Proof
) {
assert(SNARK.verify(this.vk, this.publicInputs, proof))
}

}

As we can see, the contract has two properties, namely the verification key and the value(s) of the public inputs to our ZK program.

The contract also has a public method named verifyProof. As the name implies it verifies a ZK proof and can be unlocked by a valid one. The proof is passed as a parameter. The method calls the proof verification function:

SNARK.verify(this.vk, this.publicInputs, proof)

The function takes as parameters the verification key, the public inputs and the proof. It's important to note that the proof is cryptographically tied to the verification key and thus must be a proof about the correct ZoKrates program (factor.zok).

The generated project will also contain a deployment script deploy.ts. Let's take a look at the code:

async function main() {
await Verifier.loadArtifact()

// TODO: Adjust the amount of satoshis locked in the smart contract:
const amount = 100

// TODO: Insert public input values here:
const publicInputs: FixedArray<bigint, typeof N_PUB_INPUTS> = [ 0n ]

let verifier = new Verifier(
prepareVerifyingKey(VERIFYING_KEY_DATA),
publicInputs
)

// Connect to a signer.
await verifier.connect(getDefaultSigner())

// Deploy:
const deployTx = await verifier.deploy(amount)
console.log('Verifier contract deployed: ', deployTx.id)
}

main()

We can observe that we need to adjust two things. First, we need to set the amount of satoshis we will lock into the deployed smart contract. The second thing is the public input value, i.e. the product of the secret factors. Let's set it to the value 91:

const publicInputs: FixedArray<bigint, typeof N_PUB_INPUTS> = [ 91n ]

Note also, that ZoKrates already provided us with the values of the verification key, that we created during the setup phase.

Now, we can build and deploy the contract. Simply run:

npm run deploy

The first time you run the command, it will ask you to fund a testnet address. You can fund it using our faucet.

After a successful run you should see something like the following:

Verifier contract deployed:  2396a4e52555cdc29795db281d17de423697bd5cbabbcb756cb14cea8e947235

The smart contract is now deployed and can be unlocked using a valid proof, that proves the knowledge of the factors for the integer 91. You can see the transaction using a block explorer.

Let's call the deployed contract. Let's create a file named call.ts with the following content:

import { DefaultProvider } from 'scrypt-ts'
import { parseProofFile } from './src/util'
import { Verifier } from './src/contracts/verifier'
import { Proof } from './src/contracts/snark'
import { getDefaultSigner } from './tests/utils/helper'
import { PathLike } from 'fs'

export async function call(txId: string, proofPath: PathLike) {
await Verifier.loadArtifact()

// Fetch TX via provider and reconstruct contract instance
const provider = new DefaultProvider()
const tx = await provider.getTransaction(txId)
const verifier = Verifier.fromTx(tx, 0)

// Connect signer
await verifier.connect(getDefaultSigner())

// Parse proof.json
const proof: Proof = parseProofFile(proofPath)

// Call verifyProof()
const { tx: callTx } = await verifier.methods.verifyProof(
proof
)
console.log('Verifier contract unlocked: ', callTx.id)
}

(async () => {
await call('2396a4e52555cdc29795db281d17de423697bd5cbabbcb756cb14cea8e947235', '../proof.json')
})()

The function call will create the contract instance from the passed TXID and call its verifyProof method. The proof gets parsed from proof.json, which we already created in the section above.

Let's unlock our contract by running the following command:

npx ts-node call.ts

If everything goes as expected, we have now unlocked the verifier smart contract. You'll see an output similar to the following:

Verifier contract unlocked:  30127e0c340878d3fb7c165e2d082267eef2c8df79b5cf750896ef565ca7651d

Take a look at it using a block explorer.

Conclusion

Congratulations! You have successfully created a zk-SNARK and verified it on-chain!

If you want to learn how you can integrate zk-SNARKS into a fully fledged Bitcoin web application, take a look at our free course, which will teach you how to create a ZK Battleship game. Additionally, it teaches you to use snarkjs/circom.

To know more about ZKP, you can refer to this awesome list.

- + \ No newline at end of file