fix: update scripts and readme for NFT demo

This commit is contained in:
Brice Dobry
2023-02-09 23:24:03 -05:00
parent 7f19561df2
commit c89de07da5
5 changed files with 262 additions and 131 deletions

View File

@@ -1,7 +1,9 @@
# NFT Use Case Demo
## Introduction
In this demo, you will learn how to:
1. Publish an NFT contract on the Stacks (L1) chain & subnet (L2) respectively.
2. Register NFT asset with the subnet interface contract.
3. Mint an NFT on the L1 chain.
@@ -9,90 +11,123 @@ In this demo, you will learn how to:
5. Transfer the NFT in the subnet.
6. Withdraw the NFT back to the L1 chain, which has two steps.
▶️ [Watch distributed systems engineer Pavi demonstrate interacting with a Subnet here.](https://www.youtube.com/watch?v=GbGNlOsPDXM)
▶️
[Watch distributed systems engineer Pavi demonstrate interacting with a Subnet here.](https://www.youtube.com/watch?v=GbGNlOsPDXM)
This guide will follow the list above, step by step.
It is also possible to mint assets directly on the subnet, and withdraw them onto the L1. This bonus step is mentioned
at the end of step 5.
This guide will follow the list above, step by step. It is also possible to mint
assets directly on the subnet, and withdraw them onto the L1. This bonus step is
mentioned at the end of step 5.
## Subnet Background
A subnet is a network that is separate from the Stacks chain. A subnet can be thought of as a layer-2 (L2),
and the Stacks chain can be thought of as a layer-1 (L1). The subnet interfaces with the Stacks chain via a smart
contract that is specific to the subnet. Different subnet networks will use distinct Stacks contracts as an interface.
This interface contract has several functions that allow it to act as an intermediary between the Stacks chain and
some particular subnet. These functions include but are not limited to:
- `commit-block`: Called by subnet miners to record block hashes and withdrawal state on the Stacks chain.
- `deposit-ft-asset` / `deposit-stx` / `deposit-nft-asset`: Called by users to deposit assets into the subnet
contract. The subnet "listens" for calls to these functions, and performs a mint on the subnet to
replicate this state. Meanwhile, on the L1, the assets live in the contract.
- `withdraw-ft-asset` / `withdraw-stx` / `withdraw-nft-asset`: Called by miners to withdraw assets from the subnet.
In an upcoming update to the subnet repo, this function will be called by users directly.
In order to register new allowed assets, a valid miner may call `setup-allowed-contracts`, `register-ft-contract`, or `register-nft-contract`.
The transaction sender must be part of the miners list defined in the subnet contract.
A subnet is a network that is separate from the Stacks chain. A subnet can be
thought of as a layer-2 (L2), and the Stacks chain can be thought of as a
layer-1 (L1). The subnet interfaces with the Stacks chain via a smart contract
that is specific to the subnet. Different subnet networks will use distinct
Stacks contracts as an interface. This interface contract has several functions
that allow it to act as an intermediary between the Stacks chain and some
particular subnet. These functions include but are not limited to:
- `commit-block`: Called by subnet miners to record block hashes and withdrawal
state on the Stacks chain.
- `deposit-ft-asset` / `deposit-stx` / `deposit-nft-asset`: Called by users to
deposit assets into the subnet contract. The subnet "listens" for calls to
these functions, and performs a mint on the subnet to replicate this state.
Meanwhile, on the L1, the assets live in the contract.
- `withdraw-ft-asset` / `withdraw-stx` / `withdraw-nft-asset`: Called by miners
to withdraw assets from the subnet. In an upcoming update to the subnet repo,
this function will be called by users directly.
In order to register new allowed assets, a valid miner may call
`setup-allowed-contracts`, `register-ft-contract`, or `register-nft-contract`.
The transaction sender must be part of the miners list defined in the subnet
contract.
## Setup
Make sure you have `clarinet` installed locally, and that it is at version 0.33.0 or above.
If you do not have clarinet, you can find installation instructions [here](https://github.com/hirosystems/clarinet).
Make sure you have `clarinet` installed locally, and that it is at version
0.33.0 or above. If you do not have clarinet, you can find installation
instructions [here](https://github.com/hirosystems/clarinet).
Let's create a new Clarinet project. This will create a new directory with the
Clarinet project initialized.
Let's create a new Clarinet project. This will create a new directory with the Clarinet project initialized.
```
clarinet new nft-use-case
clarinet new nft-use-case
```
Let us copy contract files and scripts over from the `stacks-subnets` repository into the `nft-use-case` directory.
If you don't already have the stacks-subnets repository, you can [clone it](https://github.com/hirosystems/stacks-subnets).
Let us copy contract files and scripts over from the `stacks-subnets` repository
into the `nft-use-case` directory. If you don't already have the stacks-subnets
repository, you can [clone it](https://github.com/hirosystems/stacks-subnets).
Here's the command to clone the stacks-subnets repository:
```
git clone https://github.com/hirosystems/stacks-subnets.git
```
Set the environment variable `SUBNET_PATH` to the location of the stacks-subnets repository on your computer.
Set the environment variable `SUBNET_PATH` to the location of the stacks-subnets
repository on your computer.
```
export SUBNET_PATH=<YOUR_PATH_HERE>
```
Now, we can copy files from the stacks-subnets repository. These files are contracts which will define the layer-1
and layer-2 Clarity traits for NFTs and fungible tokens, implement an NFT in layer-1 and layer-2, and some NodeJS scripts for
helping to deploy the contracts.
Now, we can copy files from the stacks-subnets repository. These files are
contracts which will define the layer-1 and layer-2 Clarity traits for NFTs and
fungible tokens, implement an NFT in layer-1 and layer-2, and some NodeJS
scripts for helping to deploy the contracts.
```
mkdir nft-use-case/contracts-l2
mkdir nft-use-case/scripts
cp $SUBNET_PATH/core-contracts/contracts/helper/simple-nft.clar nft-use-case/contracts/
cp $SUBNET_PATH/core-contracts/contracts/helper/sip-traits.clar nft-use-case/contracts/
cp $SUBNET_PATH/core-contracts/contracts/helper/simple-nft-l2.clar nft-use-case/contracts-l2/
cp $SUBNET_PATH/contrib/scripts/nft-use-case/* nft-use-case/scripts/
cd nft-use-case/scripts
```
To use the scripts in this demo, we need to install some NodeJS libraries.
Before running the following instructions, make sure you have [node](https://nodejs.org/en/) installed.
To use the scripts in this demo, we need to install some NodeJS libraries.
Before running the following instructions, make sure you have
[node](https://nodejs.org/en/) installed.
```
npm install
cd .. # back to nft-use-case/
```
The `Devnet.toml` file in the `nft-use-case` directory is responsible for configuring the `clarinet integrate`
local network. Make the following change in `settings/Devnet.toml` to enable the subnet:
The `Devnet.toml` file in the `nft-use-case` directory is responsible for
configuring the `clarinet integrate` local network. Make the following change in
`settings/Devnet.toml` to enable the subnet:
```
[devnet]
...
enable_subnet_node = true
```
Let's spin up a subnet node. Before you call this, make sure that you have a working installation of Docker running
locally.
Our NFT contract relies on the published SIP-009 contract, so let's add it as a
requirement:
```
clarinet requirements add SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait
```
Let's spin up a subnet node. Before you call this, make sure that you have a
working installation of Docker running locally.
```
clarinet integrate
```
Before we publish any transactions, you will need to set up some environment variables.
These environment variables contain the address and private key of the subnet miner, two user addresses
and private keys, and the RPC URL which we can query for subnet state.
Open a separate terminal window, navigate to the directory `nft-use-case/scripts`, and enter the following.
Before we publish any transactions, you will need to set up some environment
variables. These environment variables contain the address and private key of
the subnet miner, two user addresses and private keys, and the RPC URL which we
can query for subnet state. Open a separate terminal window, navigate to the
directory `nft-use-case/scripts`, and enter the following.
```
export AUTH_SUBNET_MINER_ADDR=ST3AM1A56AK2C1XAFJ4115ZSV26EB49BVQ10MGCS0
export AUTH_SUBNET_MINER_KEY=7036b29cb5e235e5fd9b09ae3e8eec4404e44906814d5d01cbca968a60ed4bfb01
export AUTH_SUBNET_MINER_ADDR=ST3NBRSFKX28FQ2ZJ1MAKX58HKHSDGNV5N7R21XCP
export AUTH_SUBNET_MINER_KEY=6a1a754ba863d7bab14adbbc3f8ebb090af9e871ace621d3e5ab634e1422885e01
export USER_ADDR=ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND
export USER_KEY=f9d7206a47f14d2870c163ebab4bf3e70d18f5d14ce1031f3902fbbc894fe4c701
@@ -103,133 +138,181 @@ export SUBNET_URL="http://localhost:30443"
```
## Step 1: Publish the NFT contract to the Stacks L1 and the Subnet
Once the Stacks node and the subnet node boots up (use the indicators in the top right panel to determine this), we can
start to interact with the chains. To begin with, we want to publish NFT contracts onto both the L1 and L2. When the user
deposits their L1 NFT onto the subnet, their asset gets minted by the L2 NFT contract.
The publish script takes in four arguments: the name of the contract to be published, the filename for the contract
source code, the layer on which to broadcast the transaction (1 or 2), and the nonce of the transaction.
First, publish the layer 1 contracts. You can enter this command (and the following transaction commands) in the same
terminal window as you entered the environment variables. Make sure you are in the `scripts` directory.
Once the Stacks node and the subnet node boots up (use the indicators in the top
right panel to determine this), we can start to interact with the chains. To
begin with, we want to publish NFT contracts onto both the L1 and L2. When the
user deposits their L1 NFT onto the subnet, their asset gets minted by the L2
NFT contract. The publish script takes in four arguments: the name of the
contract to be published, the filename for the contract source code, the layer
on which to broadcast the transaction (1 or 2), and the nonce of the
transaction. First, publish the layer 1 contracts. You can enter this command
(and the following transaction commands) in the same terminal window as you
entered the environment variables. Make sure you are in the `scripts` directory.
These transactions are called by the principal `USER_ADDR`.
```
node ./publish_tx.js trait-standards ../contracts/trait-standards.clar 1 0
node ./publish_tx.js simple-nft-l1 ../contracts/simple-nft.clar 1 1
node ./publish_tx.js simple-nft-l1 ../contracts/simple-nft.clar 1 0
```
Verify that the contracts were published by using the Clarinet console.
For the layer 1 contracts, you should see the following in the "transactions" region in a recent block.
Verify that the contracts were published by using the Clarinet console. For the
layer 1 contracts, you should see the following in the "transactions" region in
a recent block.
🟩 deployed: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.trait-standards (ok true)
🟩 deployed: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.trait-standards (ok true)
🟩 deployed: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.simple-nft-l1 (ok true)
🟩 deployed: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.simple-nft-l1 (ok true)
Then, publish the layer 2 contracts. Note, it might take a minute for the subnet
node to start accepting transactions, so these commands could fail if you send
them too early (but you can always re-try when the node is ready). These
transactions are called by the principal `USER_ADDR`.
Then, publish the layer 2 contracts. Note, it might take a minute for the subnet node to start accepting transactions,
so these commands could fail if you send them too early (but you can always re-try when the node is ready).
These transactions are called by the principal `USER_ADDR`.
```
node ./publish_tx.js trait-standards ../contracts-l2/trait-standards.clar 2 0
node ./publish_tx.js simple-nft-l2 ../contracts-l2/simple-nft-l2.clar 2 1
node ./publish_tx.js simple-nft-l2 ../contracts-l2/simple-nft-l2.clar 2 0
```
To verify that the layer 2 transactions were processed, grep the subnet log for the transaction IDs
of *each* subnet transaction.
The transaction ID is logged to the console after the call to `publish_tx` - make sure this is the ID you grep for.
To verify that the layer 2 transactions were processed, grep the subnet log for
the transaction IDs of _each_ subnet transaction. The transaction ID is logged
to the console after the call to `publish_tx` - make sure this is the ID you
grep for.
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "17901e5ad0587d414d5bb7b1c24c3d17bb1533f5025d154719ba1a2a0f570246"
```
Look for a log line similar to the following in the results:
```
Jul 19 12:34:41.683519 INFO Tx successfully processed. (ThreadId(9), src/chainstate/stacks/miner.rs:235), event_name: transaction_result, tx_id: 17901e5ad0587d414d5bb7b1c24c3d17bb1533f5025d154719ba1a2a0f570246, event_type: success, payload: SmartContract
```
To ensure the contracts were successfully parsed and published, we will grep for the name of the contract and ensure there are no
error lines returned (not atypical for no lines to be returned at this step).
To ensure the contracts were successfully parsed and published, we will grep for
the name of the contract and ensure there are no error lines returned (not
atypical for no lines to be returned at this step).
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "simple-nft-l2"
```
## Step 2: Register the new NFT asset in the interface subnet contract
Create the transaction to register the new NFT asset we just published. This must be called by a miner of the subnet contract.
Specifically, this transaction will be sent by `AUTH_SUBNET_MINER_ADDR`.
```
node ./register_nft.js 0
```
Look for the following transaction confirmation in the Clarinet console in an upcoming block on the layer 1.
🟩 invoked: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet::register-new-nft-contract(ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l1, "subnet-deposit-nft-token") (ok true)
Create the transaction to register the new NFT asset we just published. This
must be called by a miner of the subnet contract. Specifically, this transaction
will be sent by `AUTH_SUBNET_MINER_ADDR`.
```
node ./register_nft.js
```
Look for the following transaction confirmation in the Clarinet console in an
upcoming block on the layer 1.
🟩 invoked:
ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet::register-new-nft-contract(ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l1,
"subnet-deposit-nft-token") (ok true)
## Step 3: Mint an NFT on the L1 Chain
Let's create a transaction to mint an NFT on the L1 chain. Once this transaction is processed, the principal `USER_ADDR`
will own an NFT.
```
node ./mint_nft.js 2
```
Verify that the transaction is acknowledged within the next few blocks in the Stacks explorer.
🟩 invoked: ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.simple-nft-l1::gift-nft(ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND, u5) (ok true)
Let's create a transaction to mint an NFT on the L1 chain. Once this transaction
is processed, the principal `USER_ADDR` will own an NFT.
## Step 4: Deposit the NFT onto the Subnet
Now, we can call the deposit NFT function in the subnet interface contract. This
function is called by the principal `USER_ADDR`.
```
node ./deposit_nft.js 3
node ./mint_nft.js 1
```
Verify that the transaction is acknowledged in the next few blocks of the L1 chain.
After the transaction is confirmed in an anchored block on the L1 (this means it is included in an explicitly
numbered block in the Clarinet console), you also may want to verify that the asset was successfully deposited
on the subnet by grepping for the deposit transaction ID.
Verify that the transaction is acknowledged within the next few blocks in the
Stacks explorer.
🟩 invoked:
ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND.simple-nft-l1::gift-nft(ST2NEB84ASENDXKYGJPQW86YXQCEFEX2ZQPG87ND,
u5) (ok true)
## Step 4: Deposit the NFT onto the Subnet
Now, we can call the deposit NFT function in the subnet interface contract. This
function is called by the principal `USER_ADDR`.
```
node ./deposit_nft.js 2
```
Verify that the transaction is acknowledged in the next few blocks of the L1
chain. After the transaction is confirmed in an anchored block on the L1 (this
means it is included in an explicitly numbered block in the Clarinet console),
you also may want to verify that the asset was successfully deposited on the
subnet by grepping for the deposit transaction ID.
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "8d042c14323cfd9d31e121cc48c2c641a8db01dce19a0f6dd531eb33689dff44"
```
Look for a line like:
```
Jul 19 12:51:02.396923 INFO ACCEPTED burnchain operation (ThreadId(8), src/chainstate/burn/db/sortdb.rs:3042), op: deposit_nft, l1_stacks_block_id: 8b5c4eb05afae6daaafdbd59aecaade6da1a8eab5eb1041062c6381cd7104b75, txid: 67cfd6220ed01c3aca3912c8f1ff55d374e5b3acadb3b995836ae913108e0514, l1_contract_id: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l1, subnet_contract_id: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l2, subnet_function_name: subnet-deposit-nft-token, id: 5, sender: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG
```
## Step 5: Transfer the NFT within the Subnet
On the subnet, the NFT should belong to the principal that sent the deposit transaction, `USER_ADDR`.
This principal can now transfer the NFT within the subnet. The principal `USER_ADDR` will now make a
transaction to transfer the NFT to `ALT_USER_ADDR`.
## Step 5: Transfer the NFT within the Subnet
On the subnet, the NFT should belong to the principal that sent the deposit
transaction, `USER_ADDR`. This principal can now transfer the NFT within the
subnet. The principal `USER_ADDR` will now make a transaction to transfer the
NFT to `ALT_USER_ADDR`.
```
node ./transfer_nft.js 2
node ./transfer_nft.js 1
```
Grep for the transaction ID of the transfer transaction.
Grep for the transaction ID of the transfer transaction.
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "6acc2c756ddaed2c4cfb7351dd5930aa93ba923504be85e47db056c99a7e81aa"
```
Look for something like the following line:
```
Jul 19 13:04:43.177993 INFO Tx successfully processed. (ThreadId(9), src/chainstate/stacks/miner.rs:235), event_name: transaction_result, tx_id: 74949992488b2519e2d8408169f242c86a6cdacd927638bd4604b3b8d48ea187, event_type: success, payload: ContractCall
```
For a bonus step, you can try minting an NFT on the subnet. This would require calling the `gift-nft` function in the
contract `simple-nft-l2`. You can tweak the `transfer_nft.js` file to make this call.
For a bonus step, you can try minting an NFT on the subnet. This would require
calling the `gift-nft` function in the contract `simple-nft-l2`. You can tweak
the `transfer_nft.js` file to make this call.
## Step 6: Withdraw the NFT back to the L1 Chain
### Background on withdrawals
Withdrawals from the subnet are a 2-step process.
The owner of an asset must call `withdraw-ft?` / `withdraw-stx?` / `withdraw-nft?` in a Clarity contract on the subnet,
which destroys those assets on the subnet, and adds that particular withdrawal to a withdrawal data structure for that block.
The withdrawal data structure serves as a cryptographic record of the withdrawals in a particular block, and has an
overall associated hash. This hash is committed to the L1 interface contract via the `commit-block` function.
Withdrawals from the subnet are a 2-step process.
The second step involves calling the appropriate withdraw function in the subnet interface
contract on the L1 chain. You must also pass in the "proof" that corresponds to your withdrawal.
This proof includes the hash of the withdrawal data structure that this withdrawal was included in,
the hash of the withdrawal itself, and a list of hashes to be used to prove that the particular withdrawal is valid. Currently,
this function must be called by a subnet miner, but in an upcoming subnet release, the asset owner must call
this function.
The owner of an asset must call `withdraw-ft?` / `withdraw-stx?` /
`withdraw-nft?` in a Clarity contract on the subnet, which destroys those assets
on the subnet, and adds that particular withdrawal to a withdrawal data
structure for that block. The withdrawal data structure serves as a
cryptographic record of the withdrawals in a particular block, and has an
overall associated hash. This hash is committed to the L1 interface contract via
the `commit-block` function.
The second step involves calling the appropriate withdraw function in the subnet
interface contract on the L1 chain. You must also pass in the "proof" that
corresponds to your withdrawal. This proof includes the hash of the withdrawal
data structure that this withdrawal was included in, the hash of the withdrawal
itself, and a list of hashes to be used to prove that the particular withdrawal
is valid. Currently, this function must be called by a subnet miner, but in an
upcoming subnet release, the asset owner must call this function.
### Step 6a: Withdraw the NFT on the subnet
Perform the withdrawal on the layer 2 by calling `withdraw-nft-asset` in the
`simple-nft-l2` contract. This will be called by the principal `ALT_USER_ADDR`.
### Step 6a: Withdraw the NFT on the subnet
Perform the withdrawal on the layer 2 by calling `withdraw-nft-asset` in the `simple-nft-l2` contract. This will be called
by the principal `ALT_USER_ADDR`.
```
node ./withdraw_nft_l2.js 0
node ./withdraw_nft_l2.js 0
```
Grep the subnet node to ensure success:
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "5b5407ab074b4d78539133fe72020b18d44535a586574d0bd1f668e05dc89c2f"
Jul 19 13:07:33.804109 INFO Tx successfully processed. (ThreadId(9), src/chainstate/stacks/miner.rs:235), event_name: transaction_result, tx_id: 3ff9b9b0f33dbd6087f302fa9a7a113466cf7700ba7785a741b391f5ec7c5ba4, event_type: success, payload: ContractCall
@@ -238,34 +321,57 @@ docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "withdraw-nft-asset"
Jul 19 13:22:34.800652 INFO Contract-call successfully processed (ThreadId(8), src/chainstate/stacks/db/transactions.rs:731), contract_name: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l2, function_name: withdraw-nft-asset, function_args: [u5, ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC], return_value: (ok true), cost: ExecutionCost { write_length: 2, write_count: 2, read_length: 1647, read_count: 5, runtime: 2002000 }
```
In order to successfully complete the withdrawal on the L1, it is necessary to know the height at which the withdrawal occurred.
You can find the height of the withdrawal using grep:
In order to successfully complete the withdrawal on the L1, it is necessary to
know the height at which the withdrawal occurred. You can find the height of the
withdrawal using grep:
```
docker logs subnet-node.nft-use-case.devnet 2>&1 | grep "Parsed L2 withdrawal event"
Jul 19 13:22:34.801290 INFO Parsed L2 withdrawal event (ThreadId(8), src/clarity_vm/withdrawal.rs:56), type: nft, block_height: 47, sender: ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05NNC, withdrawal_id: 0, asset_id: ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.simple-nft-l2::nft-token
```
Get the withdrawal height by looking at the `block_height` in the returned line. There may be multiple lines returned
by the grep. Try the higher heights first, and work backward.
### Step 6b: Complete the withdrawal on the Stacks chain
Use the withdrawal height we just obtained from the grep and substitute that for `WITHDRAWAL_BLOCK_HEIGHT`.
You might need to wait a little bit for the subnet block to become official (even if
the grep already returned a result) for the transaction to succeed. If the subnet has not advanced sufficiently, you
may get the error `Supplied block height not found`. For now, this script assumes that the requested
withdrawal was the only one in the subnet block it was a part of (thus, you may run into issues using this script
if you are attempting to withdraw multiple assets in a short span of time).
Get the withdrawal height by looking at the `block_height` in the returned line.
There may be multiple lines returned by the grep. Try the higher heights first,
and work backward.
### Step 6b: Complete the withdrawal on the Stacks chain
Use the withdrawal height we just obtained from the grep and substitute that for
`WITHDRAWAL_BLOCK_HEIGHT`. You might need to wait a little bit for the subnet
block to become official (even if the grep already returned a result) for the
transaction to succeed. If the subnet has not advanced sufficiently, you may get
the error `Supplied block height not found`. For now, this script assumes that
the requested withdrawal was the only one in the subnet block it was a part of
(thus, you may run into issues using this script if you are attempting to
withdraw multiple assets in a short span of time).
```
node ./withdraw_nft_l1.js {WITHDRAWAL_BLOCK_HEIGHT} 1
node ./withdraw_nft_l1.js {WITHDRAWAL_BLOCK_HEIGHT} 0
```
Check for the success of this transaction in the Clarinet console:
🟩 invoked: ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet::withdraw-nft-asset(u5, ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05...
🟩 invoked:
ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.subnet::withdraw-nft-asset(u5,
ST2JHG361ZXG51QTKY2NQCVBPPRRE2KZB1HR05...
You can also navigate to the Stacks Explorer (the URL of this will be listed in the Clarinet console), and check that the expected
principal now owns the NFT (`ALT_USER_ADDR`). You can check this by clicking on the transaction corresponding to
`withdraw-nft-asset`.
You can also navigate to the Stacks Explorer (the URL of this will be listed in
the Clarinet console), and check that the expected principal now owns the NFT
(`ALT_USER_ADDR`). You can check this by clicking on the transaction
corresponding to `withdraw-nft-asset`.
That is the conclusion of this demo! If you have any issues with this demo,
reach out on the Stacks Discord or leave an issue in the stacks-subnets
repository.
That is the conclusion of this demo! If you have any issues with this demo, reach out on the Stacks Discord or leave an issue in the
stacks-subnets repository.
Verify that the correct address now owns the NFT by calling:
```
node ./verify.js
```
The result is printed to the terminal, and should show:
```
(some ST2REHHS5J3CERCRBEPMGH7921Q6PYKAADT7JP2VB)
```

View File

@@ -15,7 +15,7 @@ async function main() {
const txOptions = {
contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
contractName: "subnet",
contractName: "subnet-alpha",
functionName: "deposit-stx",
functionArgs: [
uintCV(500_000_000_000), // ID

View File

@@ -2,8 +2,8 @@ import {
makeContractCall,
AnchorMode,
contractPrincipalCV,
stringAsciiCV,
broadcastTransaction,
getNonce,
} from "@stacks/transactions";
import { StacksTestnet, HIRO_MOCKNET_DEFAULT } from "@stacks/network";
@@ -11,7 +11,8 @@ async function main() {
const network = new StacksTestnet({ url: HIRO_MOCKNET_DEFAULT });
const senderKey = process.env.AUTH_SUBNET_MINER_KEY;
const userAddr = process.env.USER_ADDR;
const nonce = parseInt(process.argv[2]);
const nonce =
(await getNonce(process.env.AUTH_SUBNET_MINER_ADDR, network)) + 1n;
const txOptions = {
contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",

View File

@@ -0,0 +1,23 @@
import { uintCV, callReadOnlyFunction, cvToString } from "@stacks/transactions";
import { StacksTestnet, HIRO_MOCKNET_DEFAULT } from "@stacks/network";
async function main() {
const network = new StacksTestnet({ url: HIRO_MOCKNET_DEFAULT });
const senderAddress = process.env.ALT_USER_ADDR;
const addr = process.env.USER_ADDR;
const txOptions = {
contractAddress: addr,
contractName: "simple-nft-l1",
functionName: "get-owner",
functionArgs: [uintCV(5)],
network,
senderAddress,
};
const result = await callReadOnlyFunction(txOptions);
console.log(cvToString(result.value));
}
main();

View File

@@ -15,7 +15,7 @@ import { StacksTestnet, HIRO_MOCKNET_DEFAULT } from "@stacks/network";
async function main() {
const network = new StacksTestnet({ url: HIRO_MOCKNET_DEFAULT });
const subnetUrl = process.env.SUBNET_URL;
const senderKey = process.env.AUTH_SUBNET_MINER_KEY;
const senderKey = process.env.ALT_USER_KEY;
const addr = process.env.ALT_USER_ADDR;
const contractAddr = process.env.USER_ADDR;
const withdrawalBlockHeight = process.argv[2];
@@ -23,8 +23,9 @@ async function main() {
const withdrawalId = 0;
let json_merkle_entry = await fetch(
`${subnetUrl}/v2/withdrawal/nft/${withdrawalBlockHeight}/${addr}/${withdrawalId}/${contractAddr}/simple-nft-l2/nft-token/5`
`${subnetUrl}/v2/withdrawal/nft/${withdrawalBlockHeight}/${addr}/${withdrawalId}/${contractAddr}/simple-nft-l2/5`
).then((x) => x.json());
console.log(JSON.stringify(json_merkle_entry));
let cv_merkle_entry = {
withdrawal_leaf_hash: deserializeCV(json_merkle_entry.withdrawal_leaf_hash),
withdrawal_root: deserializeCV(json_merkle_entry.withdrawal_root),