Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Radiant is a decentralized P2P Digital Asset System and PoW UTXO Network for everyone
<REF> OP_REFTYPE_UTXO OP_0 OP_NUMNOTEQUAL
OP_PUSHINPUTREFSINGLETON a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b401000000 OP_DROP
OP_DUP OP_HASH160 3a23c6b9055b533db3900e4755dd58d1234c8582 OP_EQUALVERIFY OP_CHECKSIGOP_PUSH "gly"
OP_PUSH <CBOR Payload>payload = {
p: [* (number / string)] ; Array of protocol identifiers
name: tstr, ; Name of the token
? type: tstr, ; Type of the token (e.g. container, user or other custom types)
? in: [*bstr], ; Array of container token refs the token is part of
? by: [*bstr], ; Array of author token refs the token is created by
? main: { ; Main file
t: tstr, ; File mime type
b: bstr ; File bytes
},
? attrs: { * tstr => any } ; Attributes as key value pairs (e.g. color, rarity, etc)
? loc: number / bytes / string ; Location for link tokens
}{
p: [1, 4],
name: "My token",
ticker: "XYZ",
main: {
t: "image/jpeg",
b: <bytes>
},
in: [
<containerRefBytes>
],
by: [
<authorRefBytes>
],
} embed = {
t: tstr, ; File mime type
b: bstr ; File bytes
} remote = {
t: tstr, ; File mime type
u: tstr ; URL
? h: bstr ; File hash
? hs: bstr ; HashStamp image (low resolution copy of image)
}{
main: {
t: "image/jpeg",
b: <bytes> // File bytes embedded in transaction
},
alt: {
t: "image/jpeg",
u: "https://url/to/image.jpg", // Link to remote file
h: <hashBytes>,
hs: <hashStampBytes>
}
}// P2PKH
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG
OP_STATESEPARATOR
// Fungible token ref
OP_PUSHINPUTREF <ref>
// Get count of ref used in outputs
OP_REFOUTPUTCOUNT_OUTPUTS OP_INPUTINDEX
// Get a hash of the script after state separator
OP_CODESCRIPTBYTECODE_UTXO OP_HASH256 OP_DUP
// Sum token value for all outputs
OP_CODESCRIPTHASHVALUESUM_UTXOS
// Sum token value for all inputs
OP_OVER OP_CODESCRIPTHASHVALUESUM_OUTPUTS
// Input sum must be greater than or equal to output sum
OP_GREATERTHANOREQUAL OP_VERIFY
// To prevent supply inflation, ref must be used within this script and ref count must be equal to contract count
OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS OP_NUMEQUALVERIFYOP_PUSHINPUTREFSINGLETON <ref> OP_DROP
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG // P2PKHOP_PUSHINPUTREF <ref> OP_DROP
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG // P2PKH location = number / bytes / string// SHA256 of payload must be provided
<payloadHash> OP_DROP
OP_STATESEPARATOR
// Mutable contract ref
OP_PUSHINPUTREFSINGLETON <mutableRef>
// Build token ref (mutable ref -1)
OP_DUP 20 OP_SPLIT OP_BIN2NUM OP_1SUB OP_4 OP_NUM2BIN OP_CAT
// Check token ref exists in token output at given refdatasummary index
OP_2 OP_PICK OP_REFDATASUMMARY_OUTPUT OP_4 OP_ROLL 24 OP_MUL OP_SPLIT OP_NIP 24 OP_SPLIT OP_DROP OP_EQUALVERIFY
// Compare ref + scriptsig hash in token output to this script's ref + scriptsig hash
OP_SWAP OP_STATESCRIPTBYTECODE_OUTPUT OP_ROT OP_SPLIT OP_NIP 45 OP_SPLIT OP_DROP OP_OVER 20 OP_CAT OP_INPUTINDEX OP_INPUTBYTECODE OP_SHA256 OP_CAT OP_EQUALVERIFY
// Modify operation
OP_2 OP_PICK 6d6f64 OP_EQUAL OP_IF
// Contract script must exist unchanged in output
OP_OVER OP_CODESCRIPTBYTECODE_OUTPUT OP_INPUTINDEX OP_CODESCRIPTBYTECODE_UTXO OP_EQUALVERIFY
// State script must contain payload hash
OP_OVER OP_STATESCRIPTBYTECODE_OUTPUT 20 OP_5 OP_PICK OP_HASH256 OP_CAT 75 OP_CAT OP_EQUALVERIFY OP_ELSE
// Seal operation
OP_2 OP_PICK 736c OP_EQUALVERIFY OP_OVER OP_OUTPUTBYTECODE d8 OP_2 OP_PICK OP_CAT 6a OP_CAT OP_EQUAL OP_OVER OP_REFTYPE_OUTPUT OP_0 OP_NUMEQUAL OP_BOOLOR OP_VERIFY OP_ENDIF
// Glyph header
OP_4 OP_ROLL ${glyphMagicBytesHex} OP_EQUALVERIFY OP_2DROP OP_2DROP OP_1OP_PUSH gly
OP_PUSH mod // Modify operation
OP_PUSH <cbor payload> // Updated token payload
OP_PUSH <contract output index> // Location of mutability contract UTXO in outputs
OP_PUSH <ref+hash index in token output> // Index of the ref+hash in the token output, allowing multiple authorizations in a single transaction (zero for a single update)
OP_PUSH <ref index in token output data summary> // Position of token ref in OP_REFDATASUMMARY_OUTPUT (the sorted list of refs used in all outputs)
OP_PUSH <token output index> // Location of token UTXO in outputs// Require the mutability contract (token ref + 1)
OP_REQUIREINPUTREF a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b402000000
// Hash of mutability contract's unlocking script
OP_PUSH a204a95c54bf73975256898be5f47d7844146845f9b749408b535d12e3ad85a9
// Drop ref and hash
OP_2DROP
// Following is a standard NFT contract
OP_PUSHINPUTREFSINGLETON a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b401000000 OP_DROP
OP_DUP OP_HASH160 3a23c6b9055b533db3900e4755dd58d1234c8582 OP_EQUALVERIFY OP_CHECKSIGhash = sha256(sha256(
sha256(currentLocationTxid + contractRef) +
sha256(anyInputHash + anyOutputHash) +
nonce
))OP_PUSHINPUTREFSINGLETON 000000000000000000000000000000000000000000000000000000000000000000000000 OP_DROP OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIGOP_PUSHINPUTREFSINGLETON <tokenRef> OP_DROP <p2pkh>OP_REQUIREINPUTREF <contractRef>
<contractScriptSigHash> OP_2DROP // Hash256 of contract's script sig
OP_STATESEPARATOR
OP_PUSHINPUTREFSINGLETON <tokenRef> OP_DROP <p2pkh> // Standard token script<hashIndex> // Position of ref + hash in token output
<refIndex> // Index of ref in token output data summary
<outputIndex> // Output index of token// Contract ref
OP_PUSHINPUTREFSINGLETON <contractRef> OP_DROP
// Check token ref exists in the token output
OP_DUP OP_ROT OP_REFDATASUMMARY_OUTPUT OP_SWAP 36 OP_MUL OP_SPLIT OP_NIP 32 OP_SPLIT OP_DROP <tokenRef> OP_DUP OP_ROT OP_EQUALVERIFY
// Get ref + hash in token output script
OP_ROT OP_STATESCRIPTBYTECODE_OUTPUT OP_SWAP OP_SPLIT OP_NIP 69 OP_SPLIT OP_DROP
// Compare ref + hash in token output to ref + hash of this script's unlocking code
OP_SWAP 32 OP_INPUTINDEX OP_INPUTBYTECODE OP_HASH256 OP_CAT OP_CAT OP_EQUALVERIFY
// Propagate the contract
OP_INPUTINDEX OP_CODESCRIPTBYTECODE_UTXO OP_HASH256 OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS 1 OP_NUMEQUALVERIFY
//
// Contract code...
// npm install -g cashcnpm install cashscriptpragma cashscript ^0.7.0;
contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
// Allow the recipient to claim their received money
function transfer(sig recipientSig) {
require(checkSig(recipientSig, recipient));
}
// Allow the sender to reclaim their sent money after the timeout is reached
function timeout(sig senderSig) {
require(checkSig(senderSig, sender));
require(tx.time >= timeout);
}
}cashc ./transfer_with_timeout.cash --output ./transfer_with_timeout.jsonconst { ElectrumNetworkProvider, Contract, SignatureTemplate } = require('cashscript');
const { alice, bob, alicePk, bobPk } = require('./keys');
async function run() {
// Import the TransferWithTimeout JSON artifact
const artifact = require('./transfer_with_timeout.json');
// Initialise a network provider for network operations
const provider = new ElectrumNetworkProvider('mainnet');
// Instantiate a new TransferWithTimeout contract
const contract = new Contract(artifact, [alicePk, bobPk, 600000], provider);
// Call the transfer function with Bob's signature
// i.e. Bob claims the money that Alice has sent him
const transferDetails = await contract.functions
.transfer(new SignatureTemplate(bob))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
.send();
console.log(transferDetails);
// Call the timeout function with Alice's signature
// i.e. Alice recovers the money that Bob has not claimed
const timeoutDetails = await contract.functions
.timeout(new SignatureTemplate(alice))
.to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 10000)
.send();
console.log(timeoutDetails);
}OP_PUSHINPUTREFSINGLETON <ref> OP_DROP
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG





contract NFT {
// Asset identifier
bytes assetId;
// Current owner is the second push data
Ripemd160 currentOwnerAddress;
public function unlock(
SigHashPreimage txPreimage,
bytes outputSats,
bytes newOwnerAddress,
bool isMelt,
Sig senderSig,
PubKey unlockKey
) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
// Initial assetId is 36-bytes nulls(0x00 bytes)
bytes actAssetId = (this.assetId == num2bin(0, 36) ?
txPreimage[ 68 : 104 ] : this.assetId);
bytes lockingScript = SigHash.scriptCode(txPreimage);
// The default usage is to update/transfer
if (!isMelt) {
require(
hash256(
outputSats +
// Define length of output
b'fd' + num2bin(len(lockingScript, 2)) +
// OP_PUSHINPUTREF <assetId>
b'd0' + actAssetId +
// New owner (20 bytes)
b'14' + newOwnerAddress +
// OP_DISALLOWPUSHINPUTREFSIBLING <assetId>
b'd3' + actAssetId +
// Get entire locking script after the push vars
// 95 = 1+36 + 1+20 + 1+36
lockingScript[95 : ]
)
==
// Compare to HashOuts
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]
);
} else {
// Melt the NFT back and destroy the reference
// Use OP_DISALLOWPUSHINPUTREF and
// OP_DISALLOWPUSHINPUTREFSIBLING
// to prohibit the reference from being passed along
require(
hash256(
// Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
// 'd2' is OP_DISALLOWPUSHINPUTREF
// 'd3' is OP_DISALLOWPUSHINPUTREFSIBLING
b'00000000000000004b6ad2' + activeAssetId +
b'd3' + activeAssetId
)
==
// Compare to HashOuts
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]
);
}
require(Tx.checkPreimageOpt_(txPreimage));
}
}
contract Account {
bytes assetId;
Ripemd160 currentOwnerAddress;
bytes disallowAssetIdNotUsed;
static function createSingletonOutput(
SigHashPreimage txPreimage,
int amount,
bytes assetId,
bytes address
): bool {
bytes activeAssetId = (assetId == num2bin(0, 36) ?
txPreimage[ 68 : 104 ] : assetId);
bytes lockingScript = SigHash.scriptCode(txPreimage);
require(amount > 0);
require(
hash256(
// Add the deposit amount to the existing balance
num2bin(SigHash.value(txPreimage) + amount, 8) +
b'fd' + num2bin(len(lockingScript), 2) +
// OP_PUSHINPUTREF <assetId>
b'd0' + activeAssetId +
// Address/owner (20 bytes)
b'14' + address +
// OP_DISALLOWPUSHINPUTREFSIBLING <assetId>
b'd3' + assetId +
// Get entire locking script after the push vars
// 95 = 1+36 + 1+20 + 1+36
lockingScript[95 : ]
)
==
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] // HashOuts
);
require(Tx.checkPreimageOpt_(txPreimage));
return true;
}
// Deposit to account
// Anyone can spend this input and deposit funds into
// the account, but only the owner can withdraw funds.
public function deposit(
SigHashPreimage txPreimage,
int amount
) {
require(amount > 0);
require(
Account.createSingletonOutput(
txPreimage,
SigHash.value(txPreimage) + amount,
this.assetId,
this.currentOwnerAddress
)
);
}
// Withdraw from account
// The current owner can withdraw from the account
// via any other outputs.
public function withdraw(
SigHashPreimage txPreimage,
int amount,
Sig senderSig,
PubKey unlockKey
) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
require(
Account.createSingletonOutput(
txPreimage,
SigHash.value(txPreimage) - amount,
this.assetId,
this.currentOwnerAddress
)
);
}
// Change the account owner
// The current owner can assign the account to another
// address owner
public function changeOwner(
SigHashPreimage txPreimage,
bytes newOwnerAddress,
Sig senderSig,
PubKey unlockKey
) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
require(
Account.createSingletonOutput(
txPreimage,
SigHash.value(txPreimage),
this.assetId,
newOwnerAddress
)
);
}
// Close the account
// The current owner of the account can permanently close
// the account and withdraw any tokens via other outputs
public function close(
SigHashPreimage txPreimage,
Sig senderSig,
PubKey unlockKey
) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
bytes activeAssetId = (this.assetId == num2bin(0, 36) ?
txPreimage[ 68 : 104 ] : this.assetId);
bytes lockingScript = SigHash.scriptCode(txPreimage);
// Ensure one of the outputs is unspendable OP_RETURN
// and uses the OP codes to prohibit passing on the
// reference.
// OP_DISALLOWPUSHINPUTREF and
// OP_DISALLOWPUSHINPUTREFSIBLING which effectively
// means no output may contain the reference anymore,
// thereby ending the ability to carry on the assetId
// anywhere else forever.
require(
hash256(
b'00000000000000004b6ad2' + activeAssetId +
// Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
b'd3' + activeAssetId
)
==
// HashOuts
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]
);
require(Tx.checkPreimageOpt_(txPreimage));
}
}contract SuperAssetR201 {
// Do NOT provide a constructor as that will add unnecessary OP_0 OP_0 to the beginning of the contract
bytes assetId; // Asset identifier
Ripemd160 currentOwnerAddress; // Current owner is the second push data
// Notice that "disallowAssetIdNotUsed" is not used below. The reason is that we save space and also it should always be same as assetId
bytes disallowAssetIdNotUsed; // Disallow Asset from being used in any other output
static const int MAX_RECEIVE = 6;
static function buildOutputVector(
int amount,
bytes assetId,
bytes address,
bytes outputScriptLen,
bytes lockingScriptCodePart
): bytes {
return
num2bin(amount, 8) +
hash256(
outputScriptLen +
// OP_PUSHINPUTREF <assetId>
b'd0' + assetId +
// Address/owner (20 bytes)
b'14' + address +
lockingScriptCodePart
) +
// One color for the output
b'01000000' +
hash256(assetId);
}
public function mint(SigHashPreimage txPreimage, int amount) {
require(amount > 0);
require(this.assetId == num2bin(0, 36));
bytes lockingScript = SigHash.scriptCode(txPreimage);
require(
hash256(
num2bin(amount, 8) +
b'fd' + num2bin(len(lockingScript), 2) +
// OP_PUSHINPUTREF <assetId>
b'd0' + txPreimage[68 : 104]+
// Address/owner (20 bytes)
b'14' + this.currentOwnerAddress +
// Get entire locking script after the push vars
// 95 = 1+36 + 1+20
lockingScript[58 : ]
)
==
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] // HashOuts
);
require(Tx.checkPreimageOpt_(txPreimage));
}
public function transfer(SigHashPreimage txPreimage, Ripemd160[6] recipients, int[6] amounts, bytes otherOutputs, Sig senderSig, PubKey unlockKey) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
int expectedRefColorSum = 1337; // Placeholder for OP_INPUTREFVALUESUM
int actualAccumulatedRefColorSum = 0; // Used for counting the sum of the colors
bool break = false;
bytes expectedOutputVector = b'';
bytes lockingScript = SigHash.scriptCode(txPreimage);
// Length of the output script
bytes outputScriptLen = b'fd' + num2bin(len(lockingScript), 2);
bytes lockingScriptCodePart = lockingScript[58 : ];
loop (MAX_RECEIVE) : i {
if (!break) {
if (amounts[i] <= 0) {
break = true;
} else {
// There is a valid recipient...
// Get entire locking script after the push vars
// 58 = 1+36 + 1+20
expectedOutputVector += SuperAssetR201.buildOutputVector(amounts[i], this.assetId, recipients[i], outputScriptLen, lockingScriptCodePart);
actualAccumulatedRefColorSum += amounts[i];
}
}
}
require(expectedRefColorSum > 0 && expectedRefColorSum == actualAccumulatedRefColorSum);
require(
hash256(expectedOutputVector + otherOutputs)
==
// hashOutputsHashes
txPreimage[len(txPreimage) - 72 : len(txPreimage) - 40]
);
}
public function melt(SigHashPreimage txPreimage, Sig senderSig, PubKey unlockKey) {
require(hash160(unlockKey) == this.currentOwnerAddress);
require(checkSig(senderSig, unlockKey));
// Ensure one of the outputs is unspendable OP_RETURN and uses the OP codes to prohibit passing on the reference
// OP_DISALLOWPUSHINPUTREF and OP_DISALLOWPUSHINPUTREFSIBLING which effectively means no output may contain
// the reference anymore, thereby ending the ability to carry on the assetId anywhere else forever.
require(
hash256(
// Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
b'00000000000000004b6ad2' + this.assetId + b'd3' + this.assetId
)
==
txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]
);
require(Tx.checkPreimageOpt_(txPreimage));
}
}
// Contract functions
// function 1...
// function 2...
OP_PUSHINPUTREFSINGLETON <ref1> OP_DROP
<ref2> OP_REFTYPE_UTXO OP_2 OP_NUMEQUALOP_PUSHINPUTREFSINGLETON <ref2> OP_DROP
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG<extensionTxId> OP_SWAP OP_CAT OP_REFTYPE_UTXO OP_0 OP_NUMNOTEQUAL

























minuteshoursdaysweeksint abs(int a)int min(int a, int b)int max(int a, int b)bool within(int x, int lower, int upper)bytes20 ripemd160(any x)bytes20 sha1(any x)bytes32 sha256(any x)bytes20 hash160(any x)bytes32 hash256(any x)bool checksig(sig s, pubkey pk)bool checkMultiSig(sig[] sigs, pubkey[] pks)bool checkDataSig(datasig s, bytes msg, pubkey pk)
This associates `.cash` files with Solidity, and enables syntax highlighting for your CashScript files.
## Vim
The most popular Solidity plugin for Vim is [vim-solidity](https://github.com/TovarishFin/vim-solidity). Install this plugin and add the following snippet to your `.vimrc`:
```bash title=".vimrc"
au BufRead,BufNewFile *.cash setfiletype solidityrequire(1 sats == 1);
require(1 finney == 10);
require(1 bit == 100);
require(1 bitcoin == 1e8);
require(1 seconds == 1);
require(1 minutes == 60 seconds);
require(1 hours == 60 minutes);
require(1 days == 24 hours);
require(1 weeks == 7 days);require(tx.time >= <expression>);require(tx.age >= <expression>);int this.activeInputIndexbytes this.activeBytecodeint tx.versionint tx.locktimeint tx.inputs.lengthint tx.inputs[i].valuebytes tx.inputs[i].lockingBytecodebytes tx.inputs[i].unlockingBytecodebytes32 tx.inputs[i].outpointTransactionHashint tx.inputs[i].outpointIndexint tx.inputs[i].sequenceNumberint tx.outputs.lengthint tx.outputs[i].valuebytes tx.outputs[i].lockingBytecodebytes25 lockingBytecode = new LockingBytecodeP2PKH(pkh);
int value = 10000;
require(tx.outputs[0].lockingBytecode == lockingBytecode);
require(tx.outputs[0].value == value);new LockingBytecodeP2PKH(bytes20 pkh): bytes25new LockingBytecodeP2SH(bytes20 scriptHash): bytes23new LockingBytecodeNullData(bytes[] chunks): bytesfunction timeout(sig senderSig) {
require(checkSig(senderSig, sender));
require(tx.time >= timeout);
}
Now to put this smart contract in use in a JavaScript application we have to use the CashScript SDK in combination with a BCH library such as [BCHJS][bchjs], [bitcore-lib-cash][bitcore] or [Libauth][libauth]. These libraries are used to generate public/private keys for the contract participants. Then these keys can be used in the CashScript SDK. The key generation code is left out of this example, since this works differently for every library.
```ts title="TransferWithTimeout.js"
import { Contract, SignatureTemplate } from 'cashscript';
import { alicePriv, alicePub, bobPriv, bobPub } from './somewhere';
async function run() {
// Import the compiled TransferWithTimeout JSON artifact
const artifact = require('./transfer_with_timeout.json');
// Instantiate a new contract using the artifact and constructor arguments:
// { sender: alicePub, recipient: bobPub, timeout: 1000000 }
// No network provider is provided, so the default ElectrumNetworkProvider is used
const contract = new Contract(artifact, [alicePub, bobPub, 1000000]);
// Display contract address and balance
console.log('contract address:', contract.address);
console.log('contract balance:', await contract.getBalance());
// Call the transfer function with Bob's signature
// i.e. Bob claims the money that Alice has sent him
const txDetails = await contract.functions
.transfer(new SignatureTemplate(bobPriv))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
.send();
console.log(txDetails);
// Call the timeout function with Alice's signature
// i.e. Alice recovers the money that Bob has not claimed
// But because the timeout has not passed yet, the function fails and
// we call the meep function so the transaction can be debugged instead
const meepStr = await contract.functions
.timeout(new SignatureTemplate(alicePriv))
.to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 10000)
.meep();
console.log(meepStr);
} // Check that the first tx output matches the announcement
require(tx.outputs[0].value == 0);
require(tx.outputs[0].lockingBytecode == announcement);
// Calculate leftover money after fee (1000 sats)
// Check that the second tx output sends the change back if there's
// enough leftover for another announcement
int minerFee = 1000;
int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee;
if (changeAmount >= minerFee) {
bytes changeLock = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[1].lockingBytecode == changeLock);
require(tx.outputs[1].value == changeAmount);
}
}
The CashScript code above ensures that the smart contract **can only** be used in the way specified in the code. But the transaction needs to be created by the SDK, and to ensure that it complies with the rules of the smart contract, we need to use some of the more advanced options of the SDK.
```ts title="Announcement.js"
import { ElectrumNetworkProvider, Contract, SignatureTemplate } from 'cashscript';
import { alicePriv, alicePub } from './somewhere';
export async function run(){
// Import the compiled announcement JSON artifact
const artifact = require('./announcement.json');
// Initialise a network provider for network operations on MAINNET
const provider = new ElectrumNetworkProvider('mainnet');
// Instantiate a new contract using the compiled artifact and network provider
// AND providing the constructor parameters (none)
const contract = new Contract(artifact, [], provider);
// Display contract address, balance, opcount, and bytesize
console.log('contract address:', contract.address);
console.log('contract balance:', await contract.getBalance());
console.log('contract opcount:', contract.opcount);
console.log('contract bytesize:', contract.bytesize);
// Create the announcement string. Any other announcement will fail because
// it does not comply with the smart contract.
const str = 'A contract may not injure a human being or, '
+ 'through inaction, allow a human being to come to harm.';
// Send the announcement transaction
const txDetails = await contract.functions
.announce(alicePub, new SignatureTemplate(alicePriv))
// Add the announcement string as an OP_RETURN output
.withOpReturn(['0x6d02', str])
// Hardcodes the transaction fee (like the contract expects)
.withHardcodedFee(1000)
// Only add a "change" output if the remainder is higher than 1000
.withMinChange(1000)
.send();
console.log(txDetails);
}int timestamp = date("2021-02-17T01:30:00");
require(timestamp == 1613554200);checkMultisig([sig1, sig2], [pk1, pk2, pk3]);string question = "What is Bitcoin Cash?";
string answer = question.split(15)[0].split(8)[1];string bitcoin, string cash = "BitcoinCash".split(7);
require(bitcoin == cash);pubkey pk = pubkey(0x0000);
bytes editedPk = bytes(pk) + 0x1234;
bytes4 integer = bytes4(25);Sig alias for SignatureTemplate that was deprecated in v0.4.1.--size|-s flag that displays the size in bytes of the compiled bytecode.tx.hashOutputstx.locktimetx.hashtypehashtype parameter in signature placeholders optional.OP_VERIFY















npm install -g cashcUsage: cashc [options] [source_file]
Options:
-V, --version Output the version number.
-o, --output <path> Specify a file to output the generated artifact.
-h, --hex Compile the contract to hex format rather than a full artifact.
-A, --asm Compile the contract to ASM format rather than a full artifact.
-c, --opcount Display the number of opcodes in the compiled bytecode.
-s, --size Display the size in bytes of the compiled bytecode.
-?, --help Display helppragma cashscript ^0.7.0;
pragma cashscript >= 0.4.0 < 0.5.4;pragma cashscript ^0.7.0;
contract HTLC(pubkey sender, pubkey recipient, int expiration, bytes32 hash) {
...
}pragma cashscript ^0.7.0;
contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
function transfer(sig recipientSig) {
...
}
function timeout(sig senderSig) {
...
}
}pragma cashscript ^0.7.0;
contract P2PKH(bytes20 pkh) {
function spend(pubkey pk, sig s) {
require(hash160(pk) == pkh);
require(checkSig(s, pk));
}
}int myNumber = 3000;
string constant myString = 'Bitcoin Cash';i = i + 1;
hashedValue = sha256(hashedValue);
myString = 'Cash';pragma cashscript ^0.7.0;
contract OneOfTwo(bytes20 pkh1, bytes32 hash1, bytes20 pkh2, bytes32 hash2) {
function spend(pubkey pk, sig s, bytes message) {
require(checkSig(s, pk));
bytes20 pkh = hash160(pk);
if (pkh == pkh1) {
require(sha256(message) == hash1);
} else if (pkh == pkh2) {
require(sha256(message) == hash2);
} else {
require(false); // fail
}
}
}// This is a single-line comment.
/*
This is a
multi-line comment.
*/
async function deployContract(contract, amount) {
const address = privateKey.toAddress()
const tx = new bsv.Transaction()
tx.from(await fetchUtxos(address)) // 添加用来支付矿工费用和锁进合约的比特币的UTXO。钱包通常会在这里做 UTXO 的筛选,以防止添加过多 UTXO
.addOutput(new bsv.Transaction.Output({
script: contract.lockingScript, // 将合约部署到第0个输出的锁定脚本
satoshis: amount,
}))
.change(address) // 添加找零输出
.sign(privateKey) // 私钥签名, 只对P2PKH输入有效
await sendTx(tx) // 广播交易
return tx
}
const demo = new Demo(4, 7);
const deployTx = await deployContract(demo, 1000); // 部署合约
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(deployTx))
.change(privateKey.toAddress())
.setInputScript(0, (_) => { //设置第0个输入的解锁脚本
return demo.add(11).toScript();
})
.feePerKb(250) // 设置交易费率,可选,默认是每KB 500 satoshis
.seal() // 封印交易, 同时自动计算出正确的交易费用和找零余额
// 广播交易
await sendTx(unlockingTx)
const counter = new StateCounter(0)
let amount = 8000
const lockingTx = await deployContract(counter, amount) // 部署合约
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(prevTx))
.setOutput(0, (tx) => {
const newLockingScript = counter.getNewStateScript({
counter: i + 1
})
const newAmount = amount - tx.getEstimateFee(); // 计算合约的新余额,需要减去交易的费用
return new bsv.Transaction.Output({
script: newLockingScript,
satoshis: newAmount,
})
})
.setInputScript(0, (tx, output) => { // output 包含输入/UTXO对应的锁定脚本和 satoshis 余额
const preimage = getPreimage(tx, output.script, output.satoshis)
const newAmount = amount - tx.getEstimateFee(); //计算合约的新余额,需要减去交易的费用
return counter.unlock(new SigHashPreimage(toHex(preimage)), newAmount).toScript()
})
.seal() // 封印交易
// 广播交易
await sendTx(unlockingTx)
const unlockingTx = new bsv.Transaction();
const newAmount = amount + spendAmount; // 合约新余额与交易费用无关
unlockingTx.addInput(createInputFromPrevTx(prevTx))
.from(await fetchUtxos(privateKey.toAddress())) // 添加用于支付交易费用的输入
.addOutput(new bsv.Transaction.Output({ // 添加一个或多个的输出, newAmount 是合约新的余额
script: newLockingScript,
satoshis: newAmount,
}))
.change(privateKey.toAddress()) //添加找零输出
.setInputScript(0, (tx, output) => {
let preimage = getPreimage(
tx,
output.script,
output.satoshis,
0,
sighashType
);
return advTokenSale.buy(
new SigHashPreimage(toHex(preimage)),
new Ripemd160(toHex(pkh)), // 找零地址
tx.getChangeAmount(), // 计算找零余额
new Bytes(toHex(publicKeys[i])),
numBought
).toScript();
})
.sign(privateKey) // 签名所有添加用于支付交易费用的输入
.seal() //封印交易, 同时自动计算出正确的交易费用和找零余额
// 广播交易
lockingTxid = await sendTx(unlockingTx)
$ npm install scryptlib












async function deployContract(contract, amount) {
const address = privateKey.toAddress()
const tx = new bsv.Transaction()
tx.from(await fetchUtxos(address)) // Add UTXOs/bitcoins that are locked into the contract and pay for miner fees. In practice, wallets only add enough UTXOs, not all UTXOs as done here for ease of exposition.
.addOutput(new bsv.Transaction.Output({
script: contract.lockingScript, // Deploy the contract to the 0-th output
satoshis: amount,
}))
.change(address) // Add change output
.sign(privateKey) // Sign inputs. Only apply to P2PKH inputs.
await sendTx(tx) // Broadcast transaction
return tx
}
const demo = new Demo(4, 7);
const deployTx = await deployContract(demo, 1000); // Deploy a contract
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(deployTx))
.change(privateKey.toAddress())
.setInputScript(0, (_) => { // Set the unlocking script for 0-th input
return demo.add(11).toScript();
})
.feePerKb(250) // Set transaction fee rate. Optional, default is 500 satoshis per KB
.seal() // Finalize the transaction and automatically calculate the correct transaction fee and change amount
// Broadcast transaction
await sendTx(unlockingTx)
const counter = new StateCounter(0)
let amount = 8000
const lockingTx = await deployContract(counter, amount) // Deploy a contract
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(prevTx))
.setOutput(0, (tx) => {
const newLockingScript = counter.getNewStateScript({
counter: i + 1
})
const newAmount = amount - tx.getEstimateFee(); // To calculate the new balance of the contract, you need to subtract the transaction fee
return new bsv.Transaction.Output({
script: newLockingScript,
satoshis: newAmount,
})
})
.setInputScript(0, (tx, output) => { // output contains the locking script and satoshis balance of the UTXO
const preimage = getPreimage(tx, output.script, output.satoshis)
const newAmount = amount - tx.getEstimateFee(); // To calculate the new balance of the contract, you need to subtract the transaction fee
return counter.unlock(new SigHashPreimage(toHex(preimage)), newAmount).toScript()
})
.seal() // Finalize the transaction
// Broadcast transaction
await sendTx(unlockingTx)
...
const unlockingTx = new bsv.Transaction();
const newAmount = amount + spendAmount; // The new balance of contract has nothing to do with transaction fee
unlockingTx.addInput(createInputFromPrevTx(lockingTx))
.from(await fetchUtxos(privateKey.toAddress())) // Add inputs to pay transaction fees
.addOutput(new bsv.Transaction.Output({ // Add one or more outputs, newAmount is the new balance of the contract
script: newLockingScript,
satoshis: newAmount,
}))
.change(privateKey.toAddress()) // Add change output
.setInputScript(0, (tx, output) => {
let preimage = getPreimage(
tx,
output.script,
output.satoshis,
0,
sighashType
);
return advTokenSale.buy(
new SigHashPreimage(toHex(preimage)),
new Ripemd160(toHex(pkh)), // Change address
tx.getChangeAmount(), // Calculate the change balance
new Bytes(toHex(publicKeys[i])),
numBought
).toScript();
})
.sign(privateKey) // Sign all inputs added to pay transaction fee
.seal() // Finalize the transaction
// Broadcast transaction
lockingTxid = await sendTx(unlockingTx)
sudo docker run --name radiantnode -itd radiantcommunity/radiant-nodesudo docker run --name radiantelectrum -itd radiantcommunity/electrumx_radiant_nodesudo docker pssudo docker ps -asudo docker exec -it radiantnode shradiant-cli -getinfo sudo docker run --name radiantnode2 -itd radiantcommunity/radiant-nodesudo docker pssudo docker stop radiantnodesudo docker stop radiantnode2sudo docker stop radiantelectrumHashedMap<int, int> map = new HashedMap<int, int>(b'')HashedMap<int, int> map = new HashedMap(b'')auto map = new HashedMap<int, int>(b'')contract StateMapTest {
@state
bytes _mpData; //Save the serialized data of the map
// Add key-value pairs to the map
public function insert(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}
// update key-value pairs in the map
public function update(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}
// delete key-value pairs in the map
public function delete(int key, int keyIndex, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.delete(key, keyIndex));
// Serialize map, update state
require(this.passMap(map.data(), preimage));
}
// update state _mpData, and build a output contains new state
function passMap(bytes newData, SigHashPreimage preimage) : bool {
this._mpData = newData;
bytes outputScript = this.getStateScript();
bytes output = Util.buildOutput(outputScript, Util.value(preimage));
return (hash256(output) == Util.hashOutputs(preimage));
}
}public function insert(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}let map = new Map<number, number>();
map.set(key, val); // First add the key-value pair to the map outside the chain
const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.insert(new MapEntry({
key: key,
val: val,
keyIndex: findKeyIndex(map, key) // Get the `keyIndex` parameter
}), preimage).verify()
expect(result.success, result.error).to.be.true;
mapTest._mpData = toData(map)const mapEntrys = Array.from(sortmap(map), ([key, val]) => ({ key, val }))
.map((entry, index) => {
...entry,
keyIndex: index
})public function delete(int key, int keyIndex, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.delete(key, keyIndex));
require(this.passMap(map.data(), preimage));
}const keyIndex = findKeyIndex(map, key); // Before deleting from the off-chain map, first calculate the keyIndex and save it
map.delete(key);
const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.delete(key, keyIndex, preimage).verify() // To call the delete method of the contract, you need to provide the key and keyIndex
expect(result.success, result.error).to.be.true;
mapTest._mpData = toData(map)require(map.canGet(key, val, keyIndex));require(map.has(key, keyIndex));




electrumx.radiant.ovh 50012
electrumx.radiantexplorer.com 50012
electrumx2.radiantexplorer.com 50012
electrumx.radiantblockchain.org 50012
electrumx.radiant4people.com 50012 import { compile } from 'scryptlib';
...
compile(
{
path: contractFilePath // the file path of the contract
},
{
desc: true // set this flag to be `true` to get the description file output
asm: true // set this flag to be `true` to get the asm file output
optimize: false //set this flag to be `true` to get optimized asm opcode
sourceMap: true //set this flag to be `true` to get source map
hex: true //set this flag to be `true` to get hex format script
stdout: false//set this flag to be `true` to make that the compiler will output the compilation result through stdout
}
); import { compileAsync } from 'scryptlib';
...
compileAsync(
{
path: contractFilePath // the file path of the contract
},
settings
) : Promise<CompileResult>; # install compiler binary
npx scryptlib download
# compiling contract
npx scryptlib your_directory/your_scrypt.scrypt
[[1, 3, 1]] // represent `int[1][3]` in **sCrypt** language
[new Bytes("00"), new Bytes("00"), new Bytes("00")] // represent `bytes[3]` in **sCrypt** language
struct Person {
bytes addr;
bool isMale;
int age;
}
struct Block {
bytes hash;
bytes header;
int time;
}
type Male = Person;
type Female = Person;
contract Main {
Person person;
int x;
...
}
const PersonContract = buildContractClass(loadDescription('person_desc.json'));
/*Person is structure and Male, Female are type aliases */
const { Person, Male, Female } = buildTypeClasses(PersonContract);
let man = new Person({
isMale: true,
age: 14,
addr: new Bytes("68656c6c6f20776f726c6421")
});
man.age = 20;
let woman = new Female({
isMale: false,
age: 18,
addr: new Bytes("68656c6c6f20776f726c6421")
});
woman.addr = new Bytes("")
library L {
private int x;
constructor(int a, int b) {
this.x = a + b;
}
function f() : int {
return this.x;
}
}
contract Test {
public int x;
L l;
public function unlock(int x) {
require(this.l.f() == x + this.x);
}
}
const Test = buildContractClass(loadDescription('test_desc.json'));
const { L } = buildTypeClasses(Test);
let test = new Test(1, new L(1, 2));
const MyContract = buildContractClass(JSON.parse(descFileContent));const instance = new MyContract(1234, true, ...parameters);const lockingScript = instance.lockingScript;
// To convert it to ASM/hex format
const lockingScriptASM = lockingScript.toASM();
const lockingScriptHex = lockingScript.toHex();const funcCall = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters);
const unlockingScript = funcCall.toScript();
// To convert it to ASM/hex format
const unlockingScriptASM = unlockingScript.toASM();
const unlockingScriptHex = unlockingScript.toHex();{
tx?: any; // current transaction represented in bsv.Transaction object
inputIndex?: number; // input index, default value: 0
inputSatoshis?: number; // input amount in satoshis
}{
success: boolean; // script evaluates to true or false
error: string; // error message, empty if success
}const context = { tx, inputIndex, inputSatoshis };
// 1) set context per verify()
const funcCall = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters);
const result = funcCall.verify(context);
// 2) alternatively, context can be set at instance level and all following verify() will use it
instance.txContext = context;
const result = funcCall.verify();
expect(result.success, result.error).to.be.true;
assert.isFalse(result.success, result.error);contract Counter {
@state
int counter;
constructor(int counter) {
this.counter = counter;
}
}const instance = new Counter(0);
let state = instance.counter;
// update state
instance.counter++;const tx = newTx(inputSatoshis);
let newLockingScript = instance.getNewStateScript({
counter: 1
});
tx.addOutput(new bsv.Transaction.Output({
script: newLockingScript,
satoshis: outputAmount
}))
preimage = getPreimage(tx, instance.lockingScript, inputSatoshis)
instance.counter++;
instance.person.name = new Bytes('0001');
const asmVars = {
'contract1.function1.variable1': 'ff41',
'contract2.function2.variable2': 'OP_4'
};
instance.replaceAsmVars(asmVars);const axios = require('axios');
const Counter = buildContractClass(loadDesc("counter_debug_desc.json"));
let response = await axios.get("https://api.whatsonchain.com/v1/bsv/test/tx/7b9bc5c67c91a3caa4b3212d3a631a4b61e5c660f0369615e6e3a969f6bef4de/hex")
// constructor from raw Transaction.
let counter = Counter.fromTransaction(response.data, 0/** output index**/);
// constructor from Utxo lockingScript
let counterClone = Counter.fromHex(counter.lockingScript.toHex());
// polyfill
import 'react-app-polyfill/ie11';
import 'core-js/features/number';
import 'core-js/features/string';
import 'core-js/features/array';
let demo = new Demo("11111111111111111111111111111111111", 1);
let result = demo.add(new Int("11111111111111111111111111111111112")).verify();
console.assert(result.success, result.error)sudo apt-get install build-essential cmake git libboost-chrono-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libevent-dev libminiupnpc-dev libssl-dev libzmq3-dev help2man ninja-build python3 libdb-dev libdb++-devgit clone https://github.com/radiantblockchain/radiant-node.gitcd radiant-node/
mkdir build
cd buildcmake -GNinja .. -DBUILD_RADIANT_QT=OFFcmake -GNinja .. -DBUILD_RADIANT_WALLET=OFF -DBUILD_RADIANT_QT=OFFninjasudo cp src/radiant* /usr/local/bin/mkdir ~/.radiant
touch ~/.radiant/radiant.confcat > ~/.radiant/radiant.conf << EOL
rpcuser=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)
rpcpassword=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
listen=1
server=1
daemon=1
maxconnections=16
EOLradiantd -testnet -daemonradiant-cli -testnet -getinforadiant-cli -testnet addnode xxx.xxx.xxx.xxx addnpm install
npm test.
├── contracts # sCrypt contract files
│ ├── accumulatorMultiSig.scrypt #
│ ├── ackermann.scrypt #
│ ├── acs.scrypt # A contract which can be spent by anyone but only to a specific address
│ ├── advancedCounter.scrypt # Use external UTXOs to pay contract tx fees using sighash ANYONECANPAY
│ ├── advancedTokenSale.scrypt # Sambe as above, but for token sale contract
│ ├── asm.scrypt # Embed Script directly into sCrypt
│ ├── auction.scrypt #
│ ├── binaryOption.scrypt #
│ ├── cltv.scrypt #
│ ├── cointoss.scrypt #
│ ├── cointossxor.scrypt #
│ ├── conwaygol.scrypt #
│ ├── counter.scrypt # Count the number of times a function has been called to showcase
│ ├── ec.scrypt #
│ ├── ecdsa.scrypt #
│ ├── demo.scrypt # "hello world" contract
│ ├── faucet.scrypt #
│ ├── forward.scrypt #
│ ├── hashpuzzlep2pkh.scrypt # combining and p2pkh contracts
│ ├── kaggle.scrypt #
│ ├── lottery.scrypt #
│ ├── mast.scrypt #
│ ├── merkleToken.scrypt # Token based on Merkle Tree
│ ├── merkleTree.scrypt # validation and updating
│ ├── nonFungibleToken.scrypt #
│ ├── oracle.scrypt #
│ ├── p2pkh.scrypt # contract written in sCrypt
│ ├── p2sh.scrypt #
│ ├── perceptron.scrypt #
│ ├── perceptron2.scrypt #
│ ├── rabin.scrypt # to import off-chain data into a contract via oracle
│ ├── recurring.scrypt #
│ ├── rpuzzle.scrypt #
│ ├── schnorr.scrypt #
│ ├── rps.scrypt # Rock Paper Scissors
│ ├── simpleBVM.scrypt #
│ ├── spvToken.scrypt #
│ ├── statecounter.scrypt #
│ ├── stateStruct.scrypt # Recommended way to implement a stateful contract using struct
│ ├── sudoku.scrypt #
│ ├── svd.scrypt #
│ ├── tictactoe.scrypt # onchain p2p gaming
│ ├── timedcommit.scrypt # : Trustless contracting by combining on-chain and off-chain transactions
│ ├── token.scrypt # by storing token map as contract state in a single UTXO
│ ├── tokenSale.scrypt # Selling tokens for bitcoins using
│ ├── tokenSwap.scrypt # Merkle tree-based token and bitcoin swap
│ ├── tokenUtxo.scrypt #
│ ├── treeSig.scrypt #
│ └── util.scrypt # utility functions and constants
├── testnet # examples to deploy contract and call its function on testnet
└── fixture
└── autoGen # contract description json files
└── tests # contract test files
├── js # Javascript unit tests
└── ts # Typescript unit testsnpm install scryptlibimport { buildContractClass } from 'scryptlib';// build a contract class
// either by compiling the contract from scratch
const Demo = buildContractClass(compileContract('demo.scrypt'))
// or from contract desc file if it's already generated from compilation
const Demo = buildContractClass(loadDesc('demo_desc.json'))demo = new Demo(4, 7);const result = demo.add(7 + 4).verify()
expect(result.success, result.error).to.be.trueconst key = '$YOUR_PRIVATE_KEY_HERE'node testnet/demo.jslocking txid: 8d58ff9067f5fa893b5c695179559e108ebf850d0ce4fd1e42bc872417ffd424
unlocking txid: c60b57e93551a6c52282801130649c6a97edcca5d2b28b8b4ae2afe0ee59bf79
Succeeded on testnetconst API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/main'
// const API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/test' for Testnet
// const API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/stn' for Scaling Test Net







































































interface Artifact {
contractName: string // Contract name
constructorInputs: AbiInput[] // Arguments required to instantiate a contract
abi: AbiFunction[] // functions that can be called
bytecode: string // Compiled Script without constructor parameters added (in ASM format)
source: string // Source code of the CashScript contract
compiler: {
name: string // Compiler used to compile this contract
version: string // Compiler version used to compile this contract
}
updatedAt: string // Last datetime this artifact was updated (in ISO format)
}
interface AbiInput {
name: string // Input name
type: string // Input type (see language documentation)
}
interface AbiFunction {
name: string // Function name
inputs: AbiInput[] // Function inputs / parameters
}contract Escrow(bytes20 arbiter, bytes20 buyer, bytes20 seller) {
function spend(pubkey pk, sig s) {
require(hash160(pk) == arbiter);
require(checkSig(s, pk));
// Check that the correct amount is sent
int minerFee = 1000; // hardcoded fee
int amount = tx.inputs[this.activeInputIndex].value - minerFee;
require(tx.outputs[0].value == amount);
// Check that the transaction sends to either the buyer or the seller
bytes25 buyerLock = new LockingBytecodeP2PKH(buyer);
bytes25 sellerLock = new LockingBytecodeP2PKH(seller);
bool sendsToBuyer = tx.outputs[0].lockingBytecode == buyerLock;
bool sendsToSeller = tx.outputs[0].lockingBytecode == sellerLock;
require(sendsToBuyer || sendsToSeller);
}
}contract LastWill(bytes20 inheritor, bytes20 cold, bytes20 hot) {
function inherit(pubkey pk, sig s) {
require(tx.age >= 180 days);
require(hash160(pk) == inheritor);
require(checkSig(s, pk));
}
function cold(pubkey pk, sig s) {
require(hash160(pk) == cold);
require(checkSig(s, pk));
}
function refresh(pubkey pk, sig s) {
require(hash160(pk) == hot);
require(checkSig(s, pk));
// Check that the correct amount is sent
int minerFee = 1000; // hardcoded fee
int amount = tx.inputs[this.activeInputIndex].value - minerFee;
require(tx.outputs[0].value == amount);
// Check that the funds are sent back to the contract
bytes23 contractLock = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[0].lockingBytecode == contractLock);
}
}contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
function receive() {
require(tx.age >= period);
// Check that the first output sends to the recipient
bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);
// Calculate the value that's left
int minerFee = 1000;
int currentValue = tx.inputs[this.activeInputIndex].value;
int changeValue = currentValue - pledge - minerFee;
// If there is not enough left for *another* pledge after this one,
// we send the remainder to the recipient. Otherwise we send the
// pledge to the recipient and the change back to the contract
if (changeValue <= pledge + minerFees) {
require(tx.outputs[0].value == currentValue - minerFee);
} else {
require(tx.outputs[0].value == pledge);
bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[1].lockingBytecode == changeBytecode);
require(tx.outputs[1].value == changeValue);
}
}
function reclaim(pubkey pk, sig s) {
require(hash160(pk) == funder);
require(checkSig(s, pk));
}
}contract Mecenas(
bytes20 recipient,
bytes20 funder,
int pledgePerBlock,
bytes8 initialBlock,
) {
function receive() {
// Check that the first output sends to the recipient
bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);
// Check that time has passed and that time locks are enabled
int initial = int(initialBlock);
require(tx.time >= initial);
// Calculate the amount that has accrued since last claim
int passedBlocks = tx.locktime - initial;
int pledge = passedBlocks * pledgePerBlock;
// Calculate the leftover amount
int minerFee = 1000;
int currentValue = tx.inputs[this.activeInputIndex].value;
int changeValue = currentValue - pledge - minerFee;
// If there is not enough left for *another* pledge after this one,
// we send the remainder to the recipient. Otherwise we send the
// remainder to the recipient and the change back to the contract
if (changeValue <= pledge + minerFee) {
require(tx.outputs[0].value == currentValue - minerFee);
} else {
// Check that the outputs send the correct amounts
require(tx.outputs[0].value == pledge);
require(tx.outputs[1].value == changeValue);
// Cut out old initialBlock (OP_PUSHBYTES_8 <initialBlock>)
// Insert new initialBlock (OP_PUSHBYTES_8 <tx.locktime>)
// Note that constructor parameters are added in reverse order,
// so initialBlock is the first statement in the contract bytecode.
bytes newContract = 0x08 + bytes8(tx.locktime) + this.activeBytecode.split(9)[1];
// Create the locking bytecode for the new contract and check that
// the change output sends to that contract
bytes23 newContractLock = new LockingBytecodeP2SH(hash160(newContract));
require(tx.outputs[1].lockingBytecode == newContractLock);
}
}
function reclaim(pubkey pk, sig s) {
require(hash160(pk) == funder);
require(checkSig(s, pk));
}
}pragma cashscript ^0.7.0;
// This contract enforces making an announcement on Memo.cash and sending the
// remaining balance back to the contract.
contract Announcement() {
function announce() {
// Create the memo.cash announcement output
bytes announcement = new LockingBytecodeNullData([
0x6d02,
bytes('A contract may not injure a human being or, '
+ 'through inaction, allow a human being to come to harm.')
]);
// Check that the first tx output matches the announcement
require(tx.outputs[0].value == 0);
require(tx.outputs[0].lockingBytecode == announcement);
// Calculate leftover money after fee (1000 sats)
// Check that the second tx output sends the change back if there's
// enough leftover for another announcement
int minerFee = 1000;
int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee;
if (changeAmount >= minerFee) {
bytes changeLock = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[1].lockingBytecode == changeLock);
require(tx.outputs[1].value == changeAmount);
}
}
}pragma cashscript ^0.7.0;
contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
// Require recipient's signature to match
function transfer(sig recipientSig) {
require(checkSig(recipientSig, recipient));
}
// Require timeout time to be reached and sender's signature to match
function timeout(sig senderSig) {
require(checkSig(senderSig, sender));
require(tx.time >= timeout);
}
}pragma cashscript ^0.7.0;
// A minimum block is provided to ensure that oracle price entries from before
// this block are disregarded. i.e. when the BCH price was $1000 in the past,
// an oracle entry with the old block number and price can not be used.
contract HodlVault(pubkey ownerPk, pubkey oraclePk, int minBlock, int priceTarget) {
function spend(sig ownerSig, datasig oracleSig, bytes oracleMessage) {
// Decode the message { blockHeight, price }
bytes4 blockHeightBin, bytes4 priceBin = oracleMessage.split(4);
int blockHeight = int(blockHeightBin);
int price = int(priceBin);
// Check that blockHeight is after minBlock
require(blockHeight >= minBlock);
// Check that blockHeight is not in the future
require(tx.time >= blockHeight);
// Check that current price is at least priceTarget
require(price >= priceTarget);
// Check that the price message was signed by the oracle
require(checkDataSig(oracleSig, oracleMessage, oraclePk));
// Check that the transaction was signed by the contract owner
require(checkSig(ownerSig, ownerPk));
}
}contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
function receive() {
require(tx.age >= period);
// Check that the first output sends to the recipient
bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);
// Calculate the value that's left
int minerFee = 1000;
int currentValue = tx.inputs[this.activeInputIndex].value;
int changeValue = currentValue - pledge - minerFee;
// If there is not enough left for *another* pledge after this one,
// we send the remainder to the recipient. Otherwise we send the
// remainder to the recipient and the change back to the contract
if (changeValue <= pledge + minerFee) {
require(tx.outputs[0].value == currentValue - minerFee);
} else {
require(tx.outputs[0].value == pledge);
bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[1].lockingBytecode == changeBytecode);
require(tx.outputs[1].value == changeValue);
}
}
function reclaim(pubkey pk, sig s) {
require(hash160(pk) == funder);
require(checkSig(s, pk));
}
}grammar CashScript;
sourceFile
: pragmaDirective* contractDefinition EOF
;
pragmaDirective
: 'pragma' pragmaName pragmaValue ';'
;
pragmaName
: 'cashscript'
;
pragmaValue
: versionConstraint versionConstraint?
;
versionConstraint
: versionOperator? VersionLiteral
;
versionOperator
: '^' | '~' | '>=' | '>' | '<' | '<=' | '='
;
contractDefinition
: 'contract' Identifier parameterList '{' functionDefinition* '}'
;
functionDefinition
: 'function' Identifier parameterList '{' statement* '}'
;
parameterList
: '(' (parameter (',' parameter)* ','?)? ')'
;
parameter
: typeName Identifier
;
block
: '{' statement* '}'
| statement
;
statement
: variableDefinition
| tupleAssignment
| assignStatement
| timeOpStatement
| requireStatement
| ifStatement
;
variableDefinition
: typeName modifier? Identifier '=' expression ';'
;
tupleAssignment
: typeName Identifier ',' typeName Identifier '=' expression ';'
;
assignStatement
: Identifier '=' expression ';'
;
timeOpStatement
: 'require' '(' TxVar '>=' expression ')' ';'
;
requireStatement
: 'require' '(' expression ')' ';'
;
ifStatement
: 'if' '(' expression ')' ifBlock=block ('else' elseBlock=block)?
;
functionCall
: Identifier expressionList // Only built-in functions are accepted
;
expressionList
: '(' (expression (',' expression)* ','?)? ')'
;
expression
: '(' expression ')' # Parenthesised
| typeName '(' castable=expression (',' size=expression)? ','? ')' # Cast
| functionCall # FunctionCallExpression
| 'new' Identifier expressionList #Instantiation
| expression '[' index=NumberLiteral ']' # TupleIndexOp
| scope='tx.outputs' '[' expression ']' op=('.value' | '.lockingBytecode') # UnaryIntrospectionOp
| scope='tx.inputs' '[' expression ']' op=('.value' | '.lockingBytecode' | '.outpointTransactionHash' | '.outpointIndex' | '.unlockingBytecode' | '.sequenceNumber') # UnaryIntrospectionOp
| expression op=('.reverse()' | '.length') # UnaryOp
| left=expression op='.split' '(' right=expression ')' # BinaryOp
| op=('!' | '-') expression # UnaryOp
| left=expression op=('*' | '/' | '%') right=expression # BinaryOp
| left=expression op=('+' | '-') right=expression # BinaryOp
// | expression ('>>' | '<<') expression --- OP_LSHIFT & RSHIFT are disabled in BCH Script
| left=expression op=('<' | '<=' | '>' | '>=') right=expression # BinaryOp
| left=expression op=('==' | '!=') right=expression # BinaryOp
| left=expression op='&' right=expression # BinaryOp
| left=expression op='^' right=expression # BinaryOp
| left=expression op='|' right=expression # BinaryOp
| left=expression op='&&' right=expression # BinaryOp
| left=expression op='||' right=expression # BinaryOp
| '[' (expression (',' expression)* ','?)? ']' # Array
| NullaryOp # NullaryOp
| Identifier # Identifier
| literal # LiteralExpression
;
modifier
: 'constant'
;
literal
: BooleanLiteral
| numberLiteral
| StringLiteral
| DateLiteral
| HexLiteral
;
numberLiteral
: NumberLiteral NumberUnit?
;
typeName
: 'int' | 'bool' | 'string' | 'pubkey' | 'sig' | 'datasig' | Bytes
;
VersionLiteral
: [0-9]+ '.' [0-9]+ '.' [0-9]+
;
BooleanLiteral
: 'true' | 'false'
;
NumberUnit
: 'satoshis' | 'sats' | 'finney' | 'bits' | 'bitcoin'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks'
;
NumberLiteral
: [-]?[0-9]+ ([eE] [0-9]+)?
;
Bytes
: 'bytes' Bound? | 'byte'
;
Bound
: [1-9] [0-9]*
;
StringLiteral
: '"' ('\\"' | ~["\r\n])*? '"'
| '\'' ('\\\'' | ~['\r\n])*? '\''
;
DateLiteral
: 'date(' StringLiteral ')'
;
HexLiteral
: '0' [xX] [0-9A-Fa-f]*
;
TxVar
: 'tx.age'
| 'tx.time'
;
NullaryOp
: 'this.activeInputIndex'
| 'this.activeBytecode'
| 'tx.inputs.length'
| 'tx.outputs.length'
| 'tx.version'
| 'tx.locktime'
;
Identifier
: [a-zA-Z] [a-zA-Z0-9_]*
;
WHITESPACE
: [ \t\r\n\u000C]+ -> skip
;
COMMENT
: '/*' .*? '*/' -> channel(HIDDEN)
;
LINE_COMMENT
: '//' ~[\r\n]* -> channel(HIDDEN)
;contract Counter {
public function increment(SigHashPreimage txPreimage, int amount) {
require(Tx.checkPreimage(txPreimage));
// deserialize state (i.e., counter value)
bytes scriptCode = Util.scriptCode(txPreimage);
int scriptLen = len(scriptCode);
// counter is at the end
int counter = unpack(scriptCode[scriptLen - Util.DataLen :]);
// increment counter
counter++;
// serialize state
bytes outputScript = scriptCode[: scriptLen - Util.DataLen] + num2bin(counter, Util.DataLen);
bytes output = Util.buildOutput(outputScript, amount);
// ensure output is expected: amount is same with specified
// also output script is the same with scriptCode except counter incremented
require(hash256(output) == Util.hashOutputs(txPreimage));
}
}tx.hashPrevouts and tx.outpoint have been removed. Instead, the outpoints of individual inputs can be accessed with tx.inputs[i].outpointTransactionHash and tx.inputs[i].outpointIndex. The index of the active input can be accessed with this.activeInputIndex.outputs can be split up between several .to() calls.contract CheckLockTimeVerifyOCS {
int time;
public function unlock(SigHashPreimage preimage) {
require(Tx.checkPreimageOCS(preimage));
require(SigHash.nLocktime(preimage) > this.time);
}
}const amount = 2000
// get locking script
const CLTVOCS = buildContractClass(loadDesc('cltvOCS_debug_desc.json'));
cltv = new CLTVOCS(1422674);
// lock fund to the script
const lockingTx = await deployContract(cltv, amount)
console.log('funding txid: ', lockingTx.id);
// unlock
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(lockingTx))
.setLockTime(1422674 + 1)
.change(privateKey.toAddress())
.setInputScript(0, (tx, output) => {
const preimage = getPreimage(tx, output.script.subScript(0), output.satoshis)
return cltv.spend(new SigHashPreimage(toHex(preimage))).toScript()
})
.seal()
const unlockingTxid = await sendTx(unlockingTx)
console.log('unlocking txid: ', unlockingTxid)
console.log('Succeeded on testnet') int minerFee = 1000;
int intValue = int(bytes(tx.value));
if (intValue <= pledge + minerFee) {
// The contract has less value than the pledge, or equal.
// The recipient must claim all of of it.
bytes8 amount1 = bytes8(intValue - minerFee);
bytes34 out1 = new OutputP2PKH(amount1, recipient);
require(hash256(out1) == tx.hashOutputs);
} else {
// The contract has more value than the pledge. The recipient must
// also add one change output sending the remaining coins back
// to the contract.
bytes8 amount1 = bytes8(pledge);
bytes8 amount2 = bytes8(intValue - pledge - minerFee);
bytes34 out1 = new OutputP2PKH(amount1, recipient);
bytes32 out2 = new OutputP2SH(amount2, hash160(tx.bytecode));
require(hash256(out1 + out2) == tx.hashOutputs);
}
}
function reclaim(pubkey pk, sig s) {
require(hash160(pk) == funder);
require(checkSig(s, pk));
}
```solidity title="Mecenas.cash 0.7.0"
contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
function receive() {
require(tx.age >= period);
// Check that the first output sends to the recipient
bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);
// Calculate the value that's left
int minerFee = 1000;
int currentValue = tx.inputs[this.activeInputIndex].value;
int changeValue = currentValue - pledge - minerFee;
// If there is not enough left for *another* pledge after this one,
// we send the remainder to the recipient. Otherwise we send the
// remainder to the recipient and the change back to the contract
if (changeValue <= pledge + minerFee) {
require(tx.outputs[0].value == currentValue - minerFee);
} else {
require(tx.outputs[0].value == pledge);
bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
require(tx.outputs[1].lockingBytecode == changeBytecode);
require(tx.outputs[1].value == changeValue);
}
}
function reclaim(pubkey pk, sig s) {
require(hash160(pk) == funder);
require(checkSig(s, pk));
}
}const { Data } = require('cashc');
const encodedString = Data.encodeString('Hello World');const { utils } = require('cashc');
const { encodeString } = require('@cashscript/utils');
const encodedString = utils.encodeString('Hello World');
const encodedString = encodeString('Hello World');const { CashCompiler } = require('cashc');
const Mecenas = CashCompiler.compileFile(path.join(__dirname, 'mecenas.cash'));const { compileFile } = require('cashc');
const Mecenas = compileFile(path.join(__dirname, 'mecenas.cash'));const { Contract } = require('cashscript');
const Mecenas = Contract.compile(path.join(__dirname, 'mecenas.cash'), 'testnet');
const contract = Mecenas.new(alicePkh, bobPkh, 10000);const { CashCompiler, ElectrumNetworkProvider, Contract } = require('cashscript');
const Mecenas = CashCompiler.compileFile(path.join(__dirname, 'mecenas.cash'));
const provider = new ElectrumNetworkProvider('testnet');
const contract = new Contract(Mecenas, [alicePkh, bobPkh, 10000], provider);contract CheckLockTimeVerifyOCS {
int time;
public function unlock(SigHashPreimage preimage) {
require(Tx.checkPreimageOCS(preimage));
require(SigHash.nLocktime(preimage) > this.time);
}
}const amount = 2000
// get locking script
const CLTVOCS = buildContractClass(loadDesc('cltvOCS_debug_desc.json'));
cltv = new CLTVOCS(1422674);
// lock fund to the script
const lockingTx = await deployContract(cltv, amount)
console.log('funding txid: ', lockingTx.id);
// unlock
const unlockingTx = new bsv.Transaction();
unlockingTx.addInput(createInputFromPrevTx(lockingTx))
.setLockTime(1422674 + 1)
.change(privateKey.toAddress())
.setInputScript(0, (tx, output) => {
const preimage = getPreimage(tx, output.script.subScript(0), output.satoshis)
return cltv.spend(new SigHashPreimage(toHex(preimage))).toScript()
})
.seal()
const unlockingTxid = await sendTx(unlockingTx)
console.log('unlocking txid: ', unlockingTxid)
console.log('Succeeded on testnet')new Contract(
artifact: Artifact,
constructorArgs: Argument[],
provider?: NetworkProvider,
)const { Contract, ElectrumNetworkProvider } = require('cashscript');
const { compileFile } = require('cashc');
// Import an artifact JSON file that was compiled earlier
const P2PKH = require('./p2pkh.json');
// Or compile a contract file
const P2PKH = compileFile(path.join(__dirname, 'p2pkh.cash'));
const provider = new ElectrumNetworkProvider('testnet');
const contract = new Contract(P2PKH, [alicePkh], provider);contract.address: stringconsole.log(contract.address)contract.opcount: numberassert(contract.opcount <= 201)contract.bytesize: numberconsole.log(contract.bytesize)contract.getRedeemScriptHex: stringconsole.log(contract.getRedeemScriptHex())async contract.getBalance(): Promise<number>const contractBalance = await contract.getBalance()async contract.getUtxos(): Promise<Utxo[]>const utxos = await contract.getUtxos()contract.functions.<functionName>(...args: Argument[]): Transactionimport { alice } from './somewhere';
const tx = await contract.functions
.transfer(new SignatureTemplate(alice))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
.send()new SignatureTemplate(signer: Keypair | Uint8Array | string, hashtype?: HashType)const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const sig = new SignatureTemplate(wif, HashType.SIGHASH_ALL);new ElectrumNetworkProvider(network?: Network, electrum?: ElectrumCluster)const provider = new ElectrumProvider('testnet');new FullStackNetworkProvider(network: Network, bchjs: BCHJS)const BCHJS = require('@psf/bch-js');
const restURL = 'https://api.fullstack.cash/v3/';
const apiToken = 'eyJhbGciO...'; // Your JWT token here.
const bchjs = new BCHJS({ restURL, apiToken });
const provider = new FullStackNetworkProvider('mainnet', bchjs);new BitboxNetworkProvider(network: Network, bitbox: BITBOX)const BITBOX = require('bitbox-sdk');
const bitbox = new BITBOX({ restURL: 'https://rest.bitcoin.com/v2/' });
const provider = new FullStackNetworkProvider('mainnet', bitbox);new BitcoinRpcNetworkProvider(network: Network, url: string, options?: any)const provider = new BitcoinRpcNetworkProvider('mainnet', 'http://localhost:8332');interface NetworkProvider {
/**
* Variable indicating the network that this provider connects to.
*/
network: Network;
/**
* Retrieve all UTXOs (confirmed and unconfirmed) for a given address.
* @param address The CashAddress for which we wish to retrieve UTXOs.
* @returns List of UTXOs spendable by the provided address.
*/
getUtxos(address: string): Promise<Utxo[]>;
/**
* @returns The current block height.
*/
getBlockHeight(): Promise<number>;
/**
* Retrieve the Hex transaction details for a given transaction ID.
* @param txid Hex transaction ID.
* @throws {Error} If the transaction does not exist
* @returns The full hex transaction for the provided transaction ID.
*/
getRawTransaction(txid: string): Promise<string>;
/**
* Broadcast a raw hex transaction to the network.
* @param txHex The raw transaction hex to be broadcast.
* @throws {Error} If the transaction was not accepted by the network.
* @returns The transaction ID corresponding to the broadcast transaction.
*/
sendRawTransaction(txHex: string): Promise<string>;
}
type Network = 'mainnet' | 'testnet3' | 'testnet4' | 'chipnet' | 'regtest';
interface Utxo {
txid: string;
vout: number;
satoshis: number;
}npm install cashccompileFile(sourceFile: string): Artifactconst P2PKH = compileFile(path.join(__dirname, 'p2pkh.cash'));compileString(sourceCode: string): Artifactconst baseUrl = 'https://raw.githubusercontent.com/Bitcoin-com/cashscript'
const result = await fetch(`${baseUrl}/master/examples/p2pkh.cash`);
const source = await result.text();
const P2PKH = compileString(source);transaction.to(to: string, amount: number): this
transaction.to(outputs: { to: string, amount: number }[]): this.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)transaction.withOpReturn(chunks: string[]): this.withOpReturn(['0x6d02', 'Hello World!'])transaction.from(inputs: Utxo[]): this.from(await instance.getUtxos())transaction.withFeePerByte(feePerByte: number): this.withFeePerByte(2.3)transaction.withHardcodedFee(hardcodedFee: number): this.withHardcodedFee(1000)transaction.withMinChange(minChange: number): this.withMinChange(1000)transaction.withoutChange(): this.withoutChange()transaction.withAge(age: number): this.withAge(10)transaction.withTime(time: number): this.withTime(700000)async transaction.send(): Promise<TransactionDetails>interface TransactionDetails {
inputs: Uint8Array[];
locktime: number;
outputs: Uint8Array[];
version: number;
txid: string;
hex: string;
}import { alice } from './somewhere';
const txDetails = await instance.functions
.transfer(new SignatureTemplate(alice))
.withOpReturn(['0x6d02', 'Hello World!'])
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 200000)
.to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 100000)
.withHardcodedFee(1000)
.send()async transaction.build(): Promise<string>const txHex = await instance.functions
.transfer(new SignatureTemplate(alice))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)
.withAge(10).withFeePerByte(10).build()async transaction.meep(): Promise<string>const meepStr = await instance.functions
.transfer(new SignatureTemplate(alice))
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)
.withTime(700000)
.meep()enum Reason {
EVAL_FALSE = 'Script evaluated without error but finished with a false/empty top stack element',
VERIFY = 'Script failed an OP_VERIFY operation',
EQUALVERIFY = 'Script failed an OP_EQUALVERIFY operation',
CHECKMULTISIGVERIFY = 'Script failed an OP_CHECKMULTISIGVERIFY operation',
CHECKSIGVERIFY = 'Script failed an OP_CHECKSIGVERIFY operation',
CHECKDATASIGVERIFY = 'Script failed an OP_CHECKDATASIGVERIFY operation',
NUMEQUALVERIFY = 'Script failed an OP_NUMEQUALVERIFY operation',
SCRIPT_SIZE = 'Script is too big',
PUSH_SIZE = 'Push value size limit exceeded',
OP_COUNT = 'Operation limit exceeded',
STACK_SIZE = 'Stack size limit exceeded',
SIG_COUNT = 'Signature count negative or greater than pubkey count',
PUBKEY_COUNT = 'Pubkey count negative or limit exceeded',
INVALID_OPERAND_SIZE = 'Invalid operand size',
INVALID_NUMBER_RANGE = 'Given operand is not a number within the valid range',
IMPOSSIBLE_ENCODING = 'The requested encoding is impossible to satisfy',
INVALID_SPLIT_RANGE = 'Invalid OP_SPLIT range',
INVALID_BIT_COUNT = 'Invalid number of bit set in OP_CHECKMULTISIG',
BAD_OPCODE = 'Opcode missing or not understood',
DISABLED_OPCODE = 'Attempted to use a disabled opcode',
INVALID_STACK_OPERATION = 'Operation not valid with the current stack size',
INVALID_ALTSTACK_OPERATION = 'Operation not valid with the current altstack size',
OP_RETURN = 'OP_RETURN was encountered',
UNBALANCED_CONDITIONAL = 'Invalid OP_IF construction',
DIV_BY_ZERO = 'Division by zero error',
MOD_BY_ZERO = 'Modulo by zero error',
INVALID_BITFIELD_SIZE = 'Bitfield of unexpected size error',
INVALID_BIT_RANGE = 'Bitfield\'s bit out of the expected range',
NEGATIVE_LOCKTIME = 'Negative locktime',
UNSATISFIED_LOCKTIME = 'Locktime requirement not satisfied',
SIG_HASHTYPE = 'Signature hash type missing or not understood',
SIG_DER = 'Non-canonical DER signature',
MINIMALDATA = 'Data push larger than necessary',
SIG_PUSHONLY = 'Only push operators allowed in signature scripts',
SIG_HIGH_S = 'Non-canonical signature: S value is unnecessarily high',
MINIMALIF = 'OP_IF/NOTIF argument must be minimal',
SIG_NULLFAIL = 'Signature must be zero for failed CHECK(MULTI)SIG operation',
SIG_BADLENGTH = 'Signature cannot be 65 bytes in CHECKMULTISIG',
SIG_NONSCHNORR = 'Only Schnorr signatures allowed in this operation',
DISCOURAGE_UPGRADABLE_NOPS = 'NOPx reserved for soft-fork upgrades',
PUBKEYTYPE = 'Public key is neither compressed or uncompressed',
CLEANSTACK = 'Script did not clean its stack',
NONCOMPRESSED_PUBKEY = 'Using non-compressed public key',
ILLEGAL_FORKID = 'Illegal use of SIGHASH_FORKID',
MUST_USE_FORKID = 'Signature must use SIGHASH_FORKID',
UNKNOWN = 'unknown error',
}HashedMap<int, int> map = new HashedMap<int, int>(b'')HashedMap<int, int> map = new HashedMap(b'')auto map = new HashedMap<int, int>(b'')contract StateMapTest {
@state
bytes _mpData; //Save the serialized data of the map
// Add key-value pairs to the map
public function insert(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}
// update key-value pairs in the map
public function update(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}
// delete key-value pairs in the map
public function delete(int key, int keyIndex, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.delete(key, keyIndex));
// Serialize map, update state
require(this.passMap(map.data(), preimage));
}
// update state _mpData, and build a output contains new state
function passMap(bytes newData, SigHashPreimage preimage) : bool {
this._mpData = newData;
bytes outputScript = this.getStateScript();
bytes output = Util.buildOutput(outputScript, Util.value(preimage));
return (hash256(output) == Util.hashOutputs(preimage));
}
}public function insert(MapEntry entry, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.set(entry.key, entry.val, entry.keyIndex));
require(this.passMap(map.data(), preimage));
}let map = new Map<number, number>();
map.set(key, val); //先把键值对添加到链外的 map
const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.insert(new MapEntry({
key: key,
val: val,
keyIndex: findKeyIndex(map, key) // 获取 `keyIndex` 参数
}), preimage).verify()
expect(result.success, result.error).to.be.true;
mapTest._mpData = toData(map)const mapEntrys = Array.from(sortmap(map), ([key, val]) => ({ key, val }))
.map((entry, index) => {
...entry,
keyIndex: index
})public function delete(int key, int keyIndex, SigHashPreimage preimage) {
require(Tx.checkPreimage(preimage));
HashedMap<int, int> map = new HashedMap(this._mpData);
require(map.delete(key, keyIndex));
require(this.passMap(map.data(), preimage));
}const keyIndex = findKeyIndex(map, key); //从链外map删除之前,先计算出keyIndex,并保存
map.delete(key);
const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.delete(key, keyIndex, preimage).verify() // 调用合约的删除方法需要提供key, keyIndex
expect(result.success, result.error).to.be.true;
mapTest._mpData = toData(map)require(map.canGet(key, val, keyIndex));require(map.has(key, keyIndex));const Counter = buildContractClass(loadDescription('counter_desc.json'));
counter = new Counter();
const dataPart = counter.dataPart;
const dataPartASM = counter.dataPart.toASM();
const dataPartHex = counter.dataPart.toHex();
// to set it using ASM
counter.setDataPart('01');const preimage = getPreimage(tx, counter.lockingScript, inputSatoshis)const codePart = instance.codePart;
const codePartASM = instance.codePart.toASM();
const codePartHex = instance.codePart.toHex();